summaryrefslogtreecommitdiffstats
path: root/drivers/isdn
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/isdn')
-rw-r--r--drivers/isdn/Kconfig64
-rw-r--r--drivers/isdn/Makefile17
-rw-r--r--drivers/isdn/act2000/Kconfig13
-rw-r--r--drivers/isdn/act2000/Makefile9
-rw-r--r--drivers/isdn/act2000/act2000.h202
-rw-r--r--drivers/isdn/act2000/act2000_isa.c443
-rw-r--r--drivers/isdn/act2000/act2000_isa.h136
-rw-r--r--drivers/isdn/act2000/capi.c1181
-rw-r--r--drivers/isdn/act2000/capi.h365
-rw-r--r--drivers/isdn/act2000/module.c813
-rw-r--r--drivers/isdn/capi/Kconfig62
-rw-r--r--drivers/isdn/capi/Makefile15
-rw-r--r--drivers/isdn/capi/capi.c1599
-rw-r--r--drivers/isdn/capi/capidrv.c2355
-rw-r--r--drivers/isdn/capi/capidrv.h140
-rw-r--r--drivers/isdn/capi/capifs.c223
-rw-r--r--drivers/isdn/capi/capifs.h11
-rw-r--r--drivers/isdn/capi/capilib.c200
-rw-r--r--drivers/isdn/capi/capiutil.c1014
-rw-r--r--drivers/isdn/capi/kcapi.c1033
-rw-r--r--drivers/isdn/capi/kcapi.h49
-rw-r--r--drivers/isdn/capi/kcapi_proc.c312
-rw-r--r--drivers/isdn/divert/Makefile9
-rw-r--r--drivers/isdn/divert/divert_init.c82
-rw-r--r--drivers/isdn/divert/divert_procfs.c316
-rw-r--r--drivers/isdn/divert/isdn_divert.c862
-rw-r--r--drivers/isdn/divert/isdn_divert.h132
-rw-r--r--drivers/isdn/gigaset/Kconfig54
-rw-r--r--drivers/isdn/gigaset/Makefile9
-rw-r--r--drivers/isdn/gigaset/asyncdata.c591
-rw-r--r--drivers/isdn/gigaset/bas-gigaset.c2580
-rw-r--r--drivers/isdn/gigaset/common.c1138
-rw-r--r--drivers/isdn/gigaset/ev-layer.c1978
-rw-r--r--drivers/isdn/gigaset/gigaset.h884
-rw-r--r--drivers/isdn/gigaset/i4l.c576
-rw-r--r--drivers/isdn/gigaset/interface.c726
-rw-r--r--drivers/isdn/gigaset/isocdata.c1011
-rw-r--r--drivers/isdn/gigaset/proc.c83
-rw-r--r--drivers/isdn/gigaset/ser-gigaset.c835
-rw-r--r--drivers/isdn/gigaset/usb-gigaset.c990
-rw-r--r--drivers/isdn/hardware/Kconfig9
-rw-r--r--drivers/isdn/hardware/Makefile7
-rw-r--r--drivers/isdn/hardware/avm/Kconfig63
-rw-r--r--drivers/isdn/hardware/avm/Makefile11
-rw-r--r--drivers/isdn/hardware/avm/avm_cs.c312
-rw-r--r--drivers/isdn/hardware/avm/avmcard.h583
-rw-r--r--drivers/isdn/hardware/avm/b1.c808
-rw-r--r--drivers/isdn/hardware/avm/b1dma.c990
-rw-r--r--drivers/isdn/hardware/avm/b1isa.c243
-rw-r--r--drivers/isdn/hardware/avm/b1pci.c416
-rw-r--r--drivers/isdn/hardware/avm/b1pcmcia.c224
-rw-r--r--drivers/isdn/hardware/avm/c4.c1320
-rw-r--r--drivers/isdn/hardware/avm/t1isa.c593
-rw-r--r--drivers/isdn/hardware/avm/t1pci.c259
-rw-r--r--drivers/isdn/hardware/eicon/Kconfig51
-rw-r--r--drivers/isdn/hardware/eicon/Makefile23
-rw-r--r--drivers/isdn/hardware/eicon/adapter.h17
-rw-r--r--drivers/isdn/hardware/eicon/capi20.h699
-rw-r--r--drivers/isdn/hardware/eicon/capidtmf.c685
-rw-r--r--drivers/isdn/hardware/eicon/capidtmf.h79
-rw-r--r--drivers/isdn/hardware/eicon/capifunc.c1219
-rw-r--r--drivers/isdn/hardware/eicon/capifunc.h40
-rw-r--r--drivers/isdn/hardware/eicon/capimain.c145
-rw-r--r--drivers/isdn/hardware/eicon/cardtype.h1098
-rw-r--r--drivers/isdn/hardware/eicon/cp_vers.h26
-rw-r--r--drivers/isdn/hardware/eicon/dadapter.c366
-rw-r--r--drivers/isdn/hardware/eicon/dadapter.h34
-rw-r--r--drivers/isdn/hardware/eicon/debug.c2133
-rw-r--r--drivers/isdn/hardware/eicon/debug_if.h90
-rw-r--r--drivers/isdn/hardware/eicon/debuglib.c156
-rw-r--r--drivers/isdn/hardware/eicon/debuglib.h322
-rw-r--r--drivers/isdn/hardware/eicon/dfifo.h54
-rw-r--r--drivers/isdn/hardware/eicon/di.c835
-rw-r--r--drivers/isdn/hardware/eicon/di.h118
-rw-r--r--drivers/isdn/hardware/eicon/di_dbg.h37
-rw-r--r--drivers/isdn/hardware/eicon/di_defs.h181
-rw-r--r--drivers/isdn/hardware/eicon/did_vers.h26
-rw-r--r--drivers/isdn/hardware/eicon/diddfunc.c115
-rw-r--r--drivers/isdn/hardware/eicon/diva.c657
-rw-r--r--drivers/isdn/hardware/eicon/diva.h31
-rw-r--r--drivers/isdn/hardware/eicon/diva_didd.c149
-rw-r--r--drivers/isdn/hardware/eicon/diva_dma.c94
-rw-r--r--drivers/isdn/hardware/eicon/diva_dma.h48
-rw-r--r--drivers/isdn/hardware/eicon/diva_pci.h19
-rw-r--r--drivers/isdn/hardware/eicon/divacapi.h1360
-rw-r--r--drivers/isdn/hardware/eicon/divamnt.c257
-rw-r--r--drivers/isdn/hardware/eicon/divasfunc.c238
-rw-r--r--drivers/isdn/hardware/eicon/divasi.c578
-rw-r--r--drivers/isdn/hardware/eicon/divasmain.c850
-rw-r--r--drivers/isdn/hardware/eicon/divasproc.c436
-rw-r--r--drivers/isdn/hardware/eicon/divasync.h489
-rw-r--r--drivers/isdn/hardware/eicon/dqueue.c110
-rw-r--r--drivers/isdn/hardware/eicon/dqueue.h31
-rw-r--r--drivers/isdn/hardware/eicon/dsp_defs.h301
-rw-r--r--drivers/isdn/hardware/eicon/dsp_tst.h47
-rw-r--r--drivers/isdn/hardware/eicon/dspdids.h75
-rw-r--r--drivers/isdn/hardware/eicon/dsrv4bri.h40
-rw-r--r--drivers/isdn/hardware/eicon/dsrv_bri.h37
-rw-r--r--drivers/isdn/hardware/eicon/dsrv_pri.h38
-rw-r--r--drivers/isdn/hardware/eicon/entity.h28
-rw-r--r--drivers/isdn/hardware/eicon/helpers.h51
-rw-r--r--drivers/isdn/hardware/eicon/idifunc.c268
-rw-r--r--drivers/isdn/hardware/eicon/io.c852
-rw-r--r--drivers/isdn/hardware/eicon/io.h308
-rw-r--r--drivers/isdn/hardware/eicon/istream.c226
-rw-r--r--drivers/isdn/hardware/eicon/kst_ifc.h336
-rw-r--r--drivers/isdn/hardware/eicon/maintidi.c2194
-rw-r--r--drivers/isdn/hardware/eicon/maintidi.h172
-rw-r--r--drivers/isdn/hardware/eicon/man_defs.h133
-rw-r--r--drivers/isdn/hardware/eicon/mdm_msg.h346
-rw-r--r--drivers/isdn/hardware/eicon/message.c15045
-rw-r--r--drivers/isdn/hardware/eicon/mi_pc.h204
-rw-r--r--drivers/isdn/hardware/eicon/mntfunc.c370
-rw-r--r--drivers/isdn/hardware/eicon/os_4bri.c1132
-rw-r--r--drivers/isdn/hardware/eicon/os_4bri.h8
-rw-r--r--drivers/isdn/hardware/eicon/os_bri.c814
-rw-r--r--drivers/isdn/hardware/eicon/os_bri.h8
-rw-r--r--drivers/isdn/hardware/eicon/os_capi.h21
-rw-r--r--drivers/isdn/hardware/eicon/os_pri.c1052
-rw-r--r--drivers/isdn/hardware/eicon/os_pri.h8
-rw-r--r--drivers/isdn/hardware/eicon/pc.h738
-rw-r--r--drivers/isdn/hardware/eicon/pc_init.h267
-rw-r--r--drivers/isdn/hardware/eicon/pc_maint.h160
-rw-r--r--drivers/isdn/hardware/eicon/pkmaint.h44
-rw-r--r--drivers/isdn/hardware/eicon/platform.h372
-rw-r--r--drivers/isdn/hardware/eicon/pr_pc.h76
-rw-r--r--drivers/isdn/hardware/eicon/s_4bri.c510
-rw-r--r--drivers/isdn/hardware/eicon/s_bri.c191
-rw-r--r--drivers/isdn/hardware/eicon/s_pri.c205
-rw-r--r--drivers/isdn/hardware/eicon/sdp_hdr.h117
-rw-r--r--drivers/isdn/hardware/eicon/um_idi.c885
-rw-r--r--drivers/isdn/hardware/eicon/um_idi.h43
-rw-r--r--drivers/isdn/hardware/eicon/um_xdi.h68
-rw-r--r--drivers/isdn/hardware/eicon/xdi_adapter.h70
-rw-r--r--drivers/isdn/hardware/eicon/xdi_msg.h127
-rw-r--r--drivers/isdn/hardware/eicon/xdi_vers.h26
-rw-r--r--drivers/isdn/hardware/mISDN/Kconfig25
-rw-r--r--drivers/isdn/hardware/mISDN/Makefile7
-rw-r--r--drivers/isdn/hardware/mISDN/hfc_multi.h1204
-rw-r--r--drivers/isdn/hardware/mISDN/hfc_pci.h228
-rw-r--r--drivers/isdn/hardware/mISDN/hfcmulti.c5319
-rw-r--r--drivers/isdn/hardware/mISDN/hfcpci.c2254
-rw-r--r--drivers/isdn/hisax/Kconfig434
-rw-r--r--drivers/isdn/hisax/Makefile63
-rw-r--r--drivers/isdn/hisax/amd7930_fn.c796
-rw-r--r--drivers/isdn/hisax/amd7930_fn.h37
-rw-r--r--drivers/isdn/hisax/arcofi.c134
-rw-r--r--drivers/isdn/hisax/arcofi.h27
-rw-r--r--drivers/isdn/hisax/asuscom.c424
-rw-r--r--drivers/isdn/hisax/avm_a1.c308
-rw-r--r--drivers/isdn/hisax/avm_a1p.c267
-rw-r--r--drivers/isdn/hisax/avm_pci.c902
-rw-r--r--drivers/isdn/hisax/avma1_cs.c323
-rw-r--r--drivers/isdn/hisax/bkm_a4t.c362
-rw-r--r--drivers/isdn/hisax/bkm_a8.c435
-rw-r--r--drivers/isdn/hisax/bkm_ax.h119
-rw-r--r--drivers/isdn/hisax/callc.c1793
-rw-r--r--drivers/isdn/hisax/config.c1995
-rw-r--r--drivers/isdn/hisax/diva.c1283
-rw-r--r--drivers/isdn/hisax/elsa.c1250
-rw-r--r--drivers/isdn/hisax/elsa_cs.c371
-rw-r--r--drivers/isdn/hisax/elsa_ser.c658
-rw-r--r--drivers/isdn/hisax/enternow_pci.c424
-rw-r--r--drivers/isdn/hisax/fsm.c161
-rw-r--r--drivers/isdn/hisax/fsm.h61
-rw-r--r--drivers/isdn/hisax/gazel.c690
-rw-r--r--drivers/isdn/hisax/hfc4s8s_l1.c1715
-rw-r--r--drivers/isdn/hisax/hfc4s8s_l1.h88
-rw-r--r--drivers/isdn/hisax/hfc_2bds0.c1078
-rw-r--r--drivers/isdn/hisax/hfc_2bds0.h128
-rw-r--r--drivers/isdn/hisax/hfc_2bs0.c589
-rw-r--r--drivers/isdn/hisax/hfc_2bs0.h60
-rw-r--r--drivers/isdn/hisax/hfc_pci.c1744
-rw-r--r--drivers/isdn/hisax/hfc_pci.h235
-rw-r--r--drivers/isdn/hisax/hfc_sx.c1520
-rw-r--r--drivers/isdn/hisax/hfc_sx.h196
-rw-r--r--drivers/isdn/hisax/hfc_usb.c1608
-rw-r--r--drivers/isdn/hisax/hfc_usb.h207
-rw-r--r--drivers/isdn/hisax/hfcscard.c265
-rw-r--r--drivers/isdn/hisax/hisax.h1316
-rw-r--r--drivers/isdn/hisax/hisax_cfg.h66
-rw-r--r--drivers/isdn/hisax/hisax_debug.h80
-rw-r--r--drivers/isdn/hisax/hisax_fcpcipnp.c1023
-rw-r--r--drivers/isdn/hisax/hisax_fcpcipnp.h58
-rw-r--r--drivers/isdn/hisax/hisax_if.h66
-rw-r--r--drivers/isdn/hisax/hisax_isac.c894
-rw-r--r--drivers/isdn/hisax/hisax_isac.h45
-rw-r--r--drivers/isdn/hisax/hscx.c276
-rw-r--r--drivers/isdn/hisax/hscx.h41
-rw-r--r--drivers/isdn/hisax/hscx_irq.c292
-rw-r--r--drivers/isdn/hisax/icc.c683
-rw-r--r--drivers/isdn/hisax/icc.h72
-rw-r--r--drivers/isdn/hisax/ipac.h29
-rw-r--r--drivers/isdn/hisax/ipacx.c913
-rw-r--r--drivers/isdn/hisax/ipacx.h162
-rw-r--r--drivers/isdn/hisax/isac.c680
-rw-r--r--drivers/isdn/hisax/isac.h70
-rw-r--r--drivers/isdn/hisax/isar.c1909
-rw-r--r--drivers/isdn/hisax/isar.h222
-rw-r--r--drivers/isdn/hisax/isdnhdlc.c603
-rw-r--r--drivers/isdn/hisax/isdnhdlc.h70
-rw-r--r--drivers/isdn/hisax/isdnl1.c935
-rw-r--r--drivers/isdn/hisax/isdnl1.h31
-rw-r--r--drivers/isdn/hisax/isdnl2.c1846
-rw-r--r--drivers/isdn/hisax/isdnl2.h26
-rw-r--r--drivers/isdn/hisax/isdnl3.c597
-rw-r--r--drivers/isdn/hisax/isdnl3.h43
-rw-r--r--drivers/isdn/hisax/isurf.c301
-rw-r--r--drivers/isdn/hisax/ix1_micro.c317
-rw-r--r--drivers/isdn/hisax/jade.c314
-rw-r--r--drivers/isdn/hisax/jade.h134
-rw-r--r--drivers/isdn/hisax/jade_irq.c236
-rw-r--r--drivers/isdn/hisax/l3_1tr6.c955
-rw-r--r--drivers/isdn/hisax/l3_1tr6.h164
-rw-r--r--drivers/isdn/hisax/l3dss1.c3237
-rw-r--r--drivers/isdn/hisax/l3dss1.h124
-rw-r--r--drivers/isdn/hisax/l3ni1.c3189
-rw-r--r--drivers/isdn/hisax/l3ni1.h136
-rw-r--r--drivers/isdn/hisax/lmgr.c50
-rw-r--r--drivers/isdn/hisax/mic.c236
-rw-r--r--drivers/isdn/hisax/netjet.c981
-rw-r--r--drivers/isdn/hisax/netjet.h70
-rw-r--r--drivers/isdn/hisax/niccy.c380
-rw-r--r--drivers/isdn/hisax/nj_s.c298
-rw-r--r--drivers/isdn/hisax/nj_u.c262
-rw-r--r--drivers/isdn/hisax/q931.c1522
-rw-r--r--drivers/isdn/hisax/s0box.c261
-rw-r--r--drivers/isdn/hisax/saphir.c298
-rw-r--r--drivers/isdn/hisax/sedlbauer.c880
-rw-r--r--drivers/isdn/hisax/sedlbauer_cs.c479
-rw-r--r--drivers/isdn/hisax/sportster.c269
-rw-r--r--drivers/isdn/hisax/st5481.h530
-rw-r--r--drivers/isdn/hisax/st5481_b.c377
-rw-r--r--drivers/isdn/hisax/st5481_d.c778
-rw-r--r--drivers/isdn/hisax/st5481_init.c224
-rw-r--r--drivers/isdn/hisax/st5481_usb.c661
-rw-r--r--drivers/isdn/hisax/tei.c466
-rw-r--r--drivers/isdn/hisax/teleint.c336
-rw-r--r--drivers/isdn/hisax/teles0.c367
-rw-r--r--drivers/isdn/hisax/teles3.c500
-rw-r--r--drivers/isdn/hisax/teles_cs.c363
-rw-r--r--drivers/isdn/hisax/telespci.c354
-rw-r--r--drivers/isdn/hisax/w6692.c1086
-rw-r--r--drivers/isdn/hisax/w6692.h184
-rw-r--r--drivers/isdn/hysdn/Kconfig18
-rw-r--r--drivers/isdn/hysdn/Makefile11
-rw-r--r--drivers/isdn/hysdn/boardergo.c445
-rw-r--r--drivers/isdn/hysdn/boardergo.h100
-rw-r--r--drivers/isdn/hysdn/hycapi.c798
-rw-r--r--drivers/isdn/hysdn/hysdn_boot.c398
-rw-r--r--drivers/isdn/hysdn/hysdn_defs.h284
-rw-r--r--drivers/isdn/hysdn/hysdn_init.c237
-rw-r--r--drivers/isdn/hysdn/hysdn_net.c344
-rw-r--r--drivers/isdn/hysdn/hysdn_pof.h78
-rw-r--r--drivers/isdn/hysdn/hysdn_procconf.c429
-rw-r--r--drivers/isdn/hysdn/hysdn_proclog.c433
-rw-r--r--drivers/isdn/hysdn/hysdn_sched.c197
-rw-r--r--drivers/isdn/hysdn/ince1pc.h134
-rw-r--r--drivers/isdn/i4l/Kconfig139
-rw-r--r--drivers/isdn/i4l/Makefile18
-rw-r--r--drivers/isdn/i4l/isdn_audio.c710
-rw-r--r--drivers/isdn/i4l/isdn_audio.h44
-rw-r--r--drivers/isdn/i4l/isdn_bsdcomp.c929
-rw-r--r--drivers/isdn/i4l/isdn_common.c2390
-rw-r--r--drivers/isdn/i4l/isdn_common.h47
-rw-r--r--drivers/isdn/i4l/isdn_concap.c99
-rw-r--r--drivers/isdn/i4l/isdn_concap.h13
-rw-r--r--drivers/isdn/i4l/isdn_net.c3210
-rw-r--r--drivers/isdn/i4l/isdn_net.h190
-rw-r--r--drivers/isdn/i4l/isdn_ppp.c3042
-rw-r--r--drivers/isdn/i4l/isdn_ppp.h43
-rw-r--r--drivers/isdn/i4l/isdn_tty.c3895
-rw-r--r--drivers/isdn/i4l/isdn_tty.h120
-rw-r--r--drivers/isdn/i4l/isdn_ttyfax.c1123
-rw-r--r--drivers/isdn/i4l/isdn_ttyfax.h18
-rw-r--r--drivers/isdn/i4l/isdn_v110.c616
-rw-r--r--drivers/isdn/i4l/isdn_v110.h29
-rw-r--r--drivers/isdn/i4l/isdn_x25iface.c330
-rw-r--r--drivers/isdn/i4l/isdn_x25iface.h39
-rw-r--r--drivers/isdn/icn/Kconfig16
-rw-r--r--drivers/isdn/icn/Makefile5
-rw-r--r--drivers/isdn/icn/icn.c1692
-rw-r--r--drivers/isdn/icn/icn.h253
-rw-r--r--drivers/isdn/isdnloop/Makefile5
-rw-r--r--drivers/isdn/isdnloop/isdnloop.c1541
-rw-r--r--drivers/isdn/isdnloop/isdnloop.h112
-rw-r--r--drivers/isdn/mISDN/Kconfig44
-rw-r--r--drivers/isdn/mISDN/Makefile13
-rw-r--r--drivers/isdn/mISDN/core.c244
-rw-r--r--drivers/isdn/mISDN/core.h77
-rw-r--r--drivers/isdn/mISDN/dsp.h263
-rw-r--r--drivers/isdn/mISDN/dsp_audio.c434
-rw-r--r--drivers/isdn/mISDN/dsp_biquad.h65
-rw-r--r--drivers/isdn/mISDN/dsp_blowfish.c672
-rw-r--r--drivers/isdn/mISDN/dsp_cmx.c1886
-rw-r--r--drivers/isdn/mISDN/dsp_core.c1191
-rw-r--r--drivers/isdn/mISDN/dsp_dtmf.c303
-rw-r--r--drivers/isdn/mISDN/dsp_ecdis.h110
-rw-r--r--drivers/isdn/mISDN/dsp_hwec.c138
-rw-r--r--drivers/isdn/mISDN/dsp_hwec.h10
-rw-r--r--drivers/isdn/mISDN/dsp_pipeline.c348
-rw-r--r--drivers/isdn/mISDN/dsp_tones.c551
-rw-r--r--drivers/isdn/mISDN/fsm.c183
-rw-r--r--drivers/isdn/mISDN/fsm.h67
-rw-r--r--drivers/isdn/mISDN/hwchannel.c365
-rw-r--r--drivers/isdn/mISDN/l1oip.h91
-rw-r--r--drivers/isdn/mISDN/l1oip_codec.c374
-rw-r--r--drivers/isdn/mISDN/l1oip_core.c1516
-rw-r--r--drivers/isdn/mISDN/layer1.c403
-rw-r--r--drivers/isdn/mISDN/layer1.h26
-rw-r--r--drivers/isdn/mISDN/layer2.c2216
-rw-r--r--drivers/isdn/mISDN/layer2.h140
-rw-r--r--drivers/isdn/mISDN/socket.c781
-rw-r--r--drivers/isdn/mISDN/stack.c674
-rw-r--r--drivers/isdn/mISDN/tei.c1340
-rw-r--r--drivers/isdn/mISDN/timerdev.c291
-rw-r--r--drivers/isdn/pcbit/Kconfig14
-rw-r--r--drivers/isdn/pcbit/Makefile9
-rw-r--r--drivers/isdn/pcbit/callbacks.c349
-rw-r--r--drivers/isdn/pcbit/callbacks.h46
-rw-r--r--drivers/isdn/pcbit/capi.c654
-rw-r--r--drivers/isdn/pcbit/capi.h87
-rw-r--r--drivers/isdn/pcbit/drv.c1080
-rw-r--r--drivers/isdn/pcbit/edss1.c318
-rw-r--r--drivers/isdn/pcbit/edss1.h102
-rw-r--r--drivers/isdn/pcbit/layer2.c715
-rw-r--r--drivers/isdn/pcbit/layer2.h288
-rw-r--r--drivers/isdn/pcbit/module.c126
-rw-r--r--drivers/isdn/pcbit/pcbit.h177
-rw-r--r--drivers/isdn/sc/Kconfig12
-rw-r--r--drivers/isdn/sc/Makefile10
-rw-r--r--drivers/isdn/sc/card.h131
-rw-r--r--drivers/isdn/sc/command.c363
-rw-r--r--drivers/isdn/sc/event.c68
-rw-r--r--drivers/isdn/sc/hardware.h110
-rw-r--r--drivers/isdn/sc/includes.h16
-rw-r--r--drivers/isdn/sc/init.c550
-rw-r--r--drivers/isdn/sc/interrupt.c237
-rw-r--r--drivers/isdn/sc/ioctl.c593
-rw-r--r--drivers/isdn/sc/message.c230
-rw-r--r--drivers/isdn/sc/message.h245
-rw-r--r--drivers/isdn/sc/packet.c205
-rw-r--r--drivers/isdn/sc/scioc.h111
-rw-r--r--drivers/isdn/sc/shmem.c138
-rw-r--r--drivers/isdn/sc/timer.c123
345 files changed, 183510 insertions, 0 deletions
diff --git a/drivers/isdn/Kconfig b/drivers/isdn/Kconfig
new file mode 100644
index 0000000..3d113c6
--- /dev/null
+++ b/drivers/isdn/Kconfig
@@ -0,0 +1,64 @@
+#
+# ISDN device configuration
+#
+
+menuconfig ISDN
+ bool "ISDN support"
+ depends on NET
+ depends on !S390
+ ---help---
+ ISDN ("Integrated Services Digital Networks", called RNIS in France)
+ is a special type of fully digital telephone service; it's mostly
+ used to connect to your Internet service provider (with SLIP or
+ PPP). The main advantage is that the speed is higher than ordinary
+ modem/telephone connections, and that you can have voice
+ conversations while downloading stuff. It only works if your
+ computer is equipped with an ISDN card and both you and your service
+ provider purchased an ISDN line from the phone company. For
+ details, read <http://www.alumni.caltech.edu/~dank/isdn/> on the WWW.
+
+ Select this option if you want your kernel to support ISDN.
+
+if ISDN
+
+source "drivers/isdn/mISDN/Kconfig"
+
+menuconfig ISDN_I4L
+ tristate "Old ISDN4Linux (deprecated)"
+ ---help---
+ This driver allows you to use an ISDN adapter for networking
+ connections and as dialin/out device. The isdn-tty's have a built
+ in AT-compatible modem emulator. Network devices support autodial,
+ channel-bundling, callback and caller-authentication without having
+ a daemon running. A reduced T.70 protocol is supported with tty's
+ suitable for German BTX. On D-Channel, the protocols EDSS1
+ (Euro-ISDN) and 1TR6 (German style) are supported. See
+ <file:Documentation/isdn/README> for more information.
+
+ ISDN support in the linux kernel is moving towards a new API,
+ called CAPI (Common ISDN Application Programming Interface).
+ Therefore the old ISDN4Linux layer will eventually become obsolete.
+ It is still available, though, for use with adapters that are not
+ supported by the new CAPI subsystem yet.
+
+if ISDN_I4L
+source "drivers/isdn/i4l/Kconfig"
+endif
+
+menuconfig ISDN_CAPI
+ tristate "CAPI 2.0 subsystem"
+ help
+ This provides the CAPI (Common ISDN Application Programming
+ Interface, a standard making it easy for programs to access ISDN
+ hardware, see <http://www.capi.org/>. This is needed for AVM's set
+ of active ISDN controllers like B1, T1, M1.
+
+if ISDN_CAPI
+
+source "drivers/isdn/capi/Kconfig"
+
+source "drivers/isdn/hardware/Kconfig"
+
+endif # ISDN_CAPI
+
+endif # ISDN
diff --git a/drivers/isdn/Makefile b/drivers/isdn/Makefile
new file mode 100644
index 0000000..f1f7775
--- /dev/null
+++ b/drivers/isdn/Makefile
@@ -0,0 +1,17 @@
+# Makefile for the kernel ISDN subsystem and device drivers.
+
+# Object files in subdirectories
+
+obj-$(CONFIG_ISDN_I4L) += i4l/
+obj-$(CONFIG_ISDN_CAPI) += capi/
+obj-$(CONFIG_MISDN) += mISDN/
+obj-$(CONFIG_ISDN) += hardware/
+obj-$(CONFIG_ISDN_DIVERSION) += divert/
+obj-$(CONFIG_ISDN_DRV_HISAX) += hisax/
+obj-$(CONFIG_ISDN_DRV_ICN) += icn/
+obj-$(CONFIG_ISDN_DRV_PCBIT) += pcbit/
+obj-$(CONFIG_ISDN_DRV_SC) += sc/
+obj-$(CONFIG_ISDN_DRV_LOOP) += isdnloop/
+obj-$(CONFIG_ISDN_DRV_ACT2000) += act2000/
+obj-$(CONFIG_HYSDN) += hysdn/
+obj-$(CONFIG_ISDN_DRV_GIGASET) += gigaset/
diff --git a/drivers/isdn/act2000/Kconfig b/drivers/isdn/act2000/Kconfig
new file mode 100644
index 0000000..3fc1a54
--- /dev/null
+++ b/drivers/isdn/act2000/Kconfig
@@ -0,0 +1,13 @@
+#
+# Config.in for IBM Active 2000 ISDN driver
+#
+config ISDN_DRV_ACT2000
+ tristate "IBM Active 2000 support"
+ depends on ISA
+ help
+ Say Y here if you have an IBM Active 2000 ISDN card. In order to use
+ this card, additional firmware is necessary, which has to be loaded
+ into the card using a utility which is part of the latest
+ isdn4k-utils package. Please read the file
+ <file:Documentation/isdn/README.act2000> for more information.
+
diff --git a/drivers/isdn/act2000/Makefile b/drivers/isdn/act2000/Makefile
new file mode 100644
index 0000000..05e582f
--- /dev/null
+++ b/drivers/isdn/act2000/Makefile
@@ -0,0 +1,9 @@
+# Makefile for the act2000 ISDN device driver
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_ISDN_DRV_ACT2000) += act2000.o
+
+# Multipart objects.
+
+act2000-y := module.o capi.o act2000_isa.o
diff --git a/drivers/isdn/act2000/act2000.h b/drivers/isdn/act2000/act2000.h
new file mode 100644
index 0000000..d4c5051
--- /dev/null
+++ b/drivers/isdn/act2000/act2000.h
@@ -0,0 +1,202 @@
+/* $Id: act2000.h,v 1.8.6.3 2001/09/23 22:24:32 kai Exp $
+ *
+ * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000.
+ *
+ * Author Fritz Elfert
+ * Copyright by Fritz Elfert <fritz@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to Friedemann Baitinger and IBM Germany
+ *
+ */
+
+#ifndef act2000_h
+#define act2000_h
+
+#include <linux/compiler.h>
+
+#define ACT2000_IOCTL_SETPORT 1
+#define ACT2000_IOCTL_GETPORT 2
+#define ACT2000_IOCTL_SETIRQ 3
+#define ACT2000_IOCTL_GETIRQ 4
+#define ACT2000_IOCTL_SETBUS 5
+#define ACT2000_IOCTL_GETBUS 6
+#define ACT2000_IOCTL_SETPROTO 7
+#define ACT2000_IOCTL_GETPROTO 8
+#define ACT2000_IOCTL_SETMSN 9
+#define ACT2000_IOCTL_GETMSN 10
+#define ACT2000_IOCTL_LOADBOOT 11
+#define ACT2000_IOCTL_ADDCARD 12
+
+#define ACT2000_IOCTL_TEST 98
+#define ACT2000_IOCTL_DEBUGVAR 99
+
+#define ACT2000_BUS_ISA 1
+#define ACT2000_BUS_MCA 2
+#define ACT2000_BUS_PCMCIA 3
+
+/* Struct for adding new cards */
+typedef struct act2000_cdef {
+ int bus;
+ int port;
+ int irq;
+ char id[10];
+} act2000_cdef;
+
+/* Struct for downloading firmware */
+typedef struct act2000_ddef {
+ int length; /* Length of code */
+ char __user *buffer; /* Ptr. to code */
+} act2000_ddef;
+
+typedef struct act2000_fwid {
+ char isdn[4];
+ char revlen[2];
+ char revision[504];
+} act2000_fwid;
+
+#if defined(__KERNEL__) || defined(__DEBUGVAR__)
+
+#ifdef __KERNEL__
+/* Kernel includes */
+
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#include <linux/skbuff.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <asm/io.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+#include <linux/isdnif.h>
+
+#endif /* __KERNEL__ */
+
+#define ACT2000_PORTLEN 8
+
+#define ACT2000_FLAGS_RUNNING 1 /* Cards driver activated */
+#define ACT2000_FLAGS_PVALID 2 /* Cards port is valid */
+#define ACT2000_FLAGS_IVALID 4 /* Cards irq is valid */
+#define ACT2000_FLAGS_LOADED 8 /* Firmware loaded */
+
+#define ACT2000_BCH 2 /* # of channels per card */
+
+/* D-Channel states */
+#define ACT2000_STATE_NULL 0
+#define ACT2000_STATE_ICALL 1
+#define ACT2000_STATE_OCALL 2
+#define ACT2000_STATE_IWAIT 3
+#define ACT2000_STATE_OWAIT 4
+#define ACT2000_STATE_IBWAIT 5
+#define ACT2000_STATE_OBWAIT 6
+#define ACT2000_STATE_BWAIT 7
+#define ACT2000_STATE_BHWAIT 8
+#define ACT2000_STATE_BHWAIT2 9
+#define ACT2000_STATE_DHWAIT 10
+#define ACT2000_STATE_DHWAIT2 11
+#define ACT2000_STATE_BSETUP 12
+#define ACT2000_STATE_ACTIVE 13
+
+#define ACT2000_MAX_QUEUED 8000 /* 2 * maxbuff */
+
+#define ACT2000_LOCK_TX 0
+#define ACT2000_LOCK_RX 1
+
+typedef struct act2000_chan {
+ unsigned short callref; /* Call Reference */
+ unsigned short fsm_state; /* Current D-Channel state */
+ unsigned short eazmask; /* EAZ-Mask for this Channel */
+ short queued; /* User-Data Bytes in TX queue */
+ unsigned short plci;
+ unsigned short ncci;
+ unsigned char l2prot; /* Layer 2 protocol */
+ unsigned char l3prot; /* Layer 3 protocol */
+} act2000_chan;
+
+typedef struct msn_entry {
+ char eaz;
+ char msn[16];
+ struct msn_entry * next;
+} msn_entry;
+
+typedef struct irq_data_isa {
+ __u8 *rcvptr;
+ __u16 rcvidx;
+ __u16 rcvlen;
+ struct sk_buff *rcvskb;
+ __u8 rcvignore;
+ __u8 rcvhdr[8];
+} irq_data_isa;
+
+typedef union irq_data {
+ irq_data_isa isa;
+} irq_data;
+
+/*
+ * Per card driver data
+ */
+typedef struct act2000_card {
+ unsigned short port; /* Base-port-address */
+ unsigned short irq; /* Interrupt */
+ u_char ptype; /* Protocol type (1TR6 or Euro) */
+ u_char bus; /* Cardtype (ISA, MCA, PCMCIA) */
+ struct act2000_card *next; /* Pointer to next device struct */
+ spinlock_t lock; /* protect critical operations */
+ int myid; /* Driver-Nr. assigned by linklevel */
+ unsigned long flags; /* Statusflags */
+ unsigned long ilock; /* Semaphores for IRQ-Routines */
+ struct sk_buff_head rcvq; /* Receive-Message queue */
+ struct sk_buff_head sndq; /* Send-Message queue */
+ struct sk_buff_head ackq; /* Data-Ack-Message queue */
+ u_char *ack_msg; /* Ptr to User Data in User skb */
+ __u16 need_b3ack; /* Flag: Need ACK for current skb */
+ struct sk_buff *sbuf; /* skb which is currently sent */
+ struct timer_list ptimer; /* Poll timer */
+ struct work_struct snd_tq; /* Task struct for xmit bh */
+ struct work_struct rcv_tq; /* Task struct for rcv bh */
+ struct work_struct poll_tq; /* Task struct for polled rcv bh */
+ msn_entry *msn_list;
+ unsigned short msgnum; /* Message number for sending */
+ spinlock_t mnlock; /* lock for msgnum */
+ act2000_chan bch[ACT2000_BCH]; /* B-Channel status/control */
+ char status_buf[256]; /* Buffer for status messages */
+ char *status_buf_read;
+ char *status_buf_write;
+ char *status_buf_end;
+ irq_data idat; /* Data used for IRQ handler */
+ isdn_if interface; /* Interface to upper layer */
+ char regname[35]; /* Name used for request_region */
+} act2000_card;
+
+static inline void act2000_schedule_tx(act2000_card *card)
+{
+ schedule_work(&card->snd_tq);
+}
+
+static inline void act2000_schedule_rx(act2000_card *card)
+{
+ schedule_work(&card->rcv_tq);
+}
+
+static inline void act2000_schedule_poll(act2000_card *card)
+{
+ schedule_work(&card->poll_tq);
+}
+
+extern char *act2000_find_eaz(act2000_card *, char);
+
+#endif /* defined(__KERNEL__) || defined(__DEBUGVAR__) */
+#endif /* act2000_h */
diff --git a/drivers/isdn/act2000/act2000_isa.c b/drivers/isdn/act2000/act2000_isa.c
new file mode 100644
index 0000000..1bd8960
--- /dev/null
+++ b/drivers/isdn/act2000/act2000_isa.c
@@ -0,0 +1,443 @@
+/* $Id: act2000_isa.c,v 1.11.6.3 2001/09/23 22:24:32 kai Exp $
+ *
+ * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000 (ISA-Version).
+ *
+ * Author Fritz Elfert
+ * Copyright by Fritz Elfert <fritz@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to Friedemann Baitinger and IBM Germany
+ *
+ */
+
+#include "act2000.h"
+#include "act2000_isa.h"
+#include "capi.h"
+
+/*
+ * Reset Controller, then try to read the Card's signature.
+ + Return:
+ * 1 = Signature found.
+ * 0 = Signature not found.
+ */
+static int
+act2000_isa_reset(unsigned short portbase)
+{
+ unsigned char reg;
+ int i;
+ int found;
+ int serial = 0;
+
+ found = 0;
+ if ((reg = inb(portbase + ISA_COR)) != 0xff) {
+ outb(reg | ISA_COR_RESET, portbase + ISA_COR);
+ mdelay(10);
+ outb(reg, portbase + ISA_COR);
+ mdelay(10);
+
+ for (i = 0; i < 16; i++) {
+ if (inb(portbase + ISA_ISR) & ISA_ISR_SERIAL)
+ serial |= 0x10000;
+ serial >>= 1;
+ }
+ if (serial == ISA_SER_ID)
+ found++;
+ }
+ return found;
+}
+
+int
+act2000_isa_detect(unsigned short portbase)
+{
+ int ret = 0;
+
+ if (request_region(portbase, ACT2000_PORTLEN, "act2000isa")) {
+ ret = act2000_isa_reset(portbase);
+ release_region(portbase, ISA_REGION);
+ }
+ return ret;
+}
+
+static irqreturn_t
+act2000_isa_interrupt(int dummy, void *dev_id)
+{
+ act2000_card *card = dev_id;
+ u_char istatus;
+
+ istatus = (inb(ISA_PORT_ISR) & 0x07);
+ if (istatus & ISA_ISR_OUT) {
+ /* RX fifo has data */
+ istatus &= ISA_ISR_OUT_MASK;
+ outb(0, ISA_PORT_SIS);
+ act2000_isa_receive(card);
+ outb(ISA_SIS_INT, ISA_PORT_SIS);
+ }
+ if (istatus & ISA_ISR_ERR) {
+ /* Error Interrupt */
+ istatus &= ISA_ISR_ERR_MASK;
+ printk(KERN_WARNING "act2000: errIRQ\n");
+ }
+ if (istatus)
+ printk(KERN_DEBUG "act2000: ?IRQ %d %02x\n", card->irq, istatus);
+ return IRQ_HANDLED;
+}
+
+static void
+act2000_isa_select_irq(act2000_card * card)
+{
+ unsigned char reg;
+
+ reg = (inb(ISA_PORT_COR) & ~ISA_COR_IRQOFF) | ISA_COR_PERR;
+ switch (card->irq) {
+ case 3:
+ reg = ISA_COR_IRQ03;
+ break;
+ case 5:
+ reg = ISA_COR_IRQ05;
+ break;
+ case 7:
+ reg = ISA_COR_IRQ07;
+ break;
+ case 10:
+ reg = ISA_COR_IRQ10;
+ break;
+ case 11:
+ reg = ISA_COR_IRQ11;
+ break;
+ case 12:
+ reg = ISA_COR_IRQ12;
+ break;
+ case 15:
+ reg = ISA_COR_IRQ15;
+ break;
+ }
+ outb(reg, ISA_PORT_COR);
+}
+
+static void
+act2000_isa_enable_irq(act2000_card * card)
+{
+ act2000_isa_select_irq(card);
+ /* Enable READ irq */
+ outb(ISA_SIS_INT, ISA_PORT_SIS);
+}
+
+/*
+ * Install interrupt handler, enable irq on card.
+ * If irq is -1, choose next free irq, else irq is given explicitly.
+ */
+int
+act2000_isa_config_irq(act2000_card * card, short irq)
+{
+ int old_irq;
+
+ if (card->flags & ACT2000_FLAGS_IVALID) {
+ free_irq(card->irq, card);
+ }
+ card->flags &= ~ACT2000_FLAGS_IVALID;
+ outb(ISA_COR_IRQOFF, ISA_PORT_COR);
+ if (!irq)
+ return 0;
+
+ old_irq = card->irq;
+ card->irq = irq;
+ if (request_irq(irq, &act2000_isa_interrupt, 0, card->regname, card)) {
+ card->irq = old_irq;
+ card->flags |= ACT2000_FLAGS_IVALID;
+ printk(KERN_WARNING
+ "act2000: Could not request irq %d\n",irq);
+ return -EBUSY;
+ } else {
+ act2000_isa_select_irq(card);
+ /* Disable READ and WRITE irq */
+ outb(0, ISA_PORT_SIS);
+ outb(0, ISA_PORT_SOS);
+ }
+ return 0;
+}
+
+int
+act2000_isa_config_port(act2000_card * card, unsigned short portbase)
+{
+ if (card->flags & ACT2000_FLAGS_PVALID) {
+ release_region(card->port, ISA_REGION);
+ card->flags &= ~ACT2000_FLAGS_PVALID;
+ }
+ if (request_region(portbase, ACT2000_PORTLEN, card->regname) == NULL)
+ return -EBUSY;
+ else {
+ card->port = portbase;
+ card->flags |= ACT2000_FLAGS_PVALID;
+ return 0;
+ }
+}
+
+/*
+ * Release ressources, used by an adaptor.
+ */
+void
+act2000_isa_release(act2000_card * card)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&card->lock, flags);
+ if (card->flags & ACT2000_FLAGS_IVALID)
+ free_irq(card->irq, card);
+
+ card->flags &= ~ACT2000_FLAGS_IVALID;
+ if (card->flags & ACT2000_FLAGS_PVALID)
+ release_region(card->port, ISA_REGION);
+ card->flags &= ~ACT2000_FLAGS_PVALID;
+ spin_unlock_irqrestore(&card->lock, flags);
+}
+
+static int
+act2000_isa_writeb(act2000_card * card, u_char data)
+{
+ u_char timeout = 40;
+
+ while (timeout) {
+ if (inb(ISA_PORT_SOS) & ISA_SOS_READY) {
+ outb(data, ISA_PORT_SDO);
+ return 0;
+ } else {
+ timeout--;
+ udelay(10);
+ }
+ }
+ return 1;
+}
+
+static int
+act2000_isa_readb(act2000_card * card, u_char * data)
+{
+ u_char timeout = 40;
+
+ while (timeout) {
+ if (inb(ISA_PORT_SIS) & ISA_SIS_READY) {
+ *data = inb(ISA_PORT_SDI);
+ return 0;
+ } else {
+ timeout--;
+ udelay(10);
+ }
+ }
+ return 1;
+}
+
+void
+act2000_isa_receive(act2000_card *card)
+{
+ u_char c;
+
+ if (test_and_set_bit(ACT2000_LOCK_RX, (void *) &card->ilock) != 0)
+ return;
+ while (!act2000_isa_readb(card, &c)) {
+ if (card->idat.isa.rcvidx < 8) {
+ card->idat.isa.rcvhdr[card->idat.isa.rcvidx++] = c;
+ if (card->idat.isa.rcvidx == 8) {
+ int valid = actcapi_chkhdr(card, (actcapi_msghdr *)&card->idat.isa.rcvhdr);
+
+ if (valid) {
+ card->idat.isa.rcvlen = ((actcapi_msghdr *)&card->idat.isa.rcvhdr)->len;
+ card->idat.isa.rcvskb = dev_alloc_skb(card->idat.isa.rcvlen);
+ if (card->idat.isa.rcvskb == NULL) {
+ card->idat.isa.rcvignore = 1;
+ printk(KERN_WARNING
+ "act2000_isa_receive: no memory\n");
+ test_and_clear_bit(ACT2000_LOCK_RX, (void *) &card->ilock);
+ return;
+ }
+ memcpy(skb_put(card->idat.isa.rcvskb, 8), card->idat.isa.rcvhdr, 8);
+ card->idat.isa.rcvptr = skb_put(card->idat.isa.rcvskb, card->idat.isa.rcvlen - 8);
+ } else {
+ card->idat.isa.rcvidx = 0;
+ printk(KERN_WARNING
+ "act2000_isa_receive: Invalid CAPI msg\n");
+ {
+ int i; __u8 *p; __u8 *c; __u8 tmp[30];
+ for (i = 0, p = (__u8 *)&card->idat.isa.rcvhdr, c = tmp; i < 8; i++)
+ c += sprintf(c, "%02x ", *(p++));
+ printk(KERN_WARNING "act2000_isa_receive: %s\n", tmp);
+ }
+ }
+ }
+ } else {
+ if (!card->idat.isa.rcvignore)
+ *card->idat.isa.rcvptr++ = c;
+ if (++card->idat.isa.rcvidx >= card->idat.isa.rcvlen) {
+ if (!card->idat.isa.rcvignore) {
+ skb_queue_tail(&card->rcvq, card->idat.isa.rcvskb);
+ act2000_schedule_rx(card);
+ }
+ card->idat.isa.rcvidx = 0;
+ card->idat.isa.rcvlen = 8;
+ card->idat.isa.rcvignore = 0;
+ card->idat.isa.rcvskb = NULL;
+ card->idat.isa.rcvptr = card->idat.isa.rcvhdr;
+ }
+ }
+ }
+ if (!(card->flags & ACT2000_FLAGS_IVALID)) {
+ /* In polling mode, schedule myself */
+ if ((card->idat.isa.rcvidx) &&
+ (card->idat.isa.rcvignore ||
+ (card->idat.isa.rcvidx < card->idat.isa.rcvlen)))
+ act2000_schedule_poll(card);
+ }
+ test_and_clear_bit(ACT2000_LOCK_RX, (void *) &card->ilock);
+}
+
+void
+act2000_isa_send(act2000_card * card)
+{
+ unsigned long flags;
+ struct sk_buff *skb;
+ actcapi_msg *msg;
+ int l;
+
+ if (test_and_set_bit(ACT2000_LOCK_TX, (void *) &card->ilock) != 0)
+ return;
+ while (1) {
+ spin_lock_irqsave(&card->lock, flags);
+ if (!(card->sbuf)) {
+ if ((card->sbuf = skb_dequeue(&card->sndq))) {
+ card->ack_msg = card->sbuf->data;
+ msg = (actcapi_msg *)card->sbuf->data;
+ if ((msg->hdr.cmd.cmd == 0x86) &&
+ (msg->hdr.cmd.subcmd == 0) ) {
+ /* Save flags in message */
+ card->need_b3ack = msg->msg.data_b3_req.flags;
+ msg->msg.data_b3_req.flags = 0;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&card->lock, flags);
+ if (!(card->sbuf)) {
+ /* No more data to send */
+ test_and_clear_bit(ACT2000_LOCK_TX, (void *) &card->ilock);
+ return;
+ }
+ skb = card->sbuf;
+ l = 0;
+ while (skb->len) {
+ if (act2000_isa_writeb(card, *(skb->data))) {
+ /* Fifo is full, but more data to send */
+ test_and_clear_bit(ACT2000_LOCK_TX, (void *) &card->ilock);
+ /* Schedule myself */
+ act2000_schedule_tx(card);
+ return;
+ }
+ skb_pull(skb, 1);
+ l++;
+ }
+ msg = (actcapi_msg *)card->ack_msg;
+ if ((msg->hdr.cmd.cmd == 0x86) &&
+ (msg->hdr.cmd.subcmd == 0) ) {
+ /*
+ * If it's user data, reset data-ptr
+ * and put skb into ackq.
+ */
+ skb->data = card->ack_msg;
+ /* Restore flags in message */
+ msg->msg.data_b3_req.flags = card->need_b3ack;
+ skb_queue_tail(&card->ackq, skb);
+ } else
+ dev_kfree_skb(skb);
+ card->sbuf = NULL;
+ }
+}
+
+/*
+ * Get firmware ID, check for 'ISDN' signature.
+ */
+static int
+act2000_isa_getid(act2000_card * card)
+{
+
+ act2000_fwid fid;
+ u_char *p = (u_char *) & fid;
+ int count = 0;
+
+ while (1) {
+ if (count > 510)
+ return -EPROTO;
+ if (act2000_isa_readb(card, p++))
+ break;
+ count++;
+ }
+ if (count <= 20) {
+ printk(KERN_WARNING "act2000: No Firmware-ID!\n");
+ return -ETIME;
+ }
+ *p = '\0';
+ fid.revlen[0] = '\0';
+ if (strcmp(fid.isdn, "ISDN")) {
+ printk(KERN_WARNING "act2000: Wrong Firmware-ID!\n");
+ return -EPROTO;
+ }
+ if ((p = strchr(fid.revision, '\n')))
+ *p = '\0';
+ printk(KERN_INFO "act2000: Firmware-ID: %s\n", fid.revision);
+ if (card->flags & ACT2000_FLAGS_IVALID) {
+ printk(KERN_DEBUG "Enabling Interrupts ...\n");
+ act2000_isa_enable_irq(card);
+ }
+ return 0;
+}
+
+/*
+ * Download microcode into card, check Firmware signature.
+ */
+int
+act2000_isa_download(act2000_card * card, act2000_ddef __user * cb)
+{
+ unsigned int length;
+ int l;
+ int c;
+ long timeout;
+ u_char *b;
+ u_char __user *p;
+ u_char *buf;
+ act2000_ddef cblock;
+
+ if (!act2000_isa_reset(card->port))
+ return -ENXIO;
+ msleep_interruptible(500);
+ if (copy_from_user(&cblock, cb, sizeof(cblock)))
+ return -EFAULT;
+ length = cblock.length;
+ p = cblock.buffer;
+ if (!access_ok(VERIFY_READ, p, length))
+ return -EFAULT;
+ buf = kmalloc(1024, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ timeout = 0;
+ while (length) {
+ l = (length > 1024) ? 1024 : length;
+ c = 0;
+ b = buf;
+ if (copy_from_user(buf, p, l)) {
+ kfree(buf);
+ return -EFAULT;
+ }
+ while (c < l) {
+ if (act2000_isa_writeb(card, *b++)) {
+ printk(KERN_WARNING
+ "act2000: loader timed out"
+ " len=%d c=%d\n", length, c);
+ kfree(buf);
+ return -ETIME;
+ }
+ c++;
+ }
+ length -= l;
+ p += l;
+ }
+ kfree(buf);
+ msleep_interruptible(500);
+ return (act2000_isa_getid(card));
+}
diff --git a/drivers/isdn/act2000/act2000_isa.h b/drivers/isdn/act2000/act2000_isa.h
new file mode 100644
index 0000000..ad86c5e
--- /dev/null
+++ b/drivers/isdn/act2000/act2000_isa.h
@@ -0,0 +1,136 @@
+/* $Id: act2000_isa.h,v 1.4.6.1 2001/09/23 22:24:32 kai Exp $
+ *
+ * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000 (ISA-Version).
+ *
+ * Author Fritz Elfert
+ * Copyright by Fritz Elfert <fritz@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to Friedemann Baitinger and IBM Germany
+ *
+ */
+
+#ifndef act2000_isa_h
+#define act2000_isa_h
+
+#define ISA_POLL_LOOP 40 /* Try to read-write before give up */
+
+typedef enum {
+ INT_NO_CHANGE = 0, /* Do not change the Mask */
+ INT_ON = 1, /* Set to Enable */
+ INT_OFF = 2, /* Set to Disable */
+} ISA_INT_T;
+
+/**************************************************************************/
+/* Configuration Register COR (RW) */
+/**************************************************************************/
+/* 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 */
+/* Soft Res| IRQM | IRQ Select | N/A | WAIT |Proc err */
+/**************************************************************************/
+#define ISA_COR 0 /* Offset for ISA config register */
+#define ISA_COR_PERR 0x01 /* Processor Error Enabled */
+#define ISA_COR_WS 0x02 /* Insert Wait State if 1 */
+#define ISA_COR_IRQOFF 0x38 /* No Interrupt */
+#define ISA_COR_IRQ07 0x30 /* IRQ 7 Enable */
+#define ISA_COR_IRQ05 0x28 /* IRQ 5 Enable */
+#define ISA_COR_IRQ03 0x20 /* IRQ 3 Enable */
+#define ISA_COR_IRQ10 0x18 /* IRQ 10 Enable */
+#define ISA_COR_IRQ11 0x10 /* IRQ 11 Enable */
+#define ISA_COR_IRQ12 0x08 /* IRQ 12 Enable */
+#define ISA_COR_IRQ15 0x00 /* IRQ 15 Enable */
+#define ISA_COR_IRQPULSE 0x40 /* 0 = Level 1 = Pulse Interrupt */
+#define ISA_COR_RESET 0x80 /* Soft Reset for Transputer */
+
+/**************************************************************************/
+/* Interrupt Source Register ISR (RO) */
+/**************************************************************************/
+/* 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 */
+/* N/A | N/A | N/A |Err sig |Ser ID |IN Intr |Out Intr| Error */
+/**************************************************************************/
+#define ISA_ISR 1 /* Offset for Interrupt Register */
+#define ISA_ISR_ERR 0x01 /* Error Interrupt */
+#define ISA_ISR_OUT 0x02 /* Output Interrupt */
+#define ISA_ISR_INP 0x04 /* Input Interrupt */
+#define ISA_ISR_SERIAL 0x08 /* Read out Serial ID after Reset */
+#define ISA_ISR_ERRSIG 0x10 /* Error Signal Input */
+#define ISA_ISR_ERR_MASK 0xfe /* Mask Error Interrupt */
+#define ISA_ISR_OUT_MASK 0xfd /* Mask Output Interrupt */
+#define ISA_ISR_INP_MASK 0xfb /* Mask Input Interrupt */
+
+/* Signature delivered after Reset at ISA_ISR_SERIAL (LSB first) */
+#define ISA_SER_ID 0x0201 /* ID for ISA Card */
+
+/**************************************************************************/
+/* EEPROM Register EPR (RW) */
+/**************************************************************************/
+/* 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 */
+/* N/A | N/A | N/A |ROM Hold| ROM CS |ROM CLK | ROM IN |ROM Out */
+/**************************************************************************/
+#define ISA_EPR 2 /* Offset for this Register */
+#define ISA_EPR_OUT 0x01 /* Rome Register Out (RO) */
+#define ISA_EPR_IN 0x02 /* Rom Register In (WR) */
+#define ISA_EPR_CLK 0x04 /* Rom Clock (WR) */
+#define ISA_EPR_CS 0x08 /* Rom Cip Select (WR) */
+#define ISA_EPR_HOLD 0x10 /* Rom Hold Signal (WR) */
+
+/**************************************************************************/
+/* EEPROM enable Register EER (unused) */
+/**************************************************************************/
+#define ISA_EER 3 /* Offset for this Register */
+
+/**************************************************************************/
+/* SLC Data Input SDI (RO) */
+/**************************************************************************/
+#define ISA_SDI 4 /* Offset for this Register */
+
+/**************************************************************************/
+/* SLC Data Output SDO (WO) */
+/**************************************************************************/
+#define ISA_SDO 5 /* Offset for this Register */
+
+/**************************************************************************/
+/* IMS C011 Mode 2 Input Status Register for INMOS CPU SIS (RW) */
+/**************************************************************************/
+/* 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 */
+/* N/A | N/A | N/A | N/A | N/A | N/A |Int Ena |Data Pre */
+/**************************************************************************/
+#define ISA_SIS 6 /* Offset for this Register */
+#define ISA_SIS_READY 0x01 /* If 1 : data is available */
+#define ISA_SIS_INT 0x02 /* Enable Interrupt for READ */
+
+/**************************************************************************/
+/* IMS C011 Mode 2 Output Status Register from INMOS CPU SOS (RW) */
+/**************************************************************************/
+/* 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 */
+/* N/A | N/A | N/A | N/A | N/A | N/A |Int Ena |Out Rdy */
+/**************************************************************************/
+#define ISA_SOS 7 /* Offset for this Register */
+#define ISA_SOS_READY 0x01 /* If 1 : we can write Data */
+#define ISA_SOS_INT 0x02 /* Enable Interrupt for WRITE */
+
+#define ISA_REGION 8 /* Number of Registers */
+
+
+/* Macros for accessing ports */
+#define ISA_PORT_COR (card->port+ISA_COR)
+#define ISA_PORT_ISR (card->port+ISA_ISR)
+#define ISA_PORT_EPR (card->port+ISA_EPR)
+#define ISA_PORT_EER (card->port+ISA_EER)
+#define ISA_PORT_SDI (card->port+ISA_SDI)
+#define ISA_PORT_SDO (card->port+ISA_SDO)
+#define ISA_PORT_SIS (card->port+ISA_SIS)
+#define ISA_PORT_SOS (card->port+ISA_SOS)
+
+/* Prototypes */
+
+extern int act2000_isa_detect(unsigned short portbase);
+extern int act2000_isa_config_irq(act2000_card * card, short irq);
+extern int act2000_isa_config_port(act2000_card * card, unsigned short portbase);
+extern int act2000_isa_download(act2000_card * card, act2000_ddef __user * cb);
+extern void act2000_isa_release(act2000_card * card);
+extern void act2000_isa_receive(act2000_card *card);
+extern void act2000_isa_send(act2000_card *card);
+
+#endif /* act2000_isa_h */
diff --git a/drivers/isdn/act2000/capi.c b/drivers/isdn/act2000/capi.c
new file mode 100644
index 0000000..946c38c
--- /dev/null
+++ b/drivers/isdn/act2000/capi.c
@@ -0,0 +1,1181 @@
+/* $Id: capi.c,v 1.9.6.2 2001/09/23 22:24:32 kai Exp $
+ *
+ * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000.
+ * CAPI encoder/decoder
+ *
+ * Author Fritz Elfert
+ * Copyright by Fritz Elfert <fritz@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to Friedemann Baitinger and IBM Germany
+ *
+ */
+
+#include "act2000.h"
+#include "capi.h"
+
+static actcapi_msgdsc valid_msg[] = {
+ {{ 0x86, 0x02}, "DATA_B3_IND"}, /* DATA_B3_IND/CONF must be first because of speed!!! */
+ {{ 0x86, 0x01}, "DATA_B3_CONF"},
+ {{ 0x02, 0x01}, "CONNECT_CONF"},
+ {{ 0x02, 0x02}, "CONNECT_IND"},
+ {{ 0x09, 0x01}, "CONNECT_INFO_CONF"},
+ {{ 0x03, 0x02}, "CONNECT_ACTIVE_IND"},
+ {{ 0x04, 0x01}, "DISCONNECT_CONF"},
+ {{ 0x04, 0x02}, "DISCONNECT_IND"},
+ {{ 0x05, 0x01}, "LISTEN_CONF"},
+ {{ 0x06, 0x01}, "GET_PARAMS_CONF"},
+ {{ 0x07, 0x01}, "INFO_CONF"},
+ {{ 0x07, 0x02}, "INFO_IND"},
+ {{ 0x08, 0x01}, "DATA_CONF"},
+ {{ 0x08, 0x02}, "DATA_IND"},
+ {{ 0x40, 0x01}, "SELECT_B2_PROTOCOL_CONF"},
+ {{ 0x80, 0x01}, "SELECT_B3_PROTOCOL_CONF"},
+ {{ 0x81, 0x01}, "LISTEN_B3_CONF"},
+ {{ 0x82, 0x01}, "CONNECT_B3_CONF"},
+ {{ 0x82, 0x02}, "CONNECT_B3_IND"},
+ {{ 0x83, 0x02}, "CONNECT_B3_ACTIVE_IND"},
+ {{ 0x84, 0x01}, "DISCONNECT_B3_CONF"},
+ {{ 0x84, 0x02}, "DISCONNECT_B3_IND"},
+ {{ 0x85, 0x01}, "GET_B3_PARAMS_CONF"},
+ {{ 0x01, 0x01}, "RESET_B3_CONF"},
+ {{ 0x01, 0x02}, "RESET_B3_IND"},
+ /* {{ 0x87, 0x02, "HANDSET_IND"}, not implemented */
+ {{ 0xff, 0x01}, "MANUFACTURER_CONF"},
+ {{ 0xff, 0x02}, "MANUFACTURER_IND"},
+#ifdef DEBUG_MSG
+ /* Requests */
+ {{ 0x01, 0x00}, "RESET_B3_REQ"},
+ {{ 0x02, 0x00}, "CONNECT_REQ"},
+ {{ 0x04, 0x00}, "DISCONNECT_REQ"},
+ {{ 0x05, 0x00}, "LISTEN_REQ"},
+ {{ 0x06, 0x00}, "GET_PARAMS_REQ"},
+ {{ 0x07, 0x00}, "INFO_REQ"},
+ {{ 0x08, 0x00}, "DATA_REQ"},
+ {{ 0x09, 0x00}, "CONNECT_INFO_REQ"},
+ {{ 0x40, 0x00}, "SELECT_B2_PROTOCOL_REQ"},
+ {{ 0x80, 0x00}, "SELECT_B3_PROTOCOL_REQ"},
+ {{ 0x81, 0x00}, "LISTEN_B3_REQ"},
+ {{ 0x82, 0x00}, "CONNECT_B3_REQ"},
+ {{ 0x84, 0x00}, "DISCONNECT_B3_REQ"},
+ {{ 0x85, 0x00}, "GET_B3_PARAMS_REQ"},
+ {{ 0x86, 0x00}, "DATA_B3_REQ"},
+ {{ 0xff, 0x00}, "MANUFACTURER_REQ"},
+ /* Responses */
+ {{ 0x01, 0x03}, "RESET_B3_RESP"},
+ {{ 0x02, 0x03}, "CONNECT_RESP"},
+ {{ 0x03, 0x03}, "CONNECT_ACTIVE_RESP"},
+ {{ 0x04, 0x03}, "DISCONNECT_RESP"},
+ {{ 0x07, 0x03}, "INFO_RESP"},
+ {{ 0x08, 0x03}, "DATA_RESP"},
+ {{ 0x82, 0x03}, "CONNECT_B3_RESP"},
+ {{ 0x83, 0x03}, "CONNECT_B3_ACTIVE_RESP"},
+ {{ 0x84, 0x03}, "DISCONNECT_B3_RESP"},
+ {{ 0x86, 0x03}, "DATA_B3_RESP"},
+ {{ 0xff, 0x03}, "MANUFACTURER_RESP"},
+#endif
+ {{ 0x00, 0x00}, NULL},
+};
+#define num_valid_msg (sizeof(valid_msg)/sizeof(actcapi_msgdsc))
+#define num_valid_imsg 27 /* MANUFACTURER_IND */
+
+/*
+ * Check for a valid incoming CAPI message.
+ * Return:
+ * 0 = Invalid message
+ * 1 = Valid message, no B-Channel-data
+ * 2 = Valid message, B-Channel-data
+ */
+int
+actcapi_chkhdr(act2000_card * card, actcapi_msghdr *hdr)
+{
+ int i;
+
+ if (hdr->applicationID != 1)
+ return 0;
+ if (hdr->len < 9)
+ return 0;
+ for (i = 0; i < num_valid_imsg; i++)
+ if ((hdr->cmd.cmd == valid_msg[i].cmd.cmd) &&
+ (hdr->cmd.subcmd == valid_msg[i].cmd.subcmd)) {
+ return (i?1:2);
+ }
+ return 0;
+}
+
+#define ACTCAPI_MKHDR(l, c, s) { \
+ skb = alloc_skb(l + 8, GFP_ATOMIC); \
+ if (skb) { \
+ m = (actcapi_msg *)skb_put(skb, l + 8); \
+ m->hdr.len = l + 8; \
+ m->hdr.applicationID = 1; \
+ m->hdr.cmd.cmd = c; \
+ m->hdr.cmd.subcmd = s; \
+ m->hdr.msgnum = actcapi_nextsmsg(card); \
+ } else m = NULL;\
+}
+
+#define ACTCAPI_CHKSKB if (!skb) { \
+ printk(KERN_WARNING "actcapi: alloc_skb failed\n"); \
+ return; \
+}
+
+#define ACTCAPI_QUEUE_TX { \
+ actcapi_debug_msg(skb, 1); \
+ skb_queue_tail(&card->sndq, skb); \
+ act2000_schedule_tx(card); \
+}
+
+int
+actcapi_listen_req(act2000_card *card)
+{
+ __u16 eazmask = 0;
+ int i;
+ actcapi_msg *m;
+ struct sk_buff *skb;
+
+ for (i = 0; i < ACT2000_BCH; i++)
+ eazmask |= card->bch[i].eazmask;
+ ACTCAPI_MKHDR(9, 0x05, 0x00);
+ if (!skb) {
+ printk(KERN_WARNING "actcapi: alloc_skb failed\n");
+ return -ENOMEM;
+ }
+ m->msg.listen_req.controller = 0;
+ m->msg.listen_req.infomask = 0x3f; /* All information */
+ m->msg.listen_req.eazmask = eazmask;
+ m->msg.listen_req.simask = (eazmask)?0x86:0; /* All SI's */
+ ACTCAPI_QUEUE_TX;
+ return 0;
+}
+
+int
+actcapi_connect_req(act2000_card *card, act2000_chan *chan, char *phone,
+ char eaz, int si1, int si2)
+{
+ actcapi_msg *m;
+ struct sk_buff *skb;
+
+ ACTCAPI_MKHDR((11 + strlen(phone)), 0x02, 0x00);
+ if (!skb) {
+ printk(KERN_WARNING "actcapi: alloc_skb failed\n");
+ chan->fsm_state = ACT2000_STATE_NULL;
+ return -ENOMEM;
+ }
+ m->msg.connect_req.controller = 0;
+ m->msg.connect_req.bchan = 0x83;
+ m->msg.connect_req.infomask = 0x3f;
+ m->msg.connect_req.si1 = si1;
+ m->msg.connect_req.si2 = si2;
+ m->msg.connect_req.eaz = eaz?eaz:'0';
+ m->msg.connect_req.addr.len = strlen(phone) + 1;
+ m->msg.connect_req.addr.tnp = 0x81;
+ memcpy(m->msg.connect_req.addr.num, phone, strlen(phone));
+ chan->callref = m->hdr.msgnum;
+ ACTCAPI_QUEUE_TX;
+ return 0;
+}
+
+static void
+actcapi_connect_b3_req(act2000_card *card, act2000_chan *chan)
+{
+ actcapi_msg *m;
+ struct sk_buff *skb;
+
+ ACTCAPI_MKHDR(17, 0x82, 0x00);
+ ACTCAPI_CHKSKB;
+ m->msg.connect_b3_req.plci = chan->plci;
+ memset(&m->msg.connect_b3_req.ncpi, 0,
+ sizeof(m->msg.connect_b3_req.ncpi));
+ m->msg.connect_b3_req.ncpi.len = 13;
+ m->msg.connect_b3_req.ncpi.modulo = 8;
+ ACTCAPI_QUEUE_TX;
+}
+
+/*
+ * Set net type (1TR6) or (EDSS1)
+ */
+int
+actcapi_manufacturer_req_net(act2000_card *card)
+{
+ actcapi_msg *m;
+ struct sk_buff *skb;
+
+ ACTCAPI_MKHDR(5, 0xff, 0x00);
+ if (!skb) {
+ printk(KERN_WARNING "actcapi: alloc_skb failed\n");
+ return -ENOMEM;
+ }
+ m->msg.manufacturer_req_net.manuf_msg = 0x11;
+ m->msg.manufacturer_req_net.controller = 1;
+ m->msg.manufacturer_req_net.nettype = (card->ptype == ISDN_PTYPE_EURO)?1:0;
+ ACTCAPI_QUEUE_TX;
+ printk(KERN_INFO "act2000 %s: D-channel protocol now %s\n",
+ card->interface.id, (card->ptype == ISDN_PTYPE_EURO)?"euro":"1tr6");
+ card->interface.features &=
+ ~(ISDN_FEATURE_P_UNKNOWN | ISDN_FEATURE_P_EURO | ISDN_FEATURE_P_1TR6);
+ card->interface.features |=
+ ((card->ptype == ISDN_PTYPE_EURO)?ISDN_FEATURE_P_EURO:ISDN_FEATURE_P_1TR6);
+ return 0;
+}
+
+/*
+ * Switch V.42 on or off
+ */
+#if 0
+int
+actcapi_manufacturer_req_v42(act2000_card *card, ulong arg)
+{
+ actcapi_msg *m;
+ struct sk_buff *skb;
+
+ ACTCAPI_MKHDR(8, 0xff, 0x00);
+ if (!skb) {
+
+ printk(KERN_WARNING "actcapi: alloc_skb failed\n");
+ return -ENOMEM;
+ }
+ m->msg.manufacturer_req_v42.manuf_msg = 0x10;
+ m->msg.manufacturer_req_v42.controller = 0;
+ m->msg.manufacturer_req_v42.v42control = (arg?1:0);
+ ACTCAPI_QUEUE_TX;
+ return 0;
+}
+#endif /* 0 */
+
+/*
+ * Set error-handler
+ */
+int
+actcapi_manufacturer_req_errh(act2000_card *card)
+{
+ actcapi_msg *m;
+ struct sk_buff *skb;
+
+ ACTCAPI_MKHDR(4, 0xff, 0x00);
+ if (!skb) {
+
+ printk(KERN_WARNING "actcapi: alloc_skb failed\n");
+ return -ENOMEM;
+ }
+ m->msg.manufacturer_req_err.manuf_msg = 0x03;
+ m->msg.manufacturer_req_err.controller = 0;
+ ACTCAPI_QUEUE_TX;
+ return 0;
+}
+
+/*
+ * Set MSN-Mapping.
+ */
+int
+actcapi_manufacturer_req_msn(act2000_card *card)
+{
+ msn_entry *p = card->msn_list;
+ actcapi_msg *m;
+ struct sk_buff *skb;
+ int len;
+
+ while (p) {
+ int i;
+
+ len = strlen(p->msn);
+ for (i = 0; i < 2; i++) {
+ ACTCAPI_MKHDR(6 + len, 0xff, 0x00);
+ if (!skb) {
+ printk(KERN_WARNING "actcapi: alloc_skb failed\n");
+ return -ENOMEM;
+ }
+ m->msg.manufacturer_req_msn.manuf_msg = 0x13 + i;
+ m->msg.manufacturer_req_msn.controller = 0;
+ m->msg.manufacturer_req_msn.msnmap.eaz = p->eaz;
+ m->msg.manufacturer_req_msn.msnmap.len = len;
+ memcpy(m->msg.manufacturer_req_msn.msnmap.msn, p->msn, len);
+ ACTCAPI_QUEUE_TX;
+ }
+ p = p->next;
+ }
+ return 0;
+}
+
+void
+actcapi_select_b2_protocol_req(act2000_card *card, act2000_chan *chan)
+{
+ actcapi_msg *m;
+ struct sk_buff *skb;
+
+ ACTCAPI_MKHDR(10, 0x40, 0x00);
+ ACTCAPI_CHKSKB;
+ m->msg.select_b2_protocol_req.plci = chan->plci;
+ memset(&m->msg.select_b2_protocol_req.dlpd, 0,
+ sizeof(m->msg.select_b2_protocol_req.dlpd));
+ m->msg.select_b2_protocol_req.dlpd.len = 6;
+ switch (chan->l2prot) {
+ case ISDN_PROTO_L2_TRANS:
+ m->msg.select_b2_protocol_req.protocol = 0x03;
+ m->msg.select_b2_protocol_req.dlpd.dlen = 4000;
+ break;
+ case ISDN_PROTO_L2_HDLC:
+ m->msg.select_b2_protocol_req.protocol = 0x02;
+ m->msg.select_b2_protocol_req.dlpd.dlen = 4000;
+ break;
+ case ISDN_PROTO_L2_X75I:
+ case ISDN_PROTO_L2_X75UI:
+ case ISDN_PROTO_L2_X75BUI:
+ m->msg.select_b2_protocol_req.protocol = 0x01;
+ m->msg.select_b2_protocol_req.dlpd.dlen = 4000;
+ m->msg.select_b2_protocol_req.dlpd.laa = 3;
+ m->msg.select_b2_protocol_req.dlpd.lab = 1;
+ m->msg.select_b2_protocol_req.dlpd.win = 7;
+ m->msg.select_b2_protocol_req.dlpd.modulo = 8;
+ break;
+ }
+ ACTCAPI_QUEUE_TX;
+}
+
+static void
+actcapi_select_b3_protocol_req(act2000_card *card, act2000_chan *chan)
+{
+ actcapi_msg *m;
+ struct sk_buff *skb;
+
+ ACTCAPI_MKHDR(17, 0x80, 0x00);
+ ACTCAPI_CHKSKB;
+ m->msg.select_b3_protocol_req.plci = chan->plci;
+ memset(&m->msg.select_b3_protocol_req.ncpd, 0,
+ sizeof(m->msg.select_b3_protocol_req.ncpd));
+ switch (chan->l3prot) {
+ case ISDN_PROTO_L3_TRANS:
+ m->msg.select_b3_protocol_req.protocol = 0x04;
+ m->msg.select_b3_protocol_req.ncpd.len = 13;
+ m->msg.select_b3_protocol_req.ncpd.modulo = 8;
+ break;
+ }
+ ACTCAPI_QUEUE_TX;
+}
+
+static void
+actcapi_listen_b3_req(act2000_card *card, act2000_chan *chan)
+{
+ actcapi_msg *m;
+ struct sk_buff *skb;
+
+ ACTCAPI_MKHDR(2, 0x81, 0x00);
+ ACTCAPI_CHKSKB;
+ m->msg.listen_b3_req.plci = chan->plci;
+ ACTCAPI_QUEUE_TX;
+}
+
+static void
+actcapi_disconnect_req(act2000_card *card, act2000_chan *chan)
+{
+ actcapi_msg *m;
+ struct sk_buff *skb;
+
+ ACTCAPI_MKHDR(3, 0x04, 0x00);
+ ACTCAPI_CHKSKB;
+ m->msg.disconnect_req.plci = chan->plci;
+ m->msg.disconnect_req.cause = 0;
+ ACTCAPI_QUEUE_TX;
+}
+
+void
+actcapi_disconnect_b3_req(act2000_card *card, act2000_chan *chan)
+{
+ actcapi_msg *m;
+ struct sk_buff *skb;
+
+ ACTCAPI_MKHDR(17, 0x84, 0x00);
+ ACTCAPI_CHKSKB;
+ m->msg.disconnect_b3_req.ncci = chan->ncci;
+ memset(&m->msg.disconnect_b3_req.ncpi, 0,
+ sizeof(m->msg.disconnect_b3_req.ncpi));
+ m->msg.disconnect_b3_req.ncpi.len = 13;
+ m->msg.disconnect_b3_req.ncpi.modulo = 8;
+ chan->fsm_state = ACT2000_STATE_BHWAIT;
+ ACTCAPI_QUEUE_TX;
+}
+
+void
+actcapi_connect_resp(act2000_card *card, act2000_chan *chan, __u8 cause)
+{
+ actcapi_msg *m;
+ struct sk_buff *skb;
+
+ ACTCAPI_MKHDR(3, 0x02, 0x03);
+ ACTCAPI_CHKSKB;
+ m->msg.connect_resp.plci = chan->plci;
+ m->msg.connect_resp.rejectcause = cause;
+ if (cause) {
+ chan->fsm_state = ACT2000_STATE_NULL;
+ chan->plci = 0x8000;
+ } else
+ chan->fsm_state = ACT2000_STATE_IWAIT;
+ ACTCAPI_QUEUE_TX;
+}
+
+static void
+actcapi_connect_active_resp(act2000_card *card, act2000_chan *chan)
+{
+ actcapi_msg *m;
+ struct sk_buff *skb;
+
+ ACTCAPI_MKHDR(2, 0x03, 0x03);
+ ACTCAPI_CHKSKB;
+ m->msg.connect_resp.plci = chan->plci;
+ if (chan->fsm_state == ACT2000_STATE_IWAIT)
+ chan->fsm_state = ACT2000_STATE_IBWAIT;
+ ACTCAPI_QUEUE_TX;
+}
+
+static void
+actcapi_connect_b3_resp(act2000_card *card, act2000_chan *chan, __u8 rejectcause)
+{
+ actcapi_msg *m;
+ struct sk_buff *skb;
+
+ ACTCAPI_MKHDR((rejectcause?3:17), 0x82, 0x03);
+ ACTCAPI_CHKSKB;
+ m->msg.connect_b3_resp.ncci = chan->ncci;
+ m->msg.connect_b3_resp.rejectcause = rejectcause;
+ if (!rejectcause) {
+ memset(&m->msg.connect_b3_resp.ncpi, 0,
+ sizeof(m->msg.connect_b3_resp.ncpi));
+ m->msg.connect_b3_resp.ncpi.len = 13;
+ m->msg.connect_b3_resp.ncpi.modulo = 8;
+ chan->fsm_state = ACT2000_STATE_BWAIT;
+ }
+ ACTCAPI_QUEUE_TX;
+}
+
+static void
+actcapi_connect_b3_active_resp(act2000_card *card, act2000_chan *chan)
+{
+ actcapi_msg *m;
+ struct sk_buff *skb;
+
+ ACTCAPI_MKHDR(2, 0x83, 0x03);
+ ACTCAPI_CHKSKB;
+ m->msg.connect_b3_active_resp.ncci = chan->ncci;
+ chan->fsm_state = ACT2000_STATE_ACTIVE;
+ ACTCAPI_QUEUE_TX;
+}
+
+static void
+actcapi_info_resp(act2000_card *card, act2000_chan *chan)
+{
+ actcapi_msg *m;
+ struct sk_buff *skb;
+
+ ACTCAPI_MKHDR(2, 0x07, 0x03);
+ ACTCAPI_CHKSKB;
+ m->msg.info_resp.plci = chan->plci;
+ ACTCAPI_QUEUE_TX;
+}
+
+static void
+actcapi_disconnect_b3_resp(act2000_card *card, act2000_chan *chan)
+{
+ actcapi_msg *m;
+ struct sk_buff *skb;
+
+ ACTCAPI_MKHDR(2, 0x84, 0x03);
+ ACTCAPI_CHKSKB;
+ m->msg.disconnect_b3_resp.ncci = chan->ncci;
+ chan->ncci = 0x8000;
+ chan->queued = 0;
+ ACTCAPI_QUEUE_TX;
+}
+
+static void
+actcapi_disconnect_resp(act2000_card *card, act2000_chan *chan)
+{
+ actcapi_msg *m;
+ struct sk_buff *skb;
+
+ ACTCAPI_MKHDR(2, 0x04, 0x03);
+ ACTCAPI_CHKSKB;
+ m->msg.disconnect_resp.plci = chan->plci;
+ chan->plci = 0x8000;
+ ACTCAPI_QUEUE_TX;
+}
+
+static int
+new_plci(act2000_card *card, __u16 plci)
+{
+ int i;
+ for (i = 0; i < ACT2000_BCH; i++)
+ if (card->bch[i].plci == 0x8000) {
+ card->bch[i].plci = plci;
+ return i;
+ }
+ return -1;
+}
+
+static int
+find_plci(act2000_card *card, __u16 plci)
+{
+ int i;
+ for (i = 0; i < ACT2000_BCH; i++)
+ if (card->bch[i].plci == plci)
+ return i;
+ return -1;
+}
+
+static int
+find_ncci(act2000_card *card, __u16 ncci)
+{
+ int i;
+ for (i = 0; i < ACT2000_BCH; i++)
+ if (card->bch[i].ncci == ncci)
+ return i;
+ return -1;
+}
+
+static int
+find_dialing(act2000_card *card, __u16 callref)
+{
+ int i;
+ for (i = 0; i < ACT2000_BCH; i++)
+ if ((card->bch[i].callref == callref) &&
+ (card->bch[i].fsm_state == ACT2000_STATE_OCALL))
+ return i;
+ return -1;
+}
+
+static int
+actcapi_data_b3_ind(act2000_card *card, struct sk_buff *skb) {
+ __u16 plci;
+ __u16 ncci;
+ __u16 controller;
+ __u8 blocknr;
+ int chan;
+ actcapi_msg *msg = (actcapi_msg *)skb->data;
+
+ EVAL_NCCI(msg->msg.data_b3_ind.fakencci, plci, controller, ncci);
+ chan = find_ncci(card, ncci);
+ if (chan < 0)
+ return 0;
+ if (card->bch[chan].fsm_state != ACT2000_STATE_ACTIVE)
+ return 0;
+ if (card->bch[chan].plci != plci)
+ return 0;
+ blocknr = msg->msg.data_b3_ind.blocknr;
+ skb_pull(skb, 19);
+ card->interface.rcvcallb_skb(card->myid, chan, skb);
+ if (!(skb = alloc_skb(11, GFP_ATOMIC))) {
+ printk(KERN_WARNING "actcapi: alloc_skb failed\n");
+ return 1;
+ }
+ msg = (actcapi_msg *)skb_put(skb, 11);
+ msg->hdr.len = 11;
+ msg->hdr.applicationID = 1;
+ msg->hdr.cmd.cmd = 0x86;
+ msg->hdr.cmd.subcmd = 0x03;
+ msg->hdr.msgnum = actcapi_nextsmsg(card);
+ msg->msg.data_b3_resp.ncci = ncci;
+ msg->msg.data_b3_resp.blocknr = blocknr;
+ ACTCAPI_QUEUE_TX;
+ return 1;
+}
+
+/*
+ * Walk over ackq, unlink DATA_B3_REQ from it, if
+ * ncci and blocknr are matching.
+ * Decrement queued-bytes counter.
+ */
+static int
+handle_ack(act2000_card *card, act2000_chan *chan, __u8 blocknr) {
+ unsigned long flags;
+ struct sk_buff *skb;
+ struct sk_buff *tmp;
+ struct actcapi_msg *m;
+ int ret = 0;
+
+ spin_lock_irqsave(&card->lock, flags);
+ skb = skb_peek(&card->ackq);
+ spin_unlock_irqrestore(&card->lock, flags);
+ if (!skb) {
+ printk(KERN_WARNING "act2000: handle_ack nothing found!\n");
+ return 0;
+ }
+ tmp = skb;
+ while (1) {
+ m = (actcapi_msg *)tmp->data;
+ if ((((m->msg.data_b3_req.fakencci >> 8) & 0xff) == chan->ncci) &&
+ (m->msg.data_b3_req.blocknr == blocknr)) {
+ /* found corresponding DATA_B3_REQ */
+ skb_unlink(tmp, &card->ackq);
+ chan->queued -= m->msg.data_b3_req.datalen;
+ if (m->msg.data_b3_req.flags)
+ ret = m->msg.data_b3_req.datalen;
+ dev_kfree_skb(tmp);
+ if (chan->queued < 0)
+ chan->queued = 0;
+ return ret;
+ }
+ spin_lock_irqsave(&card->lock, flags);
+ tmp = skb_peek((struct sk_buff_head *)tmp);
+ spin_unlock_irqrestore(&card->lock, flags);
+ if ((tmp == skb) || (tmp == NULL)) {
+ /* reached end of queue */
+ printk(KERN_WARNING "act2000: handle_ack nothing found!\n");
+ return 0;
+ }
+ }
+}
+
+void
+actcapi_dispatch(struct work_struct *work)
+{
+ struct act2000_card *card =
+ container_of(work, struct act2000_card, rcv_tq);
+ struct sk_buff *skb;
+ actcapi_msg *msg;
+ __u16 ccmd;
+ int chan;
+ int len;
+ act2000_chan *ctmp;
+ isdn_ctrl cmd;
+ char tmp[170];
+
+ while ((skb = skb_dequeue(&card->rcvq))) {
+ actcapi_debug_msg(skb, 0);
+ msg = (actcapi_msg *)skb->data;
+ ccmd = ((msg->hdr.cmd.cmd << 8) | msg->hdr.cmd.subcmd);
+ switch (ccmd) {
+ case 0x8602:
+ /* DATA_B3_IND */
+ if (actcapi_data_b3_ind(card, skb))
+ return;
+ break;
+ case 0x8601:
+ /* DATA_B3_CONF */
+ chan = find_ncci(card, msg->msg.data_b3_conf.ncci);
+ if ((chan >= 0) && (card->bch[chan].fsm_state == ACT2000_STATE_ACTIVE)) {
+ if (msg->msg.data_b3_conf.info != 0)
+ printk(KERN_WARNING "act2000: DATA_B3_CONF: %04x\n",
+ msg->msg.data_b3_conf.info);
+ len = handle_ack(card, &card->bch[chan],
+ msg->msg.data_b3_conf.blocknr);
+ if (len) {
+ cmd.driver = card->myid;
+ cmd.command = ISDN_STAT_BSENT;
+ cmd.arg = chan;
+ cmd.parm.length = len;
+ card->interface.statcallb(&cmd);
+ }
+ }
+ break;
+ case 0x0201:
+ /* CONNECT_CONF */
+ chan = find_dialing(card, msg->hdr.msgnum);
+ if (chan >= 0) {
+ if (msg->msg.connect_conf.info) {
+ card->bch[chan].fsm_state = ACT2000_STATE_NULL;
+ cmd.driver = card->myid;
+ cmd.command = ISDN_STAT_DHUP;
+ cmd.arg = chan;
+ card->interface.statcallb(&cmd);
+ } else {
+ card->bch[chan].fsm_state = ACT2000_STATE_OWAIT;
+ card->bch[chan].plci = msg->msg.connect_conf.plci;
+ }
+ }
+ break;
+ case 0x0202:
+ /* CONNECT_IND */
+ chan = new_plci(card, msg->msg.connect_ind.plci);
+ if (chan < 0) {
+ ctmp = (act2000_chan *)tmp;
+ ctmp->plci = msg->msg.connect_ind.plci;
+ actcapi_connect_resp(card, ctmp, 0x11); /* All Card-Cannels busy */
+ } else {
+ card->bch[chan].fsm_state = ACT2000_STATE_ICALL;
+ cmd.driver = card->myid;
+ cmd.command = ISDN_STAT_ICALL;
+ cmd.arg = chan;
+ cmd.parm.setup.si1 = msg->msg.connect_ind.si1;
+ cmd.parm.setup.si2 = msg->msg.connect_ind.si2;
+ if (card->ptype == ISDN_PTYPE_EURO)
+ strcpy(cmd.parm.setup.eazmsn,
+ act2000_find_eaz(card, msg->msg.connect_ind.eaz));
+ else {
+ cmd.parm.setup.eazmsn[0] = msg->msg.connect_ind.eaz;
+ cmd.parm.setup.eazmsn[1] = 0;
+ }
+ memset(cmd.parm.setup.phone, 0, sizeof(cmd.parm.setup.phone));
+ memcpy(cmd.parm.setup.phone, msg->msg.connect_ind.addr.num,
+ msg->msg.connect_ind.addr.len - 1);
+ cmd.parm.setup.plan = msg->msg.connect_ind.addr.tnp;
+ cmd.parm.setup.screen = 0;
+ if (card->interface.statcallb(&cmd) == 2)
+ actcapi_connect_resp(card, &card->bch[chan], 0x15); /* Reject Call */
+ }
+ break;
+ case 0x0302:
+ /* CONNECT_ACTIVE_IND */
+ chan = find_plci(card, msg->msg.connect_active_ind.plci);
+ if (chan >= 0)
+ switch (card->bch[chan].fsm_state) {
+ case ACT2000_STATE_IWAIT:
+ actcapi_connect_active_resp(card, &card->bch[chan]);
+ break;
+ case ACT2000_STATE_OWAIT:
+ actcapi_connect_active_resp(card, &card->bch[chan]);
+ actcapi_select_b2_protocol_req(card, &card->bch[chan]);
+ break;
+ }
+ break;
+ case 0x8202:
+ /* CONNECT_B3_IND */
+ chan = find_plci(card, msg->msg.connect_b3_ind.plci);
+ if ((chan >= 0) && (card->bch[chan].fsm_state == ACT2000_STATE_IBWAIT)) {
+ card->bch[chan].ncci = msg->msg.connect_b3_ind.ncci;
+ actcapi_connect_b3_resp(card, &card->bch[chan], 0);
+ } else {
+ ctmp = (act2000_chan *)tmp;
+ ctmp->ncci = msg->msg.connect_b3_ind.ncci;
+ actcapi_connect_b3_resp(card, ctmp, 0x11); /* All Card-Cannels busy */
+ }
+ break;
+ case 0x8302:
+ /* CONNECT_B3_ACTIVE_IND */
+ chan = find_ncci(card, msg->msg.connect_b3_active_ind.ncci);
+ if ((chan >= 0) && (card->bch[chan].fsm_state == ACT2000_STATE_BWAIT)) {
+ actcapi_connect_b3_active_resp(card, &card->bch[chan]);
+ cmd.driver = card->myid;
+ cmd.command = ISDN_STAT_BCONN;
+ cmd.arg = chan;
+ card->interface.statcallb(&cmd);
+ }
+ break;
+ case 0x8402:
+ /* DISCONNECT_B3_IND */
+ chan = find_ncci(card, msg->msg.disconnect_b3_ind.ncci);
+ if (chan >= 0) {
+ ctmp = &card->bch[chan];
+ actcapi_disconnect_b3_resp(card, ctmp);
+ switch (ctmp->fsm_state) {
+ case ACT2000_STATE_ACTIVE:
+ ctmp->fsm_state = ACT2000_STATE_DHWAIT2;
+ cmd.driver = card->myid;
+ cmd.command = ISDN_STAT_BHUP;
+ cmd.arg = chan;
+ card->interface.statcallb(&cmd);
+ break;
+ case ACT2000_STATE_BHWAIT2:
+ actcapi_disconnect_req(card, ctmp);
+ ctmp->fsm_state = ACT2000_STATE_DHWAIT;
+ cmd.driver = card->myid;
+ cmd.command = ISDN_STAT_BHUP;
+ cmd.arg = chan;
+ card->interface.statcallb(&cmd);
+ break;
+ }
+ }
+ break;
+ case 0x0402:
+ /* DISCONNECT_IND */
+ chan = find_plci(card, msg->msg.disconnect_ind.plci);
+ if (chan >= 0) {
+ ctmp = &card->bch[chan];
+ actcapi_disconnect_resp(card, ctmp);
+ ctmp->fsm_state = ACT2000_STATE_NULL;
+ cmd.driver = card->myid;
+ cmd.command = ISDN_STAT_DHUP;
+ cmd.arg = chan;
+ card->interface.statcallb(&cmd);
+ } else {
+ ctmp = (act2000_chan *)tmp;
+ ctmp->plci = msg->msg.disconnect_ind.plci;
+ actcapi_disconnect_resp(card, ctmp);
+ }
+ break;
+ case 0x4001:
+ /* SELECT_B2_PROTOCOL_CONF */
+ chan = find_plci(card, msg->msg.select_b2_protocol_conf.plci);
+ if (chan >= 0)
+ switch (card->bch[chan].fsm_state) {
+ case ACT2000_STATE_ICALL:
+ case ACT2000_STATE_OWAIT:
+ ctmp = &card->bch[chan];
+ if (msg->msg.select_b2_protocol_conf.info == 0)
+ actcapi_select_b3_protocol_req(card, ctmp);
+ else {
+ ctmp->fsm_state = ACT2000_STATE_NULL;
+ cmd.driver = card->myid;
+ cmd.command = ISDN_STAT_DHUP;
+ cmd.arg = chan;
+ card->interface.statcallb(&cmd);
+ }
+ break;
+ }
+ break;
+ case 0x8001:
+ /* SELECT_B3_PROTOCOL_CONF */
+ chan = find_plci(card, msg->msg.select_b3_protocol_conf.plci);
+ if (chan >= 0)
+ switch (card->bch[chan].fsm_state) {
+ case ACT2000_STATE_ICALL:
+ case ACT2000_STATE_OWAIT:
+ ctmp = &card->bch[chan];
+ if (msg->msg.select_b3_protocol_conf.info == 0)
+ actcapi_listen_b3_req(card, ctmp);
+ else {
+ ctmp->fsm_state = ACT2000_STATE_NULL;
+ cmd.driver = card->myid;
+ cmd.command = ISDN_STAT_DHUP;
+ cmd.arg = chan;
+ card->interface.statcallb(&cmd);
+ }
+ }
+ break;
+ case 0x8101:
+ /* LISTEN_B3_CONF */
+ chan = find_plci(card, msg->msg.listen_b3_conf.plci);
+ if (chan >= 0)
+ switch (card->bch[chan].fsm_state) {
+ case ACT2000_STATE_ICALL:
+ ctmp = &card->bch[chan];
+ if (msg->msg.listen_b3_conf.info == 0)
+ actcapi_connect_resp(card, ctmp, 0);
+ else {
+ ctmp->fsm_state = ACT2000_STATE_NULL;
+ cmd.driver = card->myid;
+ cmd.command = ISDN_STAT_DHUP;
+ cmd.arg = chan;
+ card->interface.statcallb(&cmd);
+ }
+ break;
+ case ACT2000_STATE_OWAIT:
+ ctmp = &card->bch[chan];
+ if (msg->msg.listen_b3_conf.info == 0) {
+ actcapi_connect_b3_req(card, ctmp);
+ ctmp->fsm_state = ACT2000_STATE_OBWAIT;
+ cmd.driver = card->myid;
+ cmd.command = ISDN_STAT_DCONN;
+ cmd.arg = chan;
+ card->interface.statcallb(&cmd);
+ } else {
+ ctmp->fsm_state = ACT2000_STATE_NULL;
+ cmd.driver = card->myid;
+ cmd.command = ISDN_STAT_DHUP;
+ cmd.arg = chan;
+ card->interface.statcallb(&cmd);
+ }
+ break;
+ }
+ break;
+ case 0x8201:
+ /* CONNECT_B3_CONF */
+ chan = find_plci(card, msg->msg.connect_b3_conf.plci);
+ if ((chan >= 0) && (card->bch[chan].fsm_state == ACT2000_STATE_OBWAIT)) {
+ ctmp = &card->bch[chan];
+ if (msg->msg.connect_b3_conf.info) {
+ ctmp->fsm_state = ACT2000_STATE_NULL;
+ cmd.driver = card->myid;
+ cmd.command = ISDN_STAT_DHUP;
+ cmd.arg = chan;
+ card->interface.statcallb(&cmd);
+ } else {
+ ctmp->ncci = msg->msg.connect_b3_conf.ncci;
+ ctmp->fsm_state = ACT2000_STATE_BWAIT;
+ }
+ }
+ break;
+ case 0x8401:
+ /* DISCONNECT_B3_CONF */
+ chan = find_ncci(card, msg->msg.disconnect_b3_conf.ncci);
+ if ((chan >= 0) && (card->bch[chan].fsm_state == ACT2000_STATE_BHWAIT))
+ card->bch[chan].fsm_state = ACT2000_STATE_BHWAIT2;
+ break;
+ case 0x0702:
+ /* INFO_IND */
+ chan = find_plci(card, msg->msg.info_ind.plci);
+ if (chan >= 0)
+ /* TODO: Eval Charging info / cause */
+ actcapi_info_resp(card, &card->bch[chan]);
+ break;
+ case 0x0401:
+ /* LISTEN_CONF */
+ case 0x0501:
+ /* LISTEN_CONF */
+ case 0xff01:
+ /* MANUFACTURER_CONF */
+ break;
+ case 0xff02:
+ /* MANUFACTURER_IND */
+ if (msg->msg.manuf_msg == 3) {
+ memset(tmp, 0, sizeof(tmp));
+ strncpy(tmp,
+ &msg->msg.manufacturer_ind_err.errstring,
+ msg->hdr.len - 16);
+ if (msg->msg.manufacturer_ind_err.errcode)
+ printk(KERN_WARNING "act2000: %s\n", tmp);
+ else {
+ printk(KERN_DEBUG "act2000: %s\n", tmp);
+ if ((!strncmp(tmp, "INFO: Trace buffer con", 22)) ||
+ (!strncmp(tmp, "INFO: Compile Date/Tim", 22))) {
+ card->flags |= ACT2000_FLAGS_RUNNING;
+ cmd.command = ISDN_STAT_RUN;
+ cmd.driver = card->myid;
+ cmd.arg = 0;
+ actcapi_manufacturer_req_net(card);
+ actcapi_manufacturer_req_msn(card);
+ actcapi_listen_req(card);
+ card->interface.statcallb(&cmd);
+ }
+ }
+ }
+ break;
+ default:
+ printk(KERN_WARNING "act2000: UNHANDLED Message %04x\n", ccmd);
+ break;
+ }
+ dev_kfree_skb(skb);
+ }
+}
+
+#ifdef DEBUG_MSG
+static void
+actcapi_debug_caddr(actcapi_addr *addr)
+{
+ char tmp[30];
+
+ printk(KERN_DEBUG " Alen = %d\n", addr->len);
+ if (addr->len > 0)
+ printk(KERN_DEBUG " Atnp = 0x%02x\n", addr->tnp);
+ if (addr->len > 1) {
+ memset(tmp, 0, 30);
+ memcpy(tmp, addr->num, addr->len - 1);
+ printk(KERN_DEBUG " Anum = '%s'\n", tmp);
+ }
+}
+
+static void
+actcapi_debug_ncpi(actcapi_ncpi *ncpi)
+{
+ printk(KERN_DEBUG " ncpi.len = %d\n", ncpi->len);
+ if (ncpi->len >= 2)
+ printk(KERN_DEBUG " ncpi.lic = 0x%04x\n", ncpi->lic);
+ if (ncpi->len >= 4)
+ printk(KERN_DEBUG " ncpi.hic = 0x%04x\n", ncpi->hic);
+ if (ncpi->len >= 6)
+ printk(KERN_DEBUG " ncpi.ltc = 0x%04x\n", ncpi->ltc);
+ if (ncpi->len >= 8)
+ printk(KERN_DEBUG " ncpi.htc = 0x%04x\n", ncpi->htc);
+ if (ncpi->len >= 10)
+ printk(KERN_DEBUG " ncpi.loc = 0x%04x\n", ncpi->loc);
+ if (ncpi->len >= 12)
+ printk(KERN_DEBUG " ncpi.hoc = 0x%04x\n", ncpi->hoc);
+ if (ncpi->len >= 13)
+ printk(KERN_DEBUG " ncpi.mod = %d\n", ncpi->modulo);
+}
+
+static void
+actcapi_debug_dlpd(actcapi_dlpd *dlpd)
+{
+ printk(KERN_DEBUG " dlpd.len = %d\n", dlpd->len);
+ if (dlpd->len >= 2)
+ printk(KERN_DEBUG " dlpd.dlen = 0x%04x\n", dlpd->dlen);
+ if (dlpd->len >= 3)
+ printk(KERN_DEBUG " dlpd.laa = 0x%02x\n", dlpd->laa);
+ if (dlpd->len >= 4)
+ printk(KERN_DEBUG " dlpd.lab = 0x%02x\n", dlpd->lab);
+ if (dlpd->len >= 5)
+ printk(KERN_DEBUG " dlpd.modulo = %d\n", dlpd->modulo);
+ if (dlpd->len >= 6)
+ printk(KERN_DEBUG " dlpd.win = %d\n", dlpd->win);
+}
+
+#ifdef DEBUG_DUMP_SKB
+static void dump_skb(struct sk_buff *skb) {
+ char tmp[80];
+ char *p = skb->data;
+ char *t = tmp;
+ int i;
+
+ for (i = 0; i < skb->len; i++) {
+ t += sprintf(t, "%02x ", *p++ & 0xff);
+ if ((i & 0x0f) == 8) {
+ printk(KERN_DEBUG "dump: %s\n", tmp);
+ t = tmp;
+ }
+ }
+ if (i & 0x07)
+ printk(KERN_DEBUG "dump: %s\n", tmp);
+}
+#endif
+
+void
+actcapi_debug_msg(struct sk_buff *skb, int direction)
+{
+ actcapi_msg *msg = (actcapi_msg *)skb->data;
+ char *descr;
+ int i;
+ char tmp[170];
+
+#ifndef DEBUG_DATA_MSG
+ if (msg->hdr.cmd.cmd == 0x86)
+ return;
+#endif
+ descr = "INVALID";
+#ifdef DEBUG_DUMP_SKB
+ dump_skb(skb);
+#endif
+ for (i = 0; i < num_valid_msg; i++)
+ if ((msg->hdr.cmd.cmd == valid_msg[i].cmd.cmd) &&
+ (msg->hdr.cmd.subcmd == valid_msg[i].cmd.subcmd)) {
+ descr = valid_msg[i].description;
+ break;
+ }
+ printk(KERN_DEBUG "%s %s msg\n", direction?"Outgoing":"Incoming", descr);
+ printk(KERN_DEBUG " ApplID = %d\n", msg->hdr.applicationID);
+ printk(KERN_DEBUG " Len = %d\n", msg->hdr.len);
+ printk(KERN_DEBUG " MsgNum = 0x%04x\n", msg->hdr.msgnum);
+ printk(KERN_DEBUG " Cmd = 0x%02x\n", msg->hdr.cmd.cmd);
+ printk(KERN_DEBUG " SubCmd = 0x%02x\n", msg->hdr.cmd.subcmd);
+ switch (i) {
+ case 0:
+ /* DATA B3 IND */
+ printk(KERN_DEBUG " BLOCK = 0x%02x\n",
+ msg->msg.data_b3_ind.blocknr);
+ break;
+ case 2:
+ /* CONNECT CONF */
+ printk(KERN_DEBUG " PLCI = 0x%04x\n",
+ msg->msg.connect_conf.plci);
+ printk(KERN_DEBUG " Info = 0x%04x\n",
+ msg->msg.connect_conf.info);
+ break;
+ case 3:
+ /* CONNECT IND */
+ printk(KERN_DEBUG " PLCI = 0x%04x\n",
+ msg->msg.connect_ind.plci);
+ printk(KERN_DEBUG " Contr = %d\n",
+ msg->msg.connect_ind.controller);
+ printk(KERN_DEBUG " SI1 = %d\n",
+ msg->msg.connect_ind.si1);
+ printk(KERN_DEBUG " SI2 = %d\n",
+ msg->msg.connect_ind.si2);
+ printk(KERN_DEBUG " EAZ = '%c'\n",
+ msg->msg.connect_ind.eaz);
+ actcapi_debug_caddr(&msg->msg.connect_ind.addr);
+ break;
+ case 5:
+ /* CONNECT ACTIVE IND */
+ printk(KERN_DEBUG " PLCI = 0x%04x\n",
+ msg->msg.connect_active_ind.plci);
+ actcapi_debug_caddr(&msg->msg.connect_active_ind.addr);
+ break;
+ case 8:
+ /* LISTEN CONF */
+ printk(KERN_DEBUG " Contr = %d\n",
+ msg->msg.listen_conf.controller);
+ printk(KERN_DEBUG " Info = 0x%04x\n",
+ msg->msg.listen_conf.info);
+ break;
+ case 11:
+ /* INFO IND */
+ printk(KERN_DEBUG " PLCI = 0x%04x\n",
+ msg->msg.info_ind.plci);
+ printk(KERN_DEBUG " Imsk = 0x%04x\n",
+ msg->msg.info_ind.nr.mask);
+ if (msg->hdr.len > 12) {
+ int l = msg->hdr.len - 12;
+ int j;
+ char *p = tmp;
+ for (j = 0; j < l ; j++)
+ p += sprintf(p, "%02x ", msg->msg.info_ind.el.display[j]);
+ printk(KERN_DEBUG " D = '%s'\n", tmp);
+ }
+ break;
+ case 14:
+ /* SELECT B2 PROTOCOL CONF */
+ printk(KERN_DEBUG " PLCI = 0x%04x\n",
+ msg->msg.select_b2_protocol_conf.plci);
+ printk(KERN_DEBUG " Info = 0x%04x\n",
+ msg->msg.select_b2_protocol_conf.info);
+ break;
+ case 15:
+ /* SELECT B3 PROTOCOL CONF */
+ printk(KERN_DEBUG " PLCI = 0x%04x\n",
+ msg->msg.select_b3_protocol_conf.plci);
+ printk(KERN_DEBUG " Info = 0x%04x\n",
+ msg->msg.select_b3_protocol_conf.info);
+ break;
+ case 16:
+ /* LISTEN B3 CONF */
+ printk(KERN_DEBUG " PLCI = 0x%04x\n",
+ msg->msg.listen_b3_conf.plci);
+ printk(KERN_DEBUG " Info = 0x%04x\n",
+ msg->msg.listen_b3_conf.info);
+ break;
+ case 18:
+ /* CONNECT B3 IND */
+ printk(KERN_DEBUG " NCCI = 0x%04x\n",
+ msg->msg.connect_b3_ind.ncci);
+ printk(KERN_DEBUG " PLCI = 0x%04x\n",
+ msg->msg.connect_b3_ind.plci);
+ actcapi_debug_ncpi(&msg->msg.connect_b3_ind.ncpi);
+ break;
+ case 19:
+ /* CONNECT B3 ACTIVE IND */
+ printk(KERN_DEBUG " NCCI = 0x%04x\n",
+ msg->msg.connect_b3_active_ind.ncci);
+ actcapi_debug_ncpi(&msg->msg.connect_b3_active_ind.ncpi);
+ break;
+ case 26:
+ /* MANUFACTURER IND */
+ printk(KERN_DEBUG " Mmsg = 0x%02x\n",
+ msg->msg.manufacturer_ind_err.manuf_msg);
+ switch (msg->msg.manufacturer_ind_err.manuf_msg) {
+ case 3:
+ printk(KERN_DEBUG " Contr = %d\n",
+ msg->msg.manufacturer_ind_err.controller);
+ printk(KERN_DEBUG " Code = 0x%08x\n",
+ msg->msg.manufacturer_ind_err.errcode);
+ memset(tmp, 0, sizeof(tmp));
+ strncpy(tmp, &msg->msg.manufacturer_ind_err.errstring,
+ msg->hdr.len - 16);
+ printk(KERN_DEBUG " Emsg = '%s'\n", tmp);
+ break;
+ }
+ break;
+ case 30:
+ /* LISTEN REQ */
+ printk(KERN_DEBUG " Imsk = 0x%08x\n",
+ msg->msg.listen_req.infomask);
+ printk(KERN_DEBUG " Emsk = 0x%04x\n",
+ msg->msg.listen_req.eazmask);
+ printk(KERN_DEBUG " Smsk = 0x%04x\n",
+ msg->msg.listen_req.simask);
+ break;
+ case 35:
+ /* SELECT_B2_PROTOCOL_REQ */
+ printk(KERN_DEBUG " PLCI = 0x%04x\n",
+ msg->msg.select_b2_protocol_req.plci);
+ printk(KERN_DEBUG " prot = 0x%02x\n",
+ msg->msg.select_b2_protocol_req.protocol);
+ if (msg->hdr.len >= 11)
+ printk(KERN_DEBUG "No dlpd\n");
+ else
+ actcapi_debug_dlpd(&msg->msg.select_b2_protocol_req.dlpd);
+ break;
+ case 44:
+ /* CONNECT RESP */
+ printk(KERN_DEBUG " PLCI = 0x%04x\n",
+ msg->msg.connect_resp.plci);
+ printk(KERN_DEBUG " CAUSE = 0x%02x\n",
+ msg->msg.connect_resp.rejectcause);
+ break;
+ case 45:
+ /* CONNECT ACTIVE RESP */
+ printk(KERN_DEBUG " PLCI = 0x%04x\n",
+ msg->msg.connect_active_resp.plci);
+ break;
+ }
+}
+#endif
diff --git a/drivers/isdn/act2000/capi.h b/drivers/isdn/act2000/capi.h
new file mode 100644
index 0000000..e55f6a9
--- /dev/null
+++ b/drivers/isdn/act2000/capi.h
@@ -0,0 +1,365 @@
+/* $Id: capi.h,v 1.6.6.2 2001/09/23 22:24:32 kai Exp $
+ *
+ * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000.
+ *
+ * Author Fritz Elfert
+ * Copyright by Fritz Elfert <fritz@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to Friedemann Baitinger and IBM Germany
+ *
+ */
+
+#ifndef CAPI_H
+#define CAPI_H
+
+/* Command-part of a CAPI message */
+typedef struct actcapi_msgcmd {
+ __u8 cmd;
+ __u8 subcmd;
+} actcapi_msgcmd;
+
+/* CAPI message header */
+typedef struct actcapi_msghdr {
+ __u16 len;
+ __u16 applicationID;
+ actcapi_msgcmd cmd;
+ __u16 msgnum;
+} actcapi_msghdr;
+
+/* CAPI message description (for debugging) */
+typedef struct actcapi_msgdsc {
+ actcapi_msgcmd cmd;
+ char *description;
+} actcapi_msgdsc;
+
+/* CAPI Address */
+typedef struct actcapi_addr {
+ __u8 len; /* Length of element */
+ __u8 tnp; /* Type/Numbering Plan */
+ __u8 num[20]; /* Caller ID */
+} actcapi_addr;
+
+/* CAPI INFO element mask */
+typedef union actcapi_infonr { /* info number */
+ __u16 mask; /* info-mask field */
+ struct bmask { /* bit definitions */
+ unsigned codes : 3; /* code set */
+ unsigned rsvd : 5; /* reserved */
+ unsigned svind : 1; /* single, variable length ind. */
+ unsigned wtype : 7; /* W-element type */
+ } bmask;
+} actcapi_infonr;
+
+/* CAPI INFO element */
+typedef union actcapi_infoel { /* info element */
+ __u8 len; /* length of info element */
+ __u8 display[40]; /* display contents */
+ __u8 uuinfo[40]; /* User-user info field */
+ struct cause { /* Cause information */
+ unsigned ext2 : 1; /* extension */
+ unsigned cod : 2; /* coding standard */
+ unsigned spare : 1; /* spare */
+ unsigned loc : 4; /* location */
+ unsigned ext1 : 1; /* extension */
+ unsigned cval : 7; /* Cause value */
+ } cause;
+ struct charge { /* Charging information */
+ __u8 toc; /* type of charging info */
+ __u8 unit[10]; /* charging units */
+ } charge;
+ __u8 date[20]; /* date fields */
+ __u8 stat; /* state of remote party */
+} actcapi_infoel;
+
+/* Message for EAZ<->MSN Mapping */
+typedef struct actcapi_msn {
+ __u8 eaz;
+ __u8 len; /* Length of MSN */
+ __u8 msn[15];
+} __attribute__((packed)) actcapi_msn;
+
+typedef struct actcapi_dlpd {
+ __u8 len; /* Length of structure */
+ __u16 dlen; /* Data Length */
+ __u8 laa; /* Link Address A */
+ __u8 lab; /* Link Address B */
+ __u8 modulo; /* Modulo Mode */
+ __u8 win; /* Window size */
+ __u8 xid[100]; /* XID Information */
+} __attribute__((packed)) actcapi_dlpd;
+
+typedef struct actcapi_ncpd {
+ __u8 len; /* Length of structure */
+ __u16 lic;
+ __u16 hic;
+ __u16 ltc;
+ __u16 htc;
+ __u16 loc;
+ __u16 hoc;
+ __u8 modulo;
+} __attribute__((packed)) actcapi_ncpd;
+#define actcapi_ncpi actcapi_ncpd
+
+/*
+ * Layout of NCCI field in a B3 DATA CAPI message is different from
+ * standard at act2000:
+ *
+ * Bit 0-4 = PLCI
+ * Bit 5-7 = Controller
+ * Bit 8-15 = NCCI
+ */
+#define MAKE_NCCI(plci,contr,ncci) \
+ ((plci & 0x1f) | ((contr & 0x7) << 5) | ((ncci & 0xff) << 8))
+
+#define EVAL_NCCI(fakencci,plci,contr,ncci) { \
+ plci = fakencci & 0x1f; \
+ contr = (fakencci >> 5) & 0x7; \
+ ncci = (fakencci >> 8) & 0xff; \
+}
+
+/*
+ * Layout of PLCI field in a B3 DATA CAPI message is different from
+ * standard at act2000:
+ *
+ * Bit 0-4 = PLCI
+ * Bit 5-7 = Controller
+ * Bit 8-15 = reserved (must be 0)
+ */
+#define MAKE_PLCI(plci,contr) \
+ ((plci & 0x1f) | ((contr & 0x7) << 5))
+
+#define EVAL_PLCI(fakeplci,plci,contr) { \
+ plci = fakeplci & 0x1f; \
+ contr = (fakeplci >> 5) & 0x7; \
+}
+
+typedef struct actcapi_msg {
+ actcapi_msghdr hdr;
+ union {
+ __u16 manuf_msg;
+ struct manufacturer_req_net {
+ __u16 manuf_msg;
+ __u16 controller;
+ __u8 nettype;
+ } manufacturer_req_net;
+ struct manufacturer_req_v42 {
+ __u16 manuf_msg;
+ __u16 controller;
+ __u32 v42control;
+ } manufacturer_req_v42;
+ struct manufacturer_conf_v42 {
+ __u16 manuf_msg;
+ __u16 controller;
+ } manufacturer_conf_v42;
+ struct manufacturer_req_err {
+ __u16 manuf_msg;
+ __u16 controller;
+ } manufacturer_req_err;
+ struct manufacturer_ind_err {
+ __u16 manuf_msg;
+ __u16 controller;
+ __u32 errcode;
+ __u8 errstring; /* actually up to 160 */
+ } manufacturer_ind_err;
+ struct manufacturer_req_msn {
+ __u16 manuf_msg;
+ __u16 controller;
+ actcapi_msn msnmap;
+ } __attribute ((packed)) manufacturer_req_msn;
+ /* TODO: TraceInit-req/conf/ind/resp and
+ * TraceDump-req/conf/ind/resp
+ */
+ struct connect_req {
+ __u8 controller;
+ __u8 bchan;
+ __u32 infomask;
+ __u8 si1;
+ __u8 si2;
+ __u8 eaz;
+ actcapi_addr addr;
+ } __attribute__ ((packed)) connect_req;
+ struct connect_conf {
+ __u16 plci;
+ __u16 info;
+ } connect_conf;
+ struct connect_ind {
+ __u16 plci;
+ __u8 controller;
+ __u8 si1;
+ __u8 si2;
+ __u8 eaz;
+ actcapi_addr addr;
+ } __attribute__ ((packed)) connect_ind;
+ struct connect_resp {
+ __u16 plci;
+ __u8 rejectcause;
+ } connect_resp;
+ struct connect_active_ind {
+ __u16 plci;
+ actcapi_addr addr;
+ } __attribute__ ((packed)) connect_active_ind;
+ struct connect_active_resp {
+ __u16 plci;
+ } connect_active_resp;
+ struct connect_b3_req {
+ __u16 plci;
+ actcapi_ncpi ncpi;
+ } __attribute__ ((packed)) connect_b3_req;
+ struct connect_b3_conf {
+ __u16 plci;
+ __u16 ncci;
+ __u16 info;
+ } connect_b3_conf;
+ struct connect_b3_ind {
+ __u16 ncci;
+ __u16 plci;
+ actcapi_ncpi ncpi;
+ } __attribute__ ((packed)) connect_b3_ind;
+ struct connect_b3_resp {
+ __u16 ncci;
+ __u8 rejectcause;
+ actcapi_ncpi ncpi;
+ } __attribute__ ((packed)) connect_b3_resp;
+ struct disconnect_req {
+ __u16 plci;
+ __u8 cause;
+ } disconnect_req;
+ struct disconnect_conf {
+ __u16 plci;
+ __u16 info;
+ } disconnect_conf;
+ struct disconnect_ind {
+ __u16 plci;
+ __u16 info;
+ } disconnect_ind;
+ struct disconnect_resp {
+ __u16 plci;
+ } disconnect_resp;
+ struct connect_b3_active_ind {
+ __u16 ncci;
+ actcapi_ncpi ncpi;
+ } __attribute__ ((packed)) connect_b3_active_ind;
+ struct connect_b3_active_resp {
+ __u16 ncci;
+ } connect_b3_active_resp;
+ struct disconnect_b3_req {
+ __u16 ncci;
+ actcapi_ncpi ncpi;
+ } __attribute__ ((packed)) disconnect_b3_req;
+ struct disconnect_b3_conf {
+ __u16 ncci;
+ __u16 info;
+ } disconnect_b3_conf;
+ struct disconnect_b3_ind {
+ __u16 ncci;
+ __u16 info;
+ actcapi_ncpi ncpi;
+ } __attribute__ ((packed)) disconnect_b3_ind;
+ struct disconnect_b3_resp {
+ __u16 ncci;
+ } disconnect_b3_resp;
+ struct info_ind {
+ __u16 plci;
+ actcapi_infonr nr;
+ actcapi_infoel el;
+ } __attribute__ ((packed)) info_ind;
+ struct info_resp {
+ __u16 plci;
+ } info_resp;
+ struct listen_b3_req {
+ __u16 plci;
+ } listen_b3_req;
+ struct listen_b3_conf {
+ __u16 plci;
+ __u16 info;
+ } listen_b3_conf;
+ struct select_b2_protocol_req {
+ __u16 plci;
+ __u8 protocol;
+ actcapi_dlpd dlpd;
+ } __attribute__ ((packed)) select_b2_protocol_req;
+ struct select_b2_protocol_conf {
+ __u16 plci;
+ __u16 info;
+ } select_b2_protocol_conf;
+ struct select_b3_protocol_req {
+ __u16 plci;
+ __u8 protocol;
+ actcapi_ncpd ncpd;
+ } __attribute__ ((packed)) select_b3_protocol_req;
+ struct select_b3_protocol_conf {
+ __u16 plci;
+ __u16 info;
+ } select_b3_protocol_conf;
+ struct listen_req {
+ __u8 controller;
+ __u32 infomask;
+ __u16 eazmask;
+ __u16 simask;
+ } __attribute__ ((packed)) listen_req;
+ struct listen_conf {
+ __u8 controller;
+ __u16 info;
+ } __attribute__ ((packed)) listen_conf;
+ struct data_b3_req {
+ __u16 fakencci;
+ __u16 datalen;
+ __u32 unused;
+ __u8 blocknr;
+ __u16 flags;
+ } __attribute ((packed)) data_b3_req;
+ struct data_b3_ind {
+ __u16 fakencci;
+ __u16 datalen;
+ __u32 unused;
+ __u8 blocknr;
+ __u16 flags;
+ } __attribute__ ((packed)) data_b3_ind;
+ struct data_b3_resp {
+ __u16 ncci;
+ __u8 blocknr;
+ } __attribute__ ((packed)) data_b3_resp;
+ struct data_b3_conf {
+ __u16 ncci;
+ __u8 blocknr;
+ __u16 info;
+ } __attribute__ ((packed)) data_b3_conf;
+ } msg;
+} __attribute__ ((packed)) actcapi_msg;
+
+static inline unsigned short
+actcapi_nextsmsg(act2000_card *card)
+{
+ unsigned long flags;
+ unsigned short n;
+
+ spin_lock_irqsave(&card->mnlock, flags);
+ n = card->msgnum;
+ card->msgnum++;
+ card->msgnum &= 0x7fff;
+ spin_unlock_irqrestore(&card->mnlock, flags);
+ return n;
+}
+#define DEBUG_MSG
+#undef DEBUG_DATA_MSG
+#undef DEBUG_DUMP_SKB
+
+extern int actcapi_chkhdr(act2000_card *, actcapi_msghdr *);
+extern int actcapi_listen_req(act2000_card *);
+extern int actcapi_manufacturer_req_net(act2000_card *);
+extern int actcapi_manufacturer_req_errh(act2000_card *);
+extern int actcapi_manufacturer_req_msn(act2000_card *);
+extern int actcapi_connect_req(act2000_card *, act2000_chan *, char *, char, int, int);
+extern void actcapi_select_b2_protocol_req(act2000_card *, act2000_chan *);
+extern void actcapi_disconnect_b3_req(act2000_card *, act2000_chan *);
+extern void actcapi_connect_resp(act2000_card *, act2000_chan *, __u8);
+extern void actcapi_dispatch(struct work_struct *);
+#ifdef DEBUG_MSG
+extern void actcapi_debug_msg(struct sk_buff *skb, int);
+#else
+#define actcapi_debug_msg(skb, len)
+#endif
+#endif
diff --git a/drivers/isdn/act2000/module.c b/drivers/isdn/act2000/module.c
new file mode 100644
index 0000000..8325022
--- /dev/null
+++ b/drivers/isdn/act2000/module.c
@@ -0,0 +1,813 @@
+/* $Id: module.c,v 1.14.6.4 2001/09/23 22:24:32 kai Exp $
+ *
+ * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000.
+ *
+ * Author Fritz Elfert
+ * Copyright by Fritz Elfert <fritz@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to Friedemann Baitinger and IBM Germany
+ *
+ */
+
+#include "act2000.h"
+#include "act2000_isa.h"
+#include "capi.h"
+#include <linux/module.h>
+#include <linux/init.h>
+
+static unsigned short act2000_isa_ports[] =
+{
+ 0x0200, 0x0240, 0x0280, 0x02c0, 0x0300, 0x0340, 0x0380,
+ 0xcfe0, 0xcfa0, 0xcf60, 0xcf20, 0xcee0, 0xcea0, 0xce60,
+};
+#define ISA_NRPORTS (sizeof(act2000_isa_ports)/sizeof(unsigned short))
+
+static act2000_card *cards = (act2000_card *) NULL;
+
+/* Parameters to be set by insmod */
+static int act_bus = 0;
+static int act_port = -1; /* -1 = Autoprobe */
+static int act_irq = -1;
+static char *act_id = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
+
+MODULE_DESCRIPTION( "ISDN4Linux: Driver for IBM Active 2000 ISDN card");
+MODULE_AUTHOR( "Fritz Elfert");
+MODULE_LICENSE( "GPL");
+MODULE_PARM_DESC(act_bus, "BusType of first card, 1=ISA, 2=MCA, 3=PCMCIA, currently only ISA");
+MODULE_PARM_DESC(membase, "Base port address of first card");
+MODULE_PARM_DESC(act_irq, "IRQ of first card");
+MODULE_PARM_DESC(act_id, "ID-String of first card");
+module_param(act_bus, int, 0);
+module_param(act_port, int, 0);
+module_param(act_irq, int, 0);
+module_param(act_id, charp, 0);
+
+static int act2000_addcard(int, int, int, char *);
+
+static act2000_chan *
+find_channel(act2000_card *card, int channel)
+{
+ if ((channel >= 0) && (channel < ACT2000_BCH))
+ return &(card->bch[channel]);
+ printk(KERN_WARNING "act2000: Invalid channel %d\n", channel);
+ return NULL;
+}
+
+/*
+ * Free MSN list
+ */
+static void
+act2000_clear_msn(act2000_card *card)
+{
+ struct msn_entry *p = card->msn_list;
+ struct msn_entry *q;
+ unsigned long flags;
+
+ spin_lock_irqsave(&card->lock, flags);
+ card->msn_list = NULL;
+ spin_unlock_irqrestore(&card->lock, flags);
+ while (p) {
+ q = p->next;
+ kfree(p);
+ p = q;
+ }
+}
+
+/*
+ * Find an MSN entry in the list.
+ * If ia5 != 0, return IA5-encoded EAZ, else
+ * return a bitmask with corresponding bit set.
+ */
+static __u16
+act2000_find_msn(act2000_card *card, char *msn, int ia5)
+{
+ struct msn_entry *p = card->msn_list;
+ __u8 eaz = '0';
+
+ while (p) {
+ if (!strcmp(p->msn, msn)) {
+ eaz = p->eaz;
+ break;
+ }
+ p = p->next;
+ }
+ if (!ia5)
+ return (1 << (eaz - '0'));
+ else
+ return eaz;
+}
+
+/*
+ * Find an EAZ entry in the list.
+ * return a string with corresponding msn.
+ */
+char *
+act2000_find_eaz(act2000_card *card, char eaz)
+{
+ struct msn_entry *p = card->msn_list;
+
+ while (p) {
+ if (p->eaz == eaz)
+ return(p->msn);
+ p = p->next;
+ }
+ return("\0");
+}
+
+/*
+ * Add or delete an MSN to the MSN list
+ *
+ * First character of msneaz is EAZ, rest is MSN.
+ * If length of eazmsn is 1, delete that entry.
+ */
+static int
+act2000_set_msn(act2000_card *card, char *eazmsn)
+{
+ struct msn_entry *p = card->msn_list;
+ struct msn_entry *q = NULL;
+ unsigned long flags;
+ int i;
+
+ if (!strlen(eazmsn))
+ return 0;
+ if (strlen(eazmsn) > 16)
+ return -EINVAL;
+ for (i = 0; i < strlen(eazmsn); i++)
+ if (!isdigit(eazmsn[i]))
+ return -EINVAL;
+ if (strlen(eazmsn) == 1) {
+ /* Delete a single MSN */
+ while (p) {
+ if (p->eaz == eazmsn[0]) {
+ spin_lock_irqsave(&card->lock, flags);
+ if (q)
+ q->next = p->next;
+ else
+ card->msn_list = p->next;
+ spin_unlock_irqrestore(&card->lock, flags);
+ kfree(p);
+ printk(KERN_DEBUG
+ "Mapping for EAZ %c deleted\n",
+ eazmsn[0]);
+ return 0;
+ }
+ q = p;
+ p = p->next;
+ }
+ return 0;
+ }
+ /* Add a single MSN */
+ while (p) {
+ /* Found in list, replace MSN */
+ if (p->eaz == eazmsn[0]) {
+ spin_lock_irqsave(&card->lock, flags);
+ strcpy(p->msn, &eazmsn[1]);
+ spin_unlock_irqrestore(&card->lock, flags);
+ printk(KERN_DEBUG
+ "Mapping for EAZ %c changed to %s\n",
+ eazmsn[0],
+ &eazmsn[1]);
+ return 0;
+ }
+ p = p->next;
+ }
+ /* Not found in list, add new entry */
+ p = kmalloc(sizeof(msn_entry), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+ p->eaz = eazmsn[0];
+ strcpy(p->msn, &eazmsn[1]);
+ p->next = card->msn_list;
+ spin_lock_irqsave(&card->lock, flags);
+ card->msn_list = p;
+ spin_unlock_irqrestore(&card->lock, flags);
+ printk(KERN_DEBUG
+ "Mapping %c -> %s added\n",
+ eazmsn[0],
+ &eazmsn[1]);
+ return 0;
+}
+
+static void
+act2000_transmit(struct work_struct *work)
+{
+ struct act2000_card *card =
+ container_of(work, struct act2000_card, snd_tq);
+
+ switch (card->bus) {
+ case ACT2000_BUS_ISA:
+ act2000_isa_send(card);
+ break;
+ case ACT2000_BUS_PCMCIA:
+ case ACT2000_BUS_MCA:
+ default:
+ printk(KERN_WARNING
+ "act2000_transmit: Illegal bustype %d\n", card->bus);
+ }
+}
+
+static void
+act2000_receive(struct work_struct *work)
+{
+ struct act2000_card *card =
+ container_of(work, struct act2000_card, poll_tq);
+
+ switch (card->bus) {
+ case ACT2000_BUS_ISA:
+ act2000_isa_receive(card);
+ break;
+ case ACT2000_BUS_PCMCIA:
+ case ACT2000_BUS_MCA:
+ default:
+ printk(KERN_WARNING
+ "act2000_receive: Illegal bustype %d\n", card->bus);
+ }
+}
+
+static void
+act2000_poll(unsigned long data)
+{
+ act2000_card * card = (act2000_card *)data;
+ unsigned long flags;
+
+ act2000_receive(&card->poll_tq);
+ spin_lock_irqsave(&card->lock, flags);
+ mod_timer(&card->ptimer, jiffies+3);
+ spin_unlock_irqrestore(&card->lock, flags);
+}
+
+static int
+act2000_command(act2000_card * card, isdn_ctrl * c)
+{
+ ulong a;
+ act2000_chan *chan;
+ act2000_cdef cdef;
+ isdn_ctrl cmd;
+ char tmp[17];
+ int ret;
+ unsigned long flags;
+ void __user *arg;
+
+ switch (c->command) {
+ case ISDN_CMD_IOCTL:
+ memcpy(&a, c->parm.num, sizeof(ulong));
+ arg = (void __user *)a;
+ switch (c->arg) {
+ case ACT2000_IOCTL_LOADBOOT:
+ switch (card->bus) {
+ case ACT2000_BUS_ISA:
+ ret = act2000_isa_download(card,
+ arg);
+ if (!ret) {
+ card->flags |= ACT2000_FLAGS_LOADED;
+ if (!(card->flags & ACT2000_FLAGS_IVALID)) {
+ card->ptimer.expires = jiffies + 3;
+ card->ptimer.function = act2000_poll;
+ card->ptimer.data = (unsigned long)card;
+ add_timer(&card->ptimer);
+ }
+ actcapi_manufacturer_req_errh(card);
+ }
+ break;
+ default:
+ printk(KERN_WARNING
+ "act2000: Illegal BUS type %d\n",
+ card->bus);
+ ret = -EIO;
+ }
+ return ret;
+ case ACT2000_IOCTL_SETPROTO:
+ card->ptype = a?ISDN_PTYPE_EURO:ISDN_PTYPE_1TR6;
+ if (!(card->flags & ACT2000_FLAGS_RUNNING))
+ return 0;
+ actcapi_manufacturer_req_net(card);
+ return 0;
+ case ACT2000_IOCTL_SETMSN:
+ if (copy_from_user(tmp, arg,
+ sizeof(tmp)))
+ return -EFAULT;
+ if ((ret = act2000_set_msn(card, tmp)))
+ return ret;
+ if (card->flags & ACT2000_FLAGS_RUNNING)
+ return(actcapi_manufacturer_req_msn(card));
+ return 0;
+ case ACT2000_IOCTL_ADDCARD:
+ if (copy_from_user(&cdef, arg,
+ sizeof(cdef)))
+ return -EFAULT;
+ if (act2000_addcard(cdef.bus, cdef.port, cdef.irq, cdef.id))
+ return -EIO;
+ return 0;
+ case ACT2000_IOCTL_TEST:
+ if (!(card->flags & ACT2000_FLAGS_RUNNING))
+ return -ENODEV;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case ISDN_CMD_DIAL:
+ if (!(card->flags & ACT2000_FLAGS_RUNNING))
+ return -ENODEV;
+ if (!(chan = find_channel(card, c->arg & 0x0f)))
+ break;
+ spin_lock_irqsave(&card->lock, flags);
+ if (chan->fsm_state != ACT2000_STATE_NULL) {
+ spin_unlock_irqrestore(&card->lock, flags);
+ printk(KERN_WARNING "Dial on channel with state %d\n",
+ chan->fsm_state);
+ return -EBUSY;
+ }
+ if (card->ptype == ISDN_PTYPE_EURO)
+ tmp[0] = act2000_find_msn(card, c->parm.setup.eazmsn, 1);
+ else
+ tmp[0] = c->parm.setup.eazmsn[0];
+ chan->fsm_state = ACT2000_STATE_OCALL;
+ chan->callref = 0xffff;
+ spin_unlock_irqrestore(&card->lock, flags);
+ ret = actcapi_connect_req(card, chan, c->parm.setup.phone,
+ tmp[0], c->parm.setup.si1,
+ c->parm.setup.si2);
+ if (ret) {
+ cmd.driver = card->myid;
+ cmd.command = ISDN_STAT_DHUP;
+ cmd.arg &= 0x0f;
+ card->interface.statcallb(&cmd);
+ }
+ return ret;
+ case ISDN_CMD_ACCEPTD:
+ if (!(card->flags & ACT2000_FLAGS_RUNNING))
+ return -ENODEV;
+ if (!(chan = find_channel(card, c->arg & 0x0f)))
+ break;
+ if (chan->fsm_state == ACT2000_STATE_ICALL)
+ actcapi_select_b2_protocol_req(card, chan);
+ return 0;
+ case ISDN_CMD_ACCEPTB:
+ if (!(card->flags & ACT2000_FLAGS_RUNNING))
+ return -ENODEV;
+ return 0;
+ case ISDN_CMD_HANGUP:
+ if (!(card->flags & ACT2000_FLAGS_RUNNING))
+ return -ENODEV;
+ if (!(chan = find_channel(card, c->arg & 0x0f)))
+ break;
+ switch (chan->fsm_state) {
+ case ACT2000_STATE_ICALL:
+ case ACT2000_STATE_BSETUP:
+ actcapi_connect_resp(card, chan, 0x15);
+ break;
+ case ACT2000_STATE_ACTIVE:
+ actcapi_disconnect_b3_req(card, chan);
+ break;
+ }
+ return 0;
+ case ISDN_CMD_SETEAZ:
+ if (!(card->flags & ACT2000_FLAGS_RUNNING))
+ return -ENODEV;
+ if (!(chan = find_channel(card, c->arg & 0x0f)))
+ break;
+ if (strlen(c->parm.num)) {
+ if (card->ptype == ISDN_PTYPE_EURO) {
+ chan->eazmask = act2000_find_msn(card, c->parm.num, 0);
+ }
+ if (card->ptype == ISDN_PTYPE_1TR6) {
+ int i;
+ chan->eazmask = 0;
+ for (i = 0; i < strlen(c->parm.num); i++)
+ if (isdigit(c->parm.num[i]))
+ chan->eazmask |= (1 << (c->parm.num[i] - '0'));
+ }
+ } else
+ chan->eazmask = 0x3ff;
+ actcapi_listen_req(card);
+ return 0;
+ case ISDN_CMD_CLREAZ:
+ if (!(card->flags & ACT2000_FLAGS_RUNNING))
+ return -ENODEV;
+ if (!(chan = find_channel(card, c->arg & 0x0f)))
+ break;
+ chan->eazmask = 0;
+ actcapi_listen_req(card);
+ return 0;
+ case ISDN_CMD_SETL2:
+ if (!(card->flags & ACT2000_FLAGS_RUNNING))
+ return -ENODEV;
+ if (!(chan = find_channel(card, c->arg & 0x0f)))
+ break;
+ chan->l2prot = (c->arg >> 8);
+ return 0;
+ case ISDN_CMD_SETL3:
+ if (!(card->flags & ACT2000_FLAGS_RUNNING))
+ return -ENODEV;
+ if ((c->arg >> 8) != ISDN_PROTO_L3_TRANS) {
+ printk(KERN_WARNING "L3 protocol unknown\n");
+ return -1;
+ }
+ if (!(chan = find_channel(card, c->arg & 0x0f)))
+ break;
+ chan->l3prot = (c->arg >> 8);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static int
+act2000_sendbuf(act2000_card *card, int channel, int ack, struct sk_buff *skb)
+{
+ struct sk_buff *xmit_skb;
+ int len;
+ act2000_chan *chan;
+ actcapi_msg *msg;
+
+ if (!(chan = find_channel(card, channel)))
+ return -1;
+ if (chan->fsm_state != ACT2000_STATE_ACTIVE)
+ return -1;
+ len = skb->len;
+ if ((chan->queued + len) >= ACT2000_MAX_QUEUED)
+ return 0;
+ if (!len)
+ return 0;
+ if (skb_headroom(skb) < 19) {
+ printk(KERN_WARNING "act2000_sendbuf: Headroom only %d\n",
+ skb_headroom(skb));
+ xmit_skb = alloc_skb(len + 19, GFP_ATOMIC);
+ if (!xmit_skb) {
+ printk(KERN_WARNING "act2000_sendbuf: Out of memory\n");
+ return 0;
+ }
+ skb_reserve(xmit_skb, 19);
+ skb_copy_from_linear_data(skb, skb_put(xmit_skb, len), len);
+ } else {
+ xmit_skb = skb_clone(skb, GFP_ATOMIC);
+ if (!xmit_skb) {
+ printk(KERN_WARNING "act2000_sendbuf: Out of memory\n");
+ return 0;
+ }
+ }
+ dev_kfree_skb(skb);
+ msg = (actcapi_msg *)skb_push(xmit_skb, 19);
+ msg->hdr.len = 19 + len;
+ msg->hdr.applicationID = 1;
+ msg->hdr.cmd.cmd = 0x86;
+ msg->hdr.cmd.subcmd = 0x00;
+ msg->hdr.msgnum = actcapi_nextsmsg(card);
+ msg->msg.data_b3_req.datalen = len;
+ msg->msg.data_b3_req.blocknr = (msg->hdr.msgnum & 0xff);
+ msg->msg.data_b3_req.fakencci = MAKE_NCCI(chan->plci, 0, chan->ncci);
+ msg->msg.data_b3_req.flags = ack; /* Will be set to 0 on actual sending */
+ actcapi_debug_msg(xmit_skb, 1);
+ chan->queued += len;
+ skb_queue_tail(&card->sndq, xmit_skb);
+ act2000_schedule_tx(card);
+ return len;
+}
+
+
+/* Read the Status-replies from the Interface */
+static int
+act2000_readstatus(u_char __user * buf, int len, act2000_card * card)
+{
+ int count;
+ u_char __user *p;
+
+ for (p = buf, count = 0; count < len; p++, count++) {
+ if (card->status_buf_read == card->status_buf_write)
+ return count;
+ put_user(*card->status_buf_read++, p);
+ if (card->status_buf_read > card->status_buf_end)
+ card->status_buf_read = card->status_buf;
+ }
+ return count;
+}
+
+/*
+ * Find card with given driverId
+ */
+static inline act2000_card *
+act2000_findcard(int driverid)
+{
+ act2000_card *p = cards;
+
+ while (p) {
+ if (p->myid == driverid)
+ return p;
+ p = p->next;
+ }
+ return (act2000_card *) 0;
+}
+
+/*
+ * Wrapper functions for interface to linklevel
+ */
+static int
+if_command(isdn_ctrl * c)
+{
+ act2000_card *card = act2000_findcard(c->driver);
+
+ if (card)
+ return (act2000_command(card, c));
+ printk(KERN_ERR
+ "act2000: if_command %d called with invalid driverId %d!\n",
+ c->command, c->driver);
+ return -ENODEV;
+}
+
+static int
+if_writecmd(const u_char __user *buf, int len, int id, int channel)
+{
+ act2000_card *card = act2000_findcard(id);
+
+ if (card) {
+ if (!(card->flags & ACT2000_FLAGS_RUNNING))
+ return -ENODEV;
+ return (len);
+ }
+ printk(KERN_ERR
+ "act2000: if_writecmd called with invalid driverId!\n");
+ return -ENODEV;
+}
+
+static int
+if_readstatus(u_char __user * buf, int len, int id, int channel)
+{
+ act2000_card *card = act2000_findcard(id);
+
+ if (card) {
+ if (!(card->flags & ACT2000_FLAGS_RUNNING))
+ return -ENODEV;
+ return (act2000_readstatus(buf, len, card));
+ }
+ printk(KERN_ERR
+ "act2000: if_readstatus called with invalid driverId!\n");
+ return -ENODEV;
+}
+
+static int
+if_sendbuf(int id, int channel, int ack, struct sk_buff *skb)
+{
+ act2000_card *card = act2000_findcard(id);
+
+ if (card) {
+ if (!(card->flags & ACT2000_FLAGS_RUNNING))
+ return -ENODEV;
+ return (act2000_sendbuf(card, channel, ack, skb));
+ }
+ printk(KERN_ERR
+ "act2000: if_sendbuf called with invalid driverId!\n");
+ return -ENODEV;
+}
+
+
+/*
+ * Allocate a new card-struct, initialize it
+ * link it into cards-list.
+ */
+static void
+act2000_alloccard(int bus, int port, int irq, char *id)
+{
+ int i;
+ act2000_card *card;
+ if (!(card = kzalloc(sizeof(act2000_card), GFP_KERNEL))) {
+ printk(KERN_WARNING
+ "act2000: (%s) Could not allocate card-struct.\n", id);
+ return;
+ }
+ spin_lock_init(&card->lock);
+ spin_lock_init(&card->mnlock);
+ skb_queue_head_init(&card->sndq);
+ skb_queue_head_init(&card->rcvq);
+ skb_queue_head_init(&card->ackq);
+ INIT_WORK(&card->snd_tq, act2000_transmit);
+ INIT_WORK(&card->rcv_tq, actcapi_dispatch);
+ INIT_WORK(&card->poll_tq, act2000_receive);
+ init_timer(&card->ptimer);
+ card->interface.owner = THIS_MODULE;
+ card->interface.channels = ACT2000_BCH;
+ card->interface.maxbufsize = 4000;
+ card->interface.command = if_command;
+ card->interface.writebuf_skb = if_sendbuf;
+ card->interface.writecmd = if_writecmd;
+ card->interface.readstat = if_readstatus;
+ card->interface.features =
+ ISDN_FEATURE_L2_X75I |
+ ISDN_FEATURE_L2_HDLC |
+ ISDN_FEATURE_L3_TRANS |
+ ISDN_FEATURE_P_UNKNOWN;
+ card->interface.hl_hdrlen = 20;
+ card->ptype = ISDN_PTYPE_EURO;
+ strlcpy(card->interface.id, id, sizeof(card->interface.id));
+ for (i=0; i<ACT2000_BCH; i++) {
+ card->bch[i].plci = 0x8000;
+ card->bch[i].ncci = 0x8000;
+ card->bch[i].l2prot = ISDN_PROTO_L2_X75I;
+ card->bch[i].l3prot = ISDN_PROTO_L3_TRANS;
+ }
+ card->myid = -1;
+ card->bus = bus;
+ card->port = port;
+ card->irq = irq;
+ card->next = cards;
+ cards = card;
+}
+
+/*
+ * register card at linklevel
+ */
+static int
+act2000_registercard(act2000_card * card)
+{
+ switch (card->bus) {
+ case ACT2000_BUS_ISA:
+ break;
+ case ACT2000_BUS_MCA:
+ case ACT2000_BUS_PCMCIA:
+ default:
+ printk(KERN_WARNING
+ "act2000: Illegal BUS type %d\n",
+ card->bus);
+ return -1;
+ }
+ if (!register_isdn(&card->interface)) {
+ printk(KERN_WARNING
+ "act2000: Unable to register %s\n",
+ card->interface.id);
+ return -1;
+ }
+ card->myid = card->interface.channels;
+ sprintf(card->regname, "act2000-isdn (%s)", card->interface.id);
+ return 0;
+}
+
+static void
+unregister_card(act2000_card * card)
+{
+ isdn_ctrl cmd;
+
+ cmd.command = ISDN_STAT_UNLOAD;
+ cmd.driver = card->myid;
+ card->interface.statcallb(&cmd);
+ switch (card->bus) {
+ case ACT2000_BUS_ISA:
+ act2000_isa_release(card);
+ break;
+ case ACT2000_BUS_MCA:
+ case ACT2000_BUS_PCMCIA:
+ default:
+ printk(KERN_WARNING
+ "act2000: Invalid BUS type %d\n",
+ card->bus);
+ break;
+ }
+}
+
+static int
+act2000_addcard(int bus, int port, int irq, char *id)
+{
+ act2000_card *p;
+ act2000_card *q = NULL;
+ int initialized;
+ int added = 0;
+ int failed = 0;
+ int i;
+
+ if (!bus)
+ bus = ACT2000_BUS_ISA;
+ if (port != -1) {
+ /* Port defined, do fixed setup */
+ act2000_alloccard(bus, port, irq, id);
+ } else {
+ /* No port defined, perform autoprobing.
+ * This may result in more than one card detected.
+ */
+ switch (bus) {
+ case ACT2000_BUS_ISA:
+ for (i = 0; i < ISA_NRPORTS; i++)
+ if (act2000_isa_detect(act2000_isa_ports[i])) {
+ printk(KERN_INFO
+ "act2000: Detected ISA card at port 0x%x\n",
+ act2000_isa_ports[i]);
+ act2000_alloccard(bus, act2000_isa_ports[i], irq, id);
+ }
+ break;
+ case ACT2000_BUS_MCA:
+ case ACT2000_BUS_PCMCIA:
+ default:
+ printk(KERN_WARNING
+ "act2000: addcard: Invalid BUS type %d\n",
+ bus);
+ }
+ }
+ if (!cards)
+ return 1;
+ p = cards;
+ while (p) {
+ initialized = 0;
+ if (!p->interface.statcallb) {
+ /* Not yet registered.
+ * Try to register and activate it.
+ */
+ added++;
+ switch (p->bus) {
+ case ACT2000_BUS_ISA:
+ if (act2000_isa_detect(p->port)) {
+ if (act2000_registercard(p))
+ break;
+ if (act2000_isa_config_port(p, p->port)) {
+ printk(KERN_WARNING
+ "act2000: Could not request port 0x%04x\n",
+ p->port);
+ unregister_card(p);
+ p->interface.statcallb = NULL;
+ break;
+ }
+ if (act2000_isa_config_irq(p, p->irq)) {
+ printk(KERN_INFO
+ "act2000: No IRQ available, fallback to polling\n");
+ /* Fall back to polled operation */
+ p->irq = 0;
+ }
+ printk(KERN_INFO
+ "act2000: ISA"
+ "-type card at port "
+ "0x%04x ",
+ p->port);
+ if (p->irq)
+ printk("irq %d\n", p->irq);
+ else
+ printk("polled\n");
+ initialized = 1;
+ }
+ break;
+ case ACT2000_BUS_MCA:
+ case ACT2000_BUS_PCMCIA:
+ default:
+ printk(KERN_WARNING
+ "act2000: addcard: Invalid BUS type %d\n",
+ p->bus);
+ }
+ } else
+ /* Card already initialized */
+ initialized = 1;
+ if (initialized) {
+ /* Init OK, next card ... */
+ q = p;
+ p = p->next;
+ } else {
+ /* Init failed, remove card from list, free memory */
+ printk(KERN_WARNING
+ "act2000: Initialization of %s failed\n",
+ p->interface.id);
+ if (q) {
+ q->next = p->next;
+ kfree(p);
+ p = q->next;
+ } else {
+ cards = p->next;
+ kfree(p);
+ p = cards;
+ }
+ failed++;
+ }
+ }
+ return (added - failed);
+}
+
+#define DRIVERNAME "IBM Active 2000 ISDN driver"
+
+static int __init act2000_init(void)
+{
+ printk(KERN_INFO "%s\n", DRIVERNAME);
+ if (!cards)
+ act2000_addcard(act_bus, act_port, act_irq, act_id);
+ if (!cards)
+ printk(KERN_INFO "act2000: No cards defined yet\n");
+ return 0;
+}
+
+static void __exit act2000_exit(void)
+{
+ act2000_card *card = cards;
+ act2000_card *last;
+ while (card) {
+ unregister_card(card);
+ del_timer(&card->ptimer);
+ card = card->next;
+ }
+ card = cards;
+ while (card) {
+ last = card;
+ card = card->next;
+ act2000_clear_msn(last);
+ kfree(last);
+ }
+ printk(KERN_INFO "%s unloaded\n", DRIVERNAME);
+}
+
+module_init(act2000_init);
+module_exit(act2000_exit);
diff --git a/drivers/isdn/capi/Kconfig b/drivers/isdn/capi/Kconfig
new file mode 100644
index 0000000..e1afd60
--- /dev/null
+++ b/drivers/isdn/capi/Kconfig
@@ -0,0 +1,62 @@
+#
+# Config.in for the CAPI subsystem
+#
+config ISDN_DRV_AVMB1_VERBOSE_REASON
+ bool "Verbose reason code reporting"
+ default y
+ help
+ If you say Y here, the CAPI drivers will give verbose reasons for
+ disconnecting. This will increase the size of the kernel by 7 KB. If
+ unsure, say Y.
+
+config CAPI_TRACE
+ bool "CAPI trace support"
+ default y
+ help
+ If you say Y here, the kernelcapi driver can make verbose traces
+ of CAPI messages. This feature can be enabled/disabled via IOCTL for
+ every controller (default disabled).
+ This will increase the size of the kernelcapi module by 20 KB.
+ If unsure, say Y.
+
+config ISDN_CAPI_MIDDLEWARE
+ bool "CAPI2.0 Middleware support (EXPERIMENTAL)"
+ depends on EXPERIMENTAL
+ help
+ This option will enhance the capabilities of the /dev/capi20
+ interface. It will provide a means of moving a data connection,
+ established via the usual /dev/capi20 interface to a special tty
+ device. If you want to use pppd with pppdcapiplugin to dial up to
+ your ISP, say Y here.
+
+config ISDN_CAPI_CAPI20
+ tristate "CAPI2.0 /dev/capi support"
+ help
+ This option will provide the CAPI 2.0 interface to userspace
+ applications via /dev/capi20. Applications should use the
+ standardized libcapi20 to access this functionality. You should say
+ Y/M here.
+
+config ISDN_CAPI_CAPIFS_BOOL
+ bool "CAPI2.0 filesystem support"
+ depends on ISDN_CAPI_MIDDLEWARE && ISDN_CAPI_CAPI20
+
+config ISDN_CAPI_CAPIFS
+ tristate
+ depends on ISDN_CAPI_CAPIFS_BOOL
+ default ISDN_CAPI_CAPI20
+ help
+ This option provides a special file system, similar to /dev/pts with
+ device nodes for the special ttys established by using the
+ middleware extension above. If you want to use pppd with
+ pppdcapiplugin to dial up to your ISP, say Y here.
+
+config ISDN_CAPI_CAPIDRV
+ tristate "CAPI2.0 capidrv interface support"
+ depends on ISDN_I4L
+ help
+ This option provides the glue code to hook up CAPI driven cards to
+ the legacy isdn4linux link layer. If you have a card which is
+ supported by a CAPI driver, but still want to use old features like
+ ippp interfaces or ttyI emulation, say Y/M here.
+
diff --git a/drivers/isdn/capi/Makefile b/drivers/isdn/capi/Makefile
new file mode 100644
index 0000000..57123e3
--- /dev/null
+++ b/drivers/isdn/capi/Makefile
@@ -0,0 +1,15 @@
+# Makefile for the CAPI subsystem.
+
+# Ordering constraints: kernelcapi.o first
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_ISDN_CAPI) += kernelcapi.o
+obj-$(CONFIG_ISDN_CAPI_CAPI20) += capi.o
+obj-$(CONFIG_ISDN_CAPI_CAPIDRV) += capidrv.o
+obj-$(CONFIG_ISDN_CAPI_CAPIFS) += capifs.o
+
+# Multipart objects.
+
+kernelcapi-y := kcapi.o capiutil.o capilib.o
+kernelcapi-$(CONFIG_PROC_FS) += kcapi_proc.o
diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c
new file mode 100644
index 0000000..1b5bf87
--- /dev/null
+++ b/drivers/isdn/capi/capi.c
@@ -0,0 +1,1599 @@
+/* $Id: capi.c,v 1.1.2.7 2004/04/28 09:48:59 armin Exp $
+ *
+ * CAPI 2.0 Interface for Linux
+ *
+ * Copyright 1996 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/fcntl.h>
+#include <linux/fs.h>
+#include <linux/signal.h>
+#include <linux/mutex.h>
+#include <linux/mm.h>
+#include <linux/smp_lock.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+#include <linux/tty.h>
+#ifdef CONFIG_PPP
+#include <linux/netdevice.h>
+#include <linux/ppp_defs.h>
+#include <linux/if_ppp.h>
+#endif /* CONFIG_PPP */
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+#include <linux/skbuff.h>
+#include <linux/proc_fs.h>
+#include <linux/poll.h>
+#include <linux/capi.h>
+#include <linux/kernelcapi.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/moduleparam.h>
+#include <linux/isdn/capiutil.h>
+#include <linux/isdn/capicmd.h>
+#if defined(CONFIG_ISDN_CAPI_CAPIFS) || defined(CONFIG_ISDN_CAPI_CAPIFS_MODULE)
+#include "capifs.h"
+#endif
+
+static char *revision = "$Revision: 1.1.2.7 $";
+
+MODULE_DESCRIPTION("CAPI4Linux: Userspace /dev/capi20 interface");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+
+#undef _DEBUG_REFCOUNT /* alloc/free and open/close debug */
+#undef _DEBUG_TTYFUNCS /* call to tty_driver */
+#undef _DEBUG_DATAFLOW /* data flow */
+
+/* -------- driver information -------------------------------------- */
+
+static struct class *capi_class;
+
+static int capi_major = 68; /* allocated */
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+#define CAPINC_NR_PORTS 32
+#define CAPINC_MAX_PORTS 256
+static int capi_ttymajor = 191;
+static int capi_ttyminors = CAPINC_NR_PORTS;
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+module_param_named(major, capi_major, uint, 0);
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+module_param_named(ttymajor, capi_ttymajor, uint, 0);
+module_param_named(ttyminors, capi_ttyminors, uint, 0);
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+/* -------- defines ------------------------------------------------- */
+
+#define CAPINC_MAX_RECVQUEUE 10
+#define CAPINC_MAX_SENDQUEUE 10
+#define CAPI_MAX_BLKSIZE 2048
+
+/* -------- data structures ----------------------------------------- */
+
+struct capidev;
+struct capincci;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+struct capiminor;
+
+struct datahandle_queue {
+ struct list_head list;
+ u16 datahandle;
+};
+
+struct capiminor {
+ struct list_head list;
+ struct capincci *nccip;
+ unsigned int minor;
+
+ struct capi20_appl *ap;
+ u32 ncci;
+ u16 datahandle;
+ u16 msgid;
+
+ struct tty_struct *tty;
+ int ttyinstop;
+ int ttyoutstop;
+ struct sk_buff *ttyskb;
+ atomic_t ttyopencount;
+
+ struct sk_buff_head inqueue;
+ int inbytes;
+ struct sk_buff_head outqueue;
+ int outbytes;
+
+ /* transmit path */
+ struct list_head ackqueue;
+ int nack;
+ spinlock_t ackqlock;
+};
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+/* FIXME: The following lock is a sledgehammer-workaround to a
+ * locking issue with the capiminor (and maybe other) data structure(s).
+ * Access to this data is done in a racy way and crashes the machine with
+ * a FritzCard DSL driver; sooner or later. This is a workaround
+ * which trades scalability vs stability, so it doesn't crash the kernel anymore.
+ * The correct (and scalable) fix for the issue seems to require
+ * an API change to the drivers... . */
+static DEFINE_SPINLOCK(workaround_lock);
+
+struct capincci {
+ struct capincci *next;
+ u32 ncci;
+ struct capidev *cdev;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+ struct capiminor *minorp;
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+};
+
+struct capidev {
+ struct list_head list;
+ struct capi20_appl ap;
+ u16 errcode;
+ unsigned userflags;
+
+ struct sk_buff_head recvqueue;
+ wait_queue_head_t recvwait;
+
+ struct capincci *nccis;
+
+ struct mutex ncci_list_mtx;
+};
+
+/* -------- global variables ---------------------------------------- */
+
+static DEFINE_RWLOCK(capidev_list_lock);
+static LIST_HEAD(capidev_list);
+
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+static DEFINE_RWLOCK(capiminor_list_lock);
+static LIST_HEAD(capiminor_list);
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+/* -------- datahandles --------------------------------------------- */
+
+static int capincci_add_ack(struct capiminor *mp, u16 datahandle)
+{
+ struct datahandle_queue *n;
+ unsigned long flags;
+
+ n = kmalloc(sizeof(*n), GFP_ATOMIC);
+ if (unlikely(!n)) {
+ printk(KERN_ERR "capi: alloc datahandle failed\n");
+ return -1;
+ }
+ n->datahandle = datahandle;
+ INIT_LIST_HEAD(&n->list);
+ spin_lock_irqsave(&mp->ackqlock, flags);
+ list_add_tail(&n->list, &mp->ackqueue);
+ mp->nack++;
+ spin_unlock_irqrestore(&mp->ackqlock, flags);
+ return 0;
+}
+
+static int capiminor_del_ack(struct capiminor *mp, u16 datahandle)
+{
+ struct datahandle_queue *p, *tmp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mp->ackqlock, flags);
+ list_for_each_entry_safe(p, tmp, &mp->ackqueue, list) {
+ if (p->datahandle == datahandle) {
+ list_del(&p->list);
+ kfree(p);
+ mp->nack--;
+ spin_unlock_irqrestore(&mp->ackqlock, flags);
+ return 0;
+ }
+ }
+ spin_unlock_irqrestore(&mp->ackqlock, flags);
+ return -1;
+}
+
+static void capiminor_del_all_ack(struct capiminor *mp)
+{
+ struct datahandle_queue *p, *tmp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&mp->ackqlock, flags);
+ list_for_each_entry_safe(p, tmp, &mp->ackqueue, list) {
+ list_del(&p->list);
+ kfree(p);
+ mp->nack--;
+ }
+ spin_unlock_irqrestore(&mp->ackqlock, flags);
+}
+
+
+/* -------- struct capiminor ---------------------------------------- */
+
+static struct capiminor *capiminor_alloc(struct capi20_appl *ap, u32 ncci)
+{
+ struct capiminor *mp, *p;
+ unsigned int minor = 0;
+ unsigned long flags;
+
+ mp = kzalloc(sizeof(*mp), GFP_ATOMIC);
+ if (!mp) {
+ printk(KERN_ERR "capi: can't alloc capiminor\n");
+ return NULL;
+ }
+
+ mp->ap = ap;
+ mp->ncci = ncci;
+ mp->msgid = 0;
+ atomic_set(&mp->ttyopencount,0);
+ INIT_LIST_HEAD(&mp->ackqueue);
+ spin_lock_init(&mp->ackqlock);
+
+ skb_queue_head_init(&mp->inqueue);
+ skb_queue_head_init(&mp->outqueue);
+
+ /* Allocate the least unused minor number.
+ */
+ write_lock_irqsave(&capiminor_list_lock, flags);
+ if (list_empty(&capiminor_list))
+ list_add(&mp->list, &capiminor_list);
+ else {
+ list_for_each_entry(p, &capiminor_list, list) {
+ if (p->minor > minor)
+ break;
+ minor++;
+ }
+
+ if (minor < capi_ttyminors) {
+ mp->minor = minor;
+ list_add(&mp->list, p->list.prev);
+ }
+ }
+ write_unlock_irqrestore(&capiminor_list_lock, flags);
+
+ if (!(minor < capi_ttyminors)) {
+ printk(KERN_NOTICE "capi: out of minors\n");
+ kfree(mp);
+ return NULL;
+ }
+
+ return mp;
+}
+
+static void capiminor_free(struct capiminor *mp)
+{
+ unsigned long flags;
+
+ write_lock_irqsave(&capiminor_list_lock, flags);
+ list_del(&mp->list);
+ write_unlock_irqrestore(&capiminor_list_lock, flags);
+
+ if (mp->ttyskb) kfree_skb(mp->ttyskb);
+ mp->ttyskb = NULL;
+ skb_queue_purge(&mp->inqueue);
+ skb_queue_purge(&mp->outqueue);
+ capiminor_del_all_ack(mp);
+ kfree(mp);
+}
+
+static struct capiminor *capiminor_find(unsigned int minor)
+{
+ struct list_head *l;
+ struct capiminor *p = NULL;
+
+ read_lock(&capiminor_list_lock);
+ list_for_each(l, &capiminor_list) {
+ p = list_entry(l, struct capiminor, list);
+ if (p->minor == minor)
+ break;
+ }
+ read_unlock(&capiminor_list_lock);
+ if (l == &capiminor_list)
+ return NULL;
+
+ return p;
+}
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+/* -------- struct capincci ----------------------------------------- */
+
+static struct capincci *capincci_alloc(struct capidev *cdev, u32 ncci)
+{
+ struct capincci *np, **pp;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+ struct capiminor *mp = NULL;
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+ np = kzalloc(sizeof(*np), GFP_ATOMIC);
+ if (!np)
+ return NULL;
+ np->ncci = ncci;
+ np->cdev = cdev;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+ mp = NULL;
+ if (cdev->userflags & CAPIFLAG_HIGHJACKING)
+ mp = np->minorp = capiminor_alloc(&cdev->ap, ncci);
+ if (mp) {
+ mp->nccip = np;
+#ifdef _DEBUG_REFCOUNT
+ printk(KERN_DEBUG "set mp->nccip\n");
+#endif
+#if defined(CONFIG_ISDN_CAPI_CAPIFS) || defined(CONFIG_ISDN_CAPI_CAPIFS_MODULE)
+ capifs_new_ncci(mp->minor, MKDEV(capi_ttymajor, mp->minor));
+#endif
+ }
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+ for (pp=&cdev->nccis; *pp; pp = &(*pp)->next)
+ ;
+ *pp = np;
+ return np;
+}
+
+static void capincci_free(struct capidev *cdev, u32 ncci)
+{
+ struct capincci *np, **pp;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+ struct capiminor *mp;
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+ pp=&cdev->nccis;
+ while (*pp) {
+ np = *pp;
+ if (ncci == 0xffffffff || np->ncci == ncci) {
+ *pp = (*pp)->next;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+ if ((mp = np->minorp) != NULL) {
+#if defined(CONFIG_ISDN_CAPI_CAPIFS) || defined(CONFIG_ISDN_CAPI_CAPIFS_MODULE)
+ capifs_free_ncci(mp->minor);
+#endif
+ if (mp->tty) {
+ mp->nccip = NULL;
+#ifdef _DEBUG_REFCOUNT
+ printk(KERN_DEBUG "reset mp->nccip\n");
+#endif
+ tty_hangup(mp->tty);
+ } else {
+ capiminor_free(mp);
+ }
+ }
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+ kfree(np);
+ if (*pp == NULL) return;
+ } else {
+ pp = &(*pp)->next;
+ }
+ }
+}
+
+static struct capincci *capincci_find(struct capidev *cdev, u32 ncci)
+{
+ struct capincci *p;
+
+ for (p=cdev->nccis; p ; p = p->next) {
+ if (p->ncci == ncci)
+ break;
+ }
+ return p;
+}
+
+/* -------- struct capidev ------------------------------------------ */
+
+static struct capidev *capidev_alloc(void)
+{
+ struct capidev *cdev;
+ unsigned long flags;
+
+ cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
+ if (!cdev)
+ return NULL;
+
+ mutex_init(&cdev->ncci_list_mtx);
+ skb_queue_head_init(&cdev->recvqueue);
+ init_waitqueue_head(&cdev->recvwait);
+ write_lock_irqsave(&capidev_list_lock, flags);
+ list_add_tail(&cdev->list, &capidev_list);
+ write_unlock_irqrestore(&capidev_list_lock, flags);
+ return cdev;
+}
+
+static void capidev_free(struct capidev *cdev)
+{
+ unsigned long flags;
+
+ if (cdev->ap.applid) {
+ capi20_release(&cdev->ap);
+ cdev->ap.applid = 0;
+ }
+ skb_queue_purge(&cdev->recvqueue);
+
+ mutex_lock(&cdev->ncci_list_mtx);
+ capincci_free(cdev, 0xffffffff);
+ mutex_unlock(&cdev->ncci_list_mtx);
+
+ write_lock_irqsave(&capidev_list_lock, flags);
+ list_del(&cdev->list);
+ write_unlock_irqrestore(&capidev_list_lock, flags);
+ kfree(cdev);
+}
+
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+/* -------- handle data queue --------------------------------------- */
+
+static struct sk_buff *
+gen_data_b3_resp_for(struct capiminor *mp, struct sk_buff *skb)
+{
+ struct sk_buff *nskb;
+ nskb = alloc_skb(CAPI_DATA_B3_RESP_LEN, GFP_ATOMIC);
+ if (nskb) {
+ u16 datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4+4+2);
+ unsigned char *s = skb_put(nskb, CAPI_DATA_B3_RESP_LEN);
+ capimsg_setu16(s, 0, CAPI_DATA_B3_RESP_LEN);
+ capimsg_setu16(s, 2, mp->ap->applid);
+ capimsg_setu8 (s, 4, CAPI_DATA_B3);
+ capimsg_setu8 (s, 5, CAPI_RESP);
+ capimsg_setu16(s, 6, mp->msgid++);
+ capimsg_setu32(s, 8, mp->ncci);
+ capimsg_setu16(s, 12, datahandle);
+ }
+ return nskb;
+}
+
+static int handle_recv_skb(struct capiminor *mp, struct sk_buff *skb)
+{
+ struct sk_buff *nskb;
+ int datalen;
+ u16 errcode, datahandle;
+ struct tty_ldisc *ld;
+
+ datalen = skb->len - CAPIMSG_LEN(skb->data);
+ if (mp->tty == NULL)
+ {
+#ifdef _DEBUG_DATAFLOW
+ printk(KERN_DEBUG "capi: currently no receiver\n");
+#endif
+ return -1;
+ }
+
+ ld = tty_ldisc_ref(mp->tty);
+ if (ld == NULL)
+ return -1;
+ if (ld->ops->receive_buf == NULL) {
+#if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS)
+ printk(KERN_DEBUG "capi: ldisc has no receive_buf function\n");
+#endif
+ goto bad;
+ }
+ if (mp->ttyinstop) {
+#if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS)
+ printk(KERN_DEBUG "capi: recv tty throttled\n");
+#endif
+ goto bad;
+ }
+ if (mp->tty->receive_room < datalen) {
+#if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS)
+ printk(KERN_DEBUG "capi: no room in tty\n");
+#endif
+ goto bad;
+ }
+ if ((nskb = gen_data_b3_resp_for(mp, skb)) == NULL) {
+ printk(KERN_ERR "capi: gen_data_b3_resp failed\n");
+ goto bad;
+ }
+ datahandle = CAPIMSG_U16(skb->data,CAPIMSG_BASELEN+4);
+ errcode = capi20_put_message(mp->ap, nskb);
+ if (errcode != CAPI_NOERROR) {
+ printk(KERN_ERR "capi: send DATA_B3_RESP failed=%x\n",
+ errcode);
+ kfree_skb(nskb);
+ goto bad;
+ }
+ (void)skb_pull(skb, CAPIMSG_LEN(skb->data));
+#ifdef _DEBUG_DATAFLOW
+ printk(KERN_DEBUG "capi: DATA_B3_RESP %u len=%d => ldisc\n",
+ datahandle, skb->len);
+#endif
+ ld->ops->receive_buf(mp->tty, skb->data, NULL, skb->len);
+ kfree_skb(skb);
+ tty_ldisc_deref(ld);
+ return 0;
+bad:
+ tty_ldisc_deref(ld);
+ return -1;
+}
+
+static void handle_minor_recv(struct capiminor *mp)
+{
+ struct sk_buff *skb;
+ while ((skb = skb_dequeue(&mp->inqueue)) != NULL) {
+ unsigned int len = skb->len;
+ mp->inbytes -= len;
+ if (handle_recv_skb(mp, skb) < 0) {
+ skb_queue_head(&mp->inqueue, skb);
+ mp->inbytes += len;
+ return;
+ }
+ }
+}
+
+static int handle_minor_send(struct capiminor *mp)
+{
+ struct sk_buff *skb;
+ u16 len;
+ int count = 0;
+ u16 errcode;
+ u16 datahandle;
+
+ if (mp->tty && mp->ttyoutstop) {
+#if defined(_DEBUG_DATAFLOW) || defined(_DEBUG_TTYFUNCS)
+ printk(KERN_DEBUG "capi: send: tty stopped\n");
+#endif
+ return 0;
+ }
+
+ while ((skb = skb_dequeue(&mp->outqueue)) != NULL) {
+ datahandle = mp->datahandle;
+ len = (u16)skb->len;
+ skb_push(skb, CAPI_DATA_B3_REQ_LEN);
+ memset(skb->data, 0, CAPI_DATA_B3_REQ_LEN);
+ capimsg_setu16(skb->data, 0, CAPI_DATA_B3_REQ_LEN);
+ capimsg_setu16(skb->data, 2, mp->ap->applid);
+ capimsg_setu8 (skb->data, 4, CAPI_DATA_B3);
+ capimsg_setu8 (skb->data, 5, CAPI_REQ);
+ capimsg_setu16(skb->data, 6, mp->msgid++);
+ capimsg_setu32(skb->data, 8, mp->ncci); /* NCCI */
+ capimsg_setu32(skb->data, 12, (u32)(long)skb->data);/* Data32 */
+ capimsg_setu16(skb->data, 16, len); /* Data length */
+ capimsg_setu16(skb->data, 18, datahandle);
+ capimsg_setu16(skb->data, 20, 0); /* Flags */
+
+ if (capincci_add_ack(mp, datahandle) < 0) {
+ skb_pull(skb, CAPI_DATA_B3_REQ_LEN);
+ skb_queue_head(&mp->outqueue, skb);
+ return count;
+ }
+ errcode = capi20_put_message(mp->ap, skb);
+ if (errcode == CAPI_NOERROR) {
+ mp->datahandle++;
+ count++;
+ mp->outbytes -= len;
+#ifdef _DEBUG_DATAFLOW
+ printk(KERN_DEBUG "capi: DATA_B3_REQ %u len=%u\n",
+ datahandle, len);
+#endif
+ continue;
+ }
+ capiminor_del_ack(mp, datahandle);
+
+ if (errcode == CAPI_SENDQUEUEFULL) {
+ skb_pull(skb, CAPI_DATA_B3_REQ_LEN);
+ skb_queue_head(&mp->outqueue, skb);
+ break;
+ }
+
+ /* ups, drop packet */
+ printk(KERN_ERR "capi: put_message = %x\n", errcode);
+ mp->outbytes -= len;
+ kfree_skb(skb);
+ }
+ return count;
+}
+
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+/* -------- function called by lower level -------------------------- */
+
+static void capi_recv_message(struct capi20_appl *ap, struct sk_buff *skb)
+{
+ struct capidev *cdev = ap->private;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+ struct capiminor *mp;
+ u16 datahandle;
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+ struct capincci *np;
+ u32 ncci;
+ unsigned long flags;
+
+ if (CAPIMSG_CMD(skb->data) == CAPI_CONNECT_B3_CONF) {
+ u16 info = CAPIMSG_U16(skb->data, 12); // Info field
+ if (info == 0) {
+ mutex_lock(&cdev->ncci_list_mtx);
+ capincci_alloc(cdev, CAPIMSG_NCCI(skb->data));
+ mutex_unlock(&cdev->ncci_list_mtx);
+ }
+ }
+ if (CAPIMSG_CMD(skb->data) == CAPI_CONNECT_B3_IND) {
+ mutex_lock(&cdev->ncci_list_mtx);
+ capincci_alloc(cdev, CAPIMSG_NCCI(skb->data));
+ mutex_unlock(&cdev->ncci_list_mtx);
+ }
+ spin_lock_irqsave(&workaround_lock, flags);
+ if (CAPIMSG_COMMAND(skb->data) != CAPI_DATA_B3) {
+ skb_queue_tail(&cdev->recvqueue, skb);
+ wake_up_interruptible(&cdev->recvwait);
+ spin_unlock_irqrestore(&workaround_lock, flags);
+ return;
+ }
+ ncci = CAPIMSG_CONTROL(skb->data);
+ for (np = cdev->nccis; np && np->ncci != ncci; np = np->next)
+ ;
+ if (!np) {
+ printk(KERN_ERR "BUG: capi_signal: ncci not found\n");
+ skb_queue_tail(&cdev->recvqueue, skb);
+ wake_up_interruptible(&cdev->recvwait);
+ spin_unlock_irqrestore(&workaround_lock, flags);
+ return;
+ }
+#ifndef CONFIG_ISDN_CAPI_MIDDLEWARE
+ skb_queue_tail(&cdev->recvqueue, skb);
+ wake_up_interruptible(&cdev->recvwait);
+#else /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+ mp = np->minorp;
+ if (!mp) {
+ skb_queue_tail(&cdev->recvqueue, skb);
+ wake_up_interruptible(&cdev->recvwait);
+ spin_unlock_irqrestore(&workaround_lock, flags);
+ return;
+ }
+
+
+ if (CAPIMSG_SUBCOMMAND(skb->data) == CAPI_IND) {
+
+ datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN+4+4+2);
+#ifdef _DEBUG_DATAFLOW
+ printk(KERN_DEBUG "capi_signal: DATA_B3_IND %u len=%d\n",
+ datahandle, skb->len-CAPIMSG_LEN(skb->data));
+#endif
+ skb_queue_tail(&mp->inqueue, skb);
+ mp->inbytes += skb->len;
+ handle_minor_recv(mp);
+
+ } else if (CAPIMSG_SUBCOMMAND(skb->data) == CAPI_CONF) {
+
+ datahandle = CAPIMSG_U16(skb->data, CAPIMSG_BASELEN+4);
+#ifdef _DEBUG_DATAFLOW
+ printk(KERN_DEBUG "capi_signal: DATA_B3_CONF %u 0x%x\n",
+ datahandle,
+ CAPIMSG_U16(skb->data, CAPIMSG_BASELEN+4+2));
+#endif
+ kfree_skb(skb);
+ (void)capiminor_del_ack(mp, datahandle);
+ if (mp->tty)
+ tty_wakeup(mp->tty);
+ (void)handle_minor_send(mp);
+
+ } else {
+ /* ups, let capi application handle it :-) */
+ skb_queue_tail(&cdev->recvqueue, skb);
+ wake_up_interruptible(&cdev->recvwait);
+ }
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+ spin_unlock_irqrestore(&workaround_lock, flags);
+}
+
+/* -------- file_operations for capidev ----------------------------- */
+
+static ssize_t
+capi_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+{
+ struct capidev *cdev = (struct capidev *)file->private_data;
+ struct sk_buff *skb;
+ size_t copied;
+
+ if (!cdev->ap.applid)
+ return -ENODEV;
+
+ if ((skb = skb_dequeue(&cdev->recvqueue)) == NULL) {
+
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+
+ for (;;) {
+ interruptible_sleep_on(&cdev->recvwait);
+ if ((skb = skb_dequeue(&cdev->recvqueue)) != NULL)
+ break;
+ if (signal_pending(current))
+ break;
+ }
+ if (skb == NULL)
+ return -ERESTARTNOHAND;
+ }
+ if (skb->len > count) {
+ skb_queue_head(&cdev->recvqueue, skb);
+ return -EMSGSIZE;
+ }
+ if (copy_to_user(buf, skb->data, skb->len)) {
+ skb_queue_head(&cdev->recvqueue, skb);
+ return -EFAULT;
+ }
+ copied = skb->len;
+
+ kfree_skb(skb);
+
+ return copied;
+}
+
+static ssize_t
+capi_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
+{
+ struct capidev *cdev = (struct capidev *)file->private_data;
+ struct sk_buff *skb;
+ u16 mlen;
+
+ if (!cdev->ap.applid)
+ return -ENODEV;
+
+ skb = alloc_skb(count, GFP_USER);
+ if (!skb)
+ return -ENOMEM;
+
+ if (copy_from_user(skb_put(skb, count), buf, count)) {
+ kfree_skb(skb);
+ return -EFAULT;
+ }
+ mlen = CAPIMSG_LEN(skb->data);
+ if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_REQ) {
+ if ((size_t)(mlen + CAPIMSG_DATALEN(skb->data)) != count) {
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+ } else {
+ if (mlen != count) {
+ kfree_skb(skb);
+ return -EINVAL;
+ }
+ }
+ CAPIMSG_SETAPPID(skb->data, cdev->ap.applid);
+
+ if (CAPIMSG_CMD(skb->data) == CAPI_DISCONNECT_B3_RESP) {
+ mutex_lock(&cdev->ncci_list_mtx);
+ capincci_free(cdev, CAPIMSG_NCCI(skb->data));
+ mutex_unlock(&cdev->ncci_list_mtx);
+ }
+
+ cdev->errcode = capi20_put_message(&cdev->ap, skb);
+
+ if (cdev->errcode) {
+ kfree_skb(skb);
+ return -EIO;
+ }
+ return count;
+}
+
+static unsigned int
+capi_poll(struct file *file, poll_table * wait)
+{
+ struct capidev *cdev = (struct capidev *)file->private_data;
+ unsigned int mask = 0;
+
+ if (!cdev->ap.applid)
+ return POLLERR;
+
+ poll_wait(file, &(cdev->recvwait), wait);
+ mask = POLLOUT | POLLWRNORM;
+ if (!skb_queue_empty(&cdev->recvqueue))
+ mask |= POLLIN | POLLRDNORM;
+ return mask;
+}
+
+static int
+capi_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct capidev *cdev = file->private_data;
+ struct capi20_appl *ap = &cdev->ap;
+ capi_ioctl_struct data;
+ int retval = -EINVAL;
+ void __user *argp = (void __user *)arg;
+
+ switch (cmd) {
+ case CAPI_REGISTER:
+ {
+ if (ap->applid)
+ return -EEXIST;
+
+ if (copy_from_user(&cdev->ap.rparam, argp,
+ sizeof(struct capi_register_params)))
+ return -EFAULT;
+
+ cdev->ap.private = cdev;
+ cdev->ap.recv_message = capi_recv_message;
+ cdev->errcode = capi20_register(ap);
+ if (cdev->errcode) {
+ ap->applid = 0;
+ return -EIO;
+ }
+ }
+ return (int)ap->applid;
+
+ case CAPI_GET_VERSION:
+ {
+ if (copy_from_user(&data.contr, argp,
+ sizeof(data.contr)))
+ return -EFAULT;
+ cdev->errcode = capi20_get_version(data.contr, &data.version);
+ if (cdev->errcode)
+ return -EIO;
+ if (copy_to_user(argp, &data.version,
+ sizeof(data.version)))
+ return -EFAULT;
+ }
+ return 0;
+
+ case CAPI_GET_SERIAL:
+ {
+ if (copy_from_user(&data.contr, argp,
+ sizeof(data.contr)))
+ return -EFAULT;
+ cdev->errcode = capi20_get_serial (data.contr, data.serial);
+ if (cdev->errcode)
+ return -EIO;
+ if (copy_to_user(argp, data.serial,
+ sizeof(data.serial)))
+ return -EFAULT;
+ }
+ return 0;
+ case CAPI_GET_PROFILE:
+ {
+ if (copy_from_user(&data.contr, argp,
+ sizeof(data.contr)))
+ return -EFAULT;
+
+ if (data.contr == 0) {
+ cdev->errcode = capi20_get_profile(data.contr, &data.profile);
+ if (cdev->errcode)
+ return -EIO;
+
+ retval = copy_to_user(argp,
+ &data.profile.ncontroller,
+ sizeof(data.profile.ncontroller));
+
+ } else {
+ cdev->errcode = capi20_get_profile(data.contr, &data.profile);
+ if (cdev->errcode)
+ return -EIO;
+
+ retval = copy_to_user(argp, &data.profile,
+ sizeof(data.profile));
+ }
+ if (retval)
+ return -EFAULT;
+ }
+ return 0;
+
+ case CAPI_GET_MANUFACTURER:
+ {
+ if (copy_from_user(&data.contr, argp,
+ sizeof(data.contr)))
+ return -EFAULT;
+ cdev->errcode = capi20_get_manufacturer(data.contr, data.manufacturer);
+ if (cdev->errcode)
+ return -EIO;
+
+ if (copy_to_user(argp, data.manufacturer,
+ sizeof(data.manufacturer)))
+ return -EFAULT;
+
+ }
+ return 0;
+ case CAPI_GET_ERRCODE:
+ data.errcode = cdev->errcode;
+ cdev->errcode = CAPI_NOERROR;
+ if (arg) {
+ if (copy_to_user(argp, &data.errcode,
+ sizeof(data.errcode)))
+ return -EFAULT;
+ }
+ return data.errcode;
+
+ case CAPI_INSTALLED:
+ if (capi20_isinstalled() == CAPI_NOERROR)
+ return 0;
+ return -ENXIO;
+
+ case CAPI_MANUFACTURER_CMD:
+ {
+ struct capi_manufacturer_cmd mcmd;
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (copy_from_user(&mcmd, argp, sizeof(mcmd)))
+ return -EFAULT;
+ return capi20_manufacturer(mcmd.cmd, mcmd.data);
+ }
+ return 0;
+
+ case CAPI_SET_FLAGS:
+ case CAPI_CLR_FLAGS:
+ {
+ unsigned userflags;
+ if (copy_from_user(&userflags, argp,
+ sizeof(userflags)))
+ return -EFAULT;
+ if (cmd == CAPI_SET_FLAGS)
+ cdev->userflags |= userflags;
+ else
+ cdev->userflags &= ~userflags;
+ }
+ return 0;
+
+ case CAPI_GET_FLAGS:
+ if (copy_to_user(argp, &cdev->userflags,
+ sizeof(cdev->userflags)))
+ return -EFAULT;
+ return 0;
+
+ case CAPI_NCCI_OPENCOUNT:
+ {
+ struct capincci *nccip;
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+ struct capiminor *mp;
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+ unsigned ncci;
+ int count = 0;
+ if (copy_from_user(&ncci, argp, sizeof(ncci)))
+ return -EFAULT;
+
+ mutex_lock(&cdev->ncci_list_mtx);
+ if ((nccip = capincci_find(cdev, (u32) ncci)) == NULL) {
+ mutex_unlock(&cdev->ncci_list_mtx);
+ return 0;
+ }
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+ if ((mp = nccip->minorp) != NULL) {
+ count += atomic_read(&mp->ttyopencount);
+ }
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+ mutex_unlock(&cdev->ncci_list_mtx);
+ return count;
+ }
+ return 0;
+
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+ case CAPI_NCCI_GETUNIT:
+ {
+ struct capincci *nccip;
+ struct capiminor *mp;
+ unsigned ncci;
+ int unit = 0;
+ if (copy_from_user(&ncci, argp,
+ sizeof(ncci)))
+ return -EFAULT;
+ mutex_lock(&cdev->ncci_list_mtx);
+ nccip = capincci_find(cdev, (u32) ncci);
+ if (!nccip || (mp = nccip->minorp) == NULL) {
+ mutex_unlock(&cdev->ncci_list_mtx);
+ return -ESRCH;
+ }
+ unit = mp->minor;
+ mutex_unlock(&cdev->ncci_list_mtx);
+ return unit;
+ }
+ return 0;
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+ }
+ return -EINVAL;
+}
+
+static int
+capi_open(struct inode *inode, struct file *file)
+{
+ int ret;
+
+ lock_kernel();
+ if (file->private_data)
+ ret = -EEXIST;
+ else if ((file->private_data = capidev_alloc()) == NULL)
+ ret = -ENOMEM;
+ else
+ ret = nonseekable_open(inode, file);
+ unlock_kernel();
+ return ret;
+}
+
+static int
+capi_release(struct inode *inode, struct file *file)
+{
+ struct capidev *cdev = (struct capidev *)file->private_data;
+
+ capidev_free(cdev);
+ file->private_data = NULL;
+
+ return 0;
+}
+
+static const struct file_operations capi_fops =
+{
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = capi_read,
+ .write = capi_write,
+ .poll = capi_poll,
+ .ioctl = capi_ioctl,
+ .open = capi_open,
+ .release = capi_release,
+};
+
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+/* -------- tty_operations for capincci ----------------------------- */
+
+static int capinc_tty_open(struct tty_struct * tty, struct file * file)
+{
+ struct capiminor *mp;
+ unsigned long flags;
+
+ if ((mp = capiminor_find(iminor(file->f_path.dentry->d_inode))) == NULL)
+ return -ENXIO;
+ if (mp->nccip == NULL)
+ return -ENXIO;
+
+ tty->driver_data = (void *)mp;
+
+ spin_lock_irqsave(&workaround_lock, flags);
+ if (atomic_read(&mp->ttyopencount) == 0)
+ mp->tty = tty;
+ atomic_inc(&mp->ttyopencount);
+#ifdef _DEBUG_REFCOUNT
+ printk(KERN_DEBUG "capinc_tty_open ocount=%d\n", atomic_read(&mp->ttyopencount));
+#endif
+ handle_minor_recv(mp);
+ spin_unlock_irqrestore(&workaround_lock, flags);
+ return 0;
+}
+
+static void capinc_tty_close(struct tty_struct * tty, struct file * file)
+{
+ struct capiminor *mp;
+
+ mp = (struct capiminor *)tty->driver_data;
+ if (mp) {
+ if (atomic_dec_and_test(&mp->ttyopencount)) {
+#ifdef _DEBUG_REFCOUNT
+ printk(KERN_DEBUG "capinc_tty_close lastclose\n");
+#endif
+ tty->driver_data = NULL;
+ mp->tty = NULL;
+ }
+#ifdef _DEBUG_REFCOUNT
+ printk(KERN_DEBUG "capinc_tty_close ocount=%d\n", atomic_read(&mp->ttyopencount));
+#endif
+ if (mp->nccip == NULL)
+ capiminor_free(mp);
+ }
+
+#ifdef _DEBUG_REFCOUNT
+ printk(KERN_DEBUG "capinc_tty_close\n");
+#endif
+}
+
+static int capinc_tty_write(struct tty_struct * tty,
+ const unsigned char *buf, int count)
+{
+ struct capiminor *mp = (struct capiminor *)tty->driver_data;
+ struct sk_buff *skb;
+ unsigned long flags;
+
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_write(count=%d)\n", count);
+#endif
+
+ if (!mp || !mp->nccip) {
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_write: mp or mp->ncci NULL\n");
+#endif
+ return 0;
+ }
+
+ spin_lock_irqsave(&workaround_lock, flags);
+ skb = mp->ttyskb;
+ if (skb) {
+ mp->ttyskb = NULL;
+ skb_queue_tail(&mp->outqueue, skb);
+ mp->outbytes += skb->len;
+ }
+
+ skb = alloc_skb(CAPI_DATA_B3_REQ_LEN+count, GFP_ATOMIC);
+ if (!skb) {
+ printk(KERN_ERR "capinc_tty_write: alloc_skb failed\n");
+ spin_unlock_irqrestore(&workaround_lock, flags);
+ return -ENOMEM;
+ }
+
+ skb_reserve(skb, CAPI_DATA_B3_REQ_LEN);
+ memcpy(skb_put(skb, count), buf, count);
+
+ skb_queue_tail(&mp->outqueue, skb);
+ mp->outbytes += skb->len;
+ (void)handle_minor_send(mp);
+ (void)handle_minor_recv(mp);
+ spin_unlock_irqrestore(&workaround_lock, flags);
+ return count;
+}
+
+static int capinc_tty_put_char(struct tty_struct *tty, unsigned char ch)
+{
+ struct capiminor *mp = (struct capiminor *)tty->driver_data;
+ struct sk_buff *skb;
+ unsigned long flags;
+ int ret = 1;
+
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_put_char(%u)\n", ch);
+#endif
+
+ if (!mp || !mp->nccip) {
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_put_char: mp or mp->ncci NULL\n");
+#endif
+ return 0;
+ }
+
+ spin_lock_irqsave(&workaround_lock, flags);
+ skb = mp->ttyskb;
+ if (skb) {
+ if (skb_tailroom(skb) > 0) {
+ *(skb_put(skb, 1)) = ch;
+ spin_unlock_irqrestore(&workaround_lock, flags);
+ return 1;
+ }
+ mp->ttyskb = NULL;
+ skb_queue_tail(&mp->outqueue, skb);
+ mp->outbytes += skb->len;
+ (void)handle_minor_send(mp);
+ }
+ skb = alloc_skb(CAPI_DATA_B3_REQ_LEN+CAPI_MAX_BLKSIZE, GFP_ATOMIC);
+ if (skb) {
+ skb_reserve(skb, CAPI_DATA_B3_REQ_LEN);
+ *(skb_put(skb, 1)) = ch;
+ mp->ttyskb = skb;
+ } else {
+ printk(KERN_ERR "capinc_put_char: char %u lost\n", ch);
+ ret = 0;
+ }
+ spin_unlock_irqrestore(&workaround_lock, flags);
+ return ret;
+}
+
+static void capinc_tty_flush_chars(struct tty_struct *tty)
+{
+ struct capiminor *mp = (struct capiminor *)tty->driver_data;
+ struct sk_buff *skb;
+ unsigned long flags;
+
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_flush_chars\n");
+#endif
+
+ if (!mp || !mp->nccip) {
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_flush_chars: mp or mp->ncci NULL\n");
+#endif
+ return;
+ }
+
+ spin_lock_irqsave(&workaround_lock, flags);
+ skb = mp->ttyskb;
+ if (skb) {
+ mp->ttyskb = NULL;
+ skb_queue_tail(&mp->outqueue, skb);
+ mp->outbytes += skb->len;
+ (void)handle_minor_send(mp);
+ }
+ (void)handle_minor_recv(mp);
+ spin_unlock_irqrestore(&workaround_lock, flags);
+}
+
+static int capinc_tty_write_room(struct tty_struct *tty)
+{
+ struct capiminor *mp = (struct capiminor *)tty->driver_data;
+ int room;
+ if (!mp || !mp->nccip) {
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_write_room: mp or mp->ncci NULL\n");
+#endif
+ return 0;
+ }
+ room = CAPINC_MAX_SENDQUEUE-skb_queue_len(&mp->outqueue);
+ room *= CAPI_MAX_BLKSIZE;
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_write_room = %d\n", room);
+#endif
+ return room;
+}
+
+static int capinc_tty_chars_in_buffer(struct tty_struct *tty)
+{
+ struct capiminor *mp = (struct capiminor *)tty->driver_data;
+ if (!mp || !mp->nccip) {
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_chars_in_buffer: mp or mp->ncci NULL\n");
+#endif
+ return 0;
+ }
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_chars_in_buffer = %d nack=%d sq=%d rq=%d\n",
+ mp->outbytes, mp->nack,
+ skb_queue_len(&mp->outqueue),
+ skb_queue_len(&mp->inqueue));
+#endif
+ return mp->outbytes;
+}
+
+static int capinc_tty_ioctl(struct tty_struct *tty, struct file * file,
+ unsigned int cmd, unsigned long arg)
+{
+ int error = 0;
+ switch (cmd) {
+ default:
+ error = n_tty_ioctl_helper(tty, file, cmd, arg);
+ break;
+ }
+ return error;
+}
+
+static void capinc_tty_set_termios(struct tty_struct *tty, struct ktermios * old)
+{
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_set_termios\n");
+#endif
+}
+
+static void capinc_tty_throttle(struct tty_struct * tty)
+{
+ struct capiminor *mp = (struct capiminor *)tty->driver_data;
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_throttle\n");
+#endif
+ if (mp)
+ mp->ttyinstop = 1;
+}
+
+static void capinc_tty_unthrottle(struct tty_struct * tty)
+{
+ struct capiminor *mp = (struct capiminor *)tty->driver_data;
+ unsigned long flags;
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_unthrottle\n");
+#endif
+ if (mp) {
+ spin_lock_irqsave(&workaround_lock, flags);
+ mp->ttyinstop = 0;
+ handle_minor_recv(mp);
+ spin_unlock_irqrestore(&workaround_lock, flags);
+ }
+}
+
+static void capinc_tty_stop(struct tty_struct *tty)
+{
+ struct capiminor *mp = (struct capiminor *)tty->driver_data;
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_stop\n");
+#endif
+ if (mp) {
+ mp->ttyoutstop = 1;
+ }
+}
+
+static void capinc_tty_start(struct tty_struct *tty)
+{
+ struct capiminor *mp = (struct capiminor *)tty->driver_data;
+ unsigned long flags;
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_start\n");
+#endif
+ if (mp) {
+ spin_lock_irqsave(&workaround_lock, flags);
+ mp->ttyoutstop = 0;
+ (void)handle_minor_send(mp);
+ spin_unlock_irqrestore(&workaround_lock, flags);
+ }
+}
+
+static void capinc_tty_hangup(struct tty_struct *tty)
+{
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_hangup\n");
+#endif
+}
+
+static int capinc_tty_break_ctl(struct tty_struct *tty, int state)
+{
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_break_ctl(%d)\n", state);
+#endif
+ return 0;
+}
+
+static void capinc_tty_flush_buffer(struct tty_struct *tty)
+{
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_flush_buffer\n");
+#endif
+}
+
+static void capinc_tty_set_ldisc(struct tty_struct *tty)
+{
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_set_ldisc\n");
+#endif
+}
+
+static void capinc_tty_send_xchar(struct tty_struct *tty, char ch)
+{
+#ifdef _DEBUG_TTYFUNCS
+ printk(KERN_DEBUG "capinc_tty_send_xchar(%d)\n", ch);
+#endif
+}
+
+static int capinc_tty_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ return 0;
+}
+
+static struct tty_driver *capinc_tty_driver;
+
+static const struct tty_operations capinc_ops = {
+ .open = capinc_tty_open,
+ .close = capinc_tty_close,
+ .write = capinc_tty_write,
+ .put_char = capinc_tty_put_char,
+ .flush_chars = capinc_tty_flush_chars,
+ .write_room = capinc_tty_write_room,
+ .chars_in_buffer = capinc_tty_chars_in_buffer,
+ .ioctl = capinc_tty_ioctl,
+ .set_termios = capinc_tty_set_termios,
+ .throttle = capinc_tty_throttle,
+ .unthrottle = capinc_tty_unthrottle,
+ .stop = capinc_tty_stop,
+ .start = capinc_tty_start,
+ .hangup = capinc_tty_hangup,
+ .break_ctl = capinc_tty_break_ctl,
+ .flush_buffer = capinc_tty_flush_buffer,
+ .set_ldisc = capinc_tty_set_ldisc,
+ .send_xchar = capinc_tty_send_xchar,
+ .read_proc = capinc_tty_read_proc,
+};
+
+static int capinc_tty_init(void)
+{
+ struct tty_driver *drv;
+
+ if (capi_ttyminors > CAPINC_MAX_PORTS)
+ capi_ttyminors = CAPINC_MAX_PORTS;
+ if (capi_ttyminors <= 0)
+ capi_ttyminors = CAPINC_NR_PORTS;
+
+ drv = alloc_tty_driver(capi_ttyminors);
+ if (!drv)
+ return -ENOMEM;
+
+ drv->owner = THIS_MODULE;
+ drv->driver_name = "capi_nc";
+ drv->name = "capi";
+ drv->major = capi_ttymajor;
+ drv->minor_start = 0;
+ drv->type = TTY_DRIVER_TYPE_SERIAL;
+ drv->subtype = SERIAL_TYPE_NORMAL;
+ drv->init_termios = tty_std_termios;
+ drv->init_termios.c_iflag = ICRNL;
+ drv->init_termios.c_oflag = OPOST | ONLCR;
+ drv->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ drv->init_termios.c_lflag = 0;
+ drv->flags = TTY_DRIVER_REAL_RAW|TTY_DRIVER_RESET_TERMIOS;
+ tty_set_operations(drv, &capinc_ops);
+ if (tty_register_driver(drv)) {
+ put_tty_driver(drv);
+ printk(KERN_ERR "Couldn't register capi_nc driver\n");
+ return -1;
+ }
+ capinc_tty_driver = drv;
+ return 0;
+}
+
+static void capinc_tty_exit(void)
+{
+ struct tty_driver *drv = capinc_tty_driver;
+ int retval;
+ if ((retval = tty_unregister_driver(drv)))
+ printk(KERN_ERR "capi: failed to unregister capi_nc driver (%d)\n", retval);
+ put_tty_driver(drv);
+}
+
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+/* -------- /proc functions ----------------------------------------- */
+
+/*
+ * /proc/capi/capi20:
+ * minor applid nrecvctlpkt nrecvdatapkt nsendctlpkt nsenddatapkt
+ */
+static int proc_capidev_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct capidev *cdev;
+ struct list_head *l;
+ int len = 0;
+
+ read_lock(&capidev_list_lock);
+ list_for_each(l, &capidev_list) {
+ cdev = list_entry(l, struct capidev, list);
+ len += sprintf(page+len, "0 %d %lu %lu %lu %lu\n",
+ cdev->ap.applid,
+ cdev->ap.nrecvctlpkt,
+ cdev->ap.nrecvdatapkt,
+ cdev->ap.nsentctlpkt,
+ cdev->ap.nsentdatapkt);
+ if (len <= off) {
+ off -= len;
+ len = 0;
+ } else {
+ if (len-off > count)
+ goto endloop;
+ }
+ }
+
+endloop:
+ read_unlock(&capidev_list_lock);
+ if (len < count)
+ *eof = 1;
+ if (len > count) len = count;
+ if (len < 0) len = 0;
+ return len;
+}
+
+/*
+ * /proc/capi/capi20ncci:
+ * applid ncci
+ */
+static int proc_capincci_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ struct capidev *cdev;
+ struct capincci *np;
+ struct list_head *l;
+ int len = 0;
+
+ read_lock(&capidev_list_lock);
+ list_for_each(l, &capidev_list) {
+ cdev = list_entry(l, struct capidev, list);
+ for (np=cdev->nccis; np; np = np->next) {
+ len += sprintf(page+len, "%d 0x%x\n",
+ cdev->ap.applid,
+ np->ncci);
+ if (len <= off) {
+ off -= len;
+ len = 0;
+ } else {
+ if (len-off > count)
+ goto endloop;
+ }
+ }
+ }
+endloop:
+ read_unlock(&capidev_list_lock);
+ *start = page+off;
+ if (len < count)
+ *eof = 1;
+ if (len>count) len = count;
+ if (len<0) len = 0;
+ return len;
+}
+
+static struct procfsentries {
+ char *name;
+ mode_t mode;
+ int (*read_proc)(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+ struct proc_dir_entry *procent;
+} procfsentries[] = {
+ /* { "capi", S_IFDIR, 0 }, */
+ { "capi/capi20", 0 , proc_capidev_read_proc },
+ { "capi/capi20ncci", 0 , proc_capincci_read_proc },
+};
+
+static void __init proc_init(void)
+{
+ int nelem = ARRAY_SIZE(procfsentries);
+ int i;
+
+ for (i=0; i < nelem; i++) {
+ struct procfsentries *p = procfsentries + i;
+ p->procent = create_proc_entry(p->name, p->mode, NULL);
+ if (p->procent) p->procent->read_proc = p->read_proc;
+ }
+}
+
+static void __exit proc_exit(void)
+{
+ int nelem = ARRAY_SIZE(procfsentries);
+ int i;
+
+ for (i=nelem-1; i >= 0; i--) {
+ struct procfsentries *p = procfsentries + i;
+ if (p->procent) {
+ remove_proc_entry(p->name, NULL);
+ p->procent = NULL;
+ }
+ }
+}
+
+/* -------- init function and module interface ---------------------- */
+
+
+static char rev[32];
+
+static int __init capi_init(void)
+{
+ char *p;
+ char *compileinfo;
+ int major_ret;
+
+ if ((p = strchr(revision, ':')) != NULL && p[1]) {
+ strlcpy(rev, p + 2, sizeof(rev));
+ if ((p = strchr(rev, '$')) != NULL && p > rev)
+ *(p-1) = 0;
+ } else
+ strcpy(rev, "1.0");
+
+ major_ret = register_chrdev(capi_major, "capi20", &capi_fops);
+ if (major_ret < 0) {
+ printk(KERN_ERR "capi20: unable to get major %d\n", capi_major);
+ return major_ret;
+ }
+ capi_class = class_create(THIS_MODULE, "capi");
+ if (IS_ERR(capi_class)) {
+ unregister_chrdev(capi_major, "capi20");
+ return PTR_ERR(capi_class);
+ }
+
+ device_create(capi_class, NULL, MKDEV(capi_major, 0), NULL, "capi");
+
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+ if (capinc_tty_init() < 0) {
+ device_destroy(capi_class, MKDEV(capi_major, 0));
+ class_destroy(capi_class);
+ unregister_chrdev(capi_major, "capi20");
+ return -ENOMEM;
+ }
+#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
+
+ proc_init();
+
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+#if defined(CONFIG_ISDN_CAPI_CAPIFS) || defined(CONFIG_ISDN_CAPI_CAPIFS_MODULE)
+ compileinfo = " (middleware+capifs)";
+#else
+ compileinfo = " (no capifs)";
+#endif
+#else
+ compileinfo = " (no middleware)";
+#endif
+ printk(KERN_NOTICE "capi20: Rev %s: started up with major %d%s\n",
+ rev, capi_major, compileinfo);
+
+ return 0;
+}
+
+static void __exit capi_exit(void)
+{
+ proc_exit();
+
+ device_destroy(capi_class, MKDEV(capi_major, 0));
+ class_destroy(capi_class);
+ unregister_chrdev(capi_major, "capi20");
+
+#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
+ capinc_tty_exit();
+#endif
+ printk(KERN_NOTICE "capi: Rev %s: unloaded\n", rev);
+}
+
+module_init(capi_init);
+module_exit(capi_exit);
diff --git a/drivers/isdn/capi/capidrv.c b/drivers/isdn/capi/capidrv.c
new file mode 100644
index 0000000..d5b4cc3
--- /dev/null
+++ b/drivers/isdn/capi/capidrv.c
@@ -0,0 +1,2355 @@
+/* $Id: capidrv.c,v 1.1.2.2 2004/01/12 23:17:24 keil Exp $
+ *
+ * ISDN4Linux Driver, using capi20 interface (kernelcapi)
+ *
+ * Copyright 1997 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/slab.h>
+#include <linux/fcntl.h>
+#include <linux/fs.h>
+#include <linux/signal.h>
+#include <linux/mm.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/skbuff.h>
+#include <linux/isdn.h>
+#include <linux/isdnif.h>
+#include <linux/proc_fs.h>
+#include <linux/capi.h>
+#include <linux/kernelcapi.h>
+#include <linux/ctype.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+
+#include <linux/isdn/capiutil.h>
+#include <linux/isdn/capicmd.h>
+#include "capidrv.h"
+
+static char *revision = "$Revision: 1.1.2.2 $";
+static int debugmode = 0;
+
+MODULE_DESCRIPTION("CAPI4Linux: Interface to ISDN4Linux");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+module_param(debugmode, uint, 0);
+
+/* -------- type definitions ----------------------------------------- */
+
+
+struct capidrv_contr {
+
+ struct capidrv_contr *next;
+ struct module *owner;
+ u32 contrnr;
+ char name[20];
+
+ /*
+ * for isdn4linux
+ */
+ isdn_if interface;
+ int myid;
+
+ /*
+ * LISTEN state
+ */
+ int state;
+ u32 cipmask;
+ u32 cipmask2;
+ struct timer_list listentimer;
+
+ /*
+ * ID of capi message sent
+ */
+ u16 msgid;
+
+ /*
+ * B-Channels
+ */
+ int nbchan;
+ struct capidrv_bchan {
+ struct capidrv_contr *contr;
+ u8 msn[ISDN_MSNLEN];
+ int l2;
+ int l3;
+ u8 num[ISDN_MSNLEN];
+ u8 mynum[ISDN_MSNLEN];
+ int si1;
+ int si2;
+ int incoming;
+ int disconnecting;
+ struct capidrv_plci {
+ struct capidrv_plci *next;
+ u32 plci;
+ u32 ncci; /* ncci for CONNECT_ACTIVE_IND */
+ u16 msgid; /* to identfy CONNECT_CONF */
+ int chan;
+ int state;
+ int leasedline;
+ struct capidrv_ncci {
+ struct capidrv_ncci *next;
+ struct capidrv_plci *plcip;
+ u32 ncci;
+ u16 msgid; /* to identfy CONNECT_B3_CONF */
+ int chan;
+ int state;
+ int oldstate;
+ /* */
+ u16 datahandle;
+ struct ncci_datahandle_queue {
+ struct ncci_datahandle_queue *next;
+ u16 datahandle;
+ int len;
+ } *ackqueue;
+ } *ncci_list;
+ } *plcip;
+ struct capidrv_ncci *nccip;
+ } *bchans;
+
+ struct capidrv_plci *plci_list;
+
+ /* for q931 data */
+ u8 q931_buf[4096];
+ u8 *q931_read;
+ u8 *q931_write;
+ u8 *q931_end;
+};
+
+
+struct capidrv_data {
+ struct capi20_appl ap;
+ int ncontr;
+ struct capidrv_contr *contr_list;
+};
+
+typedef struct capidrv_plci capidrv_plci;
+typedef struct capidrv_ncci capidrv_ncci;
+typedef struct capidrv_contr capidrv_contr;
+typedef struct capidrv_data capidrv_data;
+typedef struct capidrv_bchan capidrv_bchan;
+
+/* -------- data definitions ----------------------------------------- */
+
+static capidrv_data global;
+static DEFINE_SPINLOCK(global_lock);
+
+static void handle_dtrace_data(capidrv_contr *card,
+ int send, int level2, u8 *data, u16 len);
+
+/* -------- convert functions ---------------------------------------- */
+
+static inline u32 b1prot(int l2, int l3)
+{
+ switch (l2) {
+ case ISDN_PROTO_L2_X75I:
+ case ISDN_PROTO_L2_X75UI:
+ case ISDN_PROTO_L2_X75BUI:
+ return 0;
+ case ISDN_PROTO_L2_HDLC:
+ default:
+ return 0;
+ case ISDN_PROTO_L2_TRANS:
+ return 1;
+ case ISDN_PROTO_L2_V11096:
+ case ISDN_PROTO_L2_V11019:
+ case ISDN_PROTO_L2_V11038:
+ return 2;
+ case ISDN_PROTO_L2_FAX:
+ return 4;
+ case ISDN_PROTO_L2_MODEM:
+ return 8;
+ }
+}
+
+static inline u32 b2prot(int l2, int l3)
+{
+ switch (l2) {
+ case ISDN_PROTO_L2_X75I:
+ case ISDN_PROTO_L2_X75UI:
+ case ISDN_PROTO_L2_X75BUI:
+ default:
+ return 0;
+ case ISDN_PROTO_L2_HDLC:
+ case ISDN_PROTO_L2_TRANS:
+ case ISDN_PROTO_L2_V11096:
+ case ISDN_PROTO_L2_V11019:
+ case ISDN_PROTO_L2_V11038:
+ case ISDN_PROTO_L2_MODEM:
+ return 1;
+ case ISDN_PROTO_L2_FAX:
+ return 4;
+ }
+}
+
+static inline u32 b3prot(int l2, int l3)
+{
+ switch (l2) {
+ case ISDN_PROTO_L2_X75I:
+ case ISDN_PROTO_L2_X75UI:
+ case ISDN_PROTO_L2_X75BUI:
+ case ISDN_PROTO_L2_HDLC:
+ case ISDN_PROTO_L2_TRANS:
+ case ISDN_PROTO_L2_V11096:
+ case ISDN_PROTO_L2_V11019:
+ case ISDN_PROTO_L2_V11038:
+ case ISDN_PROTO_L2_MODEM:
+ default:
+ return 0;
+ case ISDN_PROTO_L2_FAX:
+ return 4;
+ }
+}
+
+static _cstruct b1config_async_v110(u16 rate)
+{
+ /* CAPI-Spec "B1 Configuration" */
+ static unsigned char buf[9];
+ buf[0] = 8; /* len */
+ /* maximum bitrate */
+ buf[1] = rate & 0xff; buf[2] = (rate >> 8) & 0xff;
+ buf[3] = 8; buf[4] = 0; /* 8 bits per character */
+ buf[5] = 0; buf[6] = 0; /* parity none */
+ buf[7] = 0; buf[8] = 0; /* 1 stop bit */
+ return buf;
+}
+
+static _cstruct b1config(int l2, int l3)
+{
+ switch (l2) {
+ case ISDN_PROTO_L2_X75I:
+ case ISDN_PROTO_L2_X75UI:
+ case ISDN_PROTO_L2_X75BUI:
+ case ISDN_PROTO_L2_HDLC:
+ case ISDN_PROTO_L2_TRANS:
+ default:
+ return NULL;
+ case ISDN_PROTO_L2_V11096:
+ return b1config_async_v110(9600);
+ case ISDN_PROTO_L2_V11019:
+ return b1config_async_v110(19200);
+ case ISDN_PROTO_L2_V11038:
+ return b1config_async_v110(38400);
+ }
+}
+
+static inline u16 si2cip(u8 si1, u8 si2)
+{
+ static const u8 cip[17][5] =
+ {
+ /* 0 1 2 3 4 */
+ {0, 0, 0, 0, 0}, /*0 */
+ {16, 16, 4, 26, 16}, /*1 */
+ {17, 17, 17, 4, 4}, /*2 */
+ {2, 2, 2, 2, 2}, /*3 */
+ {18, 18, 18, 18, 18}, /*4 */
+ {2, 2, 2, 2, 2}, /*5 */
+ {0, 0, 0, 0, 0}, /*6 */
+ {2, 2, 2, 2, 2}, /*7 */
+ {2, 2, 2, 2, 2}, /*8 */
+ {21, 21, 21, 21, 21}, /*9 */
+ {19, 19, 19, 19, 19}, /*10 */
+ {0, 0, 0, 0, 0}, /*11 */
+ {0, 0, 0, 0, 0}, /*12 */
+ {0, 0, 0, 0, 0}, /*13 */
+ {0, 0, 0, 0, 0}, /*14 */
+ {22, 22, 22, 22, 22}, /*15 */
+ {27, 27, 27, 28, 27} /*16 */
+ };
+ if (si1 > 16)
+ si1 = 0;
+ if (si2 > 4)
+ si2 = 0;
+
+ return (u16) cip[si1][si2];
+}
+
+static inline u8 cip2si1(u16 cipval)
+{
+ static const u8 si[32] =
+ {7, 1, 7, 7, 1, 1, 7, 7, /*0-7 */
+ 7, 1, 0, 0, 0, 0, 0, 0, /*8-15 */
+ 1, 2, 4, 10, 9, 9, 15, 7, /*16-23 */
+ 7, 7, 1, 16, 16, 0, 0, 0}; /*24-31 */
+
+ if (cipval > 31)
+ cipval = 0; /* .... */
+ return si[cipval];
+}
+
+static inline u8 cip2si2(u16 cipval)
+{
+ static const u8 si[32] =
+ {0, 0, 0, 0, 2, 3, 0, 0, /*0-7 */
+ 0, 3, 0, 0, 0, 0, 0, 0, /*8-15 */
+ 1, 2, 0, 0, 9, 0, 0, 0, /*16-23 */
+ 0, 0, 3, 2, 3, 0, 0, 0}; /*24-31 */
+
+ if (cipval > 31)
+ cipval = 0; /* .... */
+ return si[cipval];
+}
+
+
+/* -------- controller management ------------------------------------- */
+
+static inline capidrv_contr *findcontrbydriverid(int driverid)
+{
+ unsigned long flags;
+ capidrv_contr *p;
+
+ spin_lock_irqsave(&global_lock, flags);
+ for (p = global.contr_list; p; p = p->next)
+ if (p->myid == driverid)
+ break;
+ spin_unlock_irqrestore(&global_lock, flags);
+ return p;
+}
+
+static capidrv_contr *findcontrbynumber(u32 contr)
+{
+ unsigned long flags;
+ capidrv_contr *p = global.contr_list;
+
+ spin_lock_irqsave(&global_lock, flags);
+ for (p = global.contr_list; p; p = p->next)
+ if (p->contrnr == contr)
+ break;
+ spin_unlock_irqrestore(&global_lock, flags);
+ return p;
+}
+
+
+/* -------- plci management ------------------------------------------ */
+
+static capidrv_plci *new_plci(capidrv_contr * card, int chan)
+{
+ capidrv_plci *plcip;
+
+ plcip = kzalloc(sizeof(capidrv_plci), GFP_ATOMIC);
+
+ if (plcip == NULL)
+ return NULL;
+
+ plcip->state = ST_PLCI_NONE;
+ plcip->plci = 0;
+ plcip->msgid = 0;
+ plcip->chan = chan;
+ plcip->next = card->plci_list;
+ card->plci_list = plcip;
+ card->bchans[chan].plcip = plcip;
+
+ return plcip;
+}
+
+static capidrv_plci *find_plci_by_plci(capidrv_contr * card, u32 plci)
+{
+ capidrv_plci *p;
+ for (p = card->plci_list; p; p = p->next)
+ if (p->plci == plci)
+ return p;
+ return NULL;
+}
+
+static capidrv_plci *find_plci_by_msgid(capidrv_contr * card, u16 msgid)
+{
+ capidrv_plci *p;
+ for (p = card->plci_list; p; p = p->next)
+ if (p->msgid == msgid)
+ return p;
+ return NULL;
+}
+
+static capidrv_plci *find_plci_by_ncci(capidrv_contr * card, u32 ncci)
+{
+ capidrv_plci *p;
+ for (p = card->plci_list; p; p = p->next)
+ if (p->plci == (ncci & 0xffff))
+ return p;
+ return NULL;
+}
+
+static void free_plci(capidrv_contr * card, capidrv_plci * plcip)
+{
+ capidrv_plci **pp;
+
+ for (pp = &card->plci_list; *pp; pp = &(*pp)->next) {
+ if (*pp == plcip) {
+ *pp = (*pp)->next;
+ card->bchans[plcip->chan].plcip = NULL;
+ card->bchans[plcip->chan].disconnecting = 0;
+ card->bchans[plcip->chan].incoming = 0;
+ kfree(plcip);
+ return;
+ }
+ }
+ printk(KERN_ERR "capidrv-%d: free_plci %p (0x%x) not found, Huh?\n",
+ card->contrnr, plcip, plcip->plci);
+}
+
+/* -------- ncci management ------------------------------------------ */
+
+static inline capidrv_ncci *new_ncci(capidrv_contr * card,
+ capidrv_plci * plcip,
+ u32 ncci)
+{
+ capidrv_ncci *nccip;
+
+ nccip = kzalloc(sizeof(capidrv_ncci), GFP_ATOMIC);
+
+ if (nccip == NULL)
+ return NULL;
+
+ nccip->ncci = ncci;
+ nccip->state = ST_NCCI_NONE;
+ nccip->plcip = plcip;
+ nccip->chan = plcip->chan;
+ nccip->datahandle = 0;
+
+ nccip->next = plcip->ncci_list;
+ plcip->ncci_list = nccip;
+
+ card->bchans[plcip->chan].nccip = nccip;
+
+ return nccip;
+}
+
+static inline capidrv_ncci *find_ncci(capidrv_contr * card, u32 ncci)
+{
+ capidrv_plci *plcip;
+ capidrv_ncci *p;
+
+ if ((plcip = find_plci_by_ncci(card, ncci)) == NULL)
+ return NULL;
+
+ for (p = plcip->ncci_list; p; p = p->next)
+ if (p->ncci == ncci)
+ return p;
+ return NULL;
+}
+
+static inline capidrv_ncci *find_ncci_by_msgid(capidrv_contr * card,
+ u32 ncci, u16 msgid)
+{
+ capidrv_plci *plcip;
+ capidrv_ncci *p;
+
+ if ((plcip = find_plci_by_ncci(card, ncci)) == NULL)
+ return NULL;
+
+ for (p = plcip->ncci_list; p; p = p->next)
+ if (p->msgid == msgid)
+ return p;
+ return NULL;
+}
+
+static void free_ncci(capidrv_contr * card, struct capidrv_ncci *nccip)
+{
+ struct capidrv_ncci **pp;
+
+ for (pp = &(nccip->plcip->ncci_list); *pp; pp = &(*pp)->next) {
+ if (*pp == nccip) {
+ *pp = (*pp)->next;
+ break;
+ }
+ }
+ card->bchans[nccip->chan].nccip = NULL;
+ kfree(nccip);
+}
+
+static int capidrv_add_ack(struct capidrv_ncci *nccip,
+ u16 datahandle, int len)
+{
+ struct ncci_datahandle_queue *n, **pp;
+
+ n = (struct ncci_datahandle_queue *)
+ kmalloc(sizeof(struct ncci_datahandle_queue), GFP_ATOMIC);
+ if (!n) {
+ printk(KERN_ERR "capidrv: kmalloc ncci_datahandle failed\n");
+ return -1;
+ }
+ n->next = NULL;
+ n->datahandle = datahandle;
+ n->len = len;
+ for (pp = &nccip->ackqueue; *pp; pp = &(*pp)->next) ;
+ *pp = n;
+ return 0;
+}
+
+static int capidrv_del_ack(struct capidrv_ncci *nccip, u16 datahandle)
+{
+ struct ncci_datahandle_queue **pp, *p;
+ int len;
+
+ for (pp = &nccip->ackqueue; *pp; pp = &(*pp)->next) {
+ if ((*pp)->datahandle == datahandle) {
+ p = *pp;
+ len = p->len;
+ *pp = (*pp)->next;
+ kfree(p);
+ return len;
+ }
+ }
+ return -1;
+}
+
+/* -------- convert and send capi message ---------------------------- */
+
+static void send_message(capidrv_contr * card, _cmsg * cmsg)
+{
+ struct sk_buff *skb;
+ size_t len;
+
+ capi_cmsg2message(cmsg, cmsg->buf);
+ len = CAPIMSG_LEN(cmsg->buf);
+ skb = alloc_skb(len, GFP_ATOMIC);
+ if (!skb) {
+ printk(KERN_ERR "capidrv::send_message: can't allocate mem\n");
+ return;
+ }
+ memcpy(skb_put(skb, len), cmsg->buf, len);
+ if (capi20_put_message(&global.ap, skb) != CAPI_NOERROR)
+ kfree_skb(skb);
+}
+
+/* -------- state machine -------------------------------------------- */
+
+struct listenstatechange {
+ int actstate;
+ int nextstate;
+ int event;
+};
+
+static struct listenstatechange listentable[] =
+{
+ {ST_LISTEN_NONE, ST_LISTEN_WAIT_CONF, EV_LISTEN_REQ},
+ {ST_LISTEN_ACTIVE, ST_LISTEN_ACTIVE_WAIT_CONF, EV_LISTEN_REQ},
+ {ST_LISTEN_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_ERROR},
+ {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_ERROR},
+ {ST_LISTEN_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_EMPTY},
+ {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_NONE, EV_LISTEN_CONF_EMPTY},
+ {ST_LISTEN_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_OK},
+ {ST_LISTEN_ACTIVE_WAIT_CONF, ST_LISTEN_ACTIVE, EV_LISTEN_CONF_OK},
+ {},
+};
+
+static void listen_change_state(capidrv_contr * card, int event)
+{
+ struct listenstatechange *p = listentable;
+ while (p->event) {
+ if (card->state == p->actstate && p->event == event) {
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: listen_change_state %d -> %d\n",
+ card->contrnr, card->state, p->nextstate);
+ card->state = p->nextstate;
+ return;
+ }
+ p++;
+ }
+ printk(KERN_ERR "capidrv-%d: listen_change_state state=%d event=%d ????\n",
+ card->contrnr, card->state, event);
+
+}
+
+/* ------------------------------------------------------------------ */
+
+static void p0(capidrv_contr * card, capidrv_plci * plci)
+{
+ isdn_ctrl cmd;
+
+ card->bchans[plci->chan].contr = NULL;
+ cmd.command = ISDN_STAT_DHUP;
+ cmd.driver = card->myid;
+ cmd.arg = plci->chan;
+ card->interface.statcallb(&cmd);
+ free_plci(card, plci);
+}
+
+/* ------------------------------------------------------------------ */
+
+struct plcistatechange {
+ int actstate;
+ int nextstate;
+ int event;
+ void (*changefunc) (capidrv_contr * card, capidrv_plci * plci);
+};
+
+static struct plcistatechange plcitable[] =
+{
+ /* P-0 */
+ {ST_PLCI_NONE, ST_PLCI_OUTGOING, EV_PLCI_CONNECT_REQ, NULL},
+ {ST_PLCI_NONE, ST_PLCI_ALLOCATED, EV_PLCI_FACILITY_IND_UP, NULL},
+ {ST_PLCI_NONE, ST_PLCI_INCOMING, EV_PLCI_CONNECT_IND, NULL},
+ {ST_PLCI_NONE, ST_PLCI_RESUMEING, EV_PLCI_RESUME_REQ, NULL},
+ /* P-0.1 */
+ {ST_PLCI_OUTGOING, ST_PLCI_NONE, EV_PLCI_CONNECT_CONF_ERROR, p0},
+ {ST_PLCI_OUTGOING, ST_PLCI_ALLOCATED, EV_PLCI_CONNECT_CONF_OK, NULL},
+ /* P-1 */
+ {ST_PLCI_ALLOCATED, ST_PLCI_ACTIVE, EV_PLCI_CONNECT_ACTIVE_IND, NULL},
+ {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, NULL},
+ {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, NULL},
+ {ST_PLCI_ALLOCATED, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, NULL},
+ /* P-ACT */
+ {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, NULL},
+ {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, NULL},
+ {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, NULL},
+ {ST_PLCI_ACTIVE, ST_PLCI_HELD, EV_PLCI_HOLD_IND, NULL},
+ {ST_PLCI_ACTIVE, ST_PLCI_DISCONNECTING, EV_PLCI_SUSPEND_IND, NULL},
+ /* P-2 */
+ {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_CONNECT_REJECT, NULL},
+ {ST_PLCI_INCOMING, ST_PLCI_FACILITY_IND, EV_PLCI_FACILITY_IND_UP, NULL},
+ {ST_PLCI_INCOMING, ST_PLCI_ACCEPTING, EV_PLCI_CONNECT_RESP, NULL},
+ {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, NULL},
+ {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, NULL},
+ {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, NULL},
+ {ST_PLCI_INCOMING, ST_PLCI_DISCONNECTING, EV_PLCI_CD_IND, NULL},
+ /* P-3 */
+ {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_CONNECT_REJECT, NULL},
+ {ST_PLCI_FACILITY_IND, ST_PLCI_ACCEPTING, EV_PLCI_CONNECT_ACTIVE_IND, NULL},
+ {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, NULL},
+ {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, NULL},
+ {ST_PLCI_FACILITY_IND, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, NULL},
+ /* P-4 */
+ {ST_PLCI_ACCEPTING, ST_PLCI_ACTIVE, EV_PLCI_CONNECT_ACTIVE_IND, NULL},
+ {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTING, EV_PLCI_DISCONNECT_REQ, NULL},
+ {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTING, EV_PLCI_FACILITY_IND_DOWN, NULL},
+ {ST_PLCI_ACCEPTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, NULL},
+ /* P-5 */
+ {ST_PLCI_DISCONNECTING, ST_PLCI_DISCONNECTED, EV_PLCI_DISCONNECT_IND, NULL},
+ /* P-6 */
+ {ST_PLCI_DISCONNECTED, ST_PLCI_NONE, EV_PLCI_DISCONNECT_RESP, p0},
+ /* P-0.Res */
+ {ST_PLCI_RESUMEING, ST_PLCI_NONE, EV_PLCI_RESUME_CONF_ERROR, p0},
+ {ST_PLCI_RESUMEING, ST_PLCI_RESUME, EV_PLCI_RESUME_CONF_OK, NULL},
+ /* P-RES */
+ {ST_PLCI_RESUME, ST_PLCI_ACTIVE, EV_PLCI_RESUME_IND, NULL},
+ /* P-HELD */
+ {ST_PLCI_HELD, ST_PLCI_ACTIVE, EV_PLCI_RETRIEVE_IND, NULL},
+ {},
+};
+
+static void plci_change_state(capidrv_contr * card, capidrv_plci * plci, int event)
+{
+ struct plcistatechange *p = plcitable;
+ while (p->event) {
+ if (plci->state == p->actstate && p->event == event) {
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: plci_change_state:0x%x %d -> %d\n",
+ card->contrnr, plci->plci, plci->state, p->nextstate);
+ plci->state = p->nextstate;
+ if (p->changefunc)
+ p->changefunc(card, plci);
+ return;
+ }
+ p++;
+ }
+ printk(KERN_ERR "capidrv-%d: plci_change_state:0x%x state=%d event=%d ????\n",
+ card->contrnr, plci->plci, plci->state, event);
+}
+
+/* ------------------------------------------------------------------ */
+
+static _cmsg cmsg;
+
+static void n0(capidrv_contr * card, capidrv_ncci * ncci)
+{
+ isdn_ctrl cmd;
+
+ capi_fill_DISCONNECT_REQ(&cmsg,
+ global.ap.applid,
+ card->msgid++,
+ ncci->plcip->plci,
+ NULL, /* BChannelinformation */
+ NULL, /* Keypadfacility */
+ NULL, /* Useruserdata */ /* $$$$ */
+ NULL /* Facilitydataarray */
+ );
+ send_message(card, &cmsg);
+ plci_change_state(card, ncci->plcip, EV_PLCI_DISCONNECT_REQ);
+
+ cmd.command = ISDN_STAT_BHUP;
+ cmd.driver = card->myid;
+ cmd.arg = ncci->chan;
+ card->interface.statcallb(&cmd);
+ free_ncci(card, ncci);
+}
+
+/* ------------------------------------------------------------------ */
+
+struct nccistatechange {
+ int actstate;
+ int nextstate;
+ int event;
+ void (*changefunc) (capidrv_contr * card, capidrv_ncci * ncci);
+};
+
+static struct nccistatechange nccitable[] =
+{
+ /* N-0 */
+ {ST_NCCI_NONE, ST_NCCI_OUTGOING, EV_NCCI_CONNECT_B3_REQ, NULL},
+ {ST_NCCI_NONE, ST_NCCI_INCOMING, EV_NCCI_CONNECT_B3_IND, NULL},
+ /* N-0.1 */
+ {ST_NCCI_OUTGOING, ST_NCCI_ALLOCATED, EV_NCCI_CONNECT_B3_CONF_OK, NULL},
+ {ST_NCCI_OUTGOING, ST_NCCI_NONE, EV_NCCI_CONNECT_B3_CONF_ERROR, n0},
+ /* N-1 */
+ {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTING, EV_NCCI_CONNECT_B3_REJECT, NULL},
+ {ST_NCCI_INCOMING, ST_NCCI_ALLOCATED, EV_NCCI_CONNECT_B3_RESP, NULL},
+ {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, NULL},
+ {ST_NCCI_INCOMING, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, NULL},
+ /* N-2 */
+ {ST_NCCI_ALLOCATED, ST_NCCI_ACTIVE, EV_NCCI_CONNECT_B3_ACTIVE_IND, NULL},
+ {ST_NCCI_ALLOCATED, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, NULL},
+ {ST_NCCI_ALLOCATED, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, NULL},
+ /* N-ACT */
+ {ST_NCCI_ACTIVE, ST_NCCI_ACTIVE, EV_NCCI_RESET_B3_IND, NULL},
+ {ST_NCCI_ACTIVE, ST_NCCI_RESETING, EV_NCCI_RESET_B3_REQ, NULL},
+ {ST_NCCI_ACTIVE, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, NULL},
+ {ST_NCCI_ACTIVE, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, NULL},
+ /* N-3 */
+ {ST_NCCI_RESETING, ST_NCCI_ACTIVE, EV_NCCI_RESET_B3_IND, NULL},
+ {ST_NCCI_RESETING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, NULL},
+ {ST_NCCI_RESETING, ST_NCCI_DISCONNECTING, EV_NCCI_DISCONNECT_B3_REQ, NULL},
+ /* N-4 */
+ {ST_NCCI_DISCONNECTING, ST_NCCI_DISCONNECTED, EV_NCCI_DISCONNECT_B3_IND, NULL},
+ {ST_NCCI_DISCONNECTING, ST_NCCI_PREVIOUS, EV_NCCI_DISCONNECT_B3_CONF_ERROR,NULL},
+ /* N-5 */
+ {ST_NCCI_DISCONNECTED, ST_NCCI_NONE, EV_NCCI_DISCONNECT_B3_RESP, n0},
+ {},
+};
+
+static void ncci_change_state(capidrv_contr * card, capidrv_ncci * ncci, int event)
+{
+ struct nccistatechange *p = nccitable;
+ while (p->event) {
+ if (ncci->state == p->actstate && p->event == event) {
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: ncci_change_state:0x%x %d -> %d\n",
+ card->contrnr, ncci->ncci, ncci->state, p->nextstate);
+ if (p->nextstate == ST_NCCI_PREVIOUS) {
+ ncci->state = ncci->oldstate;
+ ncci->oldstate = p->actstate;
+ } else {
+ ncci->oldstate = p->actstate;
+ ncci->state = p->nextstate;
+ }
+ if (p->changefunc)
+ p->changefunc(card, ncci);
+ return;
+ }
+ p++;
+ }
+ printk(KERN_ERR "capidrv-%d: ncci_change_state:0x%x state=%d event=%d ????\n",
+ card->contrnr, ncci->ncci, ncci->state, event);
+}
+
+/* ------------------------------------------------------------------- */
+
+static inline int new_bchan(capidrv_contr * card)
+{
+ int i;
+ for (i = 0; i < card->nbchan; i++) {
+ if (card->bchans[i].plcip == NULL) {
+ card->bchans[i].disconnecting = 0;
+ return i;
+ }
+ }
+ return -1;
+}
+
+/* ------------------------------------------------------------------- */
+
+static void handle_controller(_cmsg * cmsg)
+{
+ capidrv_contr *card = findcontrbynumber(cmsg->adr.adrController & 0x7f);
+
+ if (!card) {
+ printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n",
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrController & 0x7f);
+ return;
+ }
+ switch (CAPICMD(cmsg->Command, cmsg->Subcommand)) {
+
+ case CAPI_LISTEN_CONF: /* Controller */
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: listenconf Info=0x%4x (%s) cipmask=0x%x\n",
+ card->contrnr, cmsg->Info, capi_info2str(cmsg->Info), card->cipmask);
+ if (cmsg->Info) {
+ listen_change_state(card, EV_LISTEN_CONF_ERROR);
+ } else if (card->cipmask == 0) {
+ listen_change_state(card, EV_LISTEN_CONF_EMPTY);
+ } else {
+ listen_change_state(card, EV_LISTEN_CONF_OK);
+ }
+ break;
+
+ case CAPI_MANUFACTURER_IND: /* Controller */
+ if ( cmsg->ManuID == 0x214D5641
+ && cmsg->Class == 0
+ && cmsg->Function == 1) {
+ u8 *data = cmsg->ManuData+3;
+ u16 len = cmsg->ManuData[0];
+ u16 layer;
+ int direction;
+ if (len == 255) {
+ len = (cmsg->ManuData[1] | (cmsg->ManuData[2] << 8));
+ data += 2;
+ }
+ len -= 2;
+ layer = ((*(data-1)) << 8) | *(data-2);
+ if (layer & 0x300)
+ direction = (layer & 0x200) ? 0 : 1;
+ else direction = (layer & 0x800) ? 0 : 1;
+ if (layer & 0x0C00) {
+ if ((layer & 0xff) == 0x80) {
+ handle_dtrace_data(card, direction, 1, data, len);
+ break;
+ }
+ } else if ((layer & 0xff) < 0x80) {
+ handle_dtrace_data(card, direction, 0, data, len);
+ break;
+ }
+ printk(KERN_INFO "capidrv-%d: %s from controller 0x%x layer 0x%x, ignored\n",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrController, layer);
+ break;
+ }
+ goto ignored;
+ case CAPI_MANUFACTURER_CONF: /* Controller */
+ if (cmsg->ManuID == 0x214D5641) {
+ char *s = NULL;
+ switch (cmsg->Class) {
+ case 0: break;
+ case 1: s = "unknown class"; break;
+ case 2: s = "unknown function"; break;
+ default: s = "unkown error"; break;
+ }
+ if (s)
+ printk(KERN_INFO "capidrv-%d: %s from controller 0x%x function %d: %s\n",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrController,
+ cmsg->Function, s);
+ break;
+ }
+ goto ignored;
+ case CAPI_FACILITY_IND: /* Controller/plci/ncci */
+ goto ignored;
+ case CAPI_FACILITY_CONF: /* Controller/plci/ncci */
+ goto ignored;
+ case CAPI_INFO_IND: /* Controller/plci */
+ goto ignored;
+ case CAPI_INFO_CONF: /* Controller/plci */
+ goto ignored;
+
+ default:
+ printk(KERN_ERR "capidrv-%d: got %s from controller 0x%x ???",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrController);
+ }
+ return;
+
+ ignored:
+ printk(KERN_INFO "capidrv-%d: %s from controller 0x%x ignored\n",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrController);
+}
+
+static void handle_incoming_call(capidrv_contr * card, _cmsg * cmsg)
+{
+ capidrv_plci *plcip;
+ capidrv_bchan *bchan;
+ isdn_ctrl cmd;
+ int chan;
+
+ if ((chan = new_bchan(card)) == -1) {
+ printk(KERN_ERR "capidrv-%d: incoming call on not existing bchan ?\n", card->contrnr);
+ return;
+ }
+ bchan = &card->bchans[chan];
+ if ((plcip = new_plci(card, chan)) == NULL) {
+ printk(KERN_ERR "capidrv-%d: incoming call: no memory, sorry.\n", card->contrnr);
+ return;
+ }
+ bchan->incoming = 1;
+ plcip->plci = cmsg->adr.adrPLCI;
+ plci_change_state(card, plcip, EV_PLCI_CONNECT_IND);
+
+ cmd.command = ISDN_STAT_ICALL;
+ cmd.driver = card->myid;
+ cmd.arg = chan;
+ memset(&cmd.parm.setup, 0, sizeof(cmd.parm.setup));
+ strncpy(cmd.parm.setup.phone,
+ cmsg->CallingPartyNumber + 3,
+ cmsg->CallingPartyNumber[0] - 2);
+ strncpy(cmd.parm.setup.eazmsn,
+ cmsg->CalledPartyNumber + 2,
+ cmsg->CalledPartyNumber[0] - 1);
+ cmd.parm.setup.si1 = cip2si1(cmsg->CIPValue);
+ cmd.parm.setup.si2 = cip2si2(cmsg->CIPValue);
+ cmd.parm.setup.plan = cmsg->CallingPartyNumber[1];
+ cmd.parm.setup.screen = cmsg->CallingPartyNumber[2];
+
+ printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s\n",
+ card->contrnr,
+ cmd.parm.setup.phone,
+ cmd.parm.setup.si1,
+ cmd.parm.setup.si2,
+ cmd.parm.setup.eazmsn);
+
+ if (cmd.parm.setup.si1 == 1 && cmd.parm.setup.si2 != 0) {
+ printk(KERN_INFO "capidrv-%d: patching si2=%d to 0 for VBOX\n",
+ card->contrnr,
+ cmd.parm.setup.si2);
+ cmd.parm.setup.si2 = 0;
+ }
+
+ switch (card->interface.statcallb(&cmd)) {
+ case 0:
+ case 3:
+ /* No device matching this call.
+ * and isdn_common.c has send a HANGUP command
+ * which is ignored in state ST_PLCI_INCOMING,
+ * so we send RESP to ignore the call
+ */
+ capi_cmsg_answer(cmsg);
+ cmsg->Reject = 1; /* ignore */
+ send_message(card, cmsg);
+ plci_change_state(card, plcip, EV_PLCI_CONNECT_REJECT);
+ printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s ignored\n",
+ card->contrnr,
+ cmd.parm.setup.phone,
+ cmd.parm.setup.si1,
+ cmd.parm.setup.si2,
+ cmd.parm.setup.eazmsn);
+ break;
+ case 1:
+ /* At least one device matching this call (RING on ttyI)
+ * HL-driver may send ALERTING on the D-channel in this
+ * case.
+ * really means: RING on ttyI or a net interface
+ * accepted this call already.
+ *
+ * If the call was accepted, state has already changed,
+ * and CONNECT_RESP already sent.
+ */
+ if (plcip->state == ST_PLCI_INCOMING) {
+ printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s tty alerting\n",
+ card->contrnr,
+ cmd.parm.setup.phone,
+ cmd.parm.setup.si1,
+ cmd.parm.setup.si2,
+ cmd.parm.setup.eazmsn);
+ capi_fill_ALERT_REQ(cmsg,
+ global.ap.applid,
+ card->msgid++,
+ plcip->plci, /* adr */
+ NULL,/* BChannelinformation */
+ NULL,/* Keypadfacility */
+ NULL,/* Useruserdata */
+ NULL /* Facilitydataarray */
+ );
+ plcip->msgid = cmsg->Messagenumber;
+ send_message(card, cmsg);
+ } else {
+ printk(KERN_INFO "capidrv-%d: incoming call %s,%d,%d,%s on netdev\n",
+ card->contrnr,
+ cmd.parm.setup.phone,
+ cmd.parm.setup.si1,
+ cmd.parm.setup.si2,
+ cmd.parm.setup.eazmsn);
+ }
+ break;
+
+ case 2: /* Call will be rejected. */
+ capi_cmsg_answer(cmsg);
+ cmsg->Reject = 2; /* reject call, normal call clearing */
+ send_message(card, cmsg);
+ plci_change_state(card, plcip, EV_PLCI_CONNECT_REJECT);
+ break;
+
+ default:
+ /* An error happened. (Invalid parameters for example.) */
+ capi_cmsg_answer(cmsg);
+ cmsg->Reject = 8; /* reject call,
+ destination out of order */
+ send_message(card, cmsg);
+ plci_change_state(card, plcip, EV_PLCI_CONNECT_REJECT);
+ break;
+ }
+ return;
+}
+
+static void handle_plci(_cmsg * cmsg)
+{
+ capidrv_contr *card = findcontrbynumber(cmsg->adr.adrController & 0x7f);
+ capidrv_plci *plcip;
+ isdn_ctrl cmd;
+ _cdebbuf *cdb;
+
+ if (!card) {
+ printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n",
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrController & 0x7f);
+ return;
+ }
+ switch (CAPICMD(cmsg->Command, cmsg->Subcommand)) {
+
+ case CAPI_DISCONNECT_IND: /* plci */
+ if (cmsg->Reason) {
+ printk(KERN_INFO "capidrv-%d: %s reason 0x%x (%s) for plci 0x%x\n",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->Reason, capi_info2str(cmsg->Reason), cmsg->adr.adrPLCI);
+ }
+ if (!(plcip = find_plci_by_plci(card, cmsg->adr.adrPLCI))) {
+ capi_cmsg_answer(cmsg);
+ send_message(card, cmsg);
+ goto notfound;
+ }
+ card->bchans[plcip->chan].disconnecting = 1;
+ plci_change_state(card, plcip, EV_PLCI_DISCONNECT_IND);
+ capi_cmsg_answer(cmsg);
+ send_message(card, cmsg);
+ plci_change_state(card, plcip, EV_PLCI_DISCONNECT_RESP);
+ break;
+
+ case CAPI_DISCONNECT_CONF: /* plci */
+ if (cmsg->Info) {
+ printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for plci 0x%x\n",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->Info, capi_info2str(cmsg->Info),
+ cmsg->adr.adrPLCI);
+ }
+ if (!(plcip = find_plci_by_plci(card, cmsg->adr.adrPLCI)))
+ goto notfound;
+
+ card->bchans[plcip->chan].disconnecting = 1;
+ break;
+
+ case CAPI_ALERT_CONF: /* plci */
+ if (cmsg->Info) {
+ printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for plci 0x%x\n",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->Info, capi_info2str(cmsg->Info),
+ cmsg->adr.adrPLCI);
+ }
+ break;
+
+ case CAPI_CONNECT_IND: /* plci */
+ handle_incoming_call(card, cmsg);
+ break;
+
+ case CAPI_CONNECT_CONF: /* plci */
+ if (cmsg->Info) {
+ printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for plci 0x%x\n",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->Info, capi_info2str(cmsg->Info),
+ cmsg->adr.adrPLCI);
+ }
+ if (!(plcip = find_plci_by_msgid(card, cmsg->Messagenumber)))
+ goto notfound;
+
+ plcip->plci = cmsg->adr.adrPLCI;
+ if (cmsg->Info) {
+ plci_change_state(card, plcip, EV_PLCI_CONNECT_CONF_ERROR);
+ } else {
+ plci_change_state(card, plcip, EV_PLCI_CONNECT_CONF_OK);
+ }
+ break;
+
+ case CAPI_CONNECT_ACTIVE_IND: /* plci */
+
+ if (!(plcip = find_plci_by_plci(card, cmsg->adr.adrPLCI)))
+ goto notfound;
+
+ if (card->bchans[plcip->chan].incoming) {
+ capi_cmsg_answer(cmsg);
+ send_message(card, cmsg);
+ plci_change_state(card, plcip, EV_PLCI_CONNECT_ACTIVE_IND);
+ } else {
+ capidrv_ncci *nccip;
+ capi_cmsg_answer(cmsg);
+ send_message(card, cmsg);
+
+ nccip = new_ncci(card, plcip, cmsg->adr.adrPLCI);
+
+ if (!nccip) {
+ printk(KERN_ERR "capidrv-%d: no mem for ncci, sorry\n", card->contrnr);
+ break; /* $$$$ */
+ }
+ capi_fill_CONNECT_B3_REQ(cmsg,
+ global.ap.applid,
+ card->msgid++,
+ plcip->plci, /* adr */
+ NULL /* NCPI */
+ );
+ nccip->msgid = cmsg->Messagenumber;
+ send_message(card, cmsg);
+ cmd.command = ISDN_STAT_DCONN;
+ cmd.driver = card->myid;
+ cmd.arg = plcip->chan;
+ card->interface.statcallb(&cmd);
+ plci_change_state(card, plcip, EV_PLCI_CONNECT_ACTIVE_IND);
+ ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_REQ);
+ }
+ break;
+
+ case CAPI_INFO_IND: /* Controller/plci */
+
+ if (!(plcip = find_plci_by_plci(card, cmsg->adr.adrPLCI)))
+ goto notfound;
+
+ if (cmsg->InfoNumber == 0x4000) {
+ if (cmsg->InfoElement[0] == 4) {
+ cmd.command = ISDN_STAT_CINF;
+ cmd.driver = card->myid;
+ cmd.arg = plcip->chan;
+ sprintf(cmd.parm.num, "%lu",
+ (unsigned long)
+ ((u32) cmsg->InfoElement[1]
+ | ((u32) (cmsg->InfoElement[2]) << 8)
+ | ((u32) (cmsg->InfoElement[3]) << 16)
+ | ((u32) (cmsg->InfoElement[4]) << 24)));
+ card->interface.statcallb(&cmd);
+ break;
+ }
+ }
+ cdb = capi_cmsg2str(cmsg);
+ if (cdb) {
+ printk(KERN_WARNING "capidrv-%d: %s\n",
+ card->contrnr, cdb->buf);
+ cdebbuf_free(cdb);
+ } else
+ printk(KERN_WARNING "capidrv-%d: CAPI_INFO_IND InfoNumber %x not handled\n",
+ card->contrnr, cmsg->InfoNumber);
+
+ break;
+
+ case CAPI_CONNECT_ACTIVE_CONF: /* plci */
+ goto ignored;
+ case CAPI_SELECT_B_PROTOCOL_CONF: /* plci */
+ goto ignored;
+ case CAPI_FACILITY_IND: /* Controller/plci/ncci */
+ goto ignored;
+ case CAPI_FACILITY_CONF: /* Controller/plci/ncci */
+ goto ignored;
+
+ case CAPI_INFO_CONF: /* Controller/plci */
+ goto ignored;
+
+ default:
+ printk(KERN_ERR "capidrv-%d: got %s for plci 0x%x ???",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrPLCI);
+ }
+ return;
+ ignored:
+ printk(KERN_INFO "capidrv-%d: %s for plci 0x%x ignored\n",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrPLCI);
+ return;
+ notfound:
+ printk(KERN_ERR "capidrv-%d: %s: plci 0x%x not found\n",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrPLCI);
+ return;
+}
+
+static void handle_ncci(_cmsg * cmsg)
+{
+ capidrv_contr *card = findcontrbynumber(cmsg->adr.adrController & 0x7f);
+ capidrv_plci *plcip;
+ capidrv_ncci *nccip;
+ isdn_ctrl cmd;
+ int len;
+
+ if (!card) {
+ printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n",
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrController & 0x7f);
+ return;
+ }
+ switch (CAPICMD(cmsg->Command, cmsg->Subcommand)) {
+
+ case CAPI_CONNECT_B3_ACTIVE_IND: /* ncci */
+ if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI)))
+ goto notfound;
+
+ capi_cmsg_answer(cmsg);
+ send_message(card, cmsg);
+ ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_ACTIVE_IND);
+
+ cmd.command = ISDN_STAT_BCONN;
+ cmd.driver = card->myid;
+ cmd.arg = nccip->chan;
+ card->interface.statcallb(&cmd);
+
+ printk(KERN_INFO "capidrv-%d: chan %d up with ncci 0x%x\n",
+ card->contrnr, nccip->chan, nccip->ncci);
+ break;
+
+ case CAPI_CONNECT_B3_ACTIVE_CONF: /* ncci */
+ goto ignored;
+
+ case CAPI_CONNECT_B3_IND: /* ncci */
+
+ plcip = find_plci_by_ncci(card, cmsg->adr.adrNCCI);
+ if (plcip) {
+ nccip = new_ncci(card, plcip, cmsg->adr.adrNCCI);
+ if (nccip) {
+ ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_IND);
+ capi_fill_CONNECT_B3_RESP(cmsg,
+ global.ap.applid,
+ card->msgid++,
+ nccip->ncci, /* adr */
+ 0, /* Reject */
+ NULL /* NCPI */
+ );
+ send_message(card, cmsg);
+ ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_RESP);
+ break;
+ }
+ printk(KERN_ERR "capidrv-%d: no mem for ncci, sorry\n", card->contrnr);
+ } else {
+ printk(KERN_ERR "capidrv-%d: %s: plci for ncci 0x%x not found\n",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrNCCI);
+ }
+ capi_fill_CONNECT_B3_RESP(cmsg,
+ global.ap.applid,
+ card->msgid++,
+ cmsg->adr.adrNCCI,
+ 2, /* Reject */
+ NULL /* NCPI */
+ );
+ send_message(card, cmsg);
+ break;
+
+ case CAPI_CONNECT_B3_CONF: /* ncci */
+
+ if (!(nccip = find_ncci_by_msgid(card,
+ cmsg->adr.adrNCCI,
+ cmsg->Messagenumber)))
+ goto notfound;
+
+ nccip->ncci = cmsg->adr.adrNCCI;
+ if (cmsg->Info) {
+ printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for ncci 0x%x\n",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->Info, capi_info2str(cmsg->Info),
+ cmsg->adr.adrNCCI);
+ }
+
+ if (cmsg->Info)
+ ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_CONF_ERROR);
+ else
+ ncci_change_state(card, nccip, EV_NCCI_CONNECT_B3_CONF_OK);
+ break;
+
+ case CAPI_CONNECT_B3_T90_ACTIVE_IND: /* ncci */
+ capi_cmsg_answer(cmsg);
+ send_message(card, cmsg);
+ break;
+
+ case CAPI_DATA_B3_IND: /* ncci */
+ /* handled in handle_data() */
+ goto ignored;
+
+ case CAPI_DATA_B3_CONF: /* ncci */
+ if (cmsg->Info) {
+ printk(KERN_WARNING "CAPI_DATA_B3_CONF: Info %x - %s\n",
+ cmsg->Info, capi_info2str(cmsg->Info));
+ }
+ if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI)))
+ goto notfound;
+
+ len = capidrv_del_ack(nccip, cmsg->DataHandle);
+ if (len < 0)
+ break;
+ cmd.command = ISDN_STAT_BSENT;
+ cmd.driver = card->myid;
+ cmd.arg = nccip->chan;
+ cmd.parm.length = len;
+ card->interface.statcallb(&cmd);
+ break;
+
+ case CAPI_DISCONNECT_B3_IND: /* ncci */
+ if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI)))
+ goto notfound;
+
+ card->bchans[nccip->chan].disconnecting = 1;
+ ncci_change_state(card, nccip, EV_NCCI_DISCONNECT_B3_IND);
+ capi_cmsg_answer(cmsg);
+ send_message(card, cmsg);
+ ncci_change_state(card, nccip, EV_NCCI_DISCONNECT_B3_RESP);
+ break;
+
+ case CAPI_DISCONNECT_B3_CONF: /* ncci */
+ if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI)))
+ goto notfound;
+ if (cmsg->Info) {
+ printk(KERN_INFO "capidrv-%d: %s info 0x%x (%s) for ncci 0x%x\n",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->Info, capi_info2str(cmsg->Info),
+ cmsg->adr.adrNCCI);
+ ncci_change_state(card, nccip, EV_NCCI_DISCONNECT_B3_CONF_ERROR);
+ }
+ break;
+
+ case CAPI_RESET_B3_IND: /* ncci */
+ if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI)))
+ goto notfound;
+ ncci_change_state(card, nccip, EV_NCCI_RESET_B3_IND);
+ capi_cmsg_answer(cmsg);
+ send_message(card, cmsg);
+ break;
+
+ case CAPI_RESET_B3_CONF: /* ncci */
+ goto ignored; /* $$$$ */
+
+ case CAPI_FACILITY_IND: /* Controller/plci/ncci */
+ goto ignored;
+ case CAPI_FACILITY_CONF: /* Controller/plci/ncci */
+ goto ignored;
+
+ default:
+ printk(KERN_ERR "capidrv-%d: got %s for ncci 0x%x ???",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrNCCI);
+ }
+ return;
+ ignored:
+ printk(KERN_INFO "capidrv-%d: %s for ncci 0x%x ignored\n",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrNCCI);
+ return;
+ notfound:
+ printk(KERN_ERR "capidrv-%d: %s: ncci 0x%x not found\n",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrNCCI);
+}
+
+
+static void handle_data(_cmsg * cmsg, struct sk_buff *skb)
+{
+ capidrv_contr *card = findcontrbynumber(cmsg->adr.adrController & 0x7f);
+ capidrv_ncci *nccip;
+
+ if (!card) {
+ printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n",
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrController & 0x7f);
+ kfree_skb(skb);
+ return;
+ }
+ if (!(nccip = find_ncci(card, cmsg->adr.adrNCCI))) {
+ printk(KERN_ERR "capidrv-%d: %s: ncci 0x%x not found\n",
+ card->contrnr,
+ capi_cmd2str(cmsg->Command, cmsg->Subcommand),
+ cmsg->adr.adrNCCI);
+ kfree_skb(skb);
+ return;
+ }
+ (void) skb_pull(skb, CAPIMSG_LEN(skb->data));
+ card->interface.rcvcallb_skb(card->myid, nccip->chan, skb);
+ capi_cmsg_answer(cmsg);
+ send_message(card, cmsg);
+}
+
+static _cmsg s_cmsg;
+
+static void capidrv_recv_message(struct capi20_appl *ap, struct sk_buff *skb)
+{
+ capi_message2cmsg(&s_cmsg, skb->data);
+ if (debugmode > 3) {
+ _cdebbuf *cdb = capi_cmsg2str(&s_cmsg);
+
+ if (cdb) {
+ printk(KERN_DEBUG "%s: applid=%d %s\n", __func__,
+ ap->applid, cdb->buf);
+ cdebbuf_free(cdb);
+ } else
+ printk(KERN_DEBUG "%s: applid=%d %s not traced\n",
+ __func__, ap->applid,
+ capi_cmd2str(s_cmsg.Command, s_cmsg.Subcommand));
+ }
+ if (s_cmsg.Command == CAPI_DATA_B3
+ && s_cmsg.Subcommand == CAPI_IND) {
+ handle_data(&s_cmsg, skb);
+ return;
+ }
+ if ((s_cmsg.adr.adrController & 0xffffff00) == 0)
+ handle_controller(&s_cmsg);
+ else if ((s_cmsg.adr.adrPLCI & 0xffff0000) == 0)
+ handle_plci(&s_cmsg);
+ else
+ handle_ncci(&s_cmsg);
+ /*
+ * data of skb used in s_cmsg,
+ * free data when s_cmsg is not used again
+ * thanks to Lars Heete <hel@admin.de>
+ */
+ kfree_skb(skb);
+}
+
+/* ------------------------------------------------------------------- */
+
+#define PUTBYTE_TO_STATUS(card, byte) \
+ do { \
+ *(card)->q931_write++ = (byte); \
+ if ((card)->q931_write > (card)->q931_end) \
+ (card)->q931_write = (card)->q931_buf; \
+ } while (0)
+
+static void handle_dtrace_data(capidrv_contr *card,
+ int send, int level2, u8 *data, u16 len)
+{
+ u8 *p, *end;
+ isdn_ctrl cmd;
+
+ if (!len) {
+ printk(KERN_DEBUG "capidrv-%d: avmb1_q931_data: len == %d\n",
+ card->contrnr, len);
+ return;
+ }
+
+ if (level2) {
+ PUTBYTE_TO_STATUS(card, 'D');
+ PUTBYTE_TO_STATUS(card, '2');
+ PUTBYTE_TO_STATUS(card, send ? '>' : '<');
+ PUTBYTE_TO_STATUS(card, ':');
+ } else {
+ PUTBYTE_TO_STATUS(card, 'D');
+ PUTBYTE_TO_STATUS(card, '3');
+ PUTBYTE_TO_STATUS(card, send ? '>' : '<');
+ PUTBYTE_TO_STATUS(card, ':');
+ }
+
+ for (p = data, end = data+len; p < end; p++) {
+ u8 w;
+ PUTBYTE_TO_STATUS(card, ' ');
+ w = (*p >> 4) & 0xf;
+ PUTBYTE_TO_STATUS(card, (w < 10) ? '0'+w : 'A'-10+w);
+ w = *p & 0xf;
+ PUTBYTE_TO_STATUS(card, (w < 10) ? '0'+w : 'A'-10+w);
+ }
+ PUTBYTE_TO_STATUS(card, '\n');
+
+ cmd.command = ISDN_STAT_STAVAIL;
+ cmd.driver = card->myid;
+ cmd.arg = len*3+5;
+ card->interface.statcallb(&cmd);
+}
+
+/* ------------------------------------------------------------------- */
+
+static _cmsg cmdcmsg;
+
+static int capidrv_ioctl(isdn_ctrl * c, capidrv_contr * card)
+{
+ switch (c->arg) {
+ case 1:
+ debugmode = (int)(*((unsigned int *)c->parm.num));
+ printk(KERN_DEBUG "capidrv-%d: debugmode=%d\n",
+ card->contrnr, debugmode);
+ return 0;
+ default:
+ printk(KERN_DEBUG "capidrv-%d: capidrv_ioctl(%ld) called ??\n",
+ card->contrnr, c->arg);
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+/*
+ * Handle leased lines (CAPI-Bundling)
+ */
+
+struct internal_bchannelinfo {
+ unsigned short channelalloc;
+ unsigned short operation;
+ unsigned char cmask[31];
+};
+
+static int decodeFVteln(char *teln, unsigned long *bmaskp, int *activep)
+{
+ unsigned long bmask = 0;
+ int active = !0;
+ char *s;
+ int i;
+
+ if (strncmp(teln, "FV:", 3) != 0)
+ return 1;
+ s = teln + 3;
+ while (*s && *s == ' ') s++;
+ if (!*s) return -2;
+ if (*s == 'p' || *s == 'P') {
+ active = 0;
+ s++;
+ }
+ if (*s == 'a' || *s == 'A') {
+ active = !0;
+ s++;
+ }
+ while (*s) {
+ int digit1 = 0;
+ int digit2 = 0;
+ if (!isdigit(*s)) return -3;
+ while (isdigit(*s)) { digit1 = digit1*10 + (*s - '0'); s++; }
+ if (digit1 <= 0 && digit1 > 30) return -4;
+ if (*s == 0 || *s == ',' || *s == ' ') {
+ bmask |= (1 << digit1);
+ digit1 = 0;
+ if (*s) s++;
+ continue;
+ }
+ if (*s != '-') return -5;
+ s++;
+ if (!isdigit(*s)) return -3;
+ while (isdigit(*s)) { digit2 = digit2*10 + (*s - '0'); s++; }
+ if (digit2 <= 0 && digit2 > 30) return -4;
+ if (*s == 0 || *s == ',' || *s == ' ') {
+ if (digit1 > digit2)
+ for (i = digit2; i <= digit1 ; i++)
+ bmask |= (1 << i);
+ else
+ for (i = digit1; i <= digit2 ; i++)
+ bmask |= (1 << i);
+ digit1 = digit2 = 0;
+ if (*s) s++;
+ continue;
+ }
+ return -6;
+ }
+ if (activep) *activep = active;
+ if (bmaskp) *bmaskp = bmask;
+ return 0;
+}
+
+static int FVteln2capi20(char *teln, u8 AdditionalInfo[1+2+2+31])
+{
+ unsigned long bmask;
+ int active;
+ int rc, i;
+
+ rc = decodeFVteln(teln, &bmask, &active);
+ if (rc) return rc;
+ /* Length */
+ AdditionalInfo[0] = 2+2+31;
+ /* Channel: 3 => use channel allocation */
+ AdditionalInfo[1] = 3; AdditionalInfo[2] = 0;
+ /* Operation: 0 => DTE mode, 1 => DCE mode */
+ if (active) {
+ AdditionalInfo[3] = 0; AdditionalInfo[4] = 0;
+ } else {
+ AdditionalInfo[3] = 1; AdditionalInfo[4] = 0;
+ }
+ /* Channel mask array */
+ AdditionalInfo[5] = 0; /* no D-Channel */
+ for (i=1; i <= 30; i++)
+ AdditionalInfo[5+i] = (bmask & (1 << i)) ? 0xff : 0;
+ return 0;
+}
+
+static int capidrv_command(isdn_ctrl * c, capidrv_contr * card)
+{
+ isdn_ctrl cmd;
+ struct capidrv_bchan *bchan;
+ struct capidrv_plci *plcip;
+ u8 AdditionalInfo[1+2+2+31];
+ int rc, isleasedline = 0;
+
+ if (c->command == ISDN_CMD_IOCTL)
+ return capidrv_ioctl(c, card);
+
+ switch (c->command) {
+ case ISDN_CMD_DIAL:{
+ u8 calling[ISDN_MSNLEN + 3];
+ u8 called[ISDN_MSNLEN + 2];
+
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_DIAL(ch=%ld,\"%s,%d,%d,%s\")\n",
+ card->contrnr,
+ c->arg,
+ c->parm.setup.phone,
+ c->parm.setup.si1,
+ c->parm.setup.si2,
+ c->parm.setup.eazmsn);
+
+ bchan = &card->bchans[c->arg % card->nbchan];
+
+ if (bchan->plcip) {
+ printk(KERN_ERR "capidrv-%d: dail ch=%ld,\"%s,%d,%d,%s\" in use (plci=0x%x)\n",
+ card->contrnr,
+ c->arg,
+ c->parm.setup.phone,
+ c->parm.setup.si1,
+ c->parm.setup.si2,
+ c->parm.setup.eazmsn,
+ bchan->plcip->plci);
+ return 0;
+ }
+ bchan->si1 = c->parm.setup.si1;
+ bchan->si2 = c->parm.setup.si2;
+
+ strncpy(bchan->num, c->parm.setup.phone, sizeof(bchan->num));
+ strncpy(bchan->mynum, c->parm.setup.eazmsn, sizeof(bchan->mynum));
+ rc = FVteln2capi20(bchan->num, AdditionalInfo);
+ isleasedline = (rc == 0);
+ if (rc < 0)
+ printk(KERN_ERR "capidrv-%d: WARNING: invalid leased linedefinition \"%s\"\n", card->contrnr, bchan->num);
+
+ if (isleasedline) {
+ calling[0] = 0;
+ called[0] = 0;
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: connecting leased line\n", card->contrnr);
+ } else {
+ calling[0] = strlen(bchan->mynum) + 2;
+ calling[1] = 0;
+ calling[2] = 0x80;
+ strncpy(calling + 3, bchan->mynum, ISDN_MSNLEN);
+ called[0] = strlen(bchan->num) + 1;
+ called[1] = 0x80;
+ strncpy(called + 2, bchan->num, ISDN_MSNLEN);
+ }
+
+ capi_fill_CONNECT_REQ(&cmdcmsg,
+ global.ap.applid,
+ card->msgid++,
+ card->contrnr, /* adr */
+ si2cip(bchan->si1, bchan->si2), /* cipvalue */
+ called, /* CalledPartyNumber */
+ calling, /* CallingPartyNumber */
+ NULL, /* CalledPartySubaddress */
+ NULL, /* CallingPartySubaddress */
+ b1prot(bchan->l2, bchan->l3), /* B1protocol */
+ b2prot(bchan->l2, bchan->l3), /* B2protocol */
+ b3prot(bchan->l2, bchan->l3), /* B3protocol */
+ b1config(bchan->l2, bchan->l3), /* B1configuration */
+ NULL, /* B2configuration */
+ NULL, /* B3configuration */
+ NULL, /* BC */
+ NULL, /* LLC */
+ NULL, /* HLC */
+ /* BChannelinformation */
+ isleasedline ? AdditionalInfo : NULL,
+ NULL, /* Keypadfacility */
+ NULL, /* Useruserdata */
+ NULL /* Facilitydataarray */
+ );
+ if ((plcip = new_plci(card, (c->arg % card->nbchan))) == NULL) {
+ cmd.command = ISDN_STAT_DHUP;
+ cmd.driver = card->myid;
+ cmd.arg = (c->arg % card->nbchan);
+ card->interface.statcallb(&cmd);
+ return -1;
+ }
+ plcip->msgid = cmdcmsg.Messagenumber;
+ plcip->leasedline = isleasedline;
+ plci_change_state(card, plcip, EV_PLCI_CONNECT_REQ);
+ send_message(card, &cmdcmsg);
+ return 0;
+ }
+
+ case ISDN_CMD_ACCEPTD:
+
+ bchan = &card->bchans[c->arg % card->nbchan];
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_ACCEPTD(ch=%ld) l2=%d l3=%d\n",
+ card->contrnr,
+ c->arg, bchan->l2, bchan->l3);
+
+ capi_fill_CONNECT_RESP(&cmdcmsg,
+ global.ap.applid,
+ card->msgid++,
+ bchan->plcip->plci, /* adr */
+ 0, /* Reject */
+ b1prot(bchan->l2, bchan->l3), /* B1protocol */
+ b2prot(bchan->l2, bchan->l3), /* B2protocol */
+ b3prot(bchan->l2, bchan->l3), /* B3protocol */
+ b1config(bchan->l2, bchan->l3), /* B1configuration */
+ NULL, /* B2configuration */
+ NULL, /* B3configuration */
+ NULL, /* ConnectedNumber */
+ NULL, /* ConnectedSubaddress */
+ NULL, /* LLC */
+ NULL, /* BChannelinformation */
+ NULL, /* Keypadfacility */
+ NULL, /* Useruserdata */
+ NULL /* Facilitydataarray */
+ );
+ capi_cmsg2message(&cmdcmsg, cmdcmsg.buf);
+ plci_change_state(card, bchan->plcip, EV_PLCI_CONNECT_RESP);
+ send_message(card, &cmdcmsg);
+ return 0;
+
+ case ISDN_CMD_ACCEPTB:
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_ACCEPTB(ch=%ld)\n",
+ card->contrnr,
+ c->arg);
+ return -ENOSYS;
+
+ case ISDN_CMD_HANGUP:
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: ISDN_CMD_HANGUP(ch=%ld)\n",
+ card->contrnr,
+ c->arg);
+ bchan = &card->bchans[c->arg % card->nbchan];
+
+ if (bchan->disconnecting) {
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: chan %ld already disconnecting ...\n",
+ card->contrnr,
+ c->arg);
+ return 0;
+ }
+ if (bchan->nccip) {
+ bchan->disconnecting = 1;
+ capi_fill_DISCONNECT_B3_REQ(&cmdcmsg,
+ global.ap.applid,
+ card->msgid++,
+ bchan->nccip->ncci,
+ NULL /* NCPI */
+ );
+ ncci_change_state(card, bchan->nccip, EV_NCCI_DISCONNECT_B3_REQ);
+ send_message(card, &cmdcmsg);
+ return 0;
+ } else if (bchan->plcip) {
+ if (bchan->plcip->state == ST_PLCI_INCOMING) {
+ /*
+ * just ignore, we a called from
+ * isdn_status_callback(),
+ * which will return 0 or 2, this is handled
+ * by the CONNECT_IND handler
+ */
+ bchan->disconnecting = 1;
+ return 0;
+ } else if (bchan->plcip->plci) {
+ bchan->disconnecting = 1;
+ capi_fill_DISCONNECT_REQ(&cmdcmsg,
+ global.ap.applid,
+ card->msgid++,
+ bchan->plcip->plci,
+ NULL, /* BChannelinformation */
+ NULL, /* Keypadfacility */
+ NULL, /* Useruserdata */
+ NULL /* Facilitydataarray */
+ );
+ plci_change_state(card, bchan->plcip, EV_PLCI_DISCONNECT_REQ);
+ send_message(card, &cmdcmsg);
+ return 0;
+ } else {
+ printk(KERN_ERR "capidrv-%d: chan %ld disconnect request while waiting for CONNECT_CONF\n",
+ card->contrnr,
+ c->arg);
+ return -EINVAL;
+ }
+ }
+ printk(KERN_ERR "capidrv-%d: chan %ld disconnect request on free channel\n",
+ card->contrnr,
+ c->arg);
+ return -EINVAL;
+/* ready */
+
+ case ISDN_CMD_SETL2:
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: set L2 on chan %ld to %ld\n",
+ card->contrnr,
+ (c->arg & 0xff), (c->arg >> 8));
+ bchan = &card->bchans[(c->arg & 0xff) % card->nbchan];
+ bchan->l2 = (c->arg >> 8);
+ return 0;
+
+ case ISDN_CMD_SETL3:
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: set L3 on chan %ld to %ld\n",
+ card->contrnr,
+ (c->arg & 0xff), (c->arg >> 8));
+ bchan = &card->bchans[(c->arg & 0xff) % card->nbchan];
+ bchan->l3 = (c->arg >> 8);
+ return 0;
+
+ case ISDN_CMD_SETEAZ:
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: set EAZ \"%s\" on chan %ld\n",
+ card->contrnr,
+ c->parm.num, c->arg);
+ bchan = &card->bchans[c->arg % card->nbchan];
+ strncpy(bchan->msn, c->parm.num, ISDN_MSNLEN);
+ return 0;
+
+ case ISDN_CMD_CLREAZ:
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: clearing EAZ on chan %ld\n",
+ card->contrnr, c->arg);
+ bchan = &card->bchans[c->arg % card->nbchan];
+ bchan->msn[0] = 0;
+ return 0;
+
+ default:
+ printk(KERN_ERR "capidrv-%d: ISDN_CMD_%d, Huh?\n",
+ card->contrnr, c->command);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int if_command(isdn_ctrl * c)
+{
+ capidrv_contr *card = findcontrbydriverid(c->driver);
+
+ if (card)
+ return capidrv_command(c, card);
+
+ printk(KERN_ERR
+ "capidrv: if_command %d called with invalid driverId %d!\n",
+ c->command, c->driver);
+ return -ENODEV;
+}
+
+static _cmsg sendcmsg;
+
+static int if_sendbuf(int id, int channel, int doack, struct sk_buff *skb)
+{
+ capidrv_contr *card = findcontrbydriverid(id);
+ capidrv_bchan *bchan;
+ capidrv_ncci *nccip;
+ int len = skb->len;
+ int msglen;
+ u16 errcode;
+ u16 datahandle;
+ u32 data;
+
+ if (!card) {
+ printk(KERN_ERR "capidrv: if_sendbuf called with invalid driverId %d!\n",
+ id);
+ return 0;
+ }
+ if (debugmode > 4)
+ printk(KERN_DEBUG "capidrv-%d: sendbuf len=%d skb=%p doack=%d\n",
+ card->contrnr, len, skb, doack);
+ bchan = &card->bchans[channel % card->nbchan];
+ nccip = bchan->nccip;
+ if (!nccip || nccip->state != ST_NCCI_ACTIVE) {
+ printk(KERN_ERR "capidrv-%d: if_sendbuf: %s:%d: chan not up!\n",
+ card->contrnr, card->name, channel);
+ return 0;
+ }
+ datahandle = nccip->datahandle;
+
+ /*
+ * Here we copy pointer skb->data into the 32-bit 'Data' field.
+ * The 'Data' field is not used in practice in linux kernel
+ * (neither in 32 or 64 bit), but should have some value,
+ * since a CAPI message trace will display it.
+ *
+ * The correct value in the 32 bit case is the address of the
+ * data, in 64 bit it makes no sense, we use 0 there.
+ */
+
+#ifdef CONFIG_64BIT
+ data = 0;
+#else
+ data = (unsigned long) skb->data;
+#endif
+
+ capi_fill_DATA_B3_REQ(&sendcmsg, global.ap.applid, card->msgid++,
+ nccip->ncci, /* adr */
+ data, /* Data */
+ skb->len, /* DataLength */
+ datahandle, /* DataHandle */
+ 0 /* Flags */
+ );
+
+ if (capidrv_add_ack(nccip, datahandle, doack ? (int)skb->len : -1) < 0)
+ return 0;
+
+ capi_cmsg2message(&sendcmsg, sendcmsg.buf);
+ msglen = CAPIMSG_LEN(sendcmsg.buf);
+ if (skb_headroom(skb) < msglen) {
+ struct sk_buff *nskb = skb_realloc_headroom(skb, msglen);
+ if (!nskb) {
+ printk(KERN_ERR "capidrv-%d: if_sendbuf: no memory\n",
+ card->contrnr);
+ (void)capidrv_del_ack(nccip, datahandle);
+ return 0;
+ }
+ printk(KERN_DEBUG "capidrv-%d: only %d bytes headroom, need %d\n",
+ card->contrnr, skb_headroom(skb), msglen);
+ memcpy(skb_push(nskb, msglen), sendcmsg.buf, msglen);
+ errcode = capi20_put_message(&global.ap, nskb);
+ if (errcode == CAPI_NOERROR) {
+ dev_kfree_skb(skb);
+ nccip->datahandle++;
+ return len;
+ }
+ if (debugmode > 3)
+ printk(KERN_DEBUG "capidrv-%d: sendbuf putmsg ret(%x) - %s\n",
+ card->contrnr, errcode, capi_info2str(errcode));
+ (void)capidrv_del_ack(nccip, datahandle);
+ dev_kfree_skb(nskb);
+ return errcode == CAPI_SENDQUEUEFULL ? 0 : -1;
+ } else {
+ memcpy(skb_push(skb, msglen), sendcmsg.buf, msglen);
+ errcode = capi20_put_message(&global.ap, skb);
+ if (errcode == CAPI_NOERROR) {
+ nccip->datahandle++;
+ return len;
+ }
+ if (debugmode > 3)
+ printk(KERN_DEBUG "capidrv-%d: sendbuf putmsg ret(%x) - %s\n",
+ card->contrnr, errcode, capi_info2str(errcode));
+ skb_pull(skb, msglen);
+ (void)capidrv_del_ack(nccip, datahandle);
+ return errcode == CAPI_SENDQUEUEFULL ? 0 : -1;
+ }
+}
+
+static int if_readstat(u8 __user *buf, int len, int id, int channel)
+{
+ capidrv_contr *card = findcontrbydriverid(id);
+ int count;
+ u8 __user *p;
+
+ if (!card) {
+ printk(KERN_ERR "capidrv: if_readstat called with invalid driverId %d!\n",
+ id);
+ return -ENODEV;
+ }
+
+ for (p=buf, count=0; count < len; p++, count++) {
+ if (put_user(*card->q931_read++, p))
+ return -EFAULT;
+ if (card->q931_read > card->q931_end)
+ card->q931_read = card->q931_buf;
+ }
+ return count;
+
+}
+
+static void enable_dchannel_trace(capidrv_contr *card)
+{
+ u8 manufacturer[CAPI_MANUFACTURER_LEN];
+ capi_version version;
+ u16 contr = card->contrnr;
+ u16 errcode;
+ u16 avmversion[3];
+
+ errcode = capi20_get_manufacturer(contr, manufacturer);
+ if (errcode != CAPI_NOERROR) {
+ printk(KERN_ERR "%s: can't get manufacturer (0x%x)\n",
+ card->name, errcode);
+ return;
+ }
+ if (strstr(manufacturer, "AVM") == NULL) {
+ printk(KERN_ERR "%s: not from AVM, no d-channel trace possible (%s)\n",
+ card->name, manufacturer);
+ return;
+ }
+ errcode = capi20_get_version(contr, &version);
+ if (errcode != CAPI_NOERROR) {
+ printk(KERN_ERR "%s: can't get version (0x%x)\n",
+ card->name, errcode);
+ return;
+ }
+ avmversion[0] = (version.majormanuversion >> 4) & 0x0f;
+ avmversion[1] = (version.majormanuversion << 4) & 0xf0;
+ avmversion[1] |= (version.minormanuversion >> 4) & 0x0f;
+ avmversion[2] |= version.minormanuversion & 0x0f;
+
+ if (avmversion[0] > 3 || (avmversion[0] == 3 && avmversion[1] > 5)) {
+ printk(KERN_INFO "%s: D2 trace enabled\n", card->name);
+ capi_fill_MANUFACTURER_REQ(&cmdcmsg, global.ap.applid,
+ card->msgid++,
+ contr,
+ 0x214D5641, /* ManuID */
+ 0, /* Class */
+ 1, /* Function */
+ (_cstruct)"\004\200\014\000\000");
+ } else {
+ printk(KERN_INFO "%s: D3 trace enabled\n", card->name);
+ capi_fill_MANUFACTURER_REQ(&cmdcmsg, global.ap.applid,
+ card->msgid++,
+ contr,
+ 0x214D5641, /* ManuID */
+ 0, /* Class */
+ 1, /* Function */
+ (_cstruct)"\004\002\003\000\000");
+ }
+ send_message(card, &cmdcmsg);
+}
+
+
+static void send_listen(capidrv_contr *card)
+{
+ capi_fill_LISTEN_REQ(&cmdcmsg, global.ap.applid,
+ card->msgid++,
+ card->contrnr, /* controller */
+ 1 << 6, /* Infomask */
+ card->cipmask,
+ card->cipmask2,
+ NULL, NULL);
+ send_message(card, &cmdcmsg);
+ listen_change_state(card, EV_LISTEN_REQ);
+}
+
+static void listentimerfunc(unsigned long x)
+{
+ capidrv_contr *card = (capidrv_contr *)x;
+ if (card->state != ST_LISTEN_NONE && card->state != ST_LISTEN_ACTIVE)
+ printk(KERN_ERR "%s: controller dead ??\n", card->name);
+ send_listen(card);
+ mod_timer(&card->listentimer, jiffies + 60*HZ);
+}
+
+
+static int capidrv_addcontr(u16 contr, struct capi_profile *profp)
+{
+ capidrv_contr *card;
+ unsigned long flags;
+ isdn_ctrl cmd;
+ char id[20];
+ int i;
+
+ sprintf(id, "capidrv-%d", contr);
+ if (!try_module_get(THIS_MODULE)) {
+ printk(KERN_WARNING "capidrv: (%s) Could not reserve module\n", id);
+ return -1;
+ }
+ if (!(card = kzalloc(sizeof(capidrv_contr), GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "capidrv: (%s) Could not allocate contr-struct.\n", id);
+ return -1;
+ }
+ card->owner = THIS_MODULE;
+ init_timer(&card->listentimer);
+ strcpy(card->name, id);
+ card->contrnr = contr;
+ card->nbchan = profp->nbchannel;
+ card->bchans = kmalloc(sizeof(capidrv_bchan) * card->nbchan, GFP_ATOMIC);
+ if (!card->bchans) {
+ printk(KERN_WARNING
+ "capidrv: (%s) Could not allocate bchan-structs.\n", id);
+ module_put(card->owner);
+ kfree(card);
+ return -1;
+ }
+ card->interface.channels = profp->nbchannel;
+ card->interface.maxbufsize = 2048;
+ card->interface.command = if_command;
+ card->interface.writebuf_skb = if_sendbuf;
+ card->interface.writecmd = NULL;
+ card->interface.readstat = if_readstat;
+ card->interface.features = ISDN_FEATURE_L2_HDLC |
+ ISDN_FEATURE_L2_TRANS |
+ ISDN_FEATURE_L3_TRANS |
+ ISDN_FEATURE_P_UNKNOWN |
+ ISDN_FEATURE_L2_X75I |
+ ISDN_FEATURE_L2_X75UI |
+ ISDN_FEATURE_L2_X75BUI;
+ if (profp->support1 & (1<<2))
+ card->interface.features |= ISDN_FEATURE_L2_V11096 |
+ ISDN_FEATURE_L2_V11019 |
+ ISDN_FEATURE_L2_V11038;
+ if (profp->support1 & (1<<8))
+ card->interface.features |= ISDN_FEATURE_L2_MODEM;
+ card->interface.hl_hdrlen = 22; /* len of DATA_B3_REQ */
+ strncpy(card->interface.id, id, sizeof(card->interface.id) - 1);
+
+
+ card->q931_read = card->q931_buf;
+ card->q931_write = card->q931_buf;
+ card->q931_end = card->q931_buf + sizeof(card->q931_buf) - 1;
+
+ if (!register_isdn(&card->interface)) {
+ printk(KERN_ERR "capidrv: Unable to register contr %s\n", id);
+ kfree(card->bchans);
+ module_put(card->owner);
+ kfree(card);
+ return -1;
+ }
+ card->myid = card->interface.channels;
+ memset(card->bchans, 0, sizeof(capidrv_bchan) * card->nbchan);
+ for (i = 0; i < card->nbchan; i++) {
+ card->bchans[i].contr = card;
+ }
+
+ spin_lock_irqsave(&global_lock, flags);
+ card->next = global.contr_list;
+ global.contr_list = card;
+ global.ncontr++;
+ spin_unlock_irqrestore(&global_lock, flags);
+
+ cmd.command = ISDN_STAT_RUN;
+ cmd.driver = card->myid;
+ card->interface.statcallb(&cmd);
+
+ card->cipmask = 0x1FFF03FF; /* any */
+ card->cipmask2 = 0;
+
+ card->listentimer.data = (unsigned long)card;
+ card->listentimer.function = listentimerfunc;
+ send_listen(card);
+ mod_timer(&card->listentimer, jiffies + 60*HZ);
+
+ printk(KERN_INFO "%s: now up (%d B channels)\n",
+ card->name, card->nbchan);
+
+ enable_dchannel_trace(card);
+
+ return 0;
+}
+
+static int capidrv_delcontr(u16 contr)
+{
+ capidrv_contr **pp, *card;
+ unsigned long flags;
+ isdn_ctrl cmd;
+
+ spin_lock_irqsave(&global_lock, flags);
+ for (card = global.contr_list; card; card = card->next) {
+ if (card->contrnr == contr)
+ break;
+ }
+ if (!card) {
+ spin_unlock_irqrestore(&global_lock, flags);
+ printk(KERN_ERR "capidrv: delcontr: no contr %u\n", contr);
+ return -1;
+ }
+
+ /* FIXME: maybe a race condition the card should be removed
+ * here from global list /kkeil
+ */
+ spin_unlock_irqrestore(&global_lock, flags);
+
+ del_timer(&card->listentimer);
+
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: id=%d unloading\n",
+ card->contrnr, card->myid);
+
+ cmd.command = ISDN_STAT_STOP;
+ cmd.driver = card->myid;
+ card->interface.statcallb(&cmd);
+
+ while (card->nbchan) {
+
+ cmd.command = ISDN_STAT_DISCH;
+ cmd.driver = card->myid;
+ cmd.arg = card->nbchan-1;
+ cmd.parm.num[0] = 0;
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: id=%d disable chan=%ld\n",
+ card->contrnr, card->myid, cmd.arg);
+ card->interface.statcallb(&cmd);
+
+ if (card->bchans[card->nbchan-1].nccip)
+ free_ncci(card, card->bchans[card->nbchan-1].nccip);
+ if (card->bchans[card->nbchan-1].plcip)
+ free_plci(card, card->bchans[card->nbchan-1].plcip);
+ if (card->plci_list)
+ printk(KERN_ERR "capidrv: bug in free_plci()\n");
+ card->nbchan--;
+ }
+ kfree(card->bchans);
+ card->bchans = NULL;
+
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: id=%d isdn unload\n",
+ card->contrnr, card->myid);
+
+ cmd.command = ISDN_STAT_UNLOAD;
+ cmd.driver = card->myid;
+ card->interface.statcallb(&cmd);
+
+ if (debugmode)
+ printk(KERN_DEBUG "capidrv-%d: id=%d remove contr from list\n",
+ card->contrnr, card->myid);
+
+ spin_lock_irqsave(&global_lock, flags);
+ for (pp = &global.contr_list; *pp; pp = &(*pp)->next) {
+ if (*pp == card) {
+ *pp = (*pp)->next;
+ card->next = NULL;
+ global.ncontr--;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&global_lock, flags);
+
+ module_put(card->owner);
+ printk(KERN_INFO "%s: now down.\n", card->name);
+ kfree(card);
+ return 0;
+}
+
+
+static void lower_callback(unsigned int cmd, u32 contr, void *data)
+{
+
+ switch (cmd) {
+ case KCI_CONTRUP:
+ printk(KERN_INFO "capidrv: controller %hu up\n", contr);
+ (void) capidrv_addcontr(contr, (capi_profile *) data);
+ break;
+ case KCI_CONTRDOWN:
+ printk(KERN_INFO "capidrv: controller %hu down\n", contr);
+ (void) capidrv_delcontr(contr);
+ break;
+ }
+}
+
+/*
+ * /proc/capi/capidrv:
+ * nrecvctlpkt nrecvdatapkt nsendctlpkt nsenddatapkt
+ */
+static int proc_capidrv_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, void *data)
+{
+ int len = 0;
+
+ len += sprintf(page+len, "%lu %lu %lu %lu\n",
+ global.ap.nrecvctlpkt,
+ global.ap.nrecvdatapkt,
+ global.ap.nsentctlpkt,
+ global.ap.nsentdatapkt);
+ if (off+count >= len)
+ *eof = 1;
+ if (len < off)
+ return 0;
+ *start = page + off;
+ return ((count < len-off) ? count : len-off);
+}
+
+static struct procfsentries {
+ char *name;
+ mode_t mode;
+ int (*read_proc)(char *page, char **start, off_t off,
+ int count, int *eof, void *data);
+ struct proc_dir_entry *procent;
+} procfsentries[] = {
+ /* { "capi", S_IFDIR, 0 }, */
+ { "capi/capidrv", 0 , proc_capidrv_read_proc },
+};
+
+static void __init proc_init(void)
+{
+ int nelem = ARRAY_SIZE(procfsentries);
+ int i;
+
+ for (i=0; i < nelem; i++) {
+ struct procfsentries *p = procfsentries + i;
+ p->procent = create_proc_entry(p->name, p->mode, NULL);
+ if (p->procent) p->procent->read_proc = p->read_proc;
+ }
+}
+
+static void __exit proc_exit(void)
+{
+ int nelem = ARRAY_SIZE(procfsentries);
+ int i;
+
+ for (i=nelem-1; i >= 0; i--) {
+ struct procfsentries *p = procfsentries + i;
+ if (p->procent) {
+ remove_proc_entry(p->name, NULL);
+ p->procent = NULL;
+ }
+ }
+}
+
+static int __init capidrv_init(void)
+{
+ capi_profile profile;
+ char rev[32];
+ char *p;
+ u32 ncontr, contr;
+ u16 errcode;
+
+ if ((p = strchr(revision, ':')) != NULL && p[1]) {
+ strncpy(rev, p + 2, sizeof(rev));
+ rev[sizeof(rev)-1] = 0;
+ if ((p = strchr(rev, '$')) != NULL && p > rev)
+ *(p-1) = 0;
+ } else
+ strcpy(rev, "1.0");
+
+ global.ap.rparam.level3cnt = -2; /* number of bchannels twice */
+ global.ap.rparam.datablkcnt = 16;
+ global.ap.rparam.datablklen = 2048;
+
+ global.ap.recv_message = capidrv_recv_message;
+ errcode = capi20_register(&global.ap);
+ if (errcode) {
+ return -EIO;
+ }
+
+ capi20_set_callback(&global.ap, lower_callback);
+
+ errcode = capi20_get_profile(0, &profile);
+ if (errcode != CAPI_NOERROR) {
+ capi20_release(&global.ap);
+ return -EIO;
+ }
+
+ ncontr = profile.ncontroller;
+ for (contr = 1; contr <= ncontr; contr++) {
+ errcode = capi20_get_profile(contr, &profile);
+ if (errcode != CAPI_NOERROR)
+ continue;
+ (void) capidrv_addcontr(contr, &profile);
+ }
+ proc_init();
+
+ printk(KERN_NOTICE "capidrv: Rev %s: loaded\n", rev);
+ return 0;
+}
+
+static void __exit capidrv_exit(void)
+{
+ char rev[32];
+ char *p;
+
+ if ((p = strchr(revision, ':')) != NULL) {
+ strncpy(rev, p + 1, sizeof(rev));
+ rev[sizeof(rev)-1] = 0;
+ if ((p = strchr(rev, '$')) != NULL)
+ *p = 0;
+ } else {
+ strcpy(rev, " ??? ");
+ }
+
+ capi20_release(&global.ap);
+
+ proc_exit();
+
+ printk(KERN_NOTICE "capidrv: Rev%s: unloaded\n", rev);
+}
+
+module_init(capidrv_init);
+module_exit(capidrv_exit);
diff --git a/drivers/isdn/capi/capidrv.h b/drivers/isdn/capi/capidrv.h
new file mode 100644
index 0000000..1e698e1
--- /dev/null
+++ b/drivers/isdn/capi/capidrv.h
@@ -0,0 +1,140 @@
+/* $Id: capidrv.h,v 1.2.8.2 2001/09/23 22:24:33 kai Exp $
+ *
+ * ISDN4Linux Driver, using capi20 interface (kernelcapi)
+ *
+ * Copyright 1997 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef __CAPIDRV_H__
+#define __CAPIDRV_H__
+
+/*
+ * LISTEN state machine
+ */
+#define ST_LISTEN_NONE 0 /* L-0 */
+#define ST_LISTEN_WAIT_CONF 1 /* L-0.1 */
+#define ST_LISTEN_ACTIVE 2 /* L-1 */
+#define ST_LISTEN_ACTIVE_WAIT_CONF 3 /* L-1.1 */
+
+
+#define EV_LISTEN_REQ 1 /* L-0 -> L-0.1
+ L-1 -> L-1.1 */
+#define EV_LISTEN_CONF_ERROR 2 /* L-0.1 -> L-0
+ L-1.1 -> L-1 */
+#define EV_LISTEN_CONF_EMPTY 3 /* L-0.1 -> L-0
+ L-1.1 -> L-0 */
+#define EV_LISTEN_CONF_OK 4 /* L-0.1 -> L-1
+ L-1.1 -> L.1 */
+
+/*
+ * per plci state machine
+ */
+#define ST_PLCI_NONE 0 /* P-0 */
+#define ST_PLCI_OUTGOING 1 /* P-0.1 */
+#define ST_PLCI_ALLOCATED 2 /* P-1 */
+#define ST_PLCI_ACTIVE 3 /* P-ACT */
+#define ST_PLCI_INCOMING 4 /* P-2 */
+#define ST_PLCI_FACILITY_IND 5 /* P-3 */
+#define ST_PLCI_ACCEPTING 6 /* P-4 */
+#define ST_PLCI_DISCONNECTING 7 /* P-5 */
+#define ST_PLCI_DISCONNECTED 8 /* P-6 */
+#define ST_PLCI_RESUMEING 9 /* P-0.Res */
+#define ST_PLCI_RESUME 10 /* P-Res */
+#define ST_PLCI_HELD 11 /* P-HELD */
+
+#define EV_PLCI_CONNECT_REQ 1 /* P-0 -> P-0.1
+ */
+#define EV_PLCI_CONNECT_CONF_ERROR 2 /* P-0.1 -> P-0
+ */
+#define EV_PLCI_CONNECT_CONF_OK 3 /* P-0.1 -> P-1
+ */
+#define EV_PLCI_FACILITY_IND_UP 4 /* P-0 -> P-1
+ */
+#define EV_PLCI_CONNECT_IND 5 /* P-0 -> P-2
+ */
+#define EV_PLCI_CONNECT_ACTIVE_IND 6 /* P-1 -> P-ACT
+ */
+#define EV_PLCI_CONNECT_REJECT 7 /* P-2 -> P-5
+ P-3 -> P-5
+ */
+#define EV_PLCI_DISCONNECT_REQ 8 /* P-1 -> P-5
+ P-2 -> P-5
+ P-3 -> P-5
+ P-4 -> P-5
+ P-ACT -> P-5
+ P-Res -> P-5 (*)
+ P-HELD -> P-5 (*)
+ */
+#define EV_PLCI_DISCONNECT_IND 9 /* P-1 -> P-6
+ P-2 -> P-6
+ P-3 -> P-6
+ P-4 -> P-6
+ P-5 -> P-6
+ P-ACT -> P-6
+ P-Res -> P-6 (*)
+ P-HELD -> P-6 (*)
+ */
+#define EV_PLCI_FACILITY_IND_DOWN 10 /* P-0.1 -> P-5
+ P-1 -> P-5
+ P-ACT -> P-5
+ P-2 -> P-5
+ P-3 -> P-5
+ P-4 -> P-5
+ */
+#define EV_PLCI_DISCONNECT_RESP 11 /* P-6 -> P-0
+ */
+#define EV_PLCI_CONNECT_RESP 12 /* P-6 -> P-0
+ */
+
+#define EV_PLCI_RESUME_REQ 13 /* P-0 -> P-0.Res
+ */
+#define EV_PLCI_RESUME_CONF_OK 14 /* P-0.Res -> P-Res
+ */
+#define EV_PLCI_RESUME_CONF_ERROR 15 /* P-0.Res -> P-0
+ */
+#define EV_PLCI_RESUME_IND 16 /* P-Res -> P-ACT
+ */
+#define EV_PLCI_HOLD_IND 17 /* P-ACT -> P-HELD
+ */
+#define EV_PLCI_RETRIEVE_IND 18 /* P-HELD -> P-ACT
+ */
+#define EV_PLCI_SUSPEND_IND 19 /* P-ACT -> P-5
+ */
+#define EV_PLCI_CD_IND 20 /* P-2 -> P-5
+ */
+
+/*
+ * per ncci state machine
+ */
+#define ST_NCCI_PREVIOUS -1
+#define ST_NCCI_NONE 0 /* N-0 */
+#define ST_NCCI_OUTGOING 1 /* N-0.1 */
+#define ST_NCCI_INCOMING 2 /* N-1 */
+#define ST_NCCI_ALLOCATED 3 /* N-2 */
+#define ST_NCCI_ACTIVE 4 /* N-ACT */
+#define ST_NCCI_RESETING 5 /* N-3 */
+#define ST_NCCI_DISCONNECTING 6 /* N-4 */
+#define ST_NCCI_DISCONNECTED 7 /* N-5 */
+
+#define EV_NCCI_CONNECT_B3_REQ 1 /* N-0 -> N-0.1 */
+#define EV_NCCI_CONNECT_B3_IND 2 /* N-0 -> N.1 */
+#define EV_NCCI_CONNECT_B3_CONF_OK 3 /* N-0.1 -> N.2 */
+#define EV_NCCI_CONNECT_B3_CONF_ERROR 4 /* N-0.1 -> N.0 */
+#define EV_NCCI_CONNECT_B3_REJECT 5 /* N-1 -> N-4 */
+#define EV_NCCI_CONNECT_B3_RESP 6 /* N-1 -> N-2 */
+#define EV_NCCI_CONNECT_B3_ACTIVE_IND 7 /* N-2 -> N-ACT */
+#define EV_NCCI_RESET_B3_REQ 8 /* N-ACT -> N-3 */
+#define EV_NCCI_RESET_B3_IND 9 /* N-3 -> N-ACT */
+#define EV_NCCI_DISCONNECT_B3_IND 10 /* N-4 -> N.5 */
+#define EV_NCCI_DISCONNECT_B3_CONF_ERROR 11 /* N-4 -> previous */
+#define EV_NCCI_DISCONNECT_B3_REQ 12 /* N-1 -> N-4
+ N-2 -> N-4
+ N-3 -> N-4
+ N-ACT -> N-4 */
+#define EV_NCCI_DISCONNECT_B3_RESP 13 /* N-5 -> N-0 */
+
+#endif /* __CAPIDRV_H__ */
diff --git a/drivers/isdn/capi/capifs.c b/drivers/isdn/capi/capifs.c
new file mode 100644
index 0000000..550e80f
--- /dev/null
+++ b/drivers/isdn/capi/capifs.c
@@ -0,0 +1,223 @@
+/* $Id: capifs.c,v 1.1.2.3 2004/01/16 21:09:26 keil Exp $
+ *
+ * Copyright 2000 by Carsten Paeth <calle@calle.de>
+ *
+ * Heavily based on devpts filesystem from H. Peter Anvin
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/namei.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ctype.h>
+#include <linux/sched.h> /* current */
+
+#include "capifs.h"
+
+MODULE_DESCRIPTION("CAPI4Linux: /dev/capi/ filesystem");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+
+/* ------------------------------------------------------------------ */
+
+static char *revision = "$Revision: 1.1.2.3 $";
+
+/* ------------------------------------------------------------------ */
+
+#define CAPIFS_SUPER_MAGIC (('C'<<8)|'N')
+
+static struct vfsmount *capifs_mnt;
+static struct dentry *capifs_root;
+
+static struct {
+ int setuid;
+ int setgid;
+ uid_t uid;
+ gid_t gid;
+ umode_t mode;
+} config = {.mode = 0600};
+
+/* ------------------------------------------------------------------ */
+
+static int capifs_remount(struct super_block *s, int *flags, char *data)
+{
+ int setuid = 0;
+ int setgid = 0;
+ uid_t uid = 0;
+ gid_t gid = 0;
+ umode_t mode = 0600;
+ char *this_char;
+ char *new_opt = kstrdup(data, GFP_KERNEL);
+
+ this_char = NULL;
+ while ((this_char = strsep(&data, ",")) != NULL) {
+ int n;
+ char dummy;
+ if (!*this_char)
+ continue;
+ if (sscanf(this_char, "uid=%i%c", &n, &dummy) == 1) {
+ setuid = 1;
+ uid = n;
+ } else if (sscanf(this_char, "gid=%i%c", &n, &dummy) == 1) {
+ setgid = 1;
+ gid = n;
+ } else if (sscanf(this_char, "mode=%o%c", &n, &dummy) == 1)
+ mode = n & ~S_IFMT;
+ else {
+ kfree(new_opt);
+ printk("capifs: called with bogus options\n");
+ return -EINVAL;
+ }
+ }
+
+ kfree(s->s_options);
+ s->s_options = new_opt;
+
+ config.setuid = setuid;
+ config.setgid = setgid;
+ config.uid = uid;
+ config.gid = gid;
+ config.mode = mode;
+
+ return 0;
+}
+
+static struct super_operations capifs_sops =
+{
+ .statfs = simple_statfs,
+ .remount_fs = capifs_remount,
+ .show_options = generic_show_options,
+};
+
+
+static int
+capifs_fill_super(struct super_block *s, void *data, int silent)
+{
+ struct inode * inode;
+
+ s->s_blocksize = 1024;
+ s->s_blocksize_bits = 10;
+ s->s_magic = CAPIFS_SUPER_MAGIC;
+ s->s_op = &capifs_sops;
+ s->s_time_gran = 1;
+
+ inode = new_inode(s);
+ if (!inode)
+ goto fail;
+ inode->i_ino = 1;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ inode->i_blocks = 0;
+ inode->i_uid = inode->i_gid = 0;
+ inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR;
+ inode->i_op = &simple_dir_inode_operations;
+ inode->i_fop = &simple_dir_operations;
+ inode->i_nlink = 2;
+
+ capifs_root = s->s_root = d_alloc_root(inode);
+ if (s->s_root)
+ return 0;
+
+ printk("capifs: get root dentry failed\n");
+ iput(inode);
+fail:
+ return -ENOMEM;
+}
+
+static int capifs_get_sb(struct file_system_type *fs_type,
+ int flags, const char *dev_name, void *data, struct vfsmount *mnt)
+{
+ return get_sb_single(fs_type, flags, data, capifs_fill_super, mnt);
+}
+
+static struct file_system_type capifs_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "capifs",
+ .get_sb = capifs_get_sb,
+ .kill_sb = kill_anon_super,
+};
+
+static struct dentry *get_node(int num)
+{
+ char s[10];
+ struct dentry *root = capifs_root;
+ mutex_lock(&root->d_inode->i_mutex);
+ return lookup_one_len(s, root, sprintf(s, "%d", num));
+}
+
+void capifs_new_ncci(unsigned int number, dev_t device)
+{
+ struct dentry *dentry;
+ struct inode *inode = new_inode(capifs_mnt->mnt_sb);
+ if (!inode)
+ return;
+ inode->i_ino = number+2;
+ inode->i_uid = config.setuid ? config.uid : current->fsuid;
+ inode->i_gid = config.setgid ? config.gid : current->fsgid;
+ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
+ init_special_inode(inode, S_IFCHR|config.mode, device);
+ //inode->i_op = &capifs_file_inode_operations;
+
+ dentry = get_node(number);
+ if (!IS_ERR(dentry) && !dentry->d_inode)
+ d_instantiate(dentry, inode);
+ mutex_unlock(&capifs_root->d_inode->i_mutex);
+}
+
+void capifs_free_ncci(unsigned int number)
+{
+ struct dentry *dentry = get_node(number);
+
+ if (!IS_ERR(dentry)) {
+ struct inode *inode = dentry->d_inode;
+ if (inode) {
+ inode->i_nlink--;
+ d_delete(dentry);
+ dput(dentry);
+ }
+ dput(dentry);
+ }
+ mutex_unlock(&capifs_root->d_inode->i_mutex);
+}
+
+static int __init capifs_init(void)
+{
+ char rev[32];
+ char *p;
+ int err;
+
+ if ((p = strchr(revision, ':')) != NULL && p[1]) {
+ strlcpy(rev, p + 2, sizeof(rev));
+ if ((p = strchr(rev, '$')) != NULL && p > rev)
+ *(p-1) = 0;
+ } else
+ strcpy(rev, "1.0");
+
+ err = register_filesystem(&capifs_fs_type);
+ if (!err) {
+ capifs_mnt = kern_mount(&capifs_fs_type);
+ if (IS_ERR(capifs_mnt)) {
+ err = PTR_ERR(capifs_mnt);
+ unregister_filesystem(&capifs_fs_type);
+ }
+ }
+ if (!err)
+ printk(KERN_NOTICE "capifs: Rev %s\n", rev);
+ return err;
+}
+
+static void __exit capifs_exit(void)
+{
+ unregister_filesystem(&capifs_fs_type);
+ mntput(capifs_mnt);
+}
+
+EXPORT_SYMBOL(capifs_new_ncci);
+EXPORT_SYMBOL(capifs_free_ncci);
+
+module_init(capifs_init);
+module_exit(capifs_exit);
diff --git a/drivers/isdn/capi/capifs.h b/drivers/isdn/capi/capifs.h
new file mode 100644
index 0000000..d0bd4c3
--- /dev/null
+++ b/drivers/isdn/capi/capifs.h
@@ -0,0 +1,11 @@
+/* $Id: capifs.h,v 1.1.2.2 2004/01/16 21:09:26 keil Exp $
+ *
+ * Copyright 2000 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+void capifs_new_ncci(unsigned int num, dev_t device);
+void capifs_free_ncci(unsigned int num);
diff --git a/drivers/isdn/capi/capilib.c b/drivers/isdn/capi/capilib.c
new file mode 100644
index 0000000..fcaa124
--- /dev/null
+++ b/drivers/isdn/capi/capilib.c
@@ -0,0 +1,200 @@
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/isdn/capilli.h>
+
+#define DBG(format, arg...) do { \
+printk(KERN_DEBUG "%s: " format "\n" , __func__ , ## arg); \
+} while (0)
+
+struct capilib_msgidqueue {
+ struct capilib_msgidqueue *next;
+ u16 msgid;
+};
+
+struct capilib_ncci {
+ struct list_head list;
+ u16 applid;
+ u32 ncci;
+ u32 winsize;
+ int nmsg;
+ struct capilib_msgidqueue *msgidqueue;
+ struct capilib_msgidqueue *msgidlast;
+ struct capilib_msgidqueue *msgidfree;
+ struct capilib_msgidqueue msgidpool[CAPI_MAXDATAWINDOW];
+};
+
+// ---------------------------------------------------------------------------
+// NCCI Handling
+
+static inline void mq_init(struct capilib_ncci * np)
+{
+ u_int i;
+ np->msgidqueue = NULL;
+ np->msgidlast = NULL;
+ np->nmsg = 0;
+ memset(np->msgidpool, 0, sizeof(np->msgidpool));
+ np->msgidfree = &np->msgidpool[0];
+ for (i = 1; i < np->winsize; i++) {
+ np->msgidpool[i].next = np->msgidfree;
+ np->msgidfree = &np->msgidpool[i];
+ }
+}
+
+static inline int mq_enqueue(struct capilib_ncci * np, u16 msgid)
+{
+ struct capilib_msgidqueue *mq;
+ if ((mq = np->msgidfree) == NULL)
+ return 0;
+ np->msgidfree = mq->next;
+ mq->msgid = msgid;
+ mq->next = NULL;
+ if (np->msgidlast)
+ np->msgidlast->next = mq;
+ np->msgidlast = mq;
+ if (!np->msgidqueue)
+ np->msgidqueue = mq;
+ np->nmsg++;
+ return 1;
+}
+
+static inline int mq_dequeue(struct capilib_ncci * np, u16 msgid)
+{
+ struct capilib_msgidqueue **pp;
+ for (pp = &np->msgidqueue; *pp; pp = &(*pp)->next) {
+ if ((*pp)->msgid == msgid) {
+ struct capilib_msgidqueue *mq = *pp;
+ *pp = mq->next;
+ if (mq == np->msgidlast)
+ np->msgidlast = NULL;
+ mq->next = np->msgidfree;
+ np->msgidfree = mq;
+ np->nmsg--;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void capilib_new_ncci(struct list_head *head, u16 applid, u32 ncci, u32 winsize)
+{
+ struct capilib_ncci *np;
+
+ np = kmalloc(sizeof(*np), GFP_ATOMIC);
+ if (!np) {
+ printk(KERN_WARNING "capilib_new_ncci: no memory.\n");
+ return;
+ }
+ if (winsize > CAPI_MAXDATAWINDOW) {
+ printk(KERN_ERR "capi_new_ncci: winsize %d too big\n",
+ winsize);
+ winsize = CAPI_MAXDATAWINDOW;
+ }
+ np->applid = applid;
+ np->ncci = ncci;
+ np->winsize = winsize;
+ mq_init(np);
+ list_add_tail(&np->list, head);
+ DBG("kcapi: appl %d ncci 0x%x up", applid, ncci);
+}
+
+EXPORT_SYMBOL(capilib_new_ncci);
+
+void capilib_free_ncci(struct list_head *head, u16 applid, u32 ncci)
+{
+ struct list_head *l;
+ struct capilib_ncci *np;
+
+ list_for_each(l, head) {
+ np = list_entry(l, struct capilib_ncci, list);
+ if (np->applid != applid)
+ continue;
+ if (np->ncci != ncci)
+ continue;
+ printk(KERN_INFO "kcapi: appl %d ncci 0x%x down\n", applid, ncci);
+ list_del(&np->list);
+ kfree(np);
+ return;
+ }
+ printk(KERN_ERR "capilib_free_ncci: ncci 0x%x not found\n", ncci);
+}
+
+EXPORT_SYMBOL(capilib_free_ncci);
+
+void capilib_release_appl(struct list_head *head, u16 applid)
+{
+ struct list_head *l, *n;
+ struct capilib_ncci *np;
+
+ list_for_each_safe(l, n, head) {
+ np = list_entry(l, struct capilib_ncci, list);
+ if (np->applid != applid)
+ continue;
+ printk(KERN_INFO "kcapi: appl %d ncci 0x%x forced down\n", applid, np->ncci);
+ list_del(&np->list);
+ kfree(np);
+ }
+}
+
+EXPORT_SYMBOL(capilib_release_appl);
+
+void capilib_release(struct list_head *head)
+{
+ struct list_head *l, *n;
+ struct capilib_ncci *np;
+
+ list_for_each_safe(l, n, head) {
+ np = list_entry(l, struct capilib_ncci, list);
+ printk(KERN_INFO "kcapi: appl %d ncci 0x%x forced down\n", np->applid, np->ncci);
+ list_del(&np->list);
+ kfree(np);
+ }
+}
+
+EXPORT_SYMBOL(capilib_release);
+
+u16 capilib_data_b3_req(struct list_head *head, u16 applid, u32 ncci, u16 msgid)
+{
+ struct list_head *l;
+ struct capilib_ncci *np;
+
+ list_for_each(l, head) {
+ np = list_entry(l, struct capilib_ncci, list);
+ if (np->applid != applid)
+ continue;
+ if (np->ncci != ncci)
+ continue;
+
+ if (mq_enqueue(np, msgid) == 0)
+ return CAPI_SENDQUEUEFULL;
+
+ return CAPI_NOERROR;
+ }
+ printk(KERN_ERR "capilib_data_b3_req: ncci 0x%x not found\n", ncci);
+ return CAPI_NOERROR;
+}
+
+EXPORT_SYMBOL(capilib_data_b3_req);
+
+void capilib_data_b3_conf(struct list_head *head, u16 applid, u32 ncci, u16 msgid)
+{
+ struct list_head *l;
+ struct capilib_ncci *np;
+
+ list_for_each(l, head) {
+ np = list_entry(l, struct capilib_ncci, list);
+ if (np->applid != applid)
+ continue;
+ if (np->ncci != ncci)
+ continue;
+
+ if (mq_dequeue(np, msgid) == 0) {
+ printk(KERN_ERR "kcapi: msgid %hu ncci 0x%x not on queue\n",
+ msgid, ncci);
+ }
+ return;
+ }
+ printk(KERN_ERR "capilib_data_b3_conf: ncci 0x%x not found\n", ncci);
+}
+
+EXPORT_SYMBOL(capilib_data_b3_conf);
diff --git a/drivers/isdn/capi/capiutil.c b/drivers/isdn/capi/capiutil.c
new file mode 100644
index 0000000..29419a8
--- /dev/null
+++ b/drivers/isdn/capi/capiutil.c
@@ -0,0 +1,1014 @@
+/* $Id: capiutil.c,v 1.13.6.4 2001/09/23 22:24:33 kai Exp $
+ *
+ * CAPI 2.0 convert capi message to capi message struct
+ *
+ * From CAPI 2.0 Development Kit AVM 1995 (msg.c)
+ * Rewritten for Linux 1996 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/stddef.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/isdn/capiutil.h>
+
+/* from CAPI2.0 DDK AVM Berlin GmbH */
+
+#ifndef CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON
+char *capi_info2str(u16 reason)
+{
+ return "..";
+}
+#else
+char *capi_info2str(u16 reason)
+{
+ switch (reason) {
+
+/*-- informative values (corresponding message was processed) -----*/
+ case 0x0001:
+ return "NCPI not supported by current protocol, NCPI ignored";
+ case 0x0002:
+ return "Flags not supported by current protocol, flags ignored";
+ case 0x0003:
+ return "Alert already sent by another application";
+
+/*-- error information concerning CAPI_REGISTER -----*/
+ case 0x1001:
+ return "Too many applications";
+ case 0x1002:
+ return "Logical block size too small, must be at least 128 Bytes";
+ case 0x1003:
+ return "Buffer exceeds 64 kByte";
+ case 0x1004:
+ return "Message buffer size too small, must be at least 1024 Bytes";
+ case 0x1005:
+ return "Max. number of logical connections not supported";
+ case 0x1006:
+ return "Reserved";
+ case 0x1007:
+ return "The message could not be accepted because of an internal busy condition";
+ case 0x1008:
+ return "OS resource error (no memory ?)";
+ case 0x1009:
+ return "CAPI not installed";
+ case 0x100A:
+ return "Controller does not support external equipment";
+ case 0x100B:
+ return "Controller does only support external equipment";
+
+/*-- error information concerning message exchange functions -----*/
+ case 0x1101:
+ return "Illegal application number";
+ case 0x1102:
+ return "Illegal command or subcommand or message length less than 12 bytes";
+ case 0x1103:
+ return "The message could not be accepted because of a queue full condition !! The error code does not imply that CAPI cannot receive messages directed to another controller, PLCI or NCCI";
+ case 0x1104:
+ return "Queue is empty";
+ case 0x1105:
+ return "Queue overflow, a message was lost !! This indicates a configuration error. The only recovery from this error is to perform a CAPI_RELEASE";
+ case 0x1106:
+ return "Unknown notification parameter";
+ case 0x1107:
+ return "The Message could not be accepted because of an internal busy condition";
+ case 0x1108:
+ return "OS Resource error (no memory ?)";
+ case 0x1109:
+ return "CAPI not installed";
+ case 0x110A:
+ return "Controller does not support external equipment";
+ case 0x110B:
+ return "Controller does only support external equipment";
+
+/*-- error information concerning resource / coding problems -----*/
+ case 0x2001:
+ return "Message not supported in current state";
+ case 0x2002:
+ return "Illegal Controller / PLCI / NCCI";
+ case 0x2003:
+ return "Out of PLCI";
+ case 0x2004:
+ return "Out of NCCI";
+ case 0x2005:
+ return "Out of LISTEN";
+ case 0x2006:
+ return "Out of FAX resources (protocol T.30)";
+ case 0x2007:
+ return "Illegal message parameter coding";
+
+/*-- error information concerning requested services -----*/
+ case 0x3001:
+ return "B1 protocol not supported";
+ case 0x3002:
+ return "B2 protocol not supported";
+ case 0x3003:
+ return "B3 protocol not supported";
+ case 0x3004:
+ return "B1 protocol parameter not supported";
+ case 0x3005:
+ return "B2 protocol parameter not supported";
+ case 0x3006:
+ return "B3 protocol parameter not supported";
+ case 0x3007:
+ return "B protocol combination not supported";
+ case 0x3008:
+ return "NCPI not supported";
+ case 0x3009:
+ return "CIP Value unknown";
+ case 0x300A:
+ return "Flags not supported (reserved bits)";
+ case 0x300B:
+ return "Facility not supported";
+ case 0x300C:
+ return "Data length not supported by current protocol";
+ case 0x300D:
+ return "Reset procedure not supported by current protocol";
+
+/*-- informations about the clearing of a physical connection -----*/
+ case 0x3301:
+ return "Protocol error layer 1 (broken line or B-channel removed by signalling protocol)";
+ case 0x3302:
+ return "Protocol error layer 2";
+ case 0x3303:
+ return "Protocol error layer 3";
+ case 0x3304:
+ return "Another application got that call";
+/*-- T.30 specific reasons -----*/
+ case 0x3311:
+ return "Connecting not successful (remote station is no FAX G3 machine)";
+ case 0x3312:
+ return "Connecting not successful (training error)";
+ case 0x3313:
+ return "Disconnected before transfer (remote station does not support transfer mode, e.g. resolution)";
+ case 0x3314:
+ return "Disconnected during transfer (remote abort)";
+ case 0x3315:
+ return "Disconnected during transfer (remote procedure error, e.g. unsuccessful repetition of T.30 commands)";
+ case 0x3316:
+ return "Disconnected during transfer (local tx data underrun)";
+ case 0x3317:
+ return "Disconnected during transfer (local rx data overflow)";
+ case 0x3318:
+ return "Disconnected during transfer (local abort)";
+ case 0x3319:
+ return "Illegal parameter coding (e.g. SFF coding error)";
+
+/*-- disconnect causes from the network according to ETS 300 102-1/Q.931 -----*/
+ case 0x3481: return "Unallocated (unassigned) number";
+ case 0x3482: return "No route to specified transit network";
+ case 0x3483: return "No route to destination";
+ case 0x3486: return "Channel unacceptable";
+ case 0x3487:
+ return "Call awarded and being delivered in an established channel";
+ case 0x3490: return "Normal call clearing";
+ case 0x3491: return "User busy";
+ case 0x3492: return "No user responding";
+ case 0x3493: return "No answer from user (user alerted)";
+ case 0x3495: return "Call rejected";
+ case 0x3496: return "Number changed";
+ case 0x349A: return "Non-selected user clearing";
+ case 0x349B: return "Destination out of order";
+ case 0x349C: return "Invalid number format";
+ case 0x349D: return "Facility rejected";
+ case 0x349E: return "Response to STATUS ENQUIRY";
+ case 0x349F: return "Normal, unspecified";
+ case 0x34A2: return "No circuit / channel available";
+ case 0x34A6: return "Network out of order";
+ case 0x34A9: return "Temporary failure";
+ case 0x34AA: return "Switching equipment congestion";
+ case 0x34AB: return "Access information discarded";
+ case 0x34AC: return "Requested circuit / channel not available";
+ case 0x34AF: return "Resources unavailable, unspecified";
+ case 0x34B1: return "Quality of service unavailable";
+ case 0x34B2: return "Requested facility not subscribed";
+ case 0x34B9: return "Bearer capability not authorized";
+ case 0x34BA: return "Bearer capability not presently available";
+ case 0x34BF: return "Service or option not available, unspecified";
+ case 0x34C1: return "Bearer capability not implemented";
+ case 0x34C2: return "Channel type not implemented";
+ case 0x34C5: return "Requested facility not implemented";
+ case 0x34C6: return "Only restricted digital information bearer capability is available";
+ case 0x34CF: return "Service or option not implemented, unspecified";
+ case 0x34D1: return "Invalid call reference value";
+ case 0x34D2: return "Identified channel does not exist";
+ case 0x34D3: return "A suspended call exists, but this call identity does not";
+ case 0x34D4: return "Call identity in use";
+ case 0x34D5: return "No call suspended";
+ case 0x34D6: return "Call having the requested call identity has been cleared";
+ case 0x34D8: return "Incompatible destination";
+ case 0x34DB: return "Invalid transit network selection";
+ case 0x34DF: return "Invalid message, unspecified";
+ case 0x34E0: return "Mandatory information element is missing";
+ case 0x34E1: return "Message type non-existent or not implemented";
+ case 0x34E2: return "Message not compatible with call state or message type non-existent or not implemented";
+ case 0x34E3: return "Information element non-existent or not implemented";
+ case 0x34E4: return "Invalid information element contents";
+ case 0x34E5: return "Message not compatible with call state";
+ case 0x34E6: return "Recovery on timer expiry";
+ case 0x34EF: return "Protocol error, unspecified";
+ case 0x34FF: return "Interworking, unspecified";
+
+ default: return "No additional information";
+ }
+}
+#endif
+
+typedef struct {
+ int typ;
+ size_t off;
+} _cdef;
+
+#define _CBYTE 1
+#define _CWORD 2
+#define _CDWORD 3
+#define _CSTRUCT 4
+#define _CMSTRUCT 5
+#define _CEND 6
+
+static _cdef cdef[] =
+{
+ /*00 */
+ {_CEND},
+ /*01 */
+ {_CEND},
+ /*02 */
+ {_CEND},
+ /*03 */
+ {_CDWORD, offsetof(_cmsg, adr.adrController)},
+ /*04 */
+ {_CMSTRUCT, offsetof(_cmsg, AdditionalInfo)},
+ /*05 */
+ {_CSTRUCT, offsetof(_cmsg, B1configuration)},
+ /*06 */
+ {_CWORD, offsetof(_cmsg, B1protocol)},
+ /*07 */
+ {_CSTRUCT, offsetof(_cmsg, B2configuration)},
+ /*08 */
+ {_CWORD, offsetof(_cmsg, B2protocol)},
+ /*09 */
+ {_CSTRUCT, offsetof(_cmsg, B3configuration)},
+ /*0a */
+ {_CWORD, offsetof(_cmsg, B3protocol)},
+ /*0b */
+ {_CSTRUCT, offsetof(_cmsg, BC)},
+ /*0c */
+ {_CSTRUCT, offsetof(_cmsg, BChannelinformation)},
+ /*0d */
+ {_CMSTRUCT, offsetof(_cmsg, BProtocol)},
+ /*0e */
+ {_CSTRUCT, offsetof(_cmsg, CalledPartyNumber)},
+ /*0f */
+ {_CSTRUCT, offsetof(_cmsg, CalledPartySubaddress)},
+ /*10 */
+ {_CSTRUCT, offsetof(_cmsg, CallingPartyNumber)},
+ /*11 */
+ {_CSTRUCT, offsetof(_cmsg, CallingPartySubaddress)},
+ /*12 */
+ {_CDWORD, offsetof(_cmsg, CIPmask)},
+ /*13 */
+ {_CDWORD, offsetof(_cmsg, CIPmask2)},
+ /*14 */
+ {_CWORD, offsetof(_cmsg, CIPValue)},
+ /*15 */
+ {_CDWORD, offsetof(_cmsg, Class)},
+ /*16 */
+ {_CSTRUCT, offsetof(_cmsg, ConnectedNumber)},
+ /*17 */
+ {_CSTRUCT, offsetof(_cmsg, ConnectedSubaddress)},
+ /*18 */
+ {_CDWORD, offsetof(_cmsg, Data)},
+ /*19 */
+ {_CWORD, offsetof(_cmsg, DataHandle)},
+ /*1a */
+ {_CWORD, offsetof(_cmsg, DataLength)},
+ /*1b */
+ {_CSTRUCT, offsetof(_cmsg, FacilityConfirmationParameter)},
+ /*1c */
+ {_CSTRUCT, offsetof(_cmsg, Facilitydataarray)},
+ /*1d */
+ {_CSTRUCT, offsetof(_cmsg, FacilityIndicationParameter)},
+ /*1e */
+ {_CSTRUCT, offsetof(_cmsg, FacilityRequestParameter)},
+ /*1f */
+ {_CWORD, offsetof(_cmsg, FacilitySelector)},
+ /*20 */
+ {_CWORD, offsetof(_cmsg, Flags)},
+ /*21 */
+ {_CDWORD, offsetof(_cmsg, Function)},
+ /*22 */
+ {_CSTRUCT, offsetof(_cmsg, HLC)},
+ /*23 */
+ {_CWORD, offsetof(_cmsg, Info)},
+ /*24 */
+ {_CSTRUCT, offsetof(_cmsg, InfoElement)},
+ /*25 */
+ {_CDWORD, offsetof(_cmsg, InfoMask)},
+ /*26 */
+ {_CWORD, offsetof(_cmsg, InfoNumber)},
+ /*27 */
+ {_CSTRUCT, offsetof(_cmsg, Keypadfacility)},
+ /*28 */
+ {_CSTRUCT, offsetof(_cmsg, LLC)},
+ /*29 */
+ {_CSTRUCT, offsetof(_cmsg, ManuData)},
+ /*2a */
+ {_CDWORD, offsetof(_cmsg, ManuID)},
+ /*2b */
+ {_CSTRUCT, offsetof(_cmsg, NCPI)},
+ /*2c */
+ {_CWORD, offsetof(_cmsg, Reason)},
+ /*2d */
+ {_CWORD, offsetof(_cmsg, Reason_B3)},
+ /*2e */
+ {_CWORD, offsetof(_cmsg, Reject)},
+ /*2f */
+ {_CSTRUCT, offsetof(_cmsg, Useruserdata)}
+};
+
+static unsigned char *cpars[] =
+{
+ /* ALERT_REQ */ [0x01] = "\x03\x04\x0c\x27\x2f\x1c\x01\x01",
+ /* CONNECT_REQ */ [0x02] = "\x03\x14\x0e\x10\x0f\x11\x0d\x06\x08\x0a\x05\x07\x09\x01\x0b\x28\x22\x04\x0c\x27\x2f\x1c\x01\x01",
+ /* DISCONNECT_REQ */ [0x04] = "\x03\x04\x0c\x27\x2f\x1c\x01\x01",
+ /* LISTEN_REQ */ [0x05] = "\x03\x25\x12\x13\x10\x11\x01",
+ /* INFO_REQ */ [0x08] = "\x03\x0e\x04\x0c\x27\x2f\x1c\x01\x01",
+ /* FACILITY_REQ */ [0x09] = "\x03\x1f\x1e\x01",
+ /* SELECT_B_PROTOCOL_REQ */ [0x0a] = "\x03\x0d\x06\x08\x0a\x05\x07\x09\x01\x01",
+ /* CONNECT_B3_REQ */ [0x0b] = "\x03\x2b\x01",
+ /* DISCONNECT_B3_REQ */ [0x0d] = "\x03\x2b\x01",
+ /* DATA_B3_REQ */ [0x0f] = "\x03\x18\x1a\x19\x20\x01",
+ /* RESET_B3_REQ */ [0x10] = "\x03\x2b\x01",
+ /* ALERT_CONF */ [0x13] = "\x03\x23\x01",
+ /* CONNECT_CONF */ [0x14] = "\x03\x23\x01",
+ /* DISCONNECT_CONF */ [0x16] = "\x03\x23\x01",
+ /* LISTEN_CONF */ [0x17] = "\x03\x23\x01",
+ /* MANUFACTURER_REQ */ [0x18] = "\x03\x2a\x15\x21\x29\x01",
+ /* INFO_CONF */ [0x1a] = "\x03\x23\x01",
+ /* FACILITY_CONF */ [0x1b] = "\x03\x23\x1f\x1b\x01",
+ /* SELECT_B_PROTOCOL_CONF */ [0x1c] = "\x03\x23\x01",
+ /* CONNECT_B3_CONF */ [0x1d] = "\x03\x23\x01",
+ /* DISCONNECT_B3_CONF */ [0x1f] = "\x03\x23\x01",
+ /* DATA_B3_CONF */ [0x21] = "\x03\x19\x23\x01",
+ /* RESET_B3_CONF */ [0x22] = "\x03\x23\x01",
+ /* CONNECT_IND */ [0x26] = "\x03\x14\x0e\x10\x0f\x11\x0b\x28\x22\x04\x0c\x27\x2f\x1c\x01\x01",
+ /* CONNECT_ACTIVE_IND */ [0x27] = "\x03\x16\x17\x28\x01",
+ /* DISCONNECT_IND */ [0x28] = "\x03\x2c\x01",
+ /* MANUFACTURER_CONF */ [0x2a] = "\x03\x2a\x15\x21\x29\x01",
+ /* INFO_IND */ [0x2c] = "\x03\x26\x24\x01",
+ /* FACILITY_IND */ [0x2d] = "\x03\x1f\x1d\x01",
+ /* CONNECT_B3_IND */ [0x2f] = "\x03\x2b\x01",
+ /* CONNECT_B3_ACTIVE_IND */ [0x30] = "\x03\x2b\x01",
+ /* DISCONNECT_B3_IND */ [0x31] = "\x03\x2d\x2b\x01",
+ /* DATA_B3_IND */ [0x33] = "\x03\x18\x1a\x19\x20\x01",
+ /* RESET_B3_IND */ [0x34] = "\x03\x2b\x01",
+ /* CONNECT_B3_T90_ACTIVE_IND */ [0x35] = "\x03\x2b\x01",
+ /* CONNECT_RESP */ [0x38] = "\x03\x2e\x0d\x06\x08\x0a\x05\x07\x09\x01\x16\x17\x28\x04\x0c\x27\x2f\x1c\x01\x01",
+ /* CONNECT_ACTIVE_RESP */ [0x39] = "\x03\x01",
+ /* DISCONNECT_RESP */ [0x3a] = "\x03\x01",
+ /* MANUFACTURER_IND */ [0x3c] = "\x03\x2a\x15\x21\x29\x01",
+ /* INFO_RESP */ [0x3e] = "\x03\x01",
+ /* FACILITY_RESP */ [0x3f] = "\x03\x1f\x01",
+ /* CONNECT_B3_RESP */ [0x41] = "\x03\x2e\x2b\x01",
+ /* CONNECT_B3_ACTIVE_RESP */ [0x42] = "\x03\x01",
+ /* DISCONNECT_B3_RESP */ [0x43] = "\x03\x01",
+ /* DATA_B3_RESP */ [0x45] = "\x03\x19\x01",
+ /* RESET_B3_RESP */ [0x46] = "\x03\x01",
+ /* CONNECT_B3_T90_ACTIVE_RESP */ [0x47] = "\x03\x01",
+ /* MANUFACTURER_RESP */ [0x4e] = "\x03\x2a\x15\x21\x29\x01",
+};
+
+/*-------------------------------------------------------*/
+
+#define byteTLcpy(x,y) *(u8 *)(x)=*(u8 *)(y);
+#define wordTLcpy(x,y) *(u16 *)(x)=*(u16 *)(y);
+#define dwordTLcpy(x,y) memcpy(x,y,4);
+#define structTLcpy(x,y,l) memcpy (x,y,l)
+#define structTLcpyovl(x,y,l) memmove (x,y,l)
+
+#define byteTRcpy(x,y) *(u8 *)(y)=*(u8 *)(x);
+#define wordTRcpy(x,y) *(u16 *)(y)=*(u16 *)(x);
+#define dwordTRcpy(x,y) memcpy(y,x,4);
+#define structTRcpy(x,y,l) memcpy (y,x,l)
+#define structTRcpyovl(x,y,l) memmove (y,x,l)
+
+/*-------------------------------------------------------*/
+static unsigned command_2_index(unsigned c, unsigned sc)
+{
+ if (c & 0x80)
+ c = 0x9 + (c & 0x0f);
+ else if (c <= 0x0f);
+ else if (c == 0x41)
+ c = 0x9 + 0x1;
+ else if (c == 0xff)
+ c = 0x00;
+ return (sc & 3) * (0x9 + 0x9) + c;
+}
+
+/*-------------------------------------------------------*/
+#define TYP (cdef[cmsg->par[cmsg->p]].typ)
+#define OFF (((u8 *)cmsg)+cdef[cmsg->par[cmsg->p]].off)
+
+static void jumpcstruct(_cmsg * cmsg)
+{
+ unsigned layer;
+ for (cmsg->p++, layer = 1; layer;) {
+ /* $$$$$ assert (cmsg->p); */
+ cmsg->p++;
+ switch (TYP) {
+ case _CMSTRUCT:
+ layer++;
+ break;
+ case _CEND:
+ layer--;
+ break;
+ }
+ }
+}
+/*-------------------------------------------------------*/
+static void pars_2_message(_cmsg * cmsg)
+{
+
+ for (; TYP != _CEND; cmsg->p++) {
+ switch (TYP) {
+ case _CBYTE:
+ byteTLcpy(cmsg->m + cmsg->l, OFF);
+ cmsg->l++;
+ break;
+ case _CWORD:
+ wordTLcpy(cmsg->m + cmsg->l, OFF);
+ cmsg->l += 2;
+ break;
+ case _CDWORD:
+ dwordTLcpy(cmsg->m + cmsg->l, OFF);
+ cmsg->l += 4;
+ break;
+ case _CSTRUCT:
+ if (*(u8 **) OFF == NULL) {
+ *(cmsg->m + cmsg->l) = '\0';
+ cmsg->l++;
+ } else if (**(_cstruct *) OFF != 0xff) {
+ structTLcpy(cmsg->m + cmsg->l, *(_cstruct *) OFF, 1 + **(_cstruct *) OFF);
+ cmsg->l += 1 + **(_cstruct *) OFF;
+ } else {
+ _cstruct s = *(_cstruct *) OFF;
+ structTLcpy(cmsg->m + cmsg->l, s, 3 + *(u16 *) (s + 1));
+ cmsg->l += 3 + *(u16 *) (s + 1);
+ }
+ break;
+ case _CMSTRUCT:
+/*----- Metastruktur 0 -----*/
+ if (*(_cmstruct *) OFF == CAPI_DEFAULT) {
+ *(cmsg->m + cmsg->l) = '\0';
+ cmsg->l++;
+ jumpcstruct(cmsg);
+ }
+/*----- Metastruktur wird composed -----*/
+ else {
+ unsigned _l = cmsg->l;
+ unsigned _ls;
+ cmsg->l++;
+ cmsg->p++;
+ pars_2_message(cmsg);
+ _ls = cmsg->l - _l - 1;
+ if (_ls < 255)
+ (cmsg->m + _l)[0] = (u8) _ls;
+ else {
+ structTLcpyovl(cmsg->m + _l + 3, cmsg->m + _l + 1, _ls);
+ (cmsg->m + _l)[0] = 0xff;
+ wordTLcpy(cmsg->m + _l + 1, &_ls);
+ }
+ }
+ break;
+ }
+ }
+}
+
+/*-------------------------------------------------------*/
+unsigned capi_cmsg2message(_cmsg * cmsg, u8 * msg)
+{
+ cmsg->m = msg;
+ cmsg->l = 8;
+ cmsg->p = 0;
+ cmsg->par = cpars[command_2_index(cmsg->Command, cmsg->Subcommand)];
+
+ pars_2_message(cmsg);
+
+ wordTLcpy(msg + 0, &cmsg->l);
+ byteTLcpy(cmsg->m + 4, &cmsg->Command);
+ byteTLcpy(cmsg->m + 5, &cmsg->Subcommand);
+ wordTLcpy(cmsg->m + 2, &cmsg->ApplId);
+ wordTLcpy(cmsg->m + 6, &cmsg->Messagenumber);
+
+ return 0;
+}
+
+/*-------------------------------------------------------*/
+static void message_2_pars(_cmsg * cmsg)
+{
+ for (; TYP != _CEND; cmsg->p++) {
+
+ switch (TYP) {
+ case _CBYTE:
+ byteTRcpy(cmsg->m + cmsg->l, OFF);
+ cmsg->l++;
+ break;
+ case _CWORD:
+ wordTRcpy(cmsg->m + cmsg->l, OFF);
+ cmsg->l += 2;
+ break;
+ case _CDWORD:
+ dwordTRcpy(cmsg->m + cmsg->l, OFF);
+ cmsg->l += 4;
+ break;
+ case _CSTRUCT:
+ *(u8 **) OFF = cmsg->m + cmsg->l;
+
+ if (cmsg->m[cmsg->l] != 0xff)
+ cmsg->l += 1 + cmsg->m[cmsg->l];
+ else
+ cmsg->l += 3 + *(u16 *) (cmsg->m + cmsg->l + 1);
+ break;
+ case _CMSTRUCT:
+/*----- Metastruktur 0 -----*/
+ if (cmsg->m[cmsg->l] == '\0') {
+ *(_cmstruct *) OFF = CAPI_DEFAULT;
+ cmsg->l++;
+ jumpcstruct(cmsg);
+ } else {
+ unsigned _l = cmsg->l;
+ *(_cmstruct *) OFF = CAPI_COMPOSE;
+ cmsg->l = (cmsg->m + _l)[0] == 255 ? cmsg->l + 3 : cmsg->l + 1;
+ cmsg->p++;
+ message_2_pars(cmsg);
+ }
+ break;
+ }
+ }
+}
+
+/*-------------------------------------------------------*/
+unsigned capi_message2cmsg(_cmsg * cmsg, u8 * msg)
+{
+ memset(cmsg, 0, sizeof(_cmsg));
+ cmsg->m = msg;
+ cmsg->l = 8;
+ cmsg->p = 0;
+ byteTRcpy(cmsg->m + 4, &cmsg->Command);
+ byteTRcpy(cmsg->m + 5, &cmsg->Subcommand);
+ cmsg->par = cpars[command_2_index(cmsg->Command, cmsg->Subcommand)];
+
+ message_2_pars(cmsg);
+
+ wordTRcpy(msg + 0, &cmsg->l);
+ wordTRcpy(cmsg->m + 2, &cmsg->ApplId);
+ wordTRcpy(cmsg->m + 6, &cmsg->Messagenumber);
+
+ return 0;
+}
+
+/*-------------------------------------------------------*/
+unsigned capi_cmsg_header(_cmsg * cmsg, u16 _ApplId,
+ u8 _Command, u8 _Subcommand,
+ u16 _Messagenumber, u32 _Controller)
+{
+ memset(cmsg, 0, sizeof(_cmsg));
+ cmsg->ApplId = _ApplId;
+ cmsg->Command = _Command;
+ cmsg->Subcommand = _Subcommand;
+ cmsg->Messagenumber = _Messagenumber;
+ cmsg->adr.adrController = _Controller;
+ return 0;
+}
+
+/*-------------------------------------------------------*/
+
+static char *mnames[] =
+{
+ [0x01] = "ALERT_REQ",
+ [0x02] = "CONNECT_REQ",
+ [0x04] = "DISCONNECT_REQ",
+ [0x05] = "LISTEN_REQ",
+ [0x08] = "INFO_REQ",
+ [0x09] = "FACILITY_REQ",
+ [0x0a] = "SELECT_B_PROTOCOL_REQ",
+ [0x0b] = "CONNECT_B3_REQ",
+ [0x0d] = "DISCONNECT_B3_REQ",
+ [0x0f] = "DATA_B3_REQ",
+ [0x10] = "RESET_B3_REQ",
+ [0x13] = "ALERT_CONF",
+ [0x14] = "CONNECT_CONF",
+ [0x16] = "DISCONNECT_CONF",
+ [0x17] = "LISTEN_CONF",
+ [0x18] = "MANUFACTURER_REQ",
+ [0x1a] = "INFO_CONF",
+ [0x1b] = "FACILITY_CONF",
+ [0x1c] = "SELECT_B_PROTOCOL_CONF",
+ [0x1d] = "CONNECT_B3_CONF",
+ [0x1f] = "DISCONNECT_B3_CONF",
+ [0x21] = "DATA_B3_CONF",
+ [0x22] = "RESET_B3_CONF",
+ [0x26] = "CONNECT_IND",
+ [0x27] = "CONNECT_ACTIVE_IND",
+ [0x28] = "DISCONNECT_IND",
+ [0x2a] = "MANUFACTURER_CONF",
+ [0x2c] = "INFO_IND",
+ [0x2d] = "FACILITY_IND",
+ [0x2f] = "CONNECT_B3_IND",
+ [0x30] = "CONNECT_B3_ACTIVE_IND",
+ [0x31] = "DISCONNECT_B3_IND",
+ [0x33] = "DATA_B3_IND",
+ [0x34] = "RESET_B3_IND",
+ [0x35] = "CONNECT_B3_T90_ACTIVE_IND",
+ [0x38] = "CONNECT_RESP",
+ [0x39] = "CONNECT_ACTIVE_RESP",
+ [0x3a] = "DISCONNECT_RESP",
+ [0x3c] = "MANUFACTURER_IND",
+ [0x3e] = "INFO_RESP",
+ [0x3f] = "FACILITY_RESP",
+ [0x41] = "CONNECT_B3_RESP",
+ [0x42] = "CONNECT_B3_ACTIVE_RESP",
+ [0x43] = "DISCONNECT_B3_RESP",
+ [0x45] = "DATA_B3_RESP",
+ [0x46] = "RESET_B3_RESP",
+ [0x47] = "CONNECT_B3_T90_ACTIVE_RESP",
+ [0x4e] = "MANUFACTURER_RESP"
+};
+
+char *capi_cmd2str(u8 cmd, u8 subcmd)
+{
+ return mnames[command_2_index(cmd, subcmd)];
+}
+
+
+/*-------------------------------------------------------*/
+
+#ifdef CONFIG_CAPI_TRACE
+
+/*-------------------------------------------------------*/
+
+static char *pnames[] =
+{
+ /*00 */ NULL,
+ /*01 */ NULL,
+ /*02 */ NULL,
+ /*03 */ "Controller/PLCI/NCCI",
+ /*04 */ "AdditionalInfo",
+ /*05 */ "B1configuration",
+ /*06 */ "B1protocol",
+ /*07 */ "B2configuration",
+ /*08 */ "B2protocol",
+ /*09 */ "B3configuration",
+ /*0a */ "B3protocol",
+ /*0b */ "BC",
+ /*0c */ "BChannelinformation",
+ /*0d */ "BProtocol",
+ /*0e */ "CalledPartyNumber",
+ /*0f */ "CalledPartySubaddress",
+ /*10 */ "CallingPartyNumber",
+ /*11 */ "CallingPartySubaddress",
+ /*12 */ "CIPmask",
+ /*13 */ "CIPmask2",
+ /*14 */ "CIPValue",
+ /*15 */ "Class",
+ /*16 */ "ConnectedNumber",
+ /*17 */ "ConnectedSubaddress",
+ /*18 */ "Data32",
+ /*19 */ "DataHandle",
+ /*1a */ "DataLength",
+ /*1b */ "FacilityConfirmationParameter",
+ /*1c */ "Facilitydataarray",
+ /*1d */ "FacilityIndicationParameter",
+ /*1e */ "FacilityRequestParameter",
+ /*1f */ "FacilitySelector",
+ /*20 */ "Flags",
+ /*21 */ "Function",
+ /*22 */ "HLC",
+ /*23 */ "Info",
+ /*24 */ "InfoElement",
+ /*25 */ "InfoMask",
+ /*26 */ "InfoNumber",
+ /*27 */ "Keypadfacility",
+ /*28 */ "LLC",
+ /*29 */ "ManuData",
+ /*2a */ "ManuID",
+ /*2b */ "NCPI",
+ /*2c */ "Reason",
+ /*2d */ "Reason_B3",
+ /*2e */ "Reject",
+ /*2f */ "Useruserdata"
+};
+
+
+
+#include <stdarg.h>
+
+/*-------------------------------------------------------*/
+static _cdebbuf *bufprint(_cdebbuf *cdb, char *fmt,...)
+{
+ va_list f;
+ size_t n,r;
+
+ if (!cdb)
+ return NULL;
+ va_start(f, fmt);
+ r = cdb->size - cdb->pos;
+ n = vsnprintf(cdb->p, r, fmt, f);
+ va_end(f);
+ if (n >= r) {
+ /* truncated, need bigger buffer */
+ size_t ns = 2 * cdb->size;
+ u_char *nb;
+
+ while ((ns - cdb->pos) <= n)
+ ns *= 2;
+ nb = kmalloc(ns, GFP_ATOMIC);
+ if (!nb) {
+ cdebbuf_free(cdb);
+ return NULL;
+ }
+ memcpy(nb, cdb->buf, cdb->pos);
+ kfree(cdb->buf);
+ nb[cdb->pos] = 0;
+ cdb->buf = nb;
+ cdb->p = cdb->buf + cdb->pos;
+ cdb->size = ns;
+ va_start(f, fmt);
+ r = cdb->size - cdb->pos;
+ n = vsnprintf(cdb->p, r, fmt, f);
+ va_end(f);
+ }
+ cdb->p += n;
+ cdb->pos += n;
+ return cdb;
+}
+
+static _cdebbuf *printstructlen(_cdebbuf *cdb, u8 * m, unsigned len)
+{
+ unsigned hex = 0;
+
+ if (!cdb)
+ return NULL;
+ for (; len; len--, m++)
+ if (isalnum(*m) || *m == ' ') {
+ if (hex)
+ cdb = bufprint(cdb, ">");
+ cdb = bufprint(cdb, "%c", *m);
+ hex = 0;
+ } else {
+ if (!hex)
+ cdb = bufprint(cdb, "<%02x", *m);
+ else
+ cdb = bufprint(cdb, " %02x", *m);
+ hex = 1;
+ }
+ if (hex)
+ cdb = bufprint(cdb, ">");
+ return cdb;
+}
+
+static _cdebbuf *printstruct(_cdebbuf *cdb, u8 * m)
+{
+ unsigned len;
+
+ if (m[0] != 0xff) {
+ len = m[0];
+ m += 1;
+ } else {
+ len = ((u16 *) (m + 1))[0];
+ m += 3;
+ }
+ cdb = printstructlen(cdb, m, len);
+ return cdb;
+}
+
+/*-------------------------------------------------------*/
+#define NAME (pnames[cmsg->par[cmsg->p]])
+
+static _cdebbuf *protocol_message_2_pars(_cdebbuf *cdb, _cmsg *cmsg, int level)
+{
+ for (; TYP != _CEND; cmsg->p++) {
+ int slen = 29 + 3 - level;
+ int i;
+
+ if (!cdb)
+ return NULL;
+ cdb = bufprint(cdb, " ");
+ for (i = 0; i < level - 1; i++)
+ cdb = bufprint(cdb, " ");
+
+ switch (TYP) {
+ case _CBYTE:
+ cdb = bufprint(cdb, "%-*s = 0x%x\n", slen, NAME, *(u8 *) (cmsg->m + cmsg->l));
+ cmsg->l++;
+ break;
+ case _CWORD:
+ cdb = bufprint(cdb, "%-*s = 0x%x\n", slen, NAME, *(u16 *) (cmsg->m + cmsg->l));
+ cmsg->l += 2;
+ break;
+ case _CDWORD:
+ cdb = bufprint(cdb, "%-*s = 0x%lx\n", slen, NAME, *(u32 *) (cmsg->m + cmsg->l));
+ cmsg->l += 4;
+ break;
+ case _CSTRUCT:
+ cdb = bufprint(cdb, "%-*s = ", slen, NAME);
+ if (cmsg->m[cmsg->l] == '\0')
+ cdb = bufprint(cdb, "default");
+ else
+ cdb = printstruct(cdb, cmsg->m + cmsg->l);
+ cdb = bufprint(cdb, "\n");
+ if (cmsg->m[cmsg->l] != 0xff)
+ cmsg->l += 1 + cmsg->m[cmsg->l];
+ else
+ cmsg->l += 3 + *(u16 *) (cmsg->m + cmsg->l + 1);
+
+ break;
+
+ case _CMSTRUCT:
+/*----- Metastruktur 0 -----*/
+ if (cmsg->m[cmsg->l] == '\0') {
+ cdb = bufprint(cdb, "%-*s = default\n", slen, NAME);
+ cmsg->l++;
+ jumpcstruct(cmsg);
+ } else {
+ char *name = NAME;
+ unsigned _l = cmsg->l;
+ cdb = bufprint(cdb, "%-*s\n", slen, name);
+ cmsg->l = (cmsg->m + _l)[0] == 255 ? cmsg->l + 3 : cmsg->l + 1;
+ cmsg->p++;
+ cdb = protocol_message_2_pars(cdb, cmsg, level + 1);
+ }
+ break;
+ }
+ }
+ return cdb;
+}
+/*-------------------------------------------------------*/
+
+static _cdebbuf *g_debbuf;
+static u_long g_debbuf_lock;
+static _cmsg *g_cmsg;
+
+static _cdebbuf *cdebbuf_alloc(void)
+{
+ _cdebbuf *cdb;
+
+ if (likely(!test_and_set_bit(1, &g_debbuf_lock))) {
+ cdb = g_debbuf;
+ goto init;
+ } else
+ cdb = kmalloc(sizeof(_cdebbuf), GFP_ATOMIC);
+ if (!cdb)
+ return NULL;
+ cdb->buf = kmalloc(CDEBUG_SIZE, GFP_ATOMIC);
+ if (!cdb->buf) {
+ kfree(cdb);
+ return NULL;
+ }
+ cdb->size = CDEBUG_SIZE;
+init:
+ cdb->buf[0] = 0;
+ cdb->p = cdb->buf;
+ cdb->pos = 0;
+ return cdb;
+}
+
+void cdebbuf_free(_cdebbuf *cdb)
+{
+ if (likely(cdb == g_debbuf)) {
+ test_and_clear_bit(1, &g_debbuf_lock);
+ return;
+ }
+ if (likely(cdb))
+ kfree(cdb->buf);
+ kfree(cdb);
+}
+
+
+_cdebbuf *capi_message2str(u8 * msg)
+{
+ _cdebbuf *cdb;
+ _cmsg *cmsg;
+
+ cdb = cdebbuf_alloc();
+ if (unlikely(!cdb))
+ return NULL;
+ if (likely(cdb == g_debbuf))
+ cmsg = g_cmsg;
+ else
+ cmsg = kmalloc(sizeof(_cmsg), GFP_ATOMIC);
+ if (unlikely(!cmsg)) {
+ cdebbuf_free(cdb);
+ return NULL;
+ }
+ cmsg->m = msg;
+ cmsg->l = 8;
+ cmsg->p = 0;
+ byteTRcpy(cmsg->m + 4, &cmsg->Command);
+ byteTRcpy(cmsg->m + 5, &cmsg->Subcommand);
+ cmsg->par = cpars[command_2_index(cmsg->Command, cmsg->Subcommand)];
+
+ cdb = bufprint(cdb, "%-26s ID=%03d #0x%04x LEN=%04d\n",
+ mnames[command_2_index(cmsg->Command, cmsg->Subcommand)],
+ ((unsigned short *) msg)[1],
+ ((unsigned short *) msg)[3],
+ ((unsigned short *) msg)[0]);
+
+ cdb = protocol_message_2_pars(cdb, cmsg, 1);
+ if (unlikely(cmsg != g_cmsg))
+ kfree(cmsg);
+ return cdb;
+}
+
+_cdebbuf *capi_cmsg2str(_cmsg * cmsg)
+{
+ _cdebbuf *cdb;
+
+ cdb = cdebbuf_alloc();
+ if (!cdb)
+ return NULL;
+ cmsg->l = 8;
+ cmsg->p = 0;
+ cdb = bufprint(cdb, "%s ID=%03d #0x%04x LEN=%04d\n",
+ mnames[command_2_index(cmsg->Command, cmsg->Subcommand)],
+ ((u16 *) cmsg->m)[1],
+ ((u16 *) cmsg->m)[3],
+ ((u16 *) cmsg->m)[0]);
+ cdb = protocol_message_2_pars(cdb, cmsg, 1);
+ return cdb;
+}
+
+int __init cdebug_init(void)
+{
+ g_cmsg= kmalloc(sizeof(_cmsg), GFP_KERNEL);
+ if (!g_cmsg)
+ return -ENOMEM;
+ g_debbuf = kmalloc(sizeof(_cdebbuf), GFP_KERNEL);
+ if (!g_debbuf) {
+ kfree(g_cmsg);
+ return -ENOMEM;
+ }
+ g_debbuf->buf = kmalloc(CDEBUG_GSIZE, GFP_KERNEL);
+ if (!g_debbuf->buf) {
+ kfree(g_cmsg);
+ kfree(g_debbuf);
+ return -ENOMEM;;
+ }
+ g_debbuf->size = CDEBUG_GSIZE;
+ g_debbuf->buf[0] = 0;
+ g_debbuf->p = g_debbuf->buf;
+ g_debbuf->pos = 0;
+ return 0;
+}
+
+void __exit cdebug_exit(void)
+{
+ if (g_debbuf)
+ kfree(g_debbuf->buf);
+ kfree(g_debbuf);
+ kfree(g_cmsg);
+}
+
+#else /* !CONFIG_CAPI_TRACE */
+
+static _cdebbuf g_debbuf = {"CONFIG_CAPI_TRACE not enabled", NULL, 0, 0};
+
+_cdebbuf *capi_message2str(u8 * msg)
+{
+ return &g_debbuf;
+}
+
+_cdebbuf *capi_cmsg2str(_cmsg * cmsg)
+{
+ return &g_debbuf;
+}
+
+void cdebbuf_free(_cdebbuf *cdb)
+{
+}
+
+int __init cdebug_init(void)
+{
+ return 0;
+}
+
+void __exit cdebug_exit(void)
+{
+}
+
+#endif
+
+EXPORT_SYMBOL(cdebbuf_free);
+EXPORT_SYMBOL(capi_cmsg2message);
+EXPORT_SYMBOL(capi_message2cmsg);
+EXPORT_SYMBOL(capi_cmsg_header);
+EXPORT_SYMBOL(capi_cmd2str);
+EXPORT_SYMBOL(capi_cmsg2str);
+EXPORT_SYMBOL(capi_message2str);
+EXPORT_SYMBOL(capi_info2str);
diff --git a/drivers/isdn/capi/kcapi.c b/drivers/isdn/capi/kcapi.c
new file mode 100644
index 0000000..5360c4f
--- /dev/null
+++ b/drivers/isdn/capi/kcapi.c
@@ -0,0 +1,1033 @@
+/* $Id: kcapi.c,v 1.1.2.8 2004/03/26 19:57:20 armin Exp $
+ *
+ * Kernel CAPI 2.0 Module
+ *
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ * Copyright 2002 by Kai Germaschewski <kai@germaschewski.name>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#define AVMB1_COMPAT
+
+#include "kcapi.h"
+#include <linux/module.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/skbuff.h>
+#include <linux/workqueue.h>
+#include <linux/capi.h>
+#include <linux/kernelcapi.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+#ifdef AVMB1_COMPAT
+#include <linux/b1lli.h>
+#endif
+#include <linux/mutex.h>
+
+static char *revision = "$Revision: 1.1.2.8 $";
+
+/* ------------------------------------------------------------- */
+
+static int showcapimsgs = 0;
+
+MODULE_DESCRIPTION("CAPI4Linux: kernel CAPI layer");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+module_param(showcapimsgs, uint, 0);
+
+/* ------------------------------------------------------------- */
+
+struct capi_notifier {
+ struct work_struct work;
+ unsigned int cmd;
+ u32 controller;
+ u16 applid;
+ u32 ncci;
+};
+
+/* ------------------------------------------------------------- */
+
+static struct capi_version driver_version = {2, 0, 1, 1<<4};
+static char driver_serial[CAPI_SERIAL_LEN] = "0004711";
+static char capi_manufakturer[64] = "AVM Berlin";
+
+#define NCCI2CTRL(ncci) (((ncci) >> 24) & 0x7f)
+
+LIST_HEAD(capi_drivers);
+DEFINE_RWLOCK(capi_drivers_list_lock);
+
+static DEFINE_RWLOCK(application_lock);
+static DEFINE_MUTEX(controller_mutex);
+
+struct capi20_appl *capi_applications[CAPI_MAXAPPL];
+struct capi_ctr *capi_cards[CAPI_MAXCONTR];
+
+static int ncards;
+
+/* -------- controller ref counting -------------------------------------- */
+
+static inline struct capi_ctr *
+capi_ctr_get(struct capi_ctr *card)
+{
+ if (!try_module_get(card->owner))
+ return NULL;
+ return card;
+}
+
+static inline void
+capi_ctr_put(struct capi_ctr *card)
+{
+ module_put(card->owner);
+}
+
+/* ------------------------------------------------------------- */
+
+static inline struct capi_ctr *get_capi_ctr_by_nr(u16 contr)
+{
+ if (contr - 1 >= CAPI_MAXCONTR)
+ return NULL;
+
+ return capi_cards[contr - 1];
+}
+
+static inline struct capi20_appl *get_capi_appl_by_nr(u16 applid)
+{
+ if (applid - 1 >= CAPI_MAXAPPL)
+ return NULL;
+
+ return capi_applications[applid - 1];
+}
+
+/* -------- util functions ------------------------------------ */
+
+static inline int capi_cmd_valid(u8 cmd)
+{
+ switch (cmd) {
+ case CAPI_ALERT:
+ case CAPI_CONNECT:
+ case CAPI_CONNECT_ACTIVE:
+ case CAPI_CONNECT_B3_ACTIVE:
+ case CAPI_CONNECT_B3:
+ case CAPI_CONNECT_B3_T90_ACTIVE:
+ case CAPI_DATA_B3:
+ case CAPI_DISCONNECT_B3:
+ case CAPI_DISCONNECT:
+ case CAPI_FACILITY:
+ case CAPI_INFO:
+ case CAPI_LISTEN:
+ case CAPI_MANUFACTURER:
+ case CAPI_RESET_B3:
+ case CAPI_SELECT_B_PROTOCOL:
+ return 1;
+ }
+ return 0;
+}
+
+static inline int capi_subcmd_valid(u8 subcmd)
+{
+ switch (subcmd) {
+ case CAPI_REQ:
+ case CAPI_CONF:
+ case CAPI_IND:
+ case CAPI_RESP:
+ return 1;
+ }
+ return 0;
+}
+
+/* ------------------------------------------------------------ */
+
+static void register_appl(struct capi_ctr *card, u16 applid, capi_register_params *rparam)
+{
+ card = capi_ctr_get(card);
+
+ if (card)
+ card->register_appl(card, applid, rparam);
+ else
+ printk(KERN_WARNING "%s: cannot get card resources\n", __func__);
+}
+
+
+static void release_appl(struct capi_ctr *card, u16 applid)
+{
+ DBG("applid %#x", applid);
+
+ card->release_appl(card, applid);
+ capi_ctr_put(card);
+}
+
+/* -------- KCI_CONTRUP --------------------------------------- */
+
+static void notify_up(u32 contr)
+{
+ struct capi_ctr *card = get_capi_ctr_by_nr(contr);
+ struct capi20_appl *ap;
+ u16 applid;
+
+ if (showcapimsgs & 1) {
+ printk(KERN_DEBUG "kcapi: notify up contr %d\n", contr);
+ }
+ if (!card) {
+ printk(KERN_WARNING "%s: invalid contr %d\n", __func__, contr);
+ return;
+ }
+ for (applid = 1; applid <= CAPI_MAXAPPL; applid++) {
+ ap = get_capi_appl_by_nr(applid);
+ if (!ap || ap->release_in_progress) continue;
+ register_appl(card, applid, &ap->rparam);
+ if (ap->callback && !ap->release_in_progress)
+ ap->callback(KCI_CONTRUP, contr, &card->profile);
+ }
+}
+
+/* -------- KCI_CONTRDOWN ------------------------------------- */
+
+static void notify_down(u32 contr)
+{
+ struct capi20_appl *ap;
+ u16 applid;
+
+ if (showcapimsgs & 1) {
+ printk(KERN_DEBUG "kcapi: notify down contr %d\n", contr);
+ }
+
+ for (applid = 1; applid <= CAPI_MAXAPPL; applid++) {
+ ap = get_capi_appl_by_nr(applid);
+ if (ap && ap->callback && !ap->release_in_progress)
+ ap->callback(KCI_CONTRDOWN, contr, NULL);
+ }
+}
+
+static void notify_handler(struct work_struct *work)
+{
+ struct capi_notifier *np =
+ container_of(work, struct capi_notifier, work);
+
+ switch (np->cmd) {
+ case KCI_CONTRUP:
+ notify_up(np->controller);
+ break;
+ case KCI_CONTRDOWN:
+ notify_down(np->controller);
+ break;
+ }
+
+ kfree(np);
+}
+
+/*
+ * The notifier will result in adding/deleteing of devices. Devices can
+ * only removed in user process, not in bh.
+ */
+static int notify_push(unsigned int cmd, u32 controller, u16 applid, u32 ncci)
+{
+ struct capi_notifier *np = kmalloc(sizeof(*np), GFP_ATOMIC);
+
+ if (!np)
+ return -ENOMEM;
+
+ INIT_WORK(&np->work, notify_handler);
+ np->cmd = cmd;
+ np->controller = controller;
+ np->applid = applid;
+ np->ncci = ncci;
+
+ schedule_work(&np->work);
+ return 0;
+}
+
+
+/* -------- Receiver ------------------------------------------ */
+
+static void recv_handler(struct work_struct *work)
+{
+ struct sk_buff *skb;
+ struct capi20_appl *ap =
+ container_of(work, struct capi20_appl, recv_work);
+
+ if ((!ap) || (ap->release_in_progress))
+ return;
+
+ mutex_lock(&ap->recv_mtx);
+ while ((skb = skb_dequeue(&ap->recv_queue))) {
+ if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_IND)
+ ap->nrecvdatapkt++;
+ else
+ ap->nrecvctlpkt++;
+
+ ap->recv_message(ap, skb);
+ }
+ mutex_unlock(&ap->recv_mtx);
+}
+
+void capi_ctr_handle_message(struct capi_ctr * card, u16 appl, struct sk_buff *skb)
+{
+ struct capi20_appl *ap;
+ int showctl = 0;
+ u8 cmd, subcmd;
+ unsigned long flags;
+ _cdebbuf *cdb;
+
+ if (card->cardstate != CARD_RUNNING) {
+ cdb = capi_message2str(skb->data);
+ if (cdb) {
+ printk(KERN_INFO "kcapi: controller [%03d] not active, got: %s",
+ card->cnr, cdb->buf);
+ cdebbuf_free(cdb);
+ } else
+ printk(KERN_INFO "kcapi: controller [%03d] not active, cannot trace\n",
+ card->cnr);
+ goto error;
+ }
+
+ cmd = CAPIMSG_COMMAND(skb->data);
+ subcmd = CAPIMSG_SUBCOMMAND(skb->data);
+ if (cmd == CAPI_DATA_B3 && subcmd == CAPI_IND) {
+ card->nrecvdatapkt++;
+ if (card->traceflag > 2) showctl |= 2;
+ } else {
+ card->nrecvctlpkt++;
+ if (card->traceflag) showctl |= 2;
+ }
+ showctl |= (card->traceflag & 1);
+ if (showctl & 2) {
+ if (showctl & 1) {
+ printk(KERN_DEBUG "kcapi: got [%03d] id#%d %s len=%u\n",
+ card->cnr, CAPIMSG_APPID(skb->data),
+ capi_cmd2str(cmd, subcmd),
+ CAPIMSG_LEN(skb->data));
+ } else {
+ cdb = capi_message2str(skb->data);
+ if (cdb) {
+ printk(KERN_DEBUG "kcapi: got [%03d] %s\n",
+ card->cnr, cdb->buf);
+ cdebbuf_free(cdb);
+ } else
+ printk(KERN_DEBUG "kcapi: got [%03d] id#%d %s len=%u, cannot trace\n",
+ card->cnr, CAPIMSG_APPID(skb->data),
+ capi_cmd2str(cmd, subcmd),
+ CAPIMSG_LEN(skb->data));
+ }
+
+ }
+
+ read_lock_irqsave(&application_lock, flags);
+ ap = get_capi_appl_by_nr(CAPIMSG_APPID(skb->data));
+ if ((!ap) || (ap->release_in_progress)) {
+ read_unlock_irqrestore(&application_lock, flags);
+ cdb = capi_message2str(skb->data);
+ if (cdb) {
+ printk(KERN_ERR "kcapi: handle_message: applid %d state released (%s)\n",
+ CAPIMSG_APPID(skb->data), cdb->buf);
+ cdebbuf_free(cdb);
+ } else
+ printk(KERN_ERR "kcapi: handle_message: applid %d state released (%s) cannot trace\n",
+ CAPIMSG_APPID(skb->data),
+ capi_cmd2str(cmd, subcmd));
+ goto error;
+ }
+ skb_queue_tail(&ap->recv_queue, skb);
+ schedule_work(&ap->recv_work);
+ read_unlock_irqrestore(&application_lock, flags);
+
+ return;
+
+error:
+ kfree_skb(skb);
+}
+
+EXPORT_SYMBOL(capi_ctr_handle_message);
+
+void capi_ctr_ready(struct capi_ctr * card)
+{
+ card->cardstate = CARD_RUNNING;
+
+ printk(KERN_NOTICE "kcapi: card [%03d] \"%s\" ready.\n",
+ card->cnr, card->name);
+
+ notify_push(KCI_CONTRUP, card->cnr, 0, 0);
+}
+
+EXPORT_SYMBOL(capi_ctr_ready);
+
+void capi_ctr_reseted(struct capi_ctr * card)
+{
+ u16 appl;
+
+ DBG("");
+
+ if (card->cardstate == CARD_DETECTED)
+ return;
+
+ card->cardstate = CARD_DETECTED;
+
+ memset(card->manu, 0, sizeof(card->manu));
+ memset(&card->version, 0, sizeof(card->version));
+ memset(&card->profile, 0, sizeof(card->profile));
+ memset(card->serial, 0, sizeof(card->serial));
+
+ for (appl = 1; appl <= CAPI_MAXAPPL; appl++) {
+ struct capi20_appl *ap = get_capi_appl_by_nr(appl);
+ if (!ap || ap->release_in_progress)
+ continue;
+
+ capi_ctr_put(card);
+ }
+
+ printk(KERN_NOTICE "kcapi: card [%03d] down.\n", card->cnr);
+
+ notify_push(KCI_CONTRDOWN, card->cnr, 0, 0);
+}
+
+EXPORT_SYMBOL(capi_ctr_reseted);
+
+void capi_ctr_suspend_output(struct capi_ctr *card)
+{
+ if (!card->blocked) {
+ printk(KERN_DEBUG "kcapi: card [%03d] suspend\n", card->cnr);
+ card->blocked = 1;
+ }
+}
+
+EXPORT_SYMBOL(capi_ctr_suspend_output);
+
+void capi_ctr_resume_output(struct capi_ctr *card)
+{
+ if (card->blocked) {
+ printk(KERN_DEBUG "kcapi: card [%03d] resume\n", card->cnr);
+ card->blocked = 0;
+ }
+}
+
+EXPORT_SYMBOL(capi_ctr_resume_output);
+
+/* ------------------------------------------------------------- */
+
+int
+attach_capi_ctr(struct capi_ctr *card)
+{
+ int i;
+
+ mutex_lock(&controller_mutex);
+
+ for (i = 0; i < CAPI_MAXCONTR; i++) {
+ if (capi_cards[i] == NULL)
+ break;
+ }
+ if (i == CAPI_MAXCONTR) {
+ mutex_unlock(&controller_mutex);
+ printk(KERN_ERR "kcapi: out of controller slots\n");
+ return -EBUSY;
+ }
+ capi_cards[i] = card;
+
+ mutex_unlock(&controller_mutex);
+
+ card->nrecvctlpkt = 0;
+ card->nrecvdatapkt = 0;
+ card->nsentctlpkt = 0;
+ card->nsentdatapkt = 0;
+ card->cnr = i + 1;
+ card->cardstate = CARD_DETECTED;
+ card->blocked = 0;
+ card->traceflag = showcapimsgs;
+
+ sprintf(card->procfn, "capi/controllers/%d", card->cnr);
+ card->procent = create_proc_entry(card->procfn, 0, NULL);
+ if (card->procent) {
+ card->procent->read_proc =
+ (int (*)(char *,char **,off_t,int,int *,void *))
+ card->ctr_read_proc;
+ card->procent->data = card;
+ }
+
+ ncards++;
+ printk(KERN_NOTICE "kcapi: Controller [%03d]: %s attached\n",
+ card->cnr, card->name);
+ return 0;
+}
+
+EXPORT_SYMBOL(attach_capi_ctr);
+
+int detach_capi_ctr(struct capi_ctr *card)
+{
+ if (card->cardstate != CARD_DETECTED)
+ capi_ctr_reseted(card);
+
+ ncards--;
+
+ if (card->procent) {
+ remove_proc_entry(card->procfn, NULL);
+ card->procent = NULL;
+ }
+ capi_cards[card->cnr - 1] = NULL;
+ printk(KERN_NOTICE "kcapi: Controller [%03d]: %s unregistered\n",
+ card->cnr, card->name);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(detach_capi_ctr);
+
+void register_capi_driver(struct capi_driver *driver)
+{
+ unsigned long flags;
+
+ write_lock_irqsave(&capi_drivers_list_lock, flags);
+ list_add_tail(&driver->list, &capi_drivers);
+ write_unlock_irqrestore(&capi_drivers_list_lock, flags);
+}
+
+EXPORT_SYMBOL(register_capi_driver);
+
+void unregister_capi_driver(struct capi_driver *driver)
+{
+ unsigned long flags;
+
+ write_lock_irqsave(&capi_drivers_list_lock, flags);
+ list_del(&driver->list);
+ write_unlock_irqrestore(&capi_drivers_list_lock, flags);
+}
+
+EXPORT_SYMBOL(unregister_capi_driver);
+
+/* ------------------------------------------------------------- */
+/* -------- CAPI2.0 Interface ---------------------------------- */
+/* ------------------------------------------------------------- */
+
+u16 capi20_isinstalled(void)
+{
+ int i;
+ for (i = 0; i < CAPI_MAXCONTR; i++) {
+ if (capi_cards[i] && capi_cards[i]->cardstate == CARD_RUNNING)
+ return CAPI_NOERROR;
+ }
+ return CAPI_REGNOTINSTALLED;
+}
+
+EXPORT_SYMBOL(capi20_isinstalled);
+
+u16 capi20_register(struct capi20_appl *ap)
+{
+ int i;
+ u16 applid;
+ unsigned long flags;
+
+ DBG("");
+
+ if (ap->rparam.datablklen < 128)
+ return CAPI_LOGBLKSIZETOSMALL;
+
+ write_lock_irqsave(&application_lock, flags);
+
+ for (applid = 1; applid <= CAPI_MAXAPPL; applid++) {
+ if (capi_applications[applid - 1] == NULL)
+ break;
+ }
+ if (applid > CAPI_MAXAPPL) {
+ write_unlock_irqrestore(&application_lock, flags);
+ return CAPI_TOOMANYAPPLS;
+ }
+
+ ap->applid = applid;
+ capi_applications[applid - 1] = ap;
+
+ ap->nrecvctlpkt = 0;
+ ap->nrecvdatapkt = 0;
+ ap->nsentctlpkt = 0;
+ ap->nsentdatapkt = 0;
+ ap->callback = NULL;
+ mutex_init(&ap->recv_mtx);
+ skb_queue_head_init(&ap->recv_queue);
+ INIT_WORK(&ap->recv_work, recv_handler);
+ ap->release_in_progress = 0;
+
+ write_unlock_irqrestore(&application_lock, flags);
+
+ mutex_lock(&controller_mutex);
+ for (i = 0; i < CAPI_MAXCONTR; i++) {
+ if (!capi_cards[i] || capi_cards[i]->cardstate != CARD_RUNNING)
+ continue;
+ register_appl(capi_cards[i], applid, &ap->rparam);
+ }
+ mutex_unlock(&controller_mutex);
+
+ if (showcapimsgs & 1) {
+ printk(KERN_DEBUG "kcapi: appl %d up\n", applid);
+ }
+
+ return CAPI_NOERROR;
+}
+
+EXPORT_SYMBOL(capi20_register);
+
+u16 capi20_release(struct capi20_appl *ap)
+{
+ int i;
+ unsigned long flags;
+
+ DBG("applid %#x", ap->applid);
+
+ write_lock_irqsave(&application_lock, flags);
+ ap->release_in_progress = 1;
+ capi_applications[ap->applid - 1] = NULL;
+ write_unlock_irqrestore(&application_lock, flags);
+
+ mutex_lock(&controller_mutex);
+ for (i = 0; i < CAPI_MAXCONTR; i++) {
+ if (!capi_cards[i] || capi_cards[i]->cardstate != CARD_RUNNING)
+ continue;
+ release_appl(capi_cards[i], ap->applid);
+ }
+ mutex_unlock(&controller_mutex);
+
+ flush_scheduled_work();
+ skb_queue_purge(&ap->recv_queue);
+
+ if (showcapimsgs & 1) {
+ printk(KERN_DEBUG "kcapi: appl %d down\n", ap->applid);
+ }
+
+ return CAPI_NOERROR;
+}
+
+EXPORT_SYMBOL(capi20_release);
+
+u16 capi20_put_message(struct capi20_appl *ap, struct sk_buff *skb)
+{
+ struct capi_ctr *card;
+ int showctl = 0;
+ u8 cmd, subcmd;
+
+ DBG("applid %#x", ap->applid);
+
+ if (ncards == 0)
+ return CAPI_REGNOTINSTALLED;
+ if ((ap->applid == 0) || ap->release_in_progress)
+ return CAPI_ILLAPPNR;
+ if (skb->len < 12
+ || !capi_cmd_valid(CAPIMSG_COMMAND(skb->data))
+ || !capi_subcmd_valid(CAPIMSG_SUBCOMMAND(skb->data)))
+ return CAPI_ILLCMDORSUBCMDORMSGTOSMALL;
+ card = get_capi_ctr_by_nr(CAPIMSG_CONTROLLER(skb->data));
+ if (!card || card->cardstate != CARD_RUNNING) {
+ card = get_capi_ctr_by_nr(1); // XXX why?
+ if (!card || card->cardstate != CARD_RUNNING)
+ return CAPI_REGNOTINSTALLED;
+ }
+ if (card->blocked)
+ return CAPI_SENDQUEUEFULL;
+
+ cmd = CAPIMSG_COMMAND(skb->data);
+ subcmd = CAPIMSG_SUBCOMMAND(skb->data);
+
+ if (cmd == CAPI_DATA_B3 && subcmd== CAPI_REQ) {
+ card->nsentdatapkt++;
+ ap->nsentdatapkt++;
+ if (card->traceflag > 2) showctl |= 2;
+ } else {
+ card->nsentctlpkt++;
+ ap->nsentctlpkt++;
+ if (card->traceflag) showctl |= 2;
+ }
+ showctl |= (card->traceflag & 1);
+ if (showctl & 2) {
+ if (showctl & 1) {
+ printk(KERN_DEBUG "kcapi: put [%03d] id#%d %s len=%u\n",
+ CAPIMSG_CONTROLLER(skb->data),
+ CAPIMSG_APPID(skb->data),
+ capi_cmd2str(cmd, subcmd),
+ CAPIMSG_LEN(skb->data));
+ } else {
+ _cdebbuf *cdb = capi_message2str(skb->data);
+ if (cdb) {
+ printk(KERN_DEBUG "kcapi: put [%03d] %s\n",
+ CAPIMSG_CONTROLLER(skb->data),
+ cdb->buf);
+ cdebbuf_free(cdb);
+ } else
+ printk(KERN_DEBUG "kcapi: put [%03d] id#%d %s len=%u cannot trace\n",
+ CAPIMSG_CONTROLLER(skb->data),
+ CAPIMSG_APPID(skb->data),
+ capi_cmd2str(cmd, subcmd),
+ CAPIMSG_LEN(skb->data));
+ }
+ }
+ return card->send_message(card, skb);
+}
+
+EXPORT_SYMBOL(capi20_put_message);
+
+u16 capi20_get_manufacturer(u32 contr, u8 *buf)
+{
+ struct capi_ctr *card;
+
+ if (contr == 0) {
+ strlcpy(buf, capi_manufakturer, CAPI_MANUFACTURER_LEN);
+ return CAPI_NOERROR;
+ }
+ card = get_capi_ctr_by_nr(contr);
+ if (!card || card->cardstate != CARD_RUNNING)
+ return CAPI_REGNOTINSTALLED;
+ strlcpy(buf, card->manu, CAPI_MANUFACTURER_LEN);
+ return CAPI_NOERROR;
+}
+
+EXPORT_SYMBOL(capi20_get_manufacturer);
+
+u16 capi20_get_version(u32 contr, struct capi_version *verp)
+{
+ struct capi_ctr *card;
+
+ if (contr == 0) {
+ *verp = driver_version;
+ return CAPI_NOERROR;
+ }
+ card = get_capi_ctr_by_nr(contr);
+ if (!card || card->cardstate != CARD_RUNNING)
+ return CAPI_REGNOTINSTALLED;
+
+ memcpy((void *) verp, &card->version, sizeof(capi_version));
+ return CAPI_NOERROR;
+}
+
+EXPORT_SYMBOL(capi20_get_version);
+
+u16 capi20_get_serial(u32 contr, u8 *serial)
+{
+ struct capi_ctr *card;
+
+ if (contr == 0) {
+ strlcpy(serial, driver_serial, CAPI_SERIAL_LEN);
+ return CAPI_NOERROR;
+ }
+ card = get_capi_ctr_by_nr(contr);
+ if (!card || card->cardstate != CARD_RUNNING)
+ return CAPI_REGNOTINSTALLED;
+
+ strlcpy((void *) serial, card->serial, CAPI_SERIAL_LEN);
+ return CAPI_NOERROR;
+}
+
+EXPORT_SYMBOL(capi20_get_serial);
+
+u16 capi20_get_profile(u32 contr, struct capi_profile *profp)
+{
+ struct capi_ctr *card;
+
+ if (contr == 0) {
+ profp->ncontroller = ncards;
+ return CAPI_NOERROR;
+ }
+ card = get_capi_ctr_by_nr(contr);
+ if (!card || card->cardstate != CARD_RUNNING)
+ return CAPI_REGNOTINSTALLED;
+
+ memcpy((void *) profp, &card->profile,
+ sizeof(struct capi_profile));
+ return CAPI_NOERROR;
+}
+
+EXPORT_SYMBOL(capi20_get_profile);
+
+#ifdef AVMB1_COMPAT
+static int old_capi_manufacturer(unsigned int cmd, void __user *data)
+{
+ avmb1_loadandconfigdef ldef;
+ avmb1_extcarddef cdef;
+ avmb1_resetdef rdef;
+ capicardparams cparams;
+ struct capi_ctr *card;
+ struct capi_driver *driver = NULL;
+ capiloaddata ldata;
+ struct list_head *l;
+ unsigned long flags;
+ int retval;
+
+ switch (cmd) {
+ case AVMB1_ADDCARD:
+ case AVMB1_ADDCARD_WITH_TYPE:
+ if (cmd == AVMB1_ADDCARD) {
+ if ((retval = copy_from_user(&cdef, data,
+ sizeof(avmb1_carddef))))
+ return retval;
+ cdef.cardtype = AVM_CARDTYPE_B1;
+ } else {
+ if ((retval = copy_from_user(&cdef, data,
+ sizeof(avmb1_extcarddef))))
+ return retval;
+ }
+ cparams.port = cdef.port;
+ cparams.irq = cdef.irq;
+ cparams.cardnr = cdef.cardnr;
+
+ read_lock_irqsave(&capi_drivers_list_lock, flags);
+ switch (cdef.cardtype) {
+ case AVM_CARDTYPE_B1:
+ list_for_each(l, &capi_drivers) {
+ driver = list_entry(l, struct capi_driver, list);
+ if (strcmp(driver->name, "b1isa") == 0)
+ break;
+ }
+ break;
+ case AVM_CARDTYPE_T1:
+ list_for_each(l, &capi_drivers) {
+ driver = list_entry(l, struct capi_driver, list);
+ if (strcmp(driver->name, "t1isa") == 0)
+ break;
+ }
+ break;
+ default:
+ driver = NULL;
+ break;
+ }
+ if (!driver) {
+ read_unlock_irqrestore(&capi_drivers_list_lock, flags);
+ printk(KERN_ERR "kcapi: driver not loaded.\n");
+ return -EIO;
+ }
+ if (!driver->add_card) {
+ read_unlock_irqrestore(&capi_drivers_list_lock, flags);
+ printk(KERN_ERR "kcapi: driver has no add card function.\n");
+ return -EIO;
+ }
+
+ retval = driver->add_card(driver, &cparams);
+ read_unlock_irqrestore(&capi_drivers_list_lock, flags);
+ return retval;
+
+ case AVMB1_LOAD:
+ case AVMB1_LOAD_AND_CONFIG:
+
+ if (cmd == AVMB1_LOAD) {
+ if (copy_from_user(&ldef, data,
+ sizeof(avmb1_loaddef)))
+ return -EFAULT;
+ ldef.t4config.len = 0;
+ ldef.t4config.data = NULL;
+ } else {
+ if (copy_from_user(&ldef, data,
+ sizeof(avmb1_loadandconfigdef)))
+ return -EFAULT;
+ }
+ card = get_capi_ctr_by_nr(ldef.contr);
+ if (!card)
+ return -EINVAL;
+ card = capi_ctr_get(card);
+ if (!card)
+ return -ESRCH;
+ if (card->load_firmware == NULL) {
+ printk(KERN_DEBUG "kcapi: load: no load function\n");
+ capi_ctr_put(card);
+ return -ESRCH;
+ }
+
+ if (ldef.t4file.len <= 0) {
+ printk(KERN_DEBUG "kcapi: load: invalid parameter: length of t4file is %d ?\n", ldef.t4file.len);
+ capi_ctr_put(card);
+ return -EINVAL;
+ }
+ if (ldef.t4file.data == NULL) {
+ printk(KERN_DEBUG "kcapi: load: invalid parameter: dataptr is 0\n");
+ capi_ctr_put(card);
+ return -EINVAL;
+ }
+
+ ldata.firmware.user = 1;
+ ldata.firmware.data = ldef.t4file.data;
+ ldata.firmware.len = ldef.t4file.len;
+ ldata.configuration.user = 1;
+ ldata.configuration.data = ldef.t4config.data;
+ ldata.configuration.len = ldef.t4config.len;
+
+ if (card->cardstate != CARD_DETECTED) {
+ printk(KERN_INFO "kcapi: load: contr=%d not in detect state\n", ldef.contr);
+ capi_ctr_put(card);
+ return -EBUSY;
+ }
+ card->cardstate = CARD_LOADING;
+
+ retval = card->load_firmware(card, &ldata);
+
+ if (retval) {
+ card->cardstate = CARD_DETECTED;
+ capi_ctr_put(card);
+ return retval;
+ }
+
+ while (card->cardstate != CARD_RUNNING) {
+
+ msleep_interruptible(100); /* 0.1 sec */
+
+ if (signal_pending(current)) {
+ capi_ctr_put(card);
+ return -EINTR;
+ }
+ }
+ capi_ctr_put(card);
+ return 0;
+
+ case AVMB1_RESETCARD:
+ if (copy_from_user(&rdef, data, sizeof(avmb1_resetdef)))
+ return -EFAULT;
+ card = get_capi_ctr_by_nr(rdef.contr);
+ if (!card)
+ return -ESRCH;
+
+ if (card->cardstate == CARD_DETECTED)
+ return 0;
+
+ card->reset_ctr(card);
+
+ while (card->cardstate > CARD_DETECTED) {
+
+ msleep_interruptible(100); /* 0.1 sec */
+
+ if (signal_pending(current))
+ return -EINTR;
+ }
+ return 0;
+
+ }
+ return -EINVAL;
+}
+#endif
+
+int capi20_manufacturer(unsigned int cmd, void __user *data)
+{
+ struct capi_ctr *card;
+
+ switch (cmd) {
+#ifdef AVMB1_COMPAT
+ case AVMB1_LOAD:
+ case AVMB1_LOAD_AND_CONFIG:
+ case AVMB1_RESETCARD:
+ case AVMB1_GET_CARDINFO:
+ case AVMB1_REMOVECARD:
+ return old_capi_manufacturer(cmd, data);
+#endif
+ case KCAPI_CMD_TRACE:
+ {
+ kcapi_flagdef fdef;
+
+ if (copy_from_user(&fdef, data, sizeof(kcapi_flagdef)))
+ return -EFAULT;
+
+ card = get_capi_ctr_by_nr(fdef.contr);
+ if (!card)
+ return -ESRCH;
+
+ card->traceflag = fdef.flag;
+ printk(KERN_INFO "kcapi: contr [%03d] set trace=%d\n",
+ card->cnr, card->traceflag);
+ return 0;
+ }
+ case KCAPI_CMD_ADDCARD:
+ {
+ struct list_head *l;
+ struct capi_driver *driver = NULL;
+ capicardparams cparams;
+ kcapi_carddef cdef;
+ int retval;
+
+ if ((retval = copy_from_user(&cdef, data, sizeof(cdef))))
+ return retval;
+
+ cparams.port = cdef.port;
+ cparams.irq = cdef.irq;
+ cparams.membase = cdef.membase;
+ cparams.cardnr = cdef.cardnr;
+ cparams.cardtype = 0;
+ cdef.driver[sizeof(cdef.driver)-1] = 0;
+
+ list_for_each(l, &capi_drivers) {
+ driver = list_entry(l, struct capi_driver, list);
+ if (strcmp(driver->name, cdef.driver) == 0)
+ break;
+ }
+ if (driver == NULL) {
+ printk(KERN_ERR "kcapi: driver \"%s\" not loaded.\n",
+ cdef.driver);
+ return -ESRCH;
+ }
+
+ if (!driver->add_card) {
+ printk(KERN_ERR "kcapi: driver \"%s\" has no add card function.\n", cdef.driver);
+ return -EIO;
+ }
+
+ return driver->add_card(driver, &cparams);
+ }
+
+ default:
+ printk(KERN_ERR "kcapi: manufacturer command %d unknown.\n",
+ cmd);
+ break;
+
+ }
+ return -EINVAL;
+}
+
+EXPORT_SYMBOL(capi20_manufacturer);
+
+/* temporary hack */
+void capi20_set_callback(struct capi20_appl *ap,
+ void (*callback) (unsigned int cmd, __u32 contr, void *data))
+{
+ ap->callback = callback;
+}
+
+EXPORT_SYMBOL(capi20_set_callback);
+
+/* ------------------------------------------------------------- */
+/* -------- Init & Cleanup ------------------------------------- */
+/* ------------------------------------------------------------- */
+
+/*
+ * init / exit functions
+ */
+
+static int __init kcapi_init(void)
+{
+ char *p;
+ char rev[32];
+ int ret;
+
+ ret = cdebug_init();
+ if (ret)
+ return ret;
+ kcapi_proc_init();
+
+ if ((p = strchr(revision, ':')) != NULL && p[1]) {
+ strlcpy(rev, p + 2, sizeof(rev));
+ if ((p = strchr(rev, '$')) != NULL && p > rev)
+ *(p-1) = 0;
+ } else
+ strcpy(rev, "1.0");
+
+ printk(KERN_NOTICE "CAPI Subsystem Rev %s\n", rev);
+
+ return 0;
+}
+
+static void __exit kcapi_exit(void)
+{
+ kcapi_proc_exit();
+
+ /* make sure all notifiers are finished */
+ flush_scheduled_work();
+ cdebug_exit();
+}
+
+module_init(kcapi_init);
+module_exit(kcapi_exit);
diff --git a/drivers/isdn/capi/kcapi.h b/drivers/isdn/capi/kcapi.h
new file mode 100644
index 0000000..244711f
--- /dev/null
+++ b/drivers/isdn/capi/kcapi.h
@@ -0,0 +1,49 @@
+/*
+ * Kernel CAPI 2.0 Module
+ *
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ * Copyright 2002 by Kai Germaschewski <kai@germaschewski.name>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/isdn/capilli.h>
+
+#ifdef KCAPI_DEBUG
+#define DBG(format, arg...) do { \
+printk(KERN_DEBUG "%s: " format "\n" , __func__ , ## arg); \
+} while (0)
+#else
+#define DBG(format, arg...) /* */
+#endif
+
+enum {
+ CARD_DETECTED = 1,
+ CARD_LOADING = 2,
+ CARD_RUNNING = 3,
+};
+
+extern struct list_head capi_drivers;
+extern rwlock_t capi_drivers_list_lock;
+
+extern struct capi20_appl *capi_applications[CAPI_MAXAPPL];
+extern struct capi_ctr *capi_cards[CAPI_MAXCONTR];
+
+#ifdef CONFIG_PROC_FS
+
+void kcapi_proc_init(void);
+void kcapi_proc_exit(void);
+
+#else
+
+static inline void kcapi_proc_init(void) { };
+static inline void kcapi_proc_exit(void) { };
+
+#endif
+
diff --git a/drivers/isdn/capi/kcapi_proc.c b/drivers/isdn/capi/kcapi_proc.c
new file mode 100644
index 0000000..c29208b
--- /dev/null
+++ b/drivers/isdn/capi/kcapi_proc.c
@@ -0,0 +1,312 @@
+/*
+ * Kernel CAPI 2.0 Module - /proc/capi handling
+ *
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ * Copyright 2002 by Kai Germaschewski <kai@germaschewski.name>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+
+#include "kcapi.h"
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/init.h>
+
+static char *
+cardstate2str(unsigned short cardstate)
+{
+ switch (cardstate) {
+ case CARD_DETECTED: return "detected";
+ case CARD_LOADING: return "loading";
+ case CARD_RUNNING: return "running";
+ default: return "???";
+ }
+}
+
+// /proc/capi
+// ===========================================================================
+
+// /proc/capi/controller:
+// cnr driver cardstate name driverinfo
+// /proc/capi/contrstats:
+// cnr nrecvctlpkt nrecvdatapkt nsentctlpkt nsentdatapkt
+// ---------------------------------------------------------------------------
+
+static void *controller_start(struct seq_file *seq, loff_t *pos)
+{
+ if (*pos < CAPI_MAXCONTR)
+ return &capi_cards[*pos];
+
+ return NULL;
+}
+
+static void *controller_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+ ++*pos;
+ if (*pos < CAPI_MAXCONTR)
+ return &capi_cards[*pos];
+
+ return NULL;
+}
+
+static void controller_stop(struct seq_file *seq, void *v)
+{
+}
+
+static int controller_show(struct seq_file *seq, void *v)
+{
+ struct capi_ctr *ctr = *(struct capi_ctr **) v;
+
+ if (!ctr)
+ return 0;
+
+ seq_printf(seq, "%d %-10s %-8s %-16s %s\n",
+ ctr->cnr, ctr->driver_name,
+ cardstate2str(ctr->cardstate),
+ ctr->name,
+ ctr->procinfo ? ctr->procinfo(ctr) : "");
+
+ return 0;
+}
+
+static int contrstats_show(struct seq_file *seq, void *v)
+{
+ struct capi_ctr *ctr = *(struct capi_ctr **) v;
+
+ if (!ctr)
+ return 0;
+
+ seq_printf(seq, "%d %lu %lu %lu %lu\n",
+ ctr->cnr,
+ ctr->nrecvctlpkt,
+ ctr->nrecvdatapkt,
+ ctr->nsentctlpkt,
+ ctr->nsentdatapkt);
+
+ return 0;
+}
+
+static struct seq_operations seq_controller_ops = {
+ .start = controller_start,
+ .next = controller_next,
+ .stop = controller_stop,
+ .show = controller_show,
+};
+
+static struct seq_operations seq_contrstats_ops = {
+ .start = controller_start,
+ .next = controller_next,
+ .stop = controller_stop,
+ .show = contrstats_show,
+};
+
+static int seq_controller_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &seq_controller_ops);
+}
+
+static int seq_contrstats_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &seq_contrstats_ops);
+}
+
+static const struct file_operations proc_controller_ops = {
+ .owner = THIS_MODULE,
+ .open = seq_controller_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static const struct file_operations proc_contrstats_ops = {
+ .owner = THIS_MODULE,
+ .open = seq_contrstats_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+// /proc/capi/applications:
+// applid l3cnt dblkcnt dblklen #ncci recvqueuelen
+// /proc/capi/applstats:
+// applid nrecvctlpkt nrecvdatapkt nsentctlpkt nsentdatapkt
+// ---------------------------------------------------------------------------
+
+static void *
+applications_start(struct seq_file *seq, loff_t *pos)
+{
+ if (*pos < CAPI_MAXAPPL)
+ return &capi_applications[*pos];
+
+ return NULL;
+}
+
+static void *
+applications_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+ ++*pos;
+ if (*pos < CAPI_MAXAPPL)
+ return &capi_applications[*pos];
+
+ return NULL;
+}
+
+static void
+applications_stop(struct seq_file *seq, void *v)
+{
+}
+
+static int
+applications_show(struct seq_file *seq, void *v)
+{
+ struct capi20_appl *ap = *(struct capi20_appl **) v;
+
+ if (!ap)
+ return 0;
+
+ seq_printf(seq, "%u %d %d %d\n",
+ ap->applid,
+ ap->rparam.level3cnt,
+ ap->rparam.datablkcnt,
+ ap->rparam.datablklen);
+
+ return 0;
+}
+
+static int
+applstats_show(struct seq_file *seq, void *v)
+{
+ struct capi20_appl *ap = *(struct capi20_appl **) v;
+
+ if (!ap)
+ return 0;
+
+ seq_printf(seq, "%u %lu %lu %lu %lu\n",
+ ap->applid,
+ ap->nrecvctlpkt,
+ ap->nrecvdatapkt,
+ ap->nsentctlpkt,
+ ap->nsentdatapkt);
+
+ return 0;
+}
+
+static struct seq_operations seq_applications_ops = {
+ .start = applications_start,
+ .next = applications_next,
+ .stop = applications_stop,
+ .show = applications_show,
+};
+
+static struct seq_operations seq_applstats_ops = {
+ .start = applications_start,
+ .next = applications_next,
+ .stop = applications_stop,
+ .show = applstats_show,
+};
+
+static int
+seq_applications_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &seq_applications_ops);
+}
+
+static int
+seq_applstats_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &seq_applstats_ops);
+}
+
+static const struct file_operations proc_applications_ops = {
+ .owner = THIS_MODULE,
+ .open = seq_applications_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static const struct file_operations proc_applstats_ops = {
+ .owner = THIS_MODULE,
+ .open = seq_applstats_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+// ---------------------------------------------------------------------------
+
+static void *capi_driver_start(struct seq_file *seq, loff_t *pos)
+{
+ read_lock(&capi_drivers_list_lock);
+ return seq_list_start(&capi_drivers, *pos);
+}
+
+static void *capi_driver_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+ return seq_list_next(v, &capi_drivers, pos);
+}
+
+static void capi_driver_stop(struct seq_file *seq, void *v)
+{
+ read_unlock(&capi_drivers_list_lock);
+}
+
+static int capi_driver_show(struct seq_file *seq, void *v)
+{
+ struct capi_driver *drv = list_entry(v, struct capi_driver, list);
+
+ seq_printf(seq, "%-32s %s\n", drv->name, drv->revision);
+ return 0;
+}
+
+static struct seq_operations seq_capi_driver_ops = {
+ .start = capi_driver_start,
+ .next = capi_driver_next,
+ .stop = capi_driver_stop,
+ .show = capi_driver_show,
+};
+
+static int
+seq_capi_driver_open(struct inode *inode, struct file *file)
+{
+ int err;
+ err = seq_open(file, &seq_capi_driver_ops);
+ return err;
+}
+
+static const struct file_operations proc_driver_ops = {
+ .owner = THIS_MODULE,
+ .open = seq_capi_driver_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+// ---------------------------------------------------------------------------
+
+void __init
+kcapi_proc_init(void)
+{
+ proc_mkdir("capi", NULL);
+ proc_mkdir("capi/controllers", NULL);
+ proc_create("capi/controller", 0, NULL, &proc_controller_ops);
+ proc_create("capi/contrstats", 0, NULL, &proc_contrstats_ops);
+ proc_create("capi/applications", 0, NULL, &proc_applications_ops);
+ proc_create("capi/applstats", 0, NULL, &proc_applstats_ops);
+ proc_create("capi/driver", 0, NULL, &proc_driver_ops);
+}
+
+void __exit
+kcapi_proc_exit(void)
+{
+ remove_proc_entry("capi/driver", NULL);
+ remove_proc_entry("capi/controller", NULL);
+ remove_proc_entry("capi/contrstats", NULL);
+ remove_proc_entry("capi/applications", NULL);
+ remove_proc_entry("capi/applstats", NULL);
+ remove_proc_entry("capi/controllers", NULL);
+ remove_proc_entry("capi", NULL);
+}
diff --git a/drivers/isdn/divert/Makefile b/drivers/isdn/divert/Makefile
new file mode 100644
index 0000000..dd4a202
--- /dev/null
+++ b/drivers/isdn/divert/Makefile
@@ -0,0 +1,9 @@
+# Makefile for the dss1_divert ISDN module
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_ISDN_DIVERSION) += dss1_divert.o
+
+# Multipart objects.
+
+dss1_divert-y := isdn_divert.o divert_procfs.o divert_init.o
diff --git a/drivers/isdn/divert/divert_init.c b/drivers/isdn/divert/divert_init.c
new file mode 100644
index 0000000..2f7c9fc
--- /dev/null
+++ b/drivers/isdn/divert/divert_init.c
@@ -0,0 +1,82 @@
+/* $Id divert_init.c,v 1.5.6.2 2001/01/24 22:18:17 kai Exp $
+ *
+ * Module init for DSS1 diversion services for i4l.
+ *
+ * Copyright 1999 by Werner Cornelius (werner@isdn4linux.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+
+#include "isdn_divert.h"
+
+MODULE_DESCRIPTION("ISDN4Linux: Call diversion support");
+MODULE_AUTHOR("Werner Cornelius");
+MODULE_LICENSE("GPL");
+
+/****************************************/
+/* structure containing interface to hl */
+/****************************************/
+isdn_divert_if divert_if =
+ { DIVERT_IF_MAGIC, /* magic value */
+ DIVERT_CMD_REG, /* register cmd */
+ ll_callback, /* callback routine from ll */
+ NULL, /* command still not specified */
+ NULL, /* drv_to_name */
+ NULL, /* name_to_drv */
+ };
+
+/*************************/
+/* Module interface code */
+/* no cmd line parms */
+/*************************/
+static int __init divert_init(void)
+{ int i;
+
+ if (divert_dev_init())
+ { printk(KERN_WARNING "dss1_divert: cannot install device, not loaded\n");
+ return(-EIO);
+ }
+ if ((i = DIVERT_REG_NAME(&divert_if)) != DIVERT_NO_ERR)
+ { divert_dev_deinit();
+ printk(KERN_WARNING "dss1_divert: error %d registering module, not loaded\n",i);
+ return(-EIO);
+ }
+ printk(KERN_INFO "dss1_divert module successfully installed\n");
+ return(0);
+}
+
+/**********************/
+/* Module deinit code */
+/**********************/
+static void __exit divert_exit(void)
+{
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&divert_lock, flags);
+ divert_if.cmd = DIVERT_CMD_REL; /* release */
+ if ((i = DIVERT_REG_NAME(&divert_if)) != DIVERT_NO_ERR)
+ { printk(KERN_WARNING "dss1_divert: error %d releasing module\n",i);
+ spin_unlock_irqrestore(&divert_lock, flags);
+ return;
+ }
+ if (divert_dev_deinit())
+ { printk(KERN_WARNING "dss1_divert: device busy, remove cancelled\n");
+ spin_unlock_irqrestore(&divert_lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&divert_lock, flags);
+ deleterule(-1); /* delete all rules and free mem */
+ deleteprocs();
+ printk(KERN_INFO "dss1_divert module successfully removed \n");
+}
+
+module_init(divert_init);
+module_exit(divert_exit);
+
diff --git a/drivers/isdn/divert/divert_procfs.c b/drivers/isdn/divert/divert_procfs.c
new file mode 100644
index 0000000..8b256a6
--- /dev/null
+++ b/drivers/isdn/divert/divert_procfs.c
@@ -0,0 +1,316 @@
+/* $Id: divert_procfs.c,v 1.11.6.2 2001/09/23 22:24:36 kai Exp $
+ *
+ * Filesystem handling for the diversion supplementary services.
+ *
+ * Copyright 1998 by Werner Cornelius (werner@isdn4linux.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/poll.h>
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#else
+#include <linux/fs.h>
+#endif
+#include <linux/isdnif.h>
+#include <net/net_namespace.h>
+#include "isdn_divert.h"
+
+
+/*********************************/
+/* Variables for interface queue */
+/*********************************/
+ulong if_used = 0; /* number of interface users */
+static struct divert_info *divert_info_head = NULL; /* head of queue */
+static struct divert_info *divert_info_tail = NULL; /* pointer to last entry */
+static DEFINE_SPINLOCK(divert_info_lock);/* lock for queue */
+static wait_queue_head_t rd_queue;
+
+/*********************************/
+/* put an info buffer into queue */
+/*********************************/
+void
+put_info_buffer(char *cp)
+{
+ struct divert_info *ib;
+ unsigned long flags;
+
+ if (if_used <= 0)
+ return;
+ if (!cp)
+ return;
+ if (!*cp)
+ return;
+ if (!(ib = kmalloc(sizeof(struct divert_info) + strlen(cp), GFP_ATOMIC)))
+ return; /* no memory */
+ strcpy(ib->info_start, cp); /* set output string */
+ ib->next = NULL;
+ spin_lock_irqsave( &divert_info_lock, flags );
+ ib->usage_cnt = if_used;
+ if (!divert_info_head)
+ divert_info_head = ib; /* new head */
+ else
+ divert_info_tail->next = ib; /* follows existing messages */
+ divert_info_tail = ib; /* new tail */
+
+ /* delete old entrys */
+ while (divert_info_head->next) {
+ if ((divert_info_head->usage_cnt <= 0) &&
+ (divert_info_head->next->usage_cnt <= 0)) {
+ ib = divert_info_head;
+ divert_info_head = divert_info_head->next;
+ kfree(ib);
+ } else
+ break;
+ } /* divert_info_head->next */
+ spin_unlock_irqrestore( &divert_info_lock, flags );
+ wake_up_interruptible(&(rd_queue));
+} /* put_info_buffer */
+
+#ifdef CONFIG_PROC_FS
+
+/**********************************/
+/* deflection device read routine */
+/**********************************/
+static ssize_t
+isdn_divert_read(struct file *file, char __user *buf, size_t count, loff_t * off)
+{
+ struct divert_info *inf;
+ int len;
+
+ if (!*((struct divert_info **) file->private_data)) {
+ if (file->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ interruptible_sleep_on(&(rd_queue));
+ }
+ if (!(inf = *((struct divert_info **) file->private_data)))
+ return (0);
+
+ inf->usage_cnt--; /* new usage count */
+ file->private_data = &inf->next; /* next structure */
+ if ((len = strlen(inf->info_start)) <= count) {
+ if (copy_to_user(buf, inf->info_start, len))
+ return -EFAULT;
+ *off += len;
+ return (len);
+ }
+ return (0);
+} /* isdn_divert_read */
+
+/**********************************/
+/* deflection device write routine */
+/**********************************/
+static ssize_t
+isdn_divert_write(struct file *file, const char __user *buf, size_t count, loff_t * off)
+{
+ return (-ENODEV);
+} /* isdn_divert_write */
+
+
+/***************************************/
+/* select routines for various kernels */
+/***************************************/
+static unsigned int
+isdn_divert_poll(struct file *file, poll_table * wait)
+{
+ unsigned int mask = 0;
+
+ poll_wait(file, &(rd_queue), wait);
+ /* mask = POLLOUT | POLLWRNORM; */
+ if (*((struct divert_info **) file->private_data)) {
+ mask |= POLLIN | POLLRDNORM;
+ }
+ return mask;
+} /* isdn_divert_poll */
+
+/****************/
+/* Open routine */
+/****************/
+static int
+isdn_divert_open(struct inode *ino, struct file *filep)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave( &divert_info_lock, flags );
+ if_used++;
+ if (divert_info_head)
+ filep->private_data = &(divert_info_tail->next);
+ else
+ filep->private_data = &divert_info_head;
+ spin_unlock_irqrestore( &divert_info_lock, flags );
+ /* start_divert(); */
+ return nonseekable_open(ino, filep);
+} /* isdn_divert_open */
+
+/*******************/
+/* close routine */
+/*******************/
+static int
+isdn_divert_close(struct inode *ino, struct file *filep)
+{
+ struct divert_info *inf;
+ unsigned long flags;
+
+ spin_lock_irqsave( &divert_info_lock, flags );
+ if_used--;
+ inf = *((struct divert_info **) filep->private_data);
+ while (inf) {
+ inf->usage_cnt--;
+ inf = inf->next;
+ }
+ if (if_used <= 0)
+ while (divert_info_head) {
+ inf = divert_info_head;
+ divert_info_head = divert_info_head->next;
+ kfree(inf);
+ }
+ spin_unlock_irqrestore( &divert_info_lock, flags );
+ return (0);
+} /* isdn_divert_close */
+
+/*********/
+/* IOCTL */
+/*********/
+static int
+isdn_divert_ioctl(struct inode *inode, struct file *file,
+ uint cmd, ulong arg)
+{
+ divert_ioctl dioctl;
+ int i;
+ unsigned long flags;
+ divert_rule *rulep;
+ char *cp;
+
+ if (copy_from_user(&dioctl, (void __user *) arg, sizeof(dioctl)))
+ return -EFAULT;
+
+ switch (cmd) {
+ case IIOCGETVER:
+ dioctl.drv_version = DIVERT_IIOC_VERSION; /* set version */
+ break;
+
+ case IIOCGETDRV:
+ if ((dioctl.getid.drvid = divert_if.name_to_drv(dioctl.getid.drvnam)) < 0)
+ return (-EINVAL);
+ break;
+
+ case IIOCGETNAM:
+ cp = divert_if.drv_to_name(dioctl.getid.drvid);
+ if (!cp)
+ return (-EINVAL);
+ if (!*cp)
+ return (-EINVAL);
+ strcpy(dioctl.getid.drvnam, cp);
+ break;
+
+ case IIOCGETRULE:
+ if (!(rulep = getruleptr(dioctl.getsetrule.ruleidx)))
+ return (-EINVAL);
+ dioctl.getsetrule.rule = *rulep; /* copy data */
+ break;
+
+ case IIOCMODRULE:
+ if (!(rulep = getruleptr(dioctl.getsetrule.ruleidx)))
+ return (-EINVAL);
+ spin_lock_irqsave(&divert_lock, flags);
+ *rulep = dioctl.getsetrule.rule; /* copy data */
+ spin_unlock_irqrestore(&divert_lock, flags);
+ return (0); /* no copy required */
+ break;
+
+ case IIOCINSRULE:
+ return (insertrule(dioctl.getsetrule.ruleidx, &dioctl.getsetrule.rule));
+ break;
+
+ case IIOCDELRULE:
+ return (deleterule(dioctl.getsetrule.ruleidx));
+ break;
+
+ case IIOCDODFACT:
+ return (deflect_extern_action(dioctl.fwd_ctrl.subcmd,
+ dioctl.fwd_ctrl.callid,
+ dioctl.fwd_ctrl.to_nr));
+
+ case IIOCDOCFACT:
+ case IIOCDOCFDIS:
+ case IIOCDOCFINT:
+ if (!divert_if.drv_to_name(dioctl.cf_ctrl.drvid))
+ return (-EINVAL); /* invalid driver */
+ if ((i = cf_command(dioctl.cf_ctrl.drvid,
+ (cmd == IIOCDOCFACT) ? 1 : (cmd == IIOCDOCFDIS) ? 0 : 2,
+ dioctl.cf_ctrl.cfproc,
+ dioctl.cf_ctrl.msn,
+ dioctl.cf_ctrl.service,
+ dioctl.cf_ctrl.fwd_nr,
+ &dioctl.cf_ctrl.procid)))
+ return (i);
+ break;
+
+ default:
+ return (-EINVAL);
+ } /* switch cmd */
+ return copy_to_user((void __user *)arg, &dioctl, sizeof(dioctl)) ? -EFAULT : 0;
+} /* isdn_divert_ioctl */
+
+static const struct file_operations isdn_fops =
+{
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = isdn_divert_read,
+ .write = isdn_divert_write,
+ .poll = isdn_divert_poll,
+ .ioctl = isdn_divert_ioctl,
+ .open = isdn_divert_open,
+ .release = isdn_divert_close,
+};
+
+/****************************/
+/* isdn subdir in /proc/net */
+/****************************/
+static struct proc_dir_entry *isdn_proc_entry = NULL;
+static struct proc_dir_entry *isdn_divert_entry = NULL;
+#endif /* CONFIG_PROC_FS */
+
+/***************************************************************************/
+/* divert_dev_init must be called before the proc filesystem may be used */
+/***************************************************************************/
+int
+divert_dev_init(void)
+{
+
+ init_waitqueue_head(&rd_queue);
+
+#ifdef CONFIG_PROC_FS
+ isdn_proc_entry = proc_mkdir("isdn", init_net.proc_net);
+ if (!isdn_proc_entry)
+ return (-1);
+ isdn_divert_entry = proc_create("divert", S_IFREG | S_IRUGO,
+ isdn_proc_entry, &isdn_fops);
+ if (!isdn_divert_entry) {
+ remove_proc_entry("isdn", init_net.proc_net);
+ return (-1);
+ }
+#endif /* CONFIG_PROC_FS */
+
+ return (0);
+} /* divert_dev_init */
+
+/***************************************************************************/
+/* divert_dev_deinit must be called before leaving isdn when included as */
+/* a module. */
+/***************************************************************************/
+int
+divert_dev_deinit(void)
+{
+
+#ifdef CONFIG_PROC_FS
+ remove_proc_entry("divert", isdn_proc_entry);
+ remove_proc_entry("isdn", init_net.proc_net);
+#endif /* CONFIG_PROC_FS */
+
+ return (0);
+} /* divert_dev_deinit */
diff --git a/drivers/isdn/divert/isdn_divert.c b/drivers/isdn/divert/isdn_divert.c
new file mode 100644
index 0000000..7d97d54
--- /dev/null
+++ b/drivers/isdn/divert/isdn_divert.c
@@ -0,0 +1,862 @@
+/* $Id: isdn_divert.c,v 1.6.6.3 2001/09/23 22:24:36 kai Exp $
+ *
+ * DSS1 main diversion supplementary handling for i4l.
+ *
+ * Copyright 1999 by Werner Cornelius (werner@isdn4linux.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/proc_fs.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+
+#include "isdn_divert.h"
+
+/**********************************/
+/* structure keeping calling info */
+/**********************************/
+struct call_struc
+ { isdn_ctrl ics; /* delivered setup + driver parameters */
+ ulong divert_id; /* Id delivered to user */
+ unsigned char akt_state; /* actual state */
+ char deflect_dest[35]; /* deflection destination */
+ struct timer_list timer; /* timer control structure */
+ char info[90]; /* device info output */
+ struct call_struc *next; /* pointer to next entry */
+ struct call_struc *prev;
+ };
+
+
+/********************************************/
+/* structure keeping deflection table entry */
+/********************************************/
+struct deflect_struc
+ { struct deflect_struc *next,*prev;
+ divert_rule rule; /* used rule */
+ };
+
+
+/*****************************************/
+/* variables for main diversion services */
+/*****************************************/
+/* diversion/deflection processes */
+static struct call_struc *divert_head = NULL; /* head of remembered entrys */
+static ulong next_id = 1; /* next info id */
+static struct deflect_struc *table_head = NULL;
+static struct deflect_struc *table_tail = NULL;
+static unsigned char extern_wait_max = 4; /* maximum wait in s for external process */
+
+DEFINE_SPINLOCK(divert_lock);
+
+/***************************/
+/* timer callback function */
+/***************************/
+static void deflect_timer_expire(ulong arg)
+{
+ unsigned long flags;
+ struct call_struc *cs = (struct call_struc *) arg;
+
+ spin_lock_irqsave(&divert_lock, flags);
+ del_timer(&cs->timer); /* delete active timer */
+ spin_unlock_irqrestore(&divert_lock, flags);
+
+ switch(cs->akt_state)
+ { case DEFLECT_PROCEED:
+ cs->ics.command = ISDN_CMD_HANGUP; /* cancel action */
+ divert_if.ll_cmd(&cs->ics);
+ spin_lock_irqsave(&divert_lock, flags);
+ cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
+ cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
+ add_timer(&cs->timer);
+ spin_unlock_irqrestore(&divert_lock, flags);
+ break;
+
+ case DEFLECT_ALERT:
+ cs->ics.command = ISDN_CMD_REDIR; /* protocol */
+ strcpy(cs->ics.parm.setup.phone,cs->deflect_dest);
+ strcpy(cs->ics.parm.setup.eazmsn,"Testtext delayed");
+ divert_if.ll_cmd(&cs->ics);
+ spin_lock_irqsave(&divert_lock, flags);
+ cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
+ cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
+ add_timer(&cs->timer);
+ spin_unlock_irqrestore(&divert_lock, flags);
+ break;
+
+ case DEFLECT_AUTODEL:
+ default:
+ spin_lock_irqsave(&divert_lock, flags);
+ if (cs->prev)
+ cs->prev->next = cs->next; /* forward link */
+ else
+ divert_head = cs->next;
+ if (cs->next)
+ cs->next->prev = cs->prev; /* back link */
+ spin_unlock_irqrestore(&divert_lock, flags);
+ kfree(cs);
+ return;
+
+ } /* switch */
+} /* deflect_timer_func */
+
+
+/*****************************************/
+/* handle call forwarding de/activations */
+/* 0 = deact, 1 = act, 2 = interrogate */
+/*****************************************/
+int cf_command(int drvid, int mode,
+ u_char proc, char *msn,
+ u_char service, char *fwd_nr, ulong *procid)
+{ unsigned long flags;
+ int retval,msnlen;
+ int fwd_len;
+ char *p,*ielenp,tmp[60];
+ struct call_struc *cs;
+
+ if (strchr(msn,'.')) return(-EINVAL); /* subaddress not allowed in msn */
+ if ((proc & 0x7F) > 2) return(-EINVAL);
+ proc &= 3;
+ p = tmp;
+ *p++ = 0x30; /* enumeration */
+ ielenp = p++; /* remember total length position */
+ *p++ = 0xa; /* proc tag */
+ *p++ = 1; /* length */
+ *p++ = proc & 0x7F; /* procedure to de/activate/interrogate */
+ *p++ = 0xa; /* service tag */
+ *p++ = 1; /* length */
+ *p++ = service; /* service to handle */
+
+ if (mode == 1)
+ { if (!*fwd_nr) return(-EINVAL); /* destination missing */
+ if (strchr(fwd_nr,'.')) return(-EINVAL); /* subaddress not allowed */
+ fwd_len = strlen(fwd_nr);
+ *p++ = 0x30; /* number enumeration */
+ *p++ = fwd_len + 2; /* complete forward to len */
+ *p++ = 0x80; /* fwd to nr */
+ *p++ = fwd_len; /* length of number */
+ strcpy(p,fwd_nr); /* copy number */
+ p += fwd_len; /* pointer beyond fwd */
+ } /* activate */
+
+ msnlen = strlen(msn);
+ *p++ = 0x80; /* msn number */
+ if (msnlen > 1)
+ { *p++ = msnlen; /* length */
+ strcpy(p,msn);
+ p += msnlen;
+ }
+ else *p++ = 0;
+
+ *ielenp = p - ielenp - 1; /* set total IE length */
+
+ /* allocate mem for information struct */
+ if (!(cs = kmalloc(sizeof(struct call_struc), GFP_ATOMIC)))
+ return(-ENOMEM); /* no memory */
+ init_timer(&cs->timer);
+ cs->info[0] = '\0';
+ cs->timer.function = deflect_timer_expire;
+ cs->timer.data = (ulong) cs; /* pointer to own structure */
+ cs->ics.driver = drvid;
+ cs->ics.command = ISDN_CMD_PROT_IO; /* protocol specific io */
+ cs->ics.arg = DSS1_CMD_INVOKE; /* invoke supplementary service */
+ cs->ics.parm.dss1_io.proc = (mode == 1) ? 7: (mode == 2) ? 11:8; /* operation */
+ cs->ics.parm.dss1_io.timeout = 4000; /* from ETS 300 207-1 */
+ cs->ics.parm.dss1_io.datalen = p - tmp; /* total len */
+ cs->ics.parm.dss1_io.data = tmp; /* start of buffer */
+
+ spin_lock_irqsave(&divert_lock, flags);
+ cs->ics.parm.dss1_io.ll_id = next_id++; /* id for callback */
+ spin_unlock_irqrestore(&divert_lock, flags);
+ *procid = cs->ics.parm.dss1_io.ll_id;
+
+ sprintf(cs->info,"%d 0x%lx %s%s 0 %s %02x %d%s%s\n",
+ (!mode ) ? DIVERT_DEACTIVATE : (mode == 1) ? DIVERT_ACTIVATE : DIVERT_REPORT,
+ cs->ics.parm.dss1_io.ll_id,
+ (mode != 2) ? "" : "0 ",
+ divert_if.drv_to_name(cs->ics.driver),
+ msn,
+ service & 0xFF,
+ proc,
+ (mode != 1) ? "" : " 0 ",
+ (mode != 1) ? "" : fwd_nr);
+
+ retval = divert_if.ll_cmd(&cs->ics); /* excute command */
+
+ if (!retval)
+ { cs->prev = NULL;
+ spin_lock_irqsave(&divert_lock, flags);
+ cs->next = divert_head;
+ divert_head = cs;
+ spin_unlock_irqrestore(&divert_lock, flags);
+ }
+ else
+ kfree(cs);
+ return(retval);
+} /* cf_command */
+
+
+/****************************************/
+/* handle a external deflection command */
+/****************************************/
+int deflect_extern_action(u_char cmd, ulong callid, char *to_nr)
+{ struct call_struc *cs;
+ isdn_ctrl ic;
+ unsigned long flags;
+ int i;
+
+ if ((cmd & 0x7F) > 2) return(-EINVAL); /* invalid command */
+ cs = divert_head; /* start of parameter list */
+ while (cs)
+ { if (cs->divert_id == callid) break; /* found */
+ cs = cs->next;
+ } /* search entry */
+ if (!cs) return(-EINVAL); /* invalid callid */
+
+ ic.driver = cs->ics.driver;
+ ic.arg = cs->ics.arg;
+ i = -EINVAL;
+ if (cs->akt_state == DEFLECT_AUTODEL) return(i); /* no valid call */
+ switch (cmd & 0x7F)
+ { case 0: /* hangup */
+ del_timer(&cs->timer);
+ ic.command = ISDN_CMD_HANGUP;
+ i = divert_if.ll_cmd(&ic);
+ spin_lock_irqsave(&divert_lock, flags);
+ cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
+ cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
+ add_timer(&cs->timer);
+ spin_unlock_irqrestore(&divert_lock, flags);
+ break;
+
+ case 1: /* alert */
+ if (cs->akt_state == DEFLECT_ALERT) return(0);
+ cmd &= 0x7F; /* never wait */
+ del_timer(&cs->timer);
+ ic.command = ISDN_CMD_ALERT;
+ if ((i = divert_if.ll_cmd(&ic)))
+ {
+ spin_lock_irqsave(&divert_lock, flags);
+ cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
+ cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
+ add_timer(&cs->timer);
+ spin_unlock_irqrestore(&divert_lock, flags);
+ }
+ else
+ cs->akt_state = DEFLECT_ALERT;
+ break;
+
+ case 2: /* redir */
+ del_timer(&cs->timer);
+ strcpy(cs->ics.parm.setup.phone, to_nr);
+ strcpy(cs->ics.parm.setup.eazmsn, "Testtext manual");
+ ic.command = ISDN_CMD_REDIR;
+ if ((i = divert_if.ll_cmd(&ic)))
+ {
+ spin_lock_irqsave(&divert_lock, flags);
+ cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
+ cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
+ add_timer(&cs->timer);
+ spin_unlock_irqrestore(&divert_lock, flags);
+ }
+ else
+ cs->akt_state = DEFLECT_ALERT;
+ break;
+
+ } /* switch */
+ return(i);
+} /* deflect_extern_action */
+
+/********************************/
+/* insert a new rule before idx */
+/********************************/
+int insertrule(int idx, divert_rule *newrule)
+{ struct deflect_struc *ds,*ds1=NULL;
+ unsigned long flags;
+
+ if (!(ds = kmalloc(sizeof(struct deflect_struc),
+ GFP_KERNEL)))
+ return(-ENOMEM); /* no memory */
+
+ ds->rule = *newrule; /* set rule */
+
+ spin_lock_irqsave(&divert_lock, flags);
+
+ if (idx >= 0)
+ { ds1 = table_head;
+ while ((ds1) && (idx > 0))
+ { idx--;
+ ds1 = ds1->next;
+ }
+ if (!ds1) idx = -1;
+ }
+
+ if (idx < 0)
+ { ds->prev = table_tail; /* previous entry */
+ ds->next = NULL; /* end of chain */
+ if (ds->prev)
+ ds->prev->next = ds; /* last forward */
+ else
+ table_head = ds; /* is first entry */
+ table_tail = ds; /* end of queue */
+ }
+ else
+ { ds->next = ds1; /* next entry */
+ ds->prev = ds1->prev; /* prev entry */
+ ds1->prev = ds; /* backward chain old element */
+ if (!ds->prev)
+ table_head = ds; /* first element */
+ }
+
+ spin_unlock_irqrestore(&divert_lock, flags);
+ return(0);
+} /* insertrule */
+
+/***********************************/
+/* delete the rule at position idx */
+/***********************************/
+int deleterule(int idx)
+{ struct deflect_struc *ds,*ds1;
+ unsigned long flags;
+
+ if (idx < 0)
+ { spin_lock_irqsave(&divert_lock, flags);
+ ds = table_head;
+ table_head = NULL;
+ table_tail = NULL;
+ spin_unlock_irqrestore(&divert_lock, flags);
+ while (ds)
+ { ds1 = ds;
+ ds = ds->next;
+ kfree(ds1);
+ }
+ return(0);
+ }
+
+ spin_lock_irqsave(&divert_lock, flags);
+ ds = table_head;
+
+ while ((ds) && (idx > 0))
+ { idx--;
+ ds = ds->next;
+ }
+
+ if (!ds)
+ {
+ spin_unlock_irqrestore(&divert_lock, flags);
+ return(-EINVAL);
+ }
+
+ if (ds->next)
+ ds->next->prev = ds->prev; /* backward chain */
+ else
+ table_tail = ds->prev; /* end of chain */
+
+ if (ds->prev)
+ ds->prev->next = ds->next; /* forward chain */
+ else
+ table_head = ds->next; /* start of chain */
+
+ spin_unlock_irqrestore(&divert_lock, flags);
+ kfree(ds);
+ return(0);
+} /* deleterule */
+
+/*******************************************/
+/* get a pointer to a specific rule number */
+/*******************************************/
+divert_rule *getruleptr(int idx)
+{ struct deflect_struc *ds = table_head;
+
+ if (idx < 0) return(NULL);
+ while ((ds) && (idx >= 0))
+ { if (!(idx--))
+ { return(&ds->rule);
+ break;
+ }
+ ds = ds->next;
+ }
+ return(NULL);
+} /* getruleptr */
+
+/*************************************************/
+/* called from common module on an incoming call */
+/*************************************************/
+static int isdn_divert_icall(isdn_ctrl *ic)
+{ int retval = 0;
+ unsigned long flags;
+ struct call_struc *cs = NULL;
+ struct deflect_struc *dv;
+ char *p,*p1;
+ u_char accept;
+
+ /* first check the internal deflection table */
+ for (dv = table_head; dv ; dv = dv->next )
+ { /* scan table */
+ if (((dv->rule.callopt == 1) && (ic->command == ISDN_STAT_ICALLW)) ||
+ ((dv->rule.callopt == 2) && (ic->command == ISDN_STAT_ICALL)))
+ continue; /* call option check */
+ if (!(dv->rule.drvid & (1L << ic->driver)))
+ continue; /* driver not matching */
+ if ((dv->rule.si1) && (dv->rule.si1 != ic->parm.setup.si1))
+ continue; /* si1 not matching */
+ if ((dv->rule.si2) && (dv->rule.si2 != ic->parm.setup.si2))
+ continue; /* si2 not matching */
+
+ p = dv->rule.my_msn;
+ p1 = ic->parm.setup.eazmsn;
+ accept = 0;
+ while (*p)
+ { /* complete compare */
+ if (*p == '-')
+ { accept = 1; /* call accepted */
+ break;
+ }
+ if (*p++ != *p1++)
+ break; /* not accepted */
+ if ((!*p) && (!*p1))
+ accept = 1;
+ } /* complete compare */
+ if (!accept) continue; /* not accepted */
+
+ if ((strcmp(dv->rule.caller,"0")) || (ic->parm.setup.phone[0]))
+ { p = dv->rule.caller;
+ p1 = ic->parm.setup.phone;
+ accept = 0;
+ while (*p)
+ { /* complete compare */
+ if (*p == '-')
+ { accept = 1; /* call accepted */
+ break;
+ }
+ if (*p++ != *p1++)
+ break; /* not accepted */
+ if ((!*p) && (!*p1))
+ accept = 1;
+ } /* complete compare */
+ if (!accept) continue; /* not accepted */
+ }
+
+ switch (dv->rule.action)
+ { case DEFLECT_IGNORE:
+ return(0);
+ break;
+
+ case DEFLECT_ALERT:
+ case DEFLECT_PROCEED:
+ case DEFLECT_REPORT:
+ case DEFLECT_REJECT:
+ if (dv->rule.action == DEFLECT_PROCEED)
+ if ((!if_used) || ((!extern_wait_max) && (!dv->rule.waittime)))
+ return(0); /* no external deflection needed */
+ if (!(cs = kmalloc(sizeof(struct call_struc), GFP_ATOMIC)))
+ return(0); /* no memory */
+ init_timer(&cs->timer);
+ cs->info[0] = '\0';
+ cs->timer.function = deflect_timer_expire;
+ cs->timer.data = (ulong) cs; /* pointer to own structure */
+
+ cs->ics = *ic; /* copy incoming data */
+ if (!cs->ics.parm.setup.phone[0]) strcpy(cs->ics.parm.setup.phone,"0");
+ if (!cs->ics.parm.setup.eazmsn[0]) strcpy(cs->ics.parm.setup.eazmsn,"0");
+ cs->ics.parm.setup.screen = dv->rule.screen;
+ if (dv->rule.waittime)
+ cs->timer.expires = jiffies + (HZ * dv->rule.waittime);
+ else
+ if (dv->rule.action == DEFLECT_PROCEED)
+ cs->timer.expires = jiffies + (HZ * extern_wait_max);
+ else
+ cs->timer.expires = 0;
+ cs->akt_state = dv->rule.action;
+ spin_lock_irqsave(&divert_lock, flags);
+ cs->divert_id = next_id++; /* new sequence number */
+ spin_unlock_irqrestore(&divert_lock, flags);
+ cs->prev = NULL;
+ if (cs->akt_state == DEFLECT_ALERT)
+ { strcpy(cs->deflect_dest,dv->rule.to_nr);
+ if (!cs->timer.expires)
+ { strcpy(ic->parm.setup.eazmsn,"Testtext direct");
+ ic->parm.setup.screen = dv->rule.screen;
+ strcpy(ic->parm.setup.phone,dv->rule.to_nr);
+ cs->akt_state = DEFLECT_AUTODEL; /* delete after timeout */
+ cs->timer.expires = jiffies + (HZ * AUTODEL_TIME);
+ retval = 5;
+ }
+ else
+ retval = 1; /* alerting */
+ }
+ else
+ { cs->deflect_dest[0] = '\0';
+ retval = 4; /* only proceed */
+ }
+ sprintf(cs->info,"%d 0x%lx %s %s %s %s 0x%x 0x%x %d %d %s\n",
+ cs->akt_state,
+ cs->divert_id,
+ divert_if.drv_to_name(cs->ics.driver),
+ (ic->command == ISDN_STAT_ICALLW) ? "1":"0",
+ cs->ics.parm.setup.phone,
+ cs->ics.parm.setup.eazmsn,
+ cs->ics.parm.setup.si1,
+ cs->ics.parm.setup.si2,
+ cs->ics.parm.setup.screen,
+ dv->rule.waittime,
+ cs->deflect_dest);
+ if ((dv->rule.action == DEFLECT_REPORT) ||
+ (dv->rule.action == DEFLECT_REJECT))
+ { put_info_buffer(cs->info);
+ kfree(cs); /* remove */
+ return((dv->rule.action == DEFLECT_REPORT) ? 0:2); /* nothing to do */
+ }
+ break;
+
+ default:
+ return(0); /* ignore call */
+ break;
+ } /* switch action */
+ break;
+ } /* scan_table */
+
+ if (cs)
+ { cs->prev = NULL;
+ spin_lock_irqsave(&divert_lock, flags);
+ cs->next = divert_head;
+ divert_head = cs;
+ if (cs->timer.expires) add_timer(&cs->timer);
+ spin_unlock_irqrestore(&divert_lock, flags);
+
+ put_info_buffer(cs->info);
+ return(retval);
+ }
+ else
+ return(0);
+} /* isdn_divert_icall */
+
+
+void deleteprocs(void)
+{ struct call_struc *cs, *cs1;
+ unsigned long flags;
+
+ spin_lock_irqsave(&divert_lock, flags);
+ cs = divert_head;
+ divert_head = NULL;
+ while (cs)
+ { del_timer(&cs->timer);
+ cs1 = cs;
+ cs = cs->next;
+ kfree(cs1);
+ }
+ spin_unlock_irqrestore(&divert_lock, flags);
+} /* deleteprocs */
+
+/****************************************************/
+/* put a address including address type into buffer */
+/****************************************************/
+static int put_address(char *st, u_char *p, int len)
+{ u_char retval = 0;
+ u_char adr_typ = 0; /* network standard */
+
+ if (len < 2) return(retval);
+ if (*p == 0xA1)
+ { retval = *(++p) + 2; /* total length */
+ if (retval > len) return(0); /* too short */
+ len = retval - 2; /* remaining length */
+ if (len < 3) return(0);
+ if ((*(++p) != 0x0A) || (*(++p) != 1)) return(0);
+ adr_typ = *(++p);
+ len -= 3;
+ p++;
+ if (len < 2) return(0);
+ if (*p++ != 0x12) return(0);
+ if (*p > len) return(0); /* check number length */
+ len = *p++;
+ }
+ else
+ if (*p == 0x80)
+ { retval = *(++p) + 2; /* total length */
+ if (retval > len) return(0);
+ len = retval - 2;
+ p++;
+ }
+ else
+ return(0); /* invalid address information */
+
+ sprintf(st,"%d ",adr_typ);
+ st += strlen(st);
+ if (!len)
+ *st++ = '-';
+ else
+ while (len--)
+ *st++ = *p++;
+ *st = '\0';
+ return(retval);
+} /* put_address */
+
+/*************************************/
+/* report a successful interrogation */
+/*************************************/
+static int interrogate_success(isdn_ctrl *ic, struct call_struc *cs)
+{ char *src = ic->parm.dss1_io.data;
+ int restlen = ic->parm.dss1_io.datalen;
+ int cnt = 1;
+ u_char n,n1;
+ char st[90], *p, *stp;
+
+ if (restlen < 2) return(-100); /* frame too short */
+ if (*src++ != 0x30) return(-101);
+ if ((n = *src++) > 0x81) return(-102); /* invalid length field */
+ restlen -= 2; /* remaining bytes */
+ if (n == 0x80)
+ { if (restlen < 2) return(-103);
+ if ((*(src+restlen-1)) || (*(src+restlen-2))) return(-104);
+ restlen -= 2;
+ }
+ else
+ if ( n == 0x81)
+ { n = *src++;
+ restlen--;
+ if (n > restlen) return(-105);
+ restlen = n;
+ }
+ else
+ if (n > restlen) return(-106);
+ else
+ restlen = n; /* standard format */
+ if (restlen < 3) return(-107); /* no procedure */
+ if ((*src++ != 2) || (*src++ != 1) || (*src++ != 0x0B)) return(-108);
+ restlen -= 3;
+ if (restlen < 2) return(-109); /* list missing */
+ if (*src == 0x31)
+ { src++;
+ if ((n = *src++) > 0x81) return(-110); /* invalid length field */
+ restlen -= 2; /* remaining bytes */
+ if (n == 0x80)
+ { if (restlen < 2) return(-111);
+ if ((*(src+restlen-1)) || (*(src+restlen-2))) return(-112);
+ restlen -= 2;
+ }
+ else
+ if ( n == 0x81)
+ { n = *src++;
+ restlen--;
+ if (n > restlen) return(-113);
+ restlen = n;
+ }
+ else
+ if (n > restlen) return(-114);
+ else
+ restlen = n; /* standard format */
+ } /* result list header */
+
+ while (restlen >= 2)
+ { stp = st;
+ sprintf(stp,"%d 0x%lx %d %s ",DIVERT_REPORT, ic->parm.dss1_io.ll_id,
+ cnt++,divert_if.drv_to_name(ic->driver));
+ stp += strlen(stp);
+ if (*src++ != 0x30) return(-115); /* invalid enum */
+ n = *src++;
+ restlen -= 2;
+ if (n > restlen) return(-116); /* enum length wrong */
+ restlen -= n;
+ p = src; /* one entry */
+ src += n;
+ if (!(n1 = put_address(stp,p,n & 0xFF))) continue;
+ stp += strlen(stp);
+ p += n1;
+ n -= n1;
+ if (n < 6) continue; /* no service and proc */
+ if ((*p++ != 0x0A) || (*p++ != 1)) continue;
+ sprintf(stp," 0x%02x ",(*p++) & 0xFF);
+ stp += strlen(stp);
+ if ((*p++ != 0x0A) || (*p++ != 1)) continue;
+ sprintf(stp,"%d ",(*p++) & 0xFF);
+ stp += strlen(stp);
+ n -= 6;
+ if (n > 2)
+ { if (*p++ != 0x30) continue;
+ if (*p > (n-2)) continue;
+ n = *p++;
+ if (!(n1 = put_address(stp,p,n & 0xFF))) continue;
+ stp += strlen(stp);
+ }
+ sprintf(stp,"\n");
+ put_info_buffer(st);
+ } /* while restlen */
+ if (restlen) return(-117);
+ return(0);
+} /* interrogate_success */
+
+/*********************************************/
+/* callback for protocol specific extensions */
+/*********************************************/
+static int prot_stat_callback(isdn_ctrl *ic)
+{ struct call_struc *cs, *cs1;
+ int i;
+ unsigned long flags;
+
+ cs = divert_head; /* start of list */
+ cs1 = NULL;
+ while (cs)
+ { if (ic->driver == cs->ics.driver)
+ { switch (cs->ics.arg)
+ { case DSS1_CMD_INVOKE:
+ if ((cs->ics.parm.dss1_io.ll_id == ic->parm.dss1_io.ll_id) &&
+ (cs->ics.parm.dss1_io.hl_id == ic->parm.dss1_io.hl_id))
+ { switch (ic->arg)
+ { case DSS1_STAT_INVOKE_ERR:
+ sprintf(cs->info,"128 0x%lx 0x%x\n",
+ ic->parm.dss1_io.ll_id,
+ ic->parm.dss1_io.timeout);
+ put_info_buffer(cs->info);
+ break;
+
+ case DSS1_STAT_INVOKE_RES:
+ switch (cs->ics.parm.dss1_io.proc)
+ { case 7:
+ case 8:
+ put_info_buffer(cs->info);
+ break;
+
+ case 11:
+ i = interrogate_success(ic,cs);
+ if (i)
+ sprintf(cs->info,"%d 0x%lx %d\n",DIVERT_REPORT,
+ ic->parm.dss1_io.ll_id,i);
+ put_info_buffer(cs->info);
+ break;
+
+ default:
+ printk(KERN_WARNING "dss1_divert: unknown proc %d\n",cs->ics.parm.dss1_io.proc);
+ break;
+ }
+
+
+ break;
+
+ default:
+ printk(KERN_WARNING "dss1_divert unknown invoke answer %lx\n",ic->arg);
+ break;
+ }
+ cs1 = cs; /* remember structure */
+ cs = NULL;
+ continue; /* abort search */
+ } /* id found */
+ break;
+
+ case DSS1_CMD_INVOKE_ABORT:
+ printk(KERN_WARNING "dss1_divert unhandled invoke abort\n");
+ break;
+
+ default:
+ printk(KERN_WARNING "dss1_divert unknown cmd 0x%lx\n",cs->ics.arg);
+ break;
+ } /* switch ics.arg */
+ cs = cs->next;
+ } /* driver ok */
+ }
+
+ if (!cs1)
+ { printk(KERN_WARNING "dss1_divert unhandled process\n");
+ return(0);
+ }
+
+ if (cs1->ics.driver == -1)
+ {
+ spin_lock_irqsave(&divert_lock, flags);
+ del_timer(&cs1->timer);
+ if (cs1->prev)
+ cs1->prev->next = cs1->next; /* forward link */
+ else
+ divert_head = cs1->next;
+ if (cs1->next)
+ cs1->next->prev = cs1->prev; /* back link */
+ spin_unlock_irqrestore(&divert_lock, flags);
+ kfree(cs1);
+ }
+
+ return(0);
+} /* prot_stat_callback */
+
+
+/***************************/
+/* status callback from HL */
+/***************************/
+static int isdn_divert_stat_callback(isdn_ctrl *ic)
+{ struct call_struc *cs, *cs1;
+ unsigned long flags;
+ int retval;
+
+ retval = -1;
+ cs = divert_head; /* start of list */
+ while (cs)
+ { if ((ic->driver == cs->ics.driver) && (ic->arg == cs->ics.arg))
+ { switch (ic->command)
+ { case ISDN_STAT_DHUP:
+ sprintf(cs->info,"129 0x%lx\n",cs->divert_id);
+ del_timer(&cs->timer);
+ cs->ics.driver = -1;
+ break;
+
+ case ISDN_STAT_CAUSE:
+ sprintf(cs->info,"130 0x%lx %s\n",cs->divert_id,ic->parm.num);
+ break;
+
+ case ISDN_STAT_REDIR:
+ sprintf(cs->info,"131 0x%lx\n",cs->divert_id);
+ del_timer(&cs->timer);
+ cs->ics.driver = -1;
+ break;
+
+ default:
+ sprintf(cs->info,"999 0x%lx 0x%x\n",cs->divert_id,(int)(ic->command));
+ break;
+ }
+ put_info_buffer(cs->info);
+ retval = 0;
+ }
+ cs1 = cs;
+ cs = cs->next;
+ if (cs1->ics.driver == -1)
+ {
+ spin_lock_irqsave(&divert_lock, flags);
+ if (cs1->prev)
+ cs1->prev->next = cs1->next; /* forward link */
+ else
+ divert_head = cs1->next;
+ if (cs1->next)
+ cs1->next->prev = cs1->prev; /* back link */
+ spin_unlock_irqrestore(&divert_lock, flags);
+ kfree(cs1);
+ }
+ }
+ return(retval); /* not found */
+} /* isdn_divert_stat_callback */
+
+
+/********************/
+/* callback from ll */
+/********************/
+int ll_callback(isdn_ctrl *ic)
+{
+ switch (ic->command)
+ { case ISDN_STAT_ICALL:
+ case ISDN_STAT_ICALLW:
+ return(isdn_divert_icall(ic));
+ break;
+
+ case ISDN_STAT_PROT:
+ if ((ic->arg & 0xFF) == ISDN_PTYPE_EURO)
+ { if (ic->arg != DSS1_STAT_INVOKE_BRD)
+ return(prot_stat_callback(ic));
+ else
+ return(0); /* DSS1 invoke broadcast */
+ }
+ else
+ return(-1); /* protocol not euro */
+
+ default:
+ return(isdn_divert_stat_callback(ic));
+ }
+} /* ll_callback */
+
diff --git a/drivers/isdn/divert/isdn_divert.h b/drivers/isdn/divert/isdn_divert.h
new file mode 100644
index 0000000..19439a6
--- /dev/null
+++ b/drivers/isdn/divert/isdn_divert.h
@@ -0,0 +1,132 @@
+/* $Id: isdn_divert.h,v 1.5.6.1 2001/09/23 22:24:36 kai Exp $
+ *
+ * Header for the diversion supplementary ioctl interface.
+ *
+ * Copyright 1998 by Werner Cornelius (werner@ikt.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+/******************************************/
+/* IOCTL codes for interface to user prog */
+/******************************************/
+#define DIVERT_IIOC_VERSION 0x01 /* actual version */
+#define IIOCGETVER _IO('I', 1) /* get version of interface */
+#define IIOCGETDRV _IO('I', 2) /* get driver number */
+#define IIOCGETNAM _IO('I', 3) /* get driver name */
+#define IIOCGETRULE _IO('I', 4) /* read one rule */
+#define IIOCMODRULE _IO('I', 5) /* modify/replace a rule */
+#define IIOCINSRULE _IO('I', 6) /* insert/append one rule */
+#define IIOCDELRULE _IO('I', 7) /* delete a rule */
+#define IIOCDODFACT _IO('I', 8) /* hangup/reject/alert/immediately deflect a call */
+#define IIOCDOCFACT _IO('I', 9) /* activate control forwarding in PBX */
+#define IIOCDOCFDIS _IO('I',10) /* deactivate control forwarding in PBX */
+#define IIOCDOCFINT _IO('I',11) /* interrogate control forwarding in PBX */
+
+/*************************************/
+/* states reported through interface */
+/*************************************/
+#define DEFLECT_IGNORE 0 /* ignore incoming call */
+#define DEFLECT_REPORT 1 /* only report */
+#define DEFLECT_PROCEED 2 /* deflect when externally triggered */
+#define DEFLECT_ALERT 3 /* alert and deflect after delay */
+#define DEFLECT_REJECT 4 /* reject immediately */
+#define DIVERT_ACTIVATE 5 /* diversion activate */
+#define DIVERT_DEACTIVATE 6 /* diversion deactivate */
+#define DIVERT_REPORT 7 /* interrogation result */
+#define DEFLECT_AUTODEL 255 /* only for internal use */
+
+#define DEFLECT_ALL_IDS 0xFFFFFFFF /* all drivers selected */
+
+typedef struct
+ { ulong drvid; /* driver ids, bit mapped */
+ char my_msn[35]; /* desired msn, subaddr allowed */
+ char caller[35]; /* caller id, partial string with * + subaddr allowed */
+ char to_nr[35]; /* deflected to number incl. subaddress */
+ u_char si1,si2; /* service indicators, si1=bitmask, si1+2 0 = all */
+ u_char screen; /* screening: 0 = no info, 1 = info, 2 = nfo with nr */
+ u_char callopt; /* option for call handling:
+ 0 = all calls
+ 1 = only non waiting calls
+ 2 = only waiting calls */
+ u_char action; /* desired action:
+ 0 = don't report call -> ignore
+ 1 = report call, do not allow/proceed for deflection
+ 2 = report call, send proceed, wait max waittime secs
+ 3 = report call, alert and deflect after waittime
+ 4 = report call, reject immediately
+ actions 1-2 only take place if interface is opened
+ */
+ u_char waittime; /* maximum wait time for proceeding */
+ } divert_rule;
+
+typedef union
+ { int drv_version; /* return of driver version */
+ struct
+ { int drvid; /* id of driver */
+ char drvnam[30]; /* name of driver */
+ } getid;
+ struct
+ { int ruleidx; /* index of rule */
+ divert_rule rule; /* rule parms */
+ } getsetrule;
+ struct
+ { u_char subcmd; /* 0 = hangup/reject,
+ 1 = alert,
+ 2 = deflect */
+ ulong callid; /* id of call delivered by ascii output */
+ char to_nr[35]; /* destination when deflect,
+ else uus1 string (maxlen 31),
+ data from rule used if empty */
+ } fwd_ctrl;
+ struct
+ { int drvid; /* id of driver */
+ u_char cfproc; /* cfu = 0, cfb = 1, cfnr = 2 */
+ ulong procid; /* process id returned when no error */
+ u_char service; /* basically coded service, 0 = all */
+ char msn[25]; /* desired msn, empty = all */
+ char fwd_nr[35];/* forwarded to number + subaddress */
+ } cf_ctrl;
+ } divert_ioctl;
+
+#ifdef __KERNEL__
+
+#include <linux/isdnif.h>
+#include <linux/isdn_divertif.h>
+
+#define AUTODEL_TIME 30 /* timeout in s to delete internal entries */
+
+/**************************************************/
+/* structure keeping ascii info for device output */
+/**************************************************/
+struct divert_info
+ { struct divert_info *next;
+ ulong usage_cnt; /* number of files still to work */
+ char info_start[2]; /* info string start */
+ };
+
+
+/**************/
+/* Prototypes */
+/**************/
+extern spinlock_t divert_lock;
+
+extern ulong if_used; /* number of interface users */
+extern int divert_dev_deinit(void);
+extern int divert_dev_init(void);
+extern void put_info_buffer(char *);
+extern int ll_callback(isdn_ctrl *);
+extern isdn_divert_if divert_if;
+extern divert_rule *getruleptr(int);
+extern int insertrule(int, divert_rule *);
+extern int deleterule(int);
+extern void deleteprocs(void);
+extern int deflect_extern_action(u_char, ulong, char *);
+extern int cf_command(int, int, u_char, char *, u_char, char *, ulong *);
+
+#endif /* __KERNEL__ */
diff --git a/drivers/isdn/gigaset/Kconfig b/drivers/isdn/gigaset/Kconfig
new file mode 100644
index 0000000..0017e50
--- /dev/null
+++ b/drivers/isdn/gigaset/Kconfig
@@ -0,0 +1,54 @@
+menuconfig ISDN_DRV_GIGASET
+ tristate "Siemens Gigaset support (isdn)"
+ select CRC_CCITT
+ select BITREVERSE
+ help
+ This driver supports the Siemens Gigaset SX205/255 family of
+ ISDN DECT bases, including the predecessors Gigaset 3070/3075
+ and 4170/4175 and their T-Com versions Sinus 45isdn and Sinus
+ 721X.
+ If you have one of these devices, say M here and for at least
+ one of the connection specific parts that follow.
+ This will build a module called "gigaset".
+
+if ISDN_DRV_GIGASET!=n
+
+config GIGASET_BASE
+ tristate "Gigaset base station support"
+ depends on ISDN_DRV_GIGASET && USB
+ help
+ Say M here if you want to use the USB interface of the Gigaset
+ base for connection to your system.
+ This will build a module called "bas_gigaset".
+
+config GIGASET_M105
+ tristate "Gigaset M105 support"
+ depends on ISDN_DRV_GIGASET && USB
+ help
+ Say M here if you want to connect to the Gigaset base via DECT
+ using a Gigaset M105 (Sinus 45 Data 2) USB DECT device.
+ This will build a module called "usb_gigaset".
+
+config GIGASET_M101
+ tristate "Gigaset M101 support"
+ depends on ISDN_DRV_GIGASET
+ help
+ Say M here if you want to connect to the Gigaset base via DECT
+ using a Gigaset M101 (Sinus 45 Data 1) RS232 DECT device.
+ This will build a module called "ser_gigaset".
+
+config GIGASET_DEBUG
+ bool "Gigaset debugging"
+ help
+ This enables debugging code in the Gigaset drivers.
+ If in doubt, say yes.
+
+config GIGASET_UNDOCREQ
+ bool "Support for undocumented USB requests"
+ help
+ This enables support for USB requests we only know from
+ reverse engineering (currently M105 only). If you need
+ features like configuration mode of M105, say yes. If you
+ care about your device, say no.
+
+endif # ISDN_DRV_GIGASET != n
diff --git a/drivers/isdn/gigaset/Makefile b/drivers/isdn/gigaset/Makefile
new file mode 100644
index 0000000..e9d3189
--- /dev/null
+++ b/drivers/isdn/gigaset/Makefile
@@ -0,0 +1,9 @@
+gigaset-y := common.o interface.o proc.o ev-layer.o i4l.o asyncdata.o
+usb_gigaset-y := usb-gigaset.o
+ser_gigaset-y := ser-gigaset.o
+bas_gigaset-y := bas-gigaset.o isocdata.o
+
+obj-$(CONFIG_ISDN_DRV_GIGASET) += gigaset.o
+obj-$(CONFIG_GIGASET_M105) += usb_gigaset.o
+obj-$(CONFIG_GIGASET_BASE) += bas_gigaset.o
+obj-$(CONFIG_GIGASET_M101) += ser_gigaset.o
diff --git a/drivers/isdn/gigaset/asyncdata.c b/drivers/isdn/gigaset/asyncdata.c
new file mode 100644
index 0000000..c2bd97d
--- /dev/null
+++ b/drivers/isdn/gigaset/asyncdata.c
@@ -0,0 +1,591 @@
+/*
+ * Common data handling layer for ser_gigaset and usb_gigaset
+ *
+ * Copyright (c) 2005 by Tilman Schmidt <tilman@imap.cc>,
+ * Hansjoerg Lipp <hjlipp@web.de>,
+ * Stefan Eilers.
+ *
+ * =====================================================================
+ * 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 of
+ * the License, or (at your option) any later version.
+ * =====================================================================
+ */
+
+#include "gigaset.h"
+#include <linux/crc-ccitt.h>
+#include <linux/bitrev.h>
+
+//#define GIG_M10x_STUFF_VOICE_DATA
+
+/* check if byte must be stuffed/escaped
+ * I'm not sure which data should be encoded.
+ * Therefore I will go the hard way and decode every value
+ * less than 0x20, the flag sequence and the control escape char.
+ */
+static inline int muststuff(unsigned char c)
+{
+ if (c < PPP_TRANS) return 1;
+ if (c == PPP_FLAG) return 1;
+ if (c == PPP_ESCAPE) return 1;
+ /* other possible candidates: */
+ /* 0x91: XON with parity set */
+ /* 0x93: XOFF with parity set */
+ return 0;
+}
+
+/* == data input =========================================================== */
+
+/* process a block of received bytes in command mode (modem response)
+ * Return value:
+ * number of processed bytes
+ */
+static inline int cmd_loop(unsigned char c, unsigned char *src, int numbytes,
+ struct inbuf_t *inbuf)
+{
+ struct cardstate *cs = inbuf->cs;
+ unsigned cbytes = cs->cbytes;
+ int inputstate = inbuf->inputstate;
+ int startbytes = numbytes;
+
+ for (;;) {
+ cs->respdata[cbytes] = c;
+ if (c == 10 || c == 13) {
+ gig_dbg(DEBUG_TRANSCMD, "%s: End of Command (%d Bytes)",
+ __func__, cbytes);
+ cs->cbytes = cbytes;
+ gigaset_handle_modem_response(cs); /* can change
+ cs->dle */
+ cbytes = 0;
+
+ if (cs->dle &&
+ !(inputstate & INS_DLE_command)) {
+ inputstate &= ~INS_command;
+ break;
+ }
+ } else {
+ /* advance in line buffer, checking for overflow */
+ if (cbytes < MAX_RESP_SIZE - 1)
+ cbytes++;
+ else
+ dev_warn(cs->dev, "response too large\n");
+ }
+
+ if (!numbytes)
+ break;
+ c = *src++;
+ --numbytes;
+ if (c == DLE_FLAG &&
+ (cs->dle || inputstate & INS_DLE_command)) {
+ inputstate |= INS_DLE_char;
+ break;
+ }
+ }
+
+ cs->cbytes = cbytes;
+ inbuf->inputstate = inputstate;
+
+ return startbytes - numbytes;
+}
+
+/* process a block of received bytes in lock mode (tty i/f)
+ * Return value:
+ * number of processed bytes
+ */
+static inline int lock_loop(unsigned char *src, int numbytes,
+ struct inbuf_t *inbuf)
+{
+ struct cardstate *cs = inbuf->cs;
+
+ gigaset_dbg_buffer(DEBUG_LOCKCMD, "received response",
+ numbytes, src);
+ gigaset_if_receive(cs, src, numbytes);
+
+ return numbytes;
+}
+
+/* process a block of received bytes in HDLC data mode
+ * Collect HDLC frames, undoing byte stuffing and watching for DLE escapes.
+ * When a frame is complete, check the FCS and pass valid frames to the LL.
+ * If DLE is encountered, return immediately to let the caller handle it.
+ * Return value:
+ * number of processed bytes
+ * numbytes (all bytes processed) on error --FIXME
+ */
+static inline int hdlc_loop(unsigned char c, unsigned char *src, int numbytes,
+ struct inbuf_t *inbuf)
+{
+ struct cardstate *cs = inbuf->cs;
+ struct bc_state *bcs = inbuf->bcs;
+ int inputstate = bcs->inputstate;
+ __u16 fcs = bcs->fcs;
+ struct sk_buff *skb = bcs->skb;
+ unsigned char error;
+ struct sk_buff *compskb;
+ int startbytes = numbytes;
+ int l;
+
+ if (unlikely(inputstate & INS_byte_stuff)) {
+ inputstate &= ~INS_byte_stuff;
+ goto byte_stuff;
+ }
+ for (;;) {
+ if (unlikely(c == PPP_ESCAPE)) {
+ if (unlikely(!numbytes)) {
+ inputstate |= INS_byte_stuff;
+ break;
+ }
+ c = *src++;
+ --numbytes;
+ if (unlikely(c == DLE_FLAG &&
+ (cs->dle ||
+ inbuf->inputstate & INS_DLE_command))) {
+ inbuf->inputstate |= INS_DLE_char;
+ inputstate |= INS_byte_stuff;
+ break;
+ }
+byte_stuff:
+ c ^= PPP_TRANS;
+#ifdef CONFIG_GIGASET_DEBUG
+ if (unlikely(!muststuff(c)))
+ gig_dbg(DEBUG_HDLC, "byte stuffed: 0x%02x", c);
+#endif
+ } else if (unlikely(c == PPP_FLAG)) {
+ if (unlikely(inputstate & INS_skip_frame)) {
+ if (!(inputstate & INS_have_data)) { /* 7E 7E */
+#ifdef CONFIG_GIGASET_DEBUG
+ ++bcs->emptycount;
+#endif
+ } else
+ gig_dbg(DEBUG_HDLC,
+ "7e----------------------------");
+
+ /* end of frame */
+ error = 1;
+ gigaset_rcv_error(NULL, cs, bcs);
+ } else if (!(inputstate & INS_have_data)) { /* 7E 7E */
+#ifdef CONFIG_GIGASET_DEBUG
+ ++bcs->emptycount;
+#endif
+ break;
+ } else {
+ gig_dbg(DEBUG_HDLC,
+ "7e----------------------------");
+
+ /* end of frame */
+ error = 0;
+
+ if (unlikely(fcs != PPP_GOODFCS)) {
+ dev_err(cs->dev,
+ "Packet checksum at %lu failed, "
+ "packet is corrupted (%u bytes)!\n",
+ bcs->rcvbytes, skb->len);
+ compskb = NULL;
+ gigaset_rcv_error(compskb, cs, bcs);
+ error = 1;
+ } else {
+ if (likely((l = skb->len) > 2)) {
+ skb->tail -= 2;
+ skb->len -= 2;
+ } else {
+ dev_kfree_skb(skb);
+ skb = NULL;
+ inputstate |= INS_skip_frame;
+ if (l == 1) {
+ dev_err(cs->dev,
+ "invalid packet size (1)!\n");
+ error = 1;
+ gigaset_rcv_error(NULL,
+ cs, bcs);
+ }
+ }
+ if (likely(!(error ||
+ (inputstate &
+ INS_skip_frame)))) {
+ gigaset_rcv_skb(skb, cs, bcs);
+ }
+ }
+ }
+
+ if (unlikely(error))
+ if (skb)
+ dev_kfree_skb(skb);
+
+ fcs = PPP_INITFCS;
+ inputstate &= ~(INS_have_data | INS_skip_frame);
+ if (unlikely(bcs->ignore)) {
+ inputstate |= INS_skip_frame;
+ skb = NULL;
+ } else if (likely((skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL)) {
+ skb_reserve(skb, HW_HDR_LEN);
+ } else {
+ dev_warn(cs->dev,
+ "could not allocate new skb\n");
+ inputstate |= INS_skip_frame;
+ }
+
+ break;
+#ifdef CONFIG_GIGASET_DEBUG
+ } else if (unlikely(muststuff(c))) {
+ /* Should not happen. Possible after ZDLE=1<CR><LF>. */
+ gig_dbg(DEBUG_HDLC, "not byte stuffed: 0x%02x", c);
+#endif
+ }
+
+ /* add character */
+
+#ifdef CONFIG_GIGASET_DEBUG
+ if (unlikely(!(inputstate & INS_have_data))) {
+ gig_dbg(DEBUG_HDLC, "7e (%d x) ================",
+ bcs->emptycount);
+ bcs->emptycount = 0;
+ }
+#endif
+
+ inputstate |= INS_have_data;
+
+ if (likely(!(inputstate & INS_skip_frame))) {
+ if (unlikely(skb->len == SBUFSIZE)) {
+ dev_warn(cs->dev, "received packet too long\n");
+ dev_kfree_skb_any(skb);
+ skb = NULL;
+ inputstate |= INS_skip_frame;
+ break;
+ }
+ *__skb_put(skb, 1) = c;
+ fcs = crc_ccitt_byte(fcs, c);
+ }
+
+ if (unlikely(!numbytes))
+ break;
+ c = *src++;
+ --numbytes;
+ if (unlikely(c == DLE_FLAG &&
+ (cs->dle ||
+ inbuf->inputstate & INS_DLE_command))) {
+ inbuf->inputstate |= INS_DLE_char;
+ break;
+ }
+ }
+ bcs->inputstate = inputstate;
+ bcs->fcs = fcs;
+ bcs->skb = skb;
+ return startbytes - numbytes;
+}
+
+/* process a block of received bytes in transparent data mode
+ * Invert bytes, undoing byte stuffing and watching for DLE escapes.
+ * If DLE is encountered, return immediately to let the caller handle it.
+ * Return value:
+ * number of processed bytes
+ * numbytes (all bytes processed) on error --FIXME
+ */
+static inline int iraw_loop(unsigned char c, unsigned char *src, int numbytes,
+ struct inbuf_t *inbuf)
+{
+ struct cardstate *cs = inbuf->cs;
+ struct bc_state *bcs = inbuf->bcs;
+ int inputstate = bcs->inputstate;
+ struct sk_buff *skb = bcs->skb;
+ int startbytes = numbytes;
+
+ for (;;) {
+ /* add character */
+ inputstate |= INS_have_data;
+
+ if (likely(!(inputstate & INS_skip_frame))) {
+ if (unlikely(skb->len == SBUFSIZE)) {
+ //FIXME just pass skb up and allocate a new one
+ dev_warn(cs->dev, "received packet too long\n");
+ dev_kfree_skb_any(skb);
+ skb = NULL;
+ inputstate |= INS_skip_frame;
+ break;
+ }
+ *__skb_put(skb, 1) = bitrev8(c);
+ }
+
+ if (unlikely(!numbytes))
+ break;
+ c = *src++;
+ --numbytes;
+ if (unlikely(c == DLE_FLAG &&
+ (cs->dle ||
+ inbuf->inputstate & INS_DLE_command))) {
+ inbuf->inputstate |= INS_DLE_char;
+ break;
+ }
+ }
+
+ /* pass data up */
+ if (likely(inputstate & INS_have_data)) {
+ if (likely(!(inputstate & INS_skip_frame))) {
+ gigaset_rcv_skb(skb, cs, bcs);
+ }
+ inputstate &= ~(INS_have_data | INS_skip_frame);
+ if (unlikely(bcs->ignore)) {
+ inputstate |= INS_skip_frame;
+ skb = NULL;
+ } else if (likely((skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN))
+ != NULL)) {
+ skb_reserve(skb, HW_HDR_LEN);
+ } else {
+ dev_warn(cs->dev, "could not allocate new skb\n");
+ inputstate |= INS_skip_frame;
+ }
+ }
+
+ bcs->inputstate = inputstate;
+ bcs->skb = skb;
+ return startbytes - numbytes;
+}
+
+/* process a block of data received from the device
+ */
+void gigaset_m10x_input(struct inbuf_t *inbuf)
+{
+ struct cardstate *cs;
+ unsigned tail, head, numbytes;
+ unsigned char *src, c;
+ int procbytes;
+
+ head = inbuf->head;
+ tail = inbuf->tail;
+ gig_dbg(DEBUG_INTR, "buffer state: %u -> %u", head, tail);
+
+ if (head != tail) {
+ cs = inbuf->cs;
+ src = inbuf->data + head;
+ numbytes = (head > tail ? RBUFSIZE : tail) - head;
+ gig_dbg(DEBUG_INTR, "processing %u bytes", numbytes);
+
+ while (numbytes) {
+ if (cs->mstate == MS_LOCKED) {
+ procbytes = lock_loop(src, numbytes, inbuf);
+ src += procbytes;
+ numbytes -= procbytes;
+ } else {
+ c = *src++;
+ --numbytes;
+ if (c == DLE_FLAG && (cs->dle ||
+ inbuf->inputstate & INS_DLE_command)) {
+ if (!(inbuf->inputstate & INS_DLE_char)) {
+ inbuf->inputstate |= INS_DLE_char;
+ goto nextbyte;
+ }
+ /* <DLE> <DLE> => <DLE> in data stream */
+ inbuf->inputstate &= ~INS_DLE_char;
+ }
+
+ if (!(inbuf->inputstate & INS_DLE_char)) {
+
+ /* FIXME use function pointers? */
+ if (inbuf->inputstate & INS_command)
+ procbytes = cmd_loop(c, src, numbytes, inbuf);
+ else if (inbuf->bcs->proto2 == ISDN_PROTO_L2_HDLC)
+ procbytes = hdlc_loop(c, src, numbytes, inbuf);
+ else
+ procbytes = iraw_loop(c, src, numbytes, inbuf);
+
+ src += procbytes;
+ numbytes -= procbytes;
+ } else { /* DLE char */
+ inbuf->inputstate &= ~INS_DLE_char;
+ switch (c) {
+ case 'X': /*begin of command*/
+#ifdef CONFIG_GIGASET_DEBUG
+ if (inbuf->inputstate & INS_command)
+ dev_err(cs->dev,
+ "received <DLE> 'X' in command mode\n");
+#endif
+ inbuf->inputstate |=
+ INS_command | INS_DLE_command;
+ break;
+ case '.': /*end of command*/
+#ifdef CONFIG_GIGASET_DEBUG
+ if (!(inbuf->inputstate & INS_command))
+ dev_err(cs->dev,
+ "received <DLE> '.' in hdlc mode\n");
+#endif
+ inbuf->inputstate &= cs->dle ?
+ ~(INS_DLE_command|INS_command)
+ : ~INS_DLE_command;
+ break;
+ //case DLE_FLAG: /*DLE_FLAG in data stream*/ /* schon oben behandelt! */
+ default:
+ dev_err(cs->dev,
+ "received 0x10 0x%02x!\n",
+ (int) c);
+ /* FIXME: reset driver?? */
+ }
+ }
+ }
+nextbyte:
+ if (!numbytes) {
+ /* end of buffer, check for wrap */
+ if (head > tail) {
+ head = 0;
+ src = inbuf->data;
+ numbytes = tail;
+ } else {
+ head = tail;
+ break;
+ }
+ }
+ }
+
+ gig_dbg(DEBUG_INTR, "setting head to %u", head);
+ inbuf->head = head;
+ }
+}
+EXPORT_SYMBOL_GPL(gigaset_m10x_input);
+
+
+/* == data output ========================================================== */
+
+/* Encoding of a PPP packet into an octet stuffed HDLC frame
+ * with FCS, opening and closing flags.
+ * parameters:
+ * skb skb containing original packet (freed upon return)
+ * head number of headroom bytes to allocate in result skb
+ * tail number of tailroom bytes to allocate in result skb
+ * Return value:
+ * pointer to newly allocated skb containing the result frame
+ */
+static struct sk_buff *HDLC_Encode(struct sk_buff *skb, int head, int tail)
+{
+ struct sk_buff *hdlc_skb;
+ __u16 fcs;
+ unsigned char c;
+ unsigned char *cp;
+ int len;
+ unsigned int stuf_cnt;
+
+ stuf_cnt = 0;
+ fcs = PPP_INITFCS;
+ cp = skb->data;
+ len = skb->len;
+ while (len--) {
+ if (muststuff(*cp))
+ stuf_cnt++;
+ fcs = crc_ccitt_byte(fcs, *cp++);
+ }
+ fcs ^= 0xffff; /* complement */
+
+ /* size of new buffer: original size + number of stuffing bytes
+ * + 2 bytes FCS + 2 stuffing bytes for FCS (if needed) + 2 flag bytes
+ */
+ hdlc_skb = dev_alloc_skb(skb->len + stuf_cnt + 6 + tail + head);
+ if (!hdlc_skb) {
+ dev_kfree_skb(skb);
+ return NULL;
+ }
+ skb_reserve(hdlc_skb, head);
+
+ /* Copy acknowledge request into new skb */
+ memcpy(hdlc_skb->head, skb->head, 2);
+
+ /* Add flag sequence in front of everything.. */
+ *(skb_put(hdlc_skb, 1)) = PPP_FLAG;
+
+ /* Perform byte stuffing while copying data. */
+ while (skb->len--) {
+ if (muststuff(*skb->data)) {
+ *(skb_put(hdlc_skb, 1)) = PPP_ESCAPE;
+ *(skb_put(hdlc_skb, 1)) = (*skb->data++) ^ PPP_TRANS;
+ } else
+ *(skb_put(hdlc_skb, 1)) = *skb->data++;
+ }
+
+ /* Finally add FCS (byte stuffed) and flag sequence */
+ c = (fcs & 0x00ff); /* least significant byte first */
+ if (muststuff(c)) {
+ *(skb_put(hdlc_skb, 1)) = PPP_ESCAPE;
+ c ^= PPP_TRANS;
+ }
+ *(skb_put(hdlc_skb, 1)) = c;
+
+ c = ((fcs >> 8) & 0x00ff);
+ if (muststuff(c)) {
+ *(skb_put(hdlc_skb, 1)) = PPP_ESCAPE;
+ c ^= PPP_TRANS;
+ }
+ *(skb_put(hdlc_skb, 1)) = c;
+
+ *(skb_put(hdlc_skb, 1)) = PPP_FLAG;
+
+ dev_kfree_skb(skb);
+ return hdlc_skb;
+}
+
+/* Encoding of a raw packet into an octet stuffed bit inverted frame
+ * parameters:
+ * skb skb containing original packet (freed upon return)
+ * head number of headroom bytes to allocate in result skb
+ * tail number of tailroom bytes to allocate in result skb
+ * Return value:
+ * pointer to newly allocated skb containing the result frame
+ */
+static struct sk_buff *iraw_encode(struct sk_buff *skb, int head, int tail)
+{
+ struct sk_buff *iraw_skb;
+ unsigned char c;
+ unsigned char *cp;
+ int len;
+
+ /* worst case: every byte must be stuffed */
+ iraw_skb = dev_alloc_skb(2*skb->len + tail + head);
+ if (!iraw_skb) {
+ dev_kfree_skb(skb);
+ return NULL;
+ }
+ skb_reserve(iraw_skb, head);
+
+ cp = skb->data;
+ len = skb->len;
+ while (len--) {
+ c = bitrev8(*cp++);
+ if (c == DLE_FLAG)
+ *(skb_put(iraw_skb, 1)) = c;
+ *(skb_put(iraw_skb, 1)) = c;
+ }
+ dev_kfree_skb(skb);
+ return iraw_skb;
+}
+
+/* gigaset_send_skb
+ * called by common.c to queue an skb for sending
+ * and start transmission if necessary
+ * parameters:
+ * B Channel control structure
+ * skb
+ * Return value:
+ * number of bytes accepted for sending
+ * (skb->len if ok, 0 if out of buffer space)
+ * or error code (< 0, eg. -EINVAL)
+ */
+int gigaset_m10x_send_skb(struct bc_state *bcs, struct sk_buff *skb)
+{
+ unsigned len = skb->len;
+ unsigned long flags;
+
+ if (bcs->proto2 == ISDN_PROTO_L2_HDLC)
+ skb = HDLC_Encode(skb, HW_HDR_LEN, 0);
+ else
+ skb = iraw_encode(skb, HW_HDR_LEN, 0);
+ if (!skb) {
+ dev_err(bcs->cs->dev,
+ "unable to allocate memory for encoding!\n");
+ return -ENOMEM;
+ }
+
+ skb_queue_tail(&bcs->squeue, skb);
+ spin_lock_irqsave(&bcs->cs->lock, flags);
+ if (bcs->cs->connected)
+ tasklet_schedule(&bcs->cs->write_tasklet);
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
+
+ return len; /* ok so far */
+}
+EXPORT_SYMBOL_GPL(gigaset_m10x_send_skb);
diff --git a/drivers/isdn/gigaset/bas-gigaset.c b/drivers/isdn/gigaset/bas-gigaset.c
new file mode 100644
index 0000000..3f11910
--- /dev/null
+++ b/drivers/isdn/gigaset/bas-gigaset.c
@@ -0,0 +1,2580 @@
+/*
+ * USB driver for Gigaset 307x base via direct USB connection.
+ *
+ * Copyright (c) 2001 by Hansjoerg Lipp <hjlipp@web.de>,
+ * Tilman Schmidt <tilman@imap.cc>,
+ * Stefan Eilers.
+ *
+ * =====================================================================
+ * 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 of
+ * the License, or (at your option) any later version.
+ * =====================================================================
+ */
+
+#include "gigaset.h"
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/usb.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+/* Version Information */
+#define DRIVER_AUTHOR "Tilman Schmidt <tilman@imap.cc>, Hansjoerg Lipp <hjlipp@web.de>, Stefan Eilers"
+#define DRIVER_DESC "USB Driver for Gigaset 307x"
+
+
+/* Module parameters */
+
+static int startmode = SM_ISDN;
+static int cidmode = 1;
+
+module_param(startmode, int, S_IRUGO);
+module_param(cidmode, int, S_IRUGO);
+MODULE_PARM_DESC(startmode, "start in isdn4linux mode");
+MODULE_PARM_DESC(cidmode, "Call-ID mode");
+
+#define GIGASET_MINORS 1
+#define GIGASET_MINOR 16
+#define GIGASET_MODULENAME "bas_gigaset"
+#define GIGASET_DEVNAME "ttyGB"
+
+/* length limit according to Siemens 3070usb-protokoll.doc ch. 2.1 */
+#define IF_WRITEBUF 264
+
+/* Values for the Gigaset 307x */
+#define USB_GIGA_VENDOR_ID 0x0681
+#define USB_3070_PRODUCT_ID 0x0001
+#define USB_3075_PRODUCT_ID 0x0002
+#define USB_SX303_PRODUCT_ID 0x0021
+#define USB_SX353_PRODUCT_ID 0x0022
+
+/* table of devices that work with this driver */
+static const struct usb_device_id gigaset_table [] = {
+ { USB_DEVICE(USB_GIGA_VENDOR_ID, USB_3070_PRODUCT_ID) },
+ { USB_DEVICE(USB_GIGA_VENDOR_ID, USB_3075_PRODUCT_ID) },
+ { USB_DEVICE(USB_GIGA_VENDOR_ID, USB_SX303_PRODUCT_ID) },
+ { USB_DEVICE(USB_GIGA_VENDOR_ID, USB_SX353_PRODUCT_ID) },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, gigaset_table);
+
+/*======================= local function prototypes ==========================*/
+
+/* function called if a new device belonging to this driver is connected */
+static int gigaset_probe(struct usb_interface *interface,
+ const struct usb_device_id *id);
+
+/* Function will be called if the device is unplugged */
+static void gigaset_disconnect(struct usb_interface *interface);
+
+/* functions called before/after suspend */
+static int gigaset_suspend(struct usb_interface *intf, pm_message_t message);
+static int gigaset_resume(struct usb_interface *intf);
+
+/* functions called before/after device reset */
+static int gigaset_pre_reset(struct usb_interface *intf);
+static int gigaset_post_reset(struct usb_interface *intf);
+
+static int atread_submit(struct cardstate *, int);
+static void stopurbs(struct bas_bc_state *);
+static int req_submit(struct bc_state *, int, int, int);
+static int atwrite_submit(struct cardstate *, unsigned char *, int);
+static int start_cbsend(struct cardstate *);
+
+/*============================================================================*/
+
+struct bas_cardstate {
+ struct usb_device *udev; /* USB device pointer */
+ struct usb_interface *interface; /* interface for this device */
+ unsigned char minor; /* starting minor number */
+
+ struct urb *urb_ctrl; /* control pipe default URB */
+ struct usb_ctrlrequest dr_ctrl;
+ struct timer_list timer_ctrl; /* control request timeout */
+ int retry_ctrl;
+
+ struct timer_list timer_atrdy; /* AT command ready timeout */
+ struct urb *urb_cmd_out; /* for sending AT commands */
+ struct usb_ctrlrequest dr_cmd_out;
+ int retry_cmd_out;
+
+ struct urb *urb_cmd_in; /* for receiving AT replies */
+ struct usb_ctrlrequest dr_cmd_in;
+ struct timer_list timer_cmd_in; /* receive request timeout */
+ unsigned char *rcvbuf; /* AT reply receive buffer */
+
+ struct urb *urb_int_in; /* URB for interrupt pipe */
+ unsigned char int_in_buf[3];
+
+ spinlock_t lock; /* locks all following */
+ int basstate; /* bitmap (BS_*) */
+ int pending; /* uncompleted base request */
+ wait_queue_head_t waitqueue;
+ int rcvbuf_size; /* size of AT receive buffer */
+ /* 0: no receive in progress */
+ int retry_cmd_in; /* receive req retry count */
+};
+
+/* status of direct USB connection to 307x base (bits in basstate) */
+#define BS_ATOPEN 0x001 /* AT channel open */
+#define BS_B1OPEN 0x002 /* B channel 1 open */
+#define BS_B2OPEN 0x004 /* B channel 2 open */
+#define BS_ATREADY 0x008 /* base ready for AT command */
+#define BS_INIT 0x010 /* base has signalled INIT_OK */
+#define BS_ATTIMER 0x020 /* waiting for HD_READY_SEND_ATDATA */
+#define BS_ATRDPEND 0x040 /* urb_cmd_in in use */
+#define BS_ATWRPEND 0x080 /* urb_cmd_out in use */
+#define BS_SUSPEND 0x100 /* USB port suspended */
+
+
+static struct gigaset_driver *driver = NULL;
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver gigaset_usb_driver = {
+ .name = GIGASET_MODULENAME,
+ .probe = gigaset_probe,
+ .disconnect = gigaset_disconnect,
+ .id_table = gigaset_table,
+ .suspend = gigaset_suspend,
+ .resume = gigaset_resume,
+ .reset_resume = gigaset_post_reset,
+ .pre_reset = gigaset_pre_reset,
+ .post_reset = gigaset_post_reset,
+};
+
+/* get message text for usb_submit_urb return code
+ */
+static char *get_usb_rcmsg(int rc)
+{
+ static char unkmsg[28];
+
+ switch (rc) {
+ case 0:
+ return "success";
+ case -ENOMEM:
+ return "out of memory";
+ case -ENODEV:
+ return "device not present";
+ case -ENOENT:
+ return "endpoint not present";
+ case -ENXIO:
+ return "URB type not supported";
+ case -EINVAL:
+ return "invalid argument";
+ case -EAGAIN:
+ return "start frame too early or too much scheduled";
+ case -EFBIG:
+ return "too many isochronous frames requested";
+ case -EPIPE:
+ return "endpoint stalled";
+ case -EMSGSIZE:
+ return "invalid packet size";
+ case -ENOSPC:
+ return "would overcommit USB bandwidth";
+ case -ESHUTDOWN:
+ return "device shut down";
+ case -EPERM:
+ return "reject flag set";
+ case -EHOSTUNREACH:
+ return "device suspended";
+ default:
+ snprintf(unkmsg, sizeof(unkmsg), "unknown error %d", rc);
+ return unkmsg;
+ }
+}
+
+/* get message text for USB status code
+ */
+static char *get_usb_statmsg(int status)
+{
+ static char unkmsg[28];
+
+ switch (status) {
+ case 0:
+ return "success";
+ case -ENOENT:
+ return "unlinked (sync)";
+ case -EINPROGRESS:
+ return "pending";
+ case -EPROTO:
+ return "bit stuffing error, timeout, or unknown USB error";
+ case -EILSEQ:
+ return "CRC mismatch, timeout, or unknown USB error";
+ case -ETIME:
+ return "timed out";
+ case -EPIPE:
+ return "endpoint stalled";
+ case -ECOMM:
+ return "IN buffer overrun";
+ case -ENOSR:
+ return "OUT buffer underrun";
+ case -EOVERFLOW:
+ return "too much data";
+ case -EREMOTEIO:
+ return "short packet detected";
+ case -ENODEV:
+ return "device removed";
+ case -EXDEV:
+ return "partial isochronous transfer";
+ case -EINVAL:
+ return "invalid argument";
+ case -ECONNRESET:
+ return "unlinked (async)";
+ case -ESHUTDOWN:
+ return "device shut down";
+ default:
+ snprintf(unkmsg, sizeof(unkmsg), "unknown status %d", status);
+ return unkmsg;
+ }
+}
+
+/* usb_pipetype_str
+ * retrieve string representation of USB pipe type
+ */
+static inline char *usb_pipetype_str(int pipe)
+{
+ if (usb_pipeisoc(pipe))
+ return "Isoc";
+ if (usb_pipeint(pipe))
+ return "Int";
+ if (usb_pipecontrol(pipe))
+ return "Ctrl";
+ if (usb_pipebulk(pipe))
+ return "Bulk";
+ return "?";
+}
+
+/* dump_urb
+ * write content of URB to syslog for debugging
+ */
+static inline void dump_urb(enum debuglevel level, const char *tag,
+ struct urb *urb)
+{
+#ifdef CONFIG_GIGASET_DEBUG
+ int i;
+ gig_dbg(level, "%s urb(0x%08lx)->{", tag, (unsigned long) urb);
+ if (urb) {
+ gig_dbg(level,
+ " dev=0x%08lx, pipe=%s:EP%d/DV%d:%s, "
+ "hcpriv=0x%08lx, transfer_flags=0x%x,",
+ (unsigned long) urb->dev,
+ usb_pipetype_str(urb->pipe),
+ usb_pipeendpoint(urb->pipe), usb_pipedevice(urb->pipe),
+ usb_pipein(urb->pipe) ? "in" : "out",
+ (unsigned long) urb->hcpriv,
+ urb->transfer_flags);
+ gig_dbg(level,
+ " transfer_buffer=0x%08lx[%d], actual_length=%d, "
+ "setup_packet=0x%08lx,",
+ (unsigned long) urb->transfer_buffer,
+ urb->transfer_buffer_length, urb->actual_length,
+ (unsigned long) urb->setup_packet);
+ gig_dbg(level,
+ " start_frame=%d, number_of_packets=%d, interval=%d, "
+ "error_count=%d,",
+ urb->start_frame, urb->number_of_packets, urb->interval,
+ urb->error_count);
+ gig_dbg(level,
+ " context=0x%08lx, complete=0x%08lx, "
+ "iso_frame_desc[]={",
+ (unsigned long) urb->context,
+ (unsigned long) urb->complete);
+ for (i = 0; i < urb->number_of_packets; i++) {
+ struct usb_iso_packet_descriptor *pifd
+ = &urb->iso_frame_desc[i];
+ gig_dbg(level,
+ " {offset=%u, length=%u, actual_length=%u, "
+ "status=%u}",
+ pifd->offset, pifd->length, pifd->actual_length,
+ pifd->status);
+ }
+ }
+ gig_dbg(level, "}}");
+#endif
+}
+
+/* read/set modem control bits etc. (m10x only) */
+static int gigaset_set_modem_ctrl(struct cardstate *cs, unsigned old_state,
+ unsigned new_state)
+{
+ return -EINVAL;
+}
+
+static int gigaset_baud_rate(struct cardstate *cs, unsigned cflag)
+{
+ return -EINVAL;
+}
+
+static int gigaset_set_line_ctrl(struct cardstate *cs, unsigned cflag)
+{
+ return -EINVAL;
+}
+
+/* error_hangup
+ * hang up any existing connection because of an unrecoverable error
+ * This function may be called from any context and takes care of scheduling
+ * the necessary actions for execution outside of interrupt context.
+ * cs->lock must not be held.
+ * argument:
+ * B channel control structure
+ */
+static inline void error_hangup(struct bc_state *bcs)
+{
+ struct cardstate *cs = bcs->cs;
+
+ gig_dbg(DEBUG_ANY, "%s: scheduling HUP for channel %d",
+ __func__, bcs->channel);
+
+ if (!gigaset_add_event(cs, &bcs->at_state, EV_HUP, NULL, 0, NULL))
+ dev_err(cs->dev, "event queue full\n");
+
+ gigaset_schedule_event(cs);
+}
+
+/* error_reset
+ * reset Gigaset device because of an unrecoverable error
+ * This function may be called from any context, and takes care of
+ * scheduling the necessary actions for execution outside of interrupt context.
+ * cs->lock must not be held.
+ * argument:
+ * controller state structure
+ */
+static inline void error_reset(struct cardstate *cs)
+{
+ /* close AT command channel to recover (ignore errors) */
+ req_submit(cs->bcs, HD_CLOSE_ATCHANNEL, 0, BAS_TIMEOUT);
+
+ //FIXME try to recover without bothering the user
+ dev_err(cs->dev,
+ "unrecoverable error - please disconnect Gigaset base to reset\n");
+}
+
+/* check_pending
+ * check for completion of pending control request
+ * parameter:
+ * ucs hardware specific controller state structure
+ */
+static void check_pending(struct bas_cardstate *ucs)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ucs->lock, flags);
+ switch (ucs->pending) {
+ case 0:
+ break;
+ case HD_OPEN_ATCHANNEL:
+ if (ucs->basstate & BS_ATOPEN)
+ ucs->pending = 0;
+ break;
+ case HD_OPEN_B1CHANNEL:
+ if (ucs->basstate & BS_B1OPEN)
+ ucs->pending = 0;
+ break;
+ case HD_OPEN_B2CHANNEL:
+ if (ucs->basstate & BS_B2OPEN)
+ ucs->pending = 0;
+ break;
+ case HD_CLOSE_ATCHANNEL:
+ if (!(ucs->basstate & BS_ATOPEN))
+ ucs->pending = 0;
+ break;
+ case HD_CLOSE_B1CHANNEL:
+ if (!(ucs->basstate & BS_B1OPEN))
+ ucs->pending = 0;
+ break;
+ case HD_CLOSE_B2CHANNEL:
+ if (!(ucs->basstate & BS_B2OPEN))
+ ucs->pending = 0;
+ break;
+ case HD_DEVICE_INIT_ACK: /* no reply expected */
+ ucs->pending = 0;
+ break;
+ /* HD_READ_ATMESSAGE, HD_WRITE_ATMESSAGE, HD_RESET_INTERRUPTPIPE
+ * are handled separately and should never end up here
+ */
+ default:
+ dev_warn(&ucs->interface->dev,
+ "unknown pending request 0x%02x cleared\n",
+ ucs->pending);
+ ucs->pending = 0;
+ }
+
+ if (!ucs->pending)
+ del_timer(&ucs->timer_ctrl);
+
+ spin_unlock_irqrestore(&ucs->lock, flags);
+}
+
+/* cmd_in_timeout
+ * timeout routine for command input request
+ * argument:
+ * controller state structure
+ */
+static void cmd_in_timeout(unsigned long data)
+{
+ struct cardstate *cs = (struct cardstate *) data;
+ struct bas_cardstate *ucs = cs->hw.bas;
+ int rc;
+
+ if (!ucs->rcvbuf_size) {
+ gig_dbg(DEBUG_USBREQ, "%s: no receive in progress", __func__);
+ return;
+ }
+
+ if (ucs->retry_cmd_in++ < BAS_RETRY) {
+ dev_notice(cs->dev, "control read: timeout, retry %d\n",
+ ucs->retry_cmd_in);
+ rc = atread_submit(cs, BAS_TIMEOUT);
+ if (rc >= 0 || rc == -ENODEV)
+ /* resubmitted or disconnected */
+ /* - bypass regular exit block */
+ return;
+ } else {
+ dev_err(cs->dev,
+ "control read: timeout, giving up after %d tries\n",
+ ucs->retry_cmd_in);
+ }
+ kfree(ucs->rcvbuf);
+ ucs->rcvbuf = NULL;
+ ucs->rcvbuf_size = 0;
+ error_reset(cs);
+}
+
+/* set/clear bits in base connection state, return previous state
+ */
+inline static int update_basstate(struct bas_cardstate *ucs,
+ int set, int clear)
+{
+ unsigned long flags;
+ int state;
+
+ spin_lock_irqsave(&ucs->lock, flags);
+ state = ucs->basstate;
+ ucs->basstate = (state & ~clear) | set;
+ spin_unlock_irqrestore(&ucs->lock, flags);
+ return state;
+}
+
+/* read_ctrl_callback
+ * USB completion handler for control pipe input
+ * called by the USB subsystem in interrupt context
+ * parameter:
+ * urb USB request block
+ * urb->context = inbuf structure for controller state
+ */
+static void read_ctrl_callback(struct urb *urb)
+{
+ struct inbuf_t *inbuf = urb->context;
+ struct cardstate *cs = inbuf->cs;
+ struct bas_cardstate *ucs = cs->hw.bas;
+ int status = urb->status;
+ int have_data = 0;
+ unsigned numbytes;
+ int rc;
+
+ update_basstate(ucs, 0, BS_ATRDPEND);
+ wake_up(&ucs->waitqueue);
+
+ if (!ucs->rcvbuf_size) {
+ dev_warn(cs->dev, "%s: no receive in progress\n", __func__);
+ return;
+ }
+
+ del_timer(&ucs->timer_cmd_in);
+
+ switch (status) {
+ case 0: /* normal completion */
+ numbytes = urb->actual_length;
+ if (unlikely(numbytes != ucs->rcvbuf_size)) {
+ dev_warn(cs->dev,
+ "control read: received %d chars, expected %d\n",
+ numbytes, ucs->rcvbuf_size);
+ if (numbytes > ucs->rcvbuf_size)
+ numbytes = ucs->rcvbuf_size;
+ }
+
+ /* copy received bytes to inbuf */
+ have_data = gigaset_fill_inbuf(inbuf, ucs->rcvbuf, numbytes);
+
+ if (unlikely(numbytes < ucs->rcvbuf_size)) {
+ /* incomplete - resubmit for remaining bytes */
+ ucs->rcvbuf_size -= numbytes;
+ ucs->retry_cmd_in = 0;
+ rc = atread_submit(cs, BAS_TIMEOUT);
+ if (rc >= 0 || rc == -ENODEV)
+ /* resubmitted or disconnected */
+ /* - bypass regular exit block */
+ return;
+ error_reset(cs);
+ }
+ break;
+
+ case -ENOENT: /* cancelled */
+ case -ECONNRESET: /* cancelled (async) */
+ case -EINPROGRESS: /* pending */
+ case -ENODEV: /* device removed */
+ case -ESHUTDOWN: /* device shut down */
+ /* no action necessary */
+ gig_dbg(DEBUG_USBREQ, "%s: %s",
+ __func__, get_usb_statmsg(status));
+ break;
+
+ default: /* severe trouble */
+ dev_warn(cs->dev, "control read: %s\n",
+ get_usb_statmsg(status));
+ if (ucs->retry_cmd_in++ < BAS_RETRY) {
+ dev_notice(cs->dev, "control read: retry %d\n",
+ ucs->retry_cmd_in);
+ rc = atread_submit(cs, BAS_TIMEOUT);
+ if (rc >= 0 || rc == -ENODEV)
+ /* resubmitted or disconnected */
+ /* - bypass regular exit block */
+ return;
+ } else {
+ dev_err(cs->dev,
+ "control read: giving up after %d tries\n",
+ ucs->retry_cmd_in);
+ }
+ error_reset(cs);
+ }
+
+ kfree(ucs->rcvbuf);
+ ucs->rcvbuf = NULL;
+ ucs->rcvbuf_size = 0;
+ if (have_data) {
+ gig_dbg(DEBUG_INTR, "%s-->BH", __func__);
+ gigaset_schedule_event(cs);
+ }
+}
+
+/* atread_submit
+ * submit an HD_READ_ATMESSAGE command URB and optionally start a timeout
+ * parameters:
+ * cs controller state structure
+ * timeout timeout in 1/10 sec., 0: none
+ * return value:
+ * 0 on success
+ * -EBUSY if another request is pending
+ * any URB submission error code
+ */
+static int atread_submit(struct cardstate *cs, int timeout)
+{
+ struct bas_cardstate *ucs = cs->hw.bas;
+ int basstate;
+ int ret;
+
+ gig_dbg(DEBUG_USBREQ, "-------> HD_READ_ATMESSAGE (%d)",
+ ucs->rcvbuf_size);
+
+ basstate = update_basstate(ucs, BS_ATRDPEND, 0);
+ if (basstate & BS_ATRDPEND) {
+ dev_err(cs->dev,
+ "could not submit HD_READ_ATMESSAGE: URB busy\n");
+ return -EBUSY;
+ }
+
+ if (basstate & BS_SUSPEND) {
+ dev_notice(cs->dev,
+ "HD_READ_ATMESSAGE not submitted, "
+ "suspend in progress\n");
+ update_basstate(ucs, 0, BS_ATRDPEND);
+ /* treat like disconnect */
+ return -ENODEV;
+ }
+
+ ucs->dr_cmd_in.bRequestType = IN_VENDOR_REQ;
+ ucs->dr_cmd_in.bRequest = HD_READ_ATMESSAGE;
+ ucs->dr_cmd_in.wValue = 0;
+ ucs->dr_cmd_in.wIndex = 0;
+ ucs->dr_cmd_in.wLength = cpu_to_le16(ucs->rcvbuf_size);
+ usb_fill_control_urb(ucs->urb_cmd_in, ucs->udev,
+ usb_rcvctrlpipe(ucs->udev, 0),
+ (unsigned char*) & ucs->dr_cmd_in,
+ ucs->rcvbuf, ucs->rcvbuf_size,
+ read_ctrl_callback, cs->inbuf);
+
+ if ((ret = usb_submit_urb(ucs->urb_cmd_in, GFP_ATOMIC)) != 0) {
+ update_basstate(ucs, 0, BS_ATRDPEND);
+ dev_err(cs->dev, "could not submit HD_READ_ATMESSAGE: %s\n",
+ get_usb_rcmsg(ret));
+ return ret;
+ }
+
+ if (timeout > 0) {
+ gig_dbg(DEBUG_USBREQ, "setting timeout of %d/10 secs", timeout);
+ ucs->timer_cmd_in.expires = jiffies + timeout * HZ / 10;
+ ucs->timer_cmd_in.data = (unsigned long) cs;
+ ucs->timer_cmd_in.function = cmd_in_timeout;
+ add_timer(&ucs->timer_cmd_in);
+ }
+ return 0;
+}
+
+/* read_int_callback
+ * USB completion handler for interrupt pipe input
+ * called by the USB subsystem in interrupt context
+ * parameter:
+ * urb USB request block
+ * urb->context = controller state structure
+ */
+static void read_int_callback(struct urb *urb)
+{
+ struct cardstate *cs = urb->context;
+ struct bas_cardstate *ucs = cs->hw.bas;
+ struct bc_state *bcs;
+ int status = urb->status;
+ unsigned long flags;
+ int rc;
+ unsigned l;
+ int channel;
+
+ switch (status) {
+ case 0: /* success */
+ break;
+ case -ENOENT: /* cancelled */
+ case -ECONNRESET: /* cancelled (async) */
+ case -EINPROGRESS: /* pending */
+ /* ignore silently */
+ gig_dbg(DEBUG_USBREQ, "%s: %s",
+ __func__, get_usb_statmsg(status));
+ return;
+ case -ENODEV: /* device removed */
+ case -ESHUTDOWN: /* device shut down */
+ //FIXME use this as disconnect indicator?
+ gig_dbg(DEBUG_USBREQ, "%s: device disconnected", __func__);
+ return;
+ default: /* severe trouble */
+ dev_warn(cs->dev, "interrupt read: %s\n",
+ get_usb_statmsg(status));
+ //FIXME corrective action? resubmission always ok?
+ goto resubmit;
+ }
+
+ /* drop incomplete packets even if the missing bytes wouldn't matter */
+ if (unlikely(urb->actual_length < 3)) {
+ dev_warn(cs->dev, "incomplete interrupt packet (%d bytes)\n",
+ urb->actual_length);
+ goto resubmit;
+ }
+
+ l = (unsigned) ucs->int_in_buf[1] +
+ (((unsigned) ucs->int_in_buf[2]) << 8);
+
+ gig_dbg(DEBUG_USBREQ, "<-------%d: 0x%02x (%u [0x%02x 0x%02x])",
+ urb->actual_length, (int)ucs->int_in_buf[0], l,
+ (int)ucs->int_in_buf[1], (int)ucs->int_in_buf[2]);
+
+ channel = 0;
+
+ switch (ucs->int_in_buf[0]) {
+ case HD_DEVICE_INIT_OK:
+ update_basstate(ucs, BS_INIT, 0);
+ break;
+
+ case HD_READY_SEND_ATDATA:
+ del_timer(&ucs->timer_atrdy);
+ update_basstate(ucs, BS_ATREADY, BS_ATTIMER);
+ start_cbsend(cs);
+ break;
+
+ case HD_OPEN_B2CHANNEL_ACK:
+ ++channel;
+ case HD_OPEN_B1CHANNEL_ACK:
+ bcs = cs->bcs + channel;
+ update_basstate(ucs, BS_B1OPEN << channel, 0);
+ gigaset_bchannel_up(bcs);
+ break;
+
+ case HD_OPEN_ATCHANNEL_ACK:
+ update_basstate(ucs, BS_ATOPEN, 0);
+ start_cbsend(cs);
+ break;
+
+ case HD_CLOSE_B2CHANNEL_ACK:
+ ++channel;
+ case HD_CLOSE_B1CHANNEL_ACK:
+ bcs = cs->bcs + channel;
+ update_basstate(ucs, 0, BS_B1OPEN << channel);
+ stopurbs(bcs->hw.bas);
+ gigaset_bchannel_down(bcs);
+ break;
+
+ case HD_CLOSE_ATCHANNEL_ACK:
+ update_basstate(ucs, 0, BS_ATOPEN);
+ break;
+
+ case HD_B2_FLOW_CONTROL:
+ ++channel;
+ case HD_B1_FLOW_CONTROL:
+ bcs = cs->bcs + channel;
+ atomic_add((l - BAS_NORMFRAME) * BAS_CORRFRAMES,
+ &bcs->hw.bas->corrbytes);
+ gig_dbg(DEBUG_ISO,
+ "Flow control (channel %d, sub %d): 0x%02x => %d",
+ channel, bcs->hw.bas->numsub, l,
+ atomic_read(&bcs->hw.bas->corrbytes));
+ break;
+
+ case HD_RECEIVEATDATA_ACK: /* AT response ready to be received */
+ if (!l) {
+ dev_warn(cs->dev,
+ "HD_RECEIVEATDATA_ACK with length 0 ignored\n");
+ break;
+ }
+ spin_lock_irqsave(&cs->lock, flags);
+ if (ucs->rcvbuf_size) {
+ /* throw away previous buffer - we have no queue */
+ dev_err(cs->dev,
+ "receive AT data overrun, %d bytes lost\n",
+ ucs->rcvbuf_size);
+ kfree(ucs->rcvbuf);
+ ucs->rcvbuf_size = 0;
+ }
+ if ((ucs->rcvbuf = kmalloc(l, GFP_ATOMIC)) == NULL) {
+ spin_unlock_irqrestore(&cs->lock, flags);
+ dev_err(cs->dev, "out of memory receiving AT data\n");
+ error_reset(cs);
+ break;
+ }
+ ucs->rcvbuf_size = l;
+ ucs->retry_cmd_in = 0;
+ if ((rc = atread_submit(cs, BAS_TIMEOUT)) < 0) {
+ kfree(ucs->rcvbuf);
+ ucs->rcvbuf = NULL;
+ ucs->rcvbuf_size = 0;
+ if (rc != -ENODEV) {
+ //FIXME corrective action?
+ spin_unlock_irqrestore(&cs->lock, flags);
+ error_reset(cs);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+
+ case HD_RESET_INTERRUPT_PIPE_ACK:
+ gig_dbg(DEBUG_USBREQ, "HD_RESET_INTERRUPT_PIPE_ACK");
+ break;
+
+ case HD_SUSPEND_END:
+ gig_dbg(DEBUG_USBREQ, "HD_SUSPEND_END");
+ break;
+
+ default:
+ dev_warn(cs->dev,
+ "unknown Gigaset signal 0x%02x (%u) ignored\n",
+ (int) ucs->int_in_buf[0], l);
+ }
+
+ check_pending(ucs);
+ wake_up(&ucs->waitqueue);
+
+resubmit:
+ rc = usb_submit_urb(urb, GFP_ATOMIC);
+ if (unlikely(rc != 0 && rc != -ENODEV)) {
+ dev_err(cs->dev, "could not resubmit interrupt URB: %s\n",
+ get_usb_rcmsg(rc));
+ error_reset(cs);
+ }
+}
+
+/* read_iso_callback
+ * USB completion handler for B channel isochronous input
+ * called by the USB subsystem in interrupt context
+ * parameter:
+ * urb USB request block of completed request
+ * urb->context = bc_state structure
+ */
+static void read_iso_callback(struct urb *urb)
+{
+ struct bc_state *bcs;
+ struct bas_bc_state *ubc;
+ int status = urb->status;
+ unsigned long flags;
+ int i, rc;
+
+ /* status codes not worth bothering the tasklet with */
+ if (unlikely(status == -ENOENT ||
+ status == -ECONNRESET ||
+ status == -EINPROGRESS ||
+ status == -ENODEV ||
+ status == -ESHUTDOWN)) {
+ gig_dbg(DEBUG_ISO, "%s: %s",
+ __func__, get_usb_statmsg(status));
+ return;
+ }
+
+ bcs = urb->context;
+ ubc = bcs->hw.bas;
+
+ spin_lock_irqsave(&ubc->isoinlock, flags);
+ if (likely(ubc->isoindone == NULL)) {
+ /* pass URB to tasklet */
+ ubc->isoindone = urb;
+ ubc->isoinstatus = status;
+ tasklet_schedule(&ubc->rcvd_tasklet);
+ } else {
+ /* tasklet still busy, drop data and resubmit URB */
+ ubc->loststatus = status;
+ for (i = 0; i < BAS_NUMFRAMES; i++) {
+ ubc->isoinlost += urb->iso_frame_desc[i].actual_length;
+ if (unlikely(urb->iso_frame_desc[i].status != 0 &&
+ urb->iso_frame_desc[i].status !=
+ -EINPROGRESS))
+ ubc->loststatus = urb->iso_frame_desc[i].status;
+ urb->iso_frame_desc[i].status = 0;
+ urb->iso_frame_desc[i].actual_length = 0;
+ }
+ if (likely(ubc->running)) {
+ /* urb->dev is clobbered by USB subsystem */
+ urb->dev = bcs->cs->hw.bas->udev;
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->number_of_packets = BAS_NUMFRAMES;
+ gig_dbg(DEBUG_ISO, "%s: isoc read overrun/resubmit",
+ __func__);
+ rc = usb_submit_urb(urb, GFP_ATOMIC);
+ if (unlikely(rc != 0 && rc != -ENODEV)) {
+ dev_err(bcs->cs->dev,
+ "could not resubmit isochronous read "
+ "URB: %s\n", get_usb_rcmsg(rc));
+ dump_urb(DEBUG_ISO, "isoc read", urb);
+ error_hangup(bcs);
+ }
+ }
+ }
+ spin_unlock_irqrestore(&ubc->isoinlock, flags);
+}
+
+/* write_iso_callback
+ * USB completion handler for B channel isochronous output
+ * called by the USB subsystem in interrupt context
+ * parameter:
+ * urb USB request block of completed request
+ * urb->context = isow_urbctx_t structure
+ */
+static void write_iso_callback(struct urb *urb)
+{
+ struct isow_urbctx_t *ucx;
+ struct bas_bc_state *ubc;
+ int status = urb->status;
+ unsigned long flags;
+
+ /* status codes not worth bothering the tasklet with */
+ if (unlikely(status == -ENOENT ||
+ status == -ECONNRESET ||
+ status == -EINPROGRESS ||
+ status == -ENODEV ||
+ status == -ESHUTDOWN)) {
+ gig_dbg(DEBUG_ISO, "%s: %s",
+ __func__, get_usb_statmsg(status));
+ return;
+ }
+
+ /* pass URB context to tasklet */
+ ucx = urb->context;
+ ubc = ucx->bcs->hw.bas;
+ ucx->status = status;
+
+ spin_lock_irqsave(&ubc->isooutlock, flags);
+ ubc->isooutovfl = ubc->isooutdone;
+ ubc->isooutdone = ucx;
+ spin_unlock_irqrestore(&ubc->isooutlock, flags);
+ tasklet_schedule(&ubc->sent_tasklet);
+}
+
+/* starturbs
+ * prepare and submit USB request blocks for isochronous input and output
+ * argument:
+ * B channel control structure
+ * return value:
+ * 0 on success
+ * < 0 on error (no URBs submitted)
+ */
+static int starturbs(struct bc_state *bcs)
+{
+ struct bas_bc_state *ubc = bcs->hw.bas;
+ struct urb *urb;
+ int j, k;
+ int rc;
+
+ /* initialize L2 reception */
+ if (bcs->proto2 == ISDN_PROTO_L2_HDLC)
+ bcs->inputstate |= INS_flag_hunt;
+
+ /* submit all isochronous input URBs */
+ ubc->running = 1;
+ for (k = 0; k < BAS_INURBS; k++) {
+ urb = ubc->isoinurbs[k];
+ if (!urb) {
+ rc = -EFAULT;
+ goto error;
+ }
+
+ urb->dev = bcs->cs->hw.bas->udev;
+ urb->pipe = usb_rcvisocpipe(urb->dev, 3 + 2 * bcs->channel);
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->transfer_buffer = ubc->isoinbuf + k * BAS_INBUFSIZE;
+ urb->transfer_buffer_length = BAS_INBUFSIZE;
+ urb->number_of_packets = BAS_NUMFRAMES;
+ urb->interval = BAS_FRAMETIME;
+ urb->complete = read_iso_callback;
+ urb->context = bcs;
+ for (j = 0; j < BAS_NUMFRAMES; j++) {
+ urb->iso_frame_desc[j].offset = j * BAS_MAXFRAME;
+ urb->iso_frame_desc[j].length = BAS_MAXFRAME;
+ urb->iso_frame_desc[j].status = 0;
+ urb->iso_frame_desc[j].actual_length = 0;
+ }
+
+ dump_urb(DEBUG_ISO, "Initial isoc read", urb);
+ if ((rc = usb_submit_urb(urb, GFP_ATOMIC)) != 0)
+ goto error;
+ }
+
+ /* initialize L2 transmission */
+ gigaset_isowbuf_init(ubc->isooutbuf, PPP_FLAG);
+
+ /* set up isochronous output URBs for flag idling */
+ for (k = 0; k < BAS_OUTURBS; ++k) {
+ urb = ubc->isoouturbs[k].urb;
+ if (!urb) {
+ rc = -EFAULT;
+ goto error;
+ }
+ urb->dev = bcs->cs->hw.bas->udev;
+ urb->pipe = usb_sndisocpipe(urb->dev, 4 + 2 * bcs->channel);
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->transfer_buffer = ubc->isooutbuf->data;
+ urb->transfer_buffer_length = sizeof(ubc->isooutbuf->data);
+ urb->number_of_packets = BAS_NUMFRAMES;
+ urb->interval = BAS_FRAMETIME;
+ urb->complete = write_iso_callback;
+ urb->context = &ubc->isoouturbs[k];
+ for (j = 0; j < BAS_NUMFRAMES; ++j) {
+ urb->iso_frame_desc[j].offset = BAS_OUTBUFSIZE;
+ urb->iso_frame_desc[j].length = BAS_NORMFRAME;
+ urb->iso_frame_desc[j].status = 0;
+ urb->iso_frame_desc[j].actual_length = 0;
+ }
+ ubc->isoouturbs[k].limit = -1;
+ }
+
+ /* keep one URB free, submit the others */
+ for (k = 0; k < BAS_OUTURBS-1; ++k) {
+ dump_urb(DEBUG_ISO, "Initial isoc write", urb);
+ rc = usb_submit_urb(ubc->isoouturbs[k].urb, GFP_ATOMIC);
+ if (rc != 0)
+ goto error;
+ }
+ dump_urb(DEBUG_ISO, "Initial isoc write (free)", urb);
+ ubc->isooutfree = &ubc->isoouturbs[BAS_OUTURBS-1];
+ ubc->isooutdone = ubc->isooutovfl = NULL;
+ return 0;
+ error:
+ stopurbs(ubc);
+ return rc;
+}
+
+/* stopurbs
+ * cancel the USB request blocks for isochronous input and output
+ * errors are silently ignored
+ * argument:
+ * B channel control structure
+ */
+static void stopurbs(struct bas_bc_state *ubc)
+{
+ int k, rc;
+
+ ubc->running = 0;
+
+ for (k = 0; k < BAS_INURBS; ++k) {
+ rc = usb_unlink_urb(ubc->isoinurbs[k]);
+ gig_dbg(DEBUG_ISO,
+ "%s: isoc input URB %d unlinked, result = %s",
+ __func__, k, get_usb_rcmsg(rc));
+ }
+
+ for (k = 0; k < BAS_OUTURBS; ++k) {
+ rc = usb_unlink_urb(ubc->isoouturbs[k].urb);
+ gig_dbg(DEBUG_ISO,
+ "%s: isoc output URB %d unlinked, result = %s",
+ __func__, k, get_usb_rcmsg(rc));
+ }
+}
+
+/* Isochronous Write - Bottom Half */
+/* =============================== */
+
+/* submit_iso_write_urb
+ * fill and submit the next isochronous write URB
+ * parameters:
+ * ucx context structure containing URB
+ * return value:
+ * number of frames submitted in URB
+ * 0 if URB not submitted because no data available (isooutbuf busy)
+ * error code < 0 on error
+ */
+static int submit_iso_write_urb(struct isow_urbctx_t *ucx)
+{
+ struct urb *urb = ucx->urb;
+ struct bas_bc_state *ubc = ucx->bcs->hw.bas;
+ struct usb_iso_packet_descriptor *ifd;
+ int corrbytes, nframe, rc;
+
+ /* urb->dev is clobbered by USB subsystem */
+ urb->dev = ucx->bcs->cs->hw.bas->udev;
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->transfer_buffer = ubc->isooutbuf->data;
+ urb->transfer_buffer_length = sizeof(ubc->isooutbuf->data);
+
+ for (nframe = 0; nframe < BAS_NUMFRAMES; nframe++) {
+ ifd = &urb->iso_frame_desc[nframe];
+
+ /* compute frame length according to flow control */
+ ifd->length = BAS_NORMFRAME;
+ if ((corrbytes = atomic_read(&ubc->corrbytes)) != 0) {
+ gig_dbg(DEBUG_ISO, "%s: corrbytes=%d",
+ __func__, corrbytes);
+ if (corrbytes > BAS_HIGHFRAME - BAS_NORMFRAME)
+ corrbytes = BAS_HIGHFRAME - BAS_NORMFRAME;
+ else if (corrbytes < BAS_LOWFRAME - BAS_NORMFRAME)
+ corrbytes = BAS_LOWFRAME - BAS_NORMFRAME;
+ ifd->length += corrbytes;
+ atomic_add(-corrbytes, &ubc->corrbytes);
+ }
+
+ /* retrieve block of data to send */
+ rc = gigaset_isowbuf_getbytes(ubc->isooutbuf, ifd->length);
+ if (rc < 0) {
+ if (rc == -EBUSY) {
+ gig_dbg(DEBUG_ISO,
+ "%s: buffer busy at frame %d",
+ __func__, nframe);
+ /* tasklet will be restarted from
+ gigaset_send_skb() */
+ } else {
+ dev_err(ucx->bcs->cs->dev,
+ "%s: buffer error %d at frame %d\n",
+ __func__, rc, nframe);
+ return rc;
+ }
+ break;
+ }
+ ifd->offset = rc;
+ ucx->limit = ubc->isooutbuf->nextread;
+ ifd->status = 0;
+ ifd->actual_length = 0;
+ }
+ if (unlikely(nframe == 0))
+ return 0; /* no data to send */
+ urb->number_of_packets = nframe;
+
+ rc = usb_submit_urb(urb, GFP_ATOMIC);
+ if (unlikely(rc)) {
+ if (rc == -ENODEV)
+ /* device removed - give up silently */
+ gig_dbg(DEBUG_ISO, "%s: disconnected", __func__);
+ else
+ dev_err(ucx->bcs->cs->dev,
+ "could not submit isochronous write URB: %s\n",
+ get_usb_rcmsg(rc));
+ return rc;
+ }
+ ++ubc->numsub;
+ return nframe;
+}
+
+/* write_iso_tasklet
+ * tasklet scheduled when an isochronous output URB from the Gigaset device
+ * has completed
+ * parameter:
+ * data B channel state structure
+ */
+static void write_iso_tasklet(unsigned long data)
+{
+ struct bc_state *bcs = (struct bc_state *) data;
+ struct bas_bc_state *ubc = bcs->hw.bas;
+ struct cardstate *cs = bcs->cs;
+ struct isow_urbctx_t *done, *next, *ovfl;
+ struct urb *urb;
+ int status;
+ struct usb_iso_packet_descriptor *ifd;
+ int offset;
+ unsigned long flags;
+ int i;
+ struct sk_buff *skb;
+ int len;
+ int rc;
+
+ /* loop while completed URBs arrive in time */
+ for (;;) {
+ if (unlikely(!(ubc->running))) {
+ gig_dbg(DEBUG_ISO, "%s: not running", __func__);
+ return;
+ }
+
+ /* retrieve completed URBs */
+ spin_lock_irqsave(&ubc->isooutlock, flags);
+ done = ubc->isooutdone;
+ ubc->isooutdone = NULL;
+ ovfl = ubc->isooutovfl;
+ ubc->isooutovfl = NULL;
+ spin_unlock_irqrestore(&ubc->isooutlock, flags);
+ if (ovfl) {
+ dev_err(cs->dev, "isochronous write buffer underrun\n");
+ error_hangup(bcs);
+ break;
+ }
+ if (!done)
+ break;
+
+ /* submit free URB if available */
+ spin_lock_irqsave(&ubc->isooutlock, flags);
+ next = ubc->isooutfree;
+ ubc->isooutfree = NULL;
+ spin_unlock_irqrestore(&ubc->isooutlock, flags);
+ if (next) {
+ rc = submit_iso_write_urb(next);
+ if (unlikely(rc <= 0 && rc != -ENODEV)) {
+ /* could not submit URB, put it back */
+ spin_lock_irqsave(&ubc->isooutlock, flags);
+ if (ubc->isooutfree == NULL) {
+ ubc->isooutfree = next;
+ next = NULL;
+ }
+ spin_unlock_irqrestore(&ubc->isooutlock, flags);
+ if (next) {
+ /* couldn't put it back */
+ dev_err(cs->dev,
+ "losing isochronous write URB\n");
+ error_hangup(bcs);
+ }
+ }
+ }
+
+ /* process completed URB */
+ urb = done->urb;
+ status = done->status;
+ switch (status) {
+ case -EXDEV: /* partial completion */
+ gig_dbg(DEBUG_ISO, "%s: URB partially completed",
+ __func__);
+ /* fall through - what's the difference anyway? */
+ case 0: /* normal completion */
+ /* inspect individual frames
+ * assumptions (for lack of documentation):
+ * - actual_length bytes of first frame in error are
+ * successfully sent
+ * - all following frames are not sent at all
+ */
+ offset = done->limit; /* default (no error) */
+ for (i = 0; i < BAS_NUMFRAMES; i++) {
+ ifd = &urb->iso_frame_desc[i];
+ if (ifd->status ||
+ ifd->actual_length != ifd->length) {
+ dev_warn(cs->dev,
+ "isochronous write: frame %d: %s, "
+ "only %d of %d bytes sent\n",
+ i, get_usb_statmsg(ifd->status),
+ ifd->actual_length, ifd->length);
+ offset = (ifd->offset +
+ ifd->actual_length)
+ % BAS_OUTBUFSIZE;
+ break;
+ }
+ }
+#ifdef CONFIG_GIGASET_DEBUG
+ /* check assumption on remaining frames */
+ for (; i < BAS_NUMFRAMES; i++) {
+ ifd = &urb->iso_frame_desc[i];
+ if (ifd->status != -EINPROGRESS
+ || ifd->actual_length != 0) {
+ dev_warn(cs->dev,
+ "isochronous write: frame %d: %s, "
+ "%d of %d bytes sent\n",
+ i, get_usb_statmsg(ifd->status),
+ ifd->actual_length, ifd->length);
+ offset = (ifd->offset +
+ ifd->actual_length)
+ % BAS_OUTBUFSIZE;
+ break;
+ }
+ }
+#endif
+ break;
+ case -EPIPE: /* stall - probably underrun */
+ dev_err(cs->dev, "isochronous write stalled\n");
+ error_hangup(bcs);
+ break;
+ default: /* severe trouble */
+ dev_warn(cs->dev, "isochronous write: %s\n",
+ get_usb_statmsg(status));
+ }
+
+ /* mark the write buffer area covered by this URB as free */
+ if (done->limit >= 0)
+ ubc->isooutbuf->read = done->limit;
+
+ /* mark URB as free */
+ spin_lock_irqsave(&ubc->isooutlock, flags);
+ next = ubc->isooutfree;
+ ubc->isooutfree = done;
+ spin_unlock_irqrestore(&ubc->isooutlock, flags);
+ if (next) {
+ /* only one URB still active - resubmit one */
+ rc = submit_iso_write_urb(next);
+ if (unlikely(rc <= 0 && rc != -ENODEV)) {
+ /* couldn't submit */
+ error_hangup(bcs);
+ }
+ }
+ }
+
+ /* process queued SKBs */
+ while ((skb = skb_dequeue(&bcs->squeue))) {
+ /* copy to output buffer, doing L2 encapsulation */
+ len = skb->len;
+ if (gigaset_isoc_buildframe(bcs, skb->data, len) == -EAGAIN) {
+ /* insufficient buffer space, push back onto queue */
+ skb_queue_head(&bcs->squeue, skb);
+ gig_dbg(DEBUG_ISO, "%s: skb requeued, qlen=%d",
+ __func__, skb_queue_len(&bcs->squeue));
+ break;
+ }
+ skb_pull(skb, len);
+ gigaset_skb_sent(bcs, skb);
+ dev_kfree_skb_any(skb);
+ }
+}
+
+/* Isochronous Read - Bottom Half */
+/* ============================== */
+
+/* read_iso_tasklet
+ * tasklet scheduled when an isochronous input URB from the Gigaset device
+ * has completed
+ * parameter:
+ * data B channel state structure
+ */
+static void read_iso_tasklet(unsigned long data)
+{
+ struct bc_state *bcs = (struct bc_state *) data;
+ struct bas_bc_state *ubc = bcs->hw.bas;
+ struct cardstate *cs = bcs->cs;
+ struct urb *urb;
+ int status;
+ char *rcvbuf;
+ unsigned long flags;
+ int totleft, numbytes, offset, frame, rc;
+
+ /* loop while more completed URBs arrive in the meantime */
+ for (;;) {
+ /* retrieve URB */
+ spin_lock_irqsave(&ubc->isoinlock, flags);
+ if (!(urb = ubc->isoindone)) {
+ spin_unlock_irqrestore(&ubc->isoinlock, flags);
+ return;
+ }
+ status = ubc->isoinstatus;
+ ubc->isoindone = NULL;
+ if (unlikely(ubc->loststatus != -EINPROGRESS)) {
+ dev_warn(cs->dev,
+ "isochronous read overrun, "
+ "dropped URB with status: %s, %d bytes lost\n",
+ get_usb_statmsg(ubc->loststatus),
+ ubc->isoinlost);
+ ubc->loststatus = -EINPROGRESS;
+ }
+ spin_unlock_irqrestore(&ubc->isoinlock, flags);
+
+ if (unlikely(!(ubc->running))) {
+ gig_dbg(DEBUG_ISO,
+ "%s: channel not running, "
+ "dropped URB with status: %s",
+ __func__, get_usb_statmsg(status));
+ return;
+ }
+
+ switch (status) {
+ case 0: /* normal completion */
+ break;
+ case -EXDEV: /* inspect individual frames
+ (we do that anyway) */
+ gig_dbg(DEBUG_ISO, "%s: URB partially completed",
+ __func__);
+ break;
+ case -ENOENT:
+ case -ECONNRESET:
+ case -EINPROGRESS:
+ gig_dbg(DEBUG_ISO, "%s: %s",
+ __func__, get_usb_statmsg(status));
+ continue; /* -> skip */
+ case -EPIPE:
+ dev_err(cs->dev, "isochronous read stalled\n");
+ error_hangup(bcs);
+ continue; /* -> skip */
+ default: /* severe trouble */
+ dev_warn(cs->dev, "isochronous read: %s\n",
+ get_usb_statmsg(status));
+ goto error;
+ }
+
+ rcvbuf = urb->transfer_buffer;
+ totleft = urb->actual_length;
+ for (frame = 0; totleft > 0 && frame < BAS_NUMFRAMES; frame++) {
+ if (unlikely(urb->iso_frame_desc[frame].status)) {
+ dev_warn(cs->dev,
+ "isochronous read: frame %d: %s\n",
+ frame,
+ get_usb_statmsg(
+ urb->iso_frame_desc[frame].status));
+ break;
+ }
+ numbytes = urb->iso_frame_desc[frame].actual_length;
+ if (unlikely(numbytes > BAS_MAXFRAME)) {
+ dev_warn(cs->dev,
+ "isochronous read: frame %d: "
+ "numbytes (%d) > BAS_MAXFRAME\n",
+ frame, numbytes);
+ break;
+ }
+ if (unlikely(numbytes > totleft)) {
+ dev_warn(cs->dev,
+ "isochronous read: frame %d: "
+ "numbytes (%d) > totleft (%d)\n",
+ frame, numbytes, totleft);
+ break;
+ }
+ offset = urb->iso_frame_desc[frame].offset;
+ if (unlikely(offset + numbytes > BAS_INBUFSIZE)) {
+ dev_warn(cs->dev,
+ "isochronous read: frame %d: "
+ "offset (%d) + numbytes (%d) "
+ "> BAS_INBUFSIZE\n",
+ frame, offset, numbytes);
+ break;
+ }
+ gigaset_isoc_receive(rcvbuf + offset, numbytes, bcs);
+ totleft -= numbytes;
+ }
+ if (unlikely(totleft > 0))
+ dev_warn(cs->dev,
+ "isochronous read: %d data bytes missing\n",
+ totleft);
+
+ error:
+ /* URB processed, resubmit */
+ for (frame = 0; frame < BAS_NUMFRAMES; frame++) {
+ urb->iso_frame_desc[frame].status = 0;
+ urb->iso_frame_desc[frame].actual_length = 0;
+ }
+ /* urb->dev is clobbered by USB subsystem */
+ urb->dev = bcs->cs->hw.bas->udev;
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->number_of_packets = BAS_NUMFRAMES;
+ rc = usb_submit_urb(urb, GFP_ATOMIC);
+ if (unlikely(rc != 0 && rc != -ENODEV)) {
+ dev_err(cs->dev,
+ "could not resubmit isochronous read URB: %s\n",
+ get_usb_rcmsg(rc));
+ dump_urb(DEBUG_ISO, "resubmit iso read", urb);
+ error_hangup(bcs);
+ }
+ }
+}
+
+/* Channel Operations */
+/* ================== */
+
+/* req_timeout
+ * timeout routine for control output request
+ * argument:
+ * B channel control structure
+ */
+static void req_timeout(unsigned long data)
+{
+ struct bc_state *bcs = (struct bc_state *) data;
+ struct bas_cardstate *ucs = bcs->cs->hw.bas;
+ int pending;
+ unsigned long flags;
+
+ check_pending(ucs);
+
+ spin_lock_irqsave(&ucs->lock, flags);
+ pending = ucs->pending;
+ ucs->pending = 0;
+ spin_unlock_irqrestore(&ucs->lock, flags);
+
+ switch (pending) {
+ case 0: /* no pending request */
+ gig_dbg(DEBUG_USBREQ, "%s: no request pending", __func__);
+ break;
+
+ case HD_OPEN_ATCHANNEL:
+ dev_err(bcs->cs->dev, "timeout opening AT channel\n");
+ error_reset(bcs->cs);
+ break;
+
+ case HD_OPEN_B2CHANNEL:
+ case HD_OPEN_B1CHANNEL:
+ dev_err(bcs->cs->dev, "timeout opening channel %d\n",
+ bcs->channel + 1);
+ error_hangup(bcs);
+ break;
+
+ case HD_CLOSE_ATCHANNEL:
+ dev_err(bcs->cs->dev, "timeout closing AT channel\n");
+ break;
+
+ case HD_CLOSE_B2CHANNEL:
+ case HD_CLOSE_B1CHANNEL:
+ dev_err(bcs->cs->dev, "timeout closing channel %d\n",
+ bcs->channel + 1);
+ error_reset(bcs->cs);
+ break;
+
+ default:
+ dev_warn(bcs->cs->dev, "request 0x%02x timed out, clearing\n",
+ pending);
+ }
+
+ wake_up(&ucs->waitqueue);
+}
+
+/* write_ctrl_callback
+ * USB completion handler for control pipe output
+ * called by the USB subsystem in interrupt context
+ * parameter:
+ * urb USB request block of completed request
+ * urb->context = hardware specific controller state structure
+ */
+static void write_ctrl_callback(struct urb *urb)
+{
+ struct bas_cardstate *ucs = urb->context;
+ int status = urb->status;
+ int rc;
+ unsigned long flags;
+
+ /* check status */
+ switch (status) {
+ case 0: /* normal completion */
+ spin_lock_irqsave(&ucs->lock, flags);
+ switch (ucs->pending) {
+ case HD_DEVICE_INIT_ACK: /* no reply expected */
+ del_timer(&ucs->timer_ctrl);
+ ucs->pending = 0;
+ break;
+ }
+ spin_unlock_irqrestore(&ucs->lock, flags);
+ return;
+
+ case -ENOENT: /* cancelled */
+ case -ECONNRESET: /* cancelled (async) */
+ case -EINPROGRESS: /* pending */
+ case -ENODEV: /* device removed */
+ case -ESHUTDOWN: /* device shut down */
+ /* ignore silently */
+ gig_dbg(DEBUG_USBREQ, "%s: %s",
+ __func__, get_usb_statmsg(status));
+ break;
+
+ default: /* any failure */
+ /* don't retry if suspend requested */
+ if (++ucs->retry_ctrl > BAS_RETRY ||
+ (ucs->basstate & BS_SUSPEND)) {
+ dev_err(&ucs->interface->dev,
+ "control request 0x%02x failed: %s\n",
+ ucs->dr_ctrl.bRequest,
+ get_usb_statmsg(status));
+ break; /* give up */
+ }
+ dev_notice(&ucs->interface->dev,
+ "control request 0x%02x: %s, retry %d\n",
+ ucs->dr_ctrl.bRequest, get_usb_statmsg(status),
+ ucs->retry_ctrl);
+ /* urb->dev is clobbered by USB subsystem */
+ urb->dev = ucs->udev;
+ rc = usb_submit_urb(urb, GFP_ATOMIC);
+ if (unlikely(rc)) {
+ dev_err(&ucs->interface->dev,
+ "could not resubmit request 0x%02x: %s\n",
+ ucs->dr_ctrl.bRequest, get_usb_rcmsg(rc));
+ break;
+ }
+ /* resubmitted */
+ return;
+ }
+
+ /* failed, clear pending request */
+ spin_lock_irqsave(&ucs->lock, flags);
+ del_timer(&ucs->timer_ctrl);
+ ucs->pending = 0;
+ spin_unlock_irqrestore(&ucs->lock, flags);
+ wake_up(&ucs->waitqueue);
+}
+
+/* req_submit
+ * submit a control output request without message buffer to the Gigaset base
+ * and optionally start a timeout
+ * parameters:
+ * bcs B channel control structure
+ * req control request code (HD_*)
+ * val control request parameter value (set to 0 if unused)
+ * timeout timeout in seconds (0: no timeout)
+ * return value:
+ * 0 on success
+ * -EBUSY if another request is pending
+ * any URB submission error code
+ */
+static int req_submit(struct bc_state *bcs, int req, int val, int timeout)
+{
+ struct bas_cardstate *ucs = bcs->cs->hw.bas;
+ int ret;
+ unsigned long flags;
+
+ gig_dbg(DEBUG_USBREQ, "-------> 0x%02x (%d)", req, val);
+
+ spin_lock_irqsave(&ucs->lock, flags);
+ if (ucs->pending) {
+ spin_unlock_irqrestore(&ucs->lock, flags);
+ dev_err(bcs->cs->dev,
+ "submission of request 0x%02x failed: "
+ "request 0x%02x still pending\n",
+ req, ucs->pending);
+ return -EBUSY;
+ }
+
+ ucs->dr_ctrl.bRequestType = OUT_VENDOR_REQ;
+ ucs->dr_ctrl.bRequest = req;
+ ucs->dr_ctrl.wValue = cpu_to_le16(val);
+ ucs->dr_ctrl.wIndex = 0;
+ ucs->dr_ctrl.wLength = 0;
+ usb_fill_control_urb(ucs->urb_ctrl, ucs->udev,
+ usb_sndctrlpipe(ucs->udev, 0),
+ (unsigned char*) &ucs->dr_ctrl, NULL, 0,
+ write_ctrl_callback, ucs);
+ ucs->retry_ctrl = 0;
+ ret = usb_submit_urb(ucs->urb_ctrl, GFP_ATOMIC);
+ if (unlikely(ret)) {
+ dev_err(bcs->cs->dev, "could not submit request 0x%02x: %s\n",
+ req, get_usb_rcmsg(ret));
+ spin_unlock_irqrestore(&ucs->lock, flags);
+ return ret;
+ }
+ ucs->pending = req;
+
+ if (timeout > 0) {
+ gig_dbg(DEBUG_USBREQ, "setting timeout of %d/10 secs", timeout);
+ ucs->timer_ctrl.expires = jiffies + timeout * HZ / 10;
+ ucs->timer_ctrl.data = (unsigned long) bcs;
+ ucs->timer_ctrl.function = req_timeout;
+ add_timer(&ucs->timer_ctrl);
+ }
+
+ spin_unlock_irqrestore(&ucs->lock, flags);
+ return 0;
+}
+
+/* gigaset_init_bchannel
+ * called by common.c to connect a B channel
+ * initialize isochronous I/O and tell the Gigaset base to open the channel
+ * argument:
+ * B channel control structure
+ * return value:
+ * 0 on success, error code < 0 on error
+ */
+static int gigaset_init_bchannel(struct bc_state *bcs)
+{
+ struct cardstate *cs = bcs->cs;
+ int req, ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ if (unlikely(!cs->connected)) {
+ gig_dbg(DEBUG_USBREQ, "%s: not connected", __func__);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return -ENODEV;
+ }
+
+ if (cs->hw.bas->basstate & BS_SUSPEND) {
+ dev_notice(cs->dev,
+ "not starting isochronous I/O, "
+ "suspend in progress\n");
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return -EHOSTUNREACH;
+ }
+
+ if ((ret = starturbs(bcs)) < 0) {
+ dev_err(cs->dev,
+ "could not start isochronous I/O for channel B%d: %s\n",
+ bcs->channel + 1,
+ ret == -EFAULT ? "null URB" : get_usb_rcmsg(ret));
+ if (ret != -ENODEV)
+ error_hangup(bcs);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return ret;
+ }
+
+ req = bcs->channel ? HD_OPEN_B2CHANNEL : HD_OPEN_B1CHANNEL;
+ if ((ret = req_submit(bcs, req, 0, BAS_TIMEOUT)) < 0) {
+ dev_err(cs->dev, "could not open channel B%d\n",
+ bcs->channel + 1);
+ stopurbs(bcs->hw.bas);
+ if (ret != -ENODEV)
+ error_hangup(bcs);
+ }
+
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return ret;
+}
+
+/* gigaset_close_bchannel
+ * called by common.c to disconnect a B channel
+ * tell the Gigaset base to close the channel
+ * stopping isochronous I/O and LL notification will be done when the
+ * acknowledgement for the close arrives
+ * argument:
+ * B channel control structure
+ * return value:
+ * 0 on success, error code < 0 on error
+ */
+static int gigaset_close_bchannel(struct bc_state *bcs)
+{
+ struct cardstate *cs = bcs->cs;
+ int req, ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ if (unlikely(!cs->connected)) {
+ spin_unlock_irqrestore(&cs->lock, flags);
+ gig_dbg(DEBUG_USBREQ, "%s: not connected", __func__);
+ return -ENODEV;
+ }
+
+ if (!(cs->hw.bas->basstate & (bcs->channel ? BS_B2OPEN : BS_B1OPEN))) {
+ /* channel not running: just signal common.c */
+ spin_unlock_irqrestore(&cs->lock, flags);
+ gigaset_bchannel_down(bcs);
+ return 0;
+ }
+
+ /* channel running: tell device to close it */
+ req = bcs->channel ? HD_CLOSE_B2CHANNEL : HD_CLOSE_B1CHANNEL;
+ if ((ret = req_submit(bcs, req, 0, BAS_TIMEOUT)) < 0)
+ dev_err(cs->dev, "closing channel B%d failed\n",
+ bcs->channel + 1);
+
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return ret;
+}
+
+/* Device Operations */
+/* ================= */
+
+/* complete_cb
+ * unqueue first command buffer from queue, waking any sleepers
+ * must be called with cs->cmdlock held
+ * parameter:
+ * cs controller state structure
+ */
+static void complete_cb(struct cardstate *cs)
+{
+ struct cmdbuf_t *cb = cs->cmdbuf;
+
+ /* unqueue completed buffer */
+ cs->cmdbytes -= cs->curlen;
+ gig_dbg(DEBUG_TRANSCMD|DEBUG_LOCKCMD,
+ "write_command: sent %u bytes, %u left",
+ cs->curlen, cs->cmdbytes);
+ if ((cs->cmdbuf = cb->next) != NULL) {
+ cs->cmdbuf->prev = NULL;
+ cs->curlen = cs->cmdbuf->len;
+ } else {
+ cs->lastcmdbuf = NULL;
+ cs->curlen = 0;
+ }
+
+ if (cb->wake_tasklet)
+ tasklet_schedule(cb->wake_tasklet);
+
+ kfree(cb);
+}
+
+/* write_command_callback
+ * USB completion handler for AT command transmission
+ * called by the USB subsystem in interrupt context
+ * parameter:
+ * urb USB request block of completed request
+ * urb->context = controller state structure
+ */
+static void write_command_callback(struct urb *urb)
+{
+ struct cardstate *cs = urb->context;
+ struct bas_cardstate *ucs = cs->hw.bas;
+ int status = urb->status;
+ unsigned long flags;
+
+ update_basstate(ucs, 0, BS_ATWRPEND);
+ wake_up(&ucs->waitqueue);
+
+ /* check status */
+ switch (status) {
+ case 0: /* normal completion */
+ break;
+ case -ENOENT: /* cancelled */
+ case -ECONNRESET: /* cancelled (async) */
+ case -EINPROGRESS: /* pending */
+ case -ENODEV: /* device removed */
+ case -ESHUTDOWN: /* device shut down */
+ /* ignore silently */
+ gig_dbg(DEBUG_USBREQ, "%s: %s",
+ __func__, get_usb_statmsg(status));
+ return;
+ default: /* any failure */
+ if (++ucs->retry_cmd_out > BAS_RETRY) {
+ dev_warn(cs->dev,
+ "command write: %s, "
+ "giving up after %d retries\n",
+ get_usb_statmsg(status),
+ ucs->retry_cmd_out);
+ break;
+ }
+ if (ucs->basstate & BS_SUSPEND) {
+ dev_warn(cs->dev,
+ "command write: %s, "
+ "won't retry - suspend requested\n",
+ get_usb_statmsg(status));
+ break;
+ }
+ if (cs->cmdbuf == NULL) {
+ dev_warn(cs->dev,
+ "command write: %s, "
+ "cannot retry - cmdbuf gone\n",
+ get_usb_statmsg(status));
+ break;
+ }
+ dev_notice(cs->dev, "command write: %s, retry %d\n",
+ get_usb_statmsg(status), ucs->retry_cmd_out);
+ if (atwrite_submit(cs, cs->cmdbuf->buf, cs->cmdbuf->len) >= 0)
+ /* resubmitted - bypass regular exit block */
+ return;
+ /* command send failed, assume base still waiting */
+ update_basstate(ucs, BS_ATREADY, 0);
+ }
+
+ spin_lock_irqsave(&cs->cmdlock, flags);
+ if (cs->cmdbuf != NULL)
+ complete_cb(cs);
+ spin_unlock_irqrestore(&cs->cmdlock, flags);
+}
+
+/* atrdy_timeout
+ * timeout routine for AT command transmission
+ * argument:
+ * controller state structure
+ */
+static void atrdy_timeout(unsigned long data)
+{
+ struct cardstate *cs = (struct cardstate *) data;
+ struct bas_cardstate *ucs = cs->hw.bas;
+
+ dev_warn(cs->dev, "timeout waiting for HD_READY_SEND_ATDATA\n");
+
+ /* fake the missing signal - what else can I do? */
+ update_basstate(ucs, BS_ATREADY, BS_ATTIMER);
+ start_cbsend(cs);
+}
+
+/* atwrite_submit
+ * submit an HD_WRITE_ATMESSAGE command URB
+ * parameters:
+ * cs controller state structure
+ * buf buffer containing command to send
+ * len length of command to send
+ * return value:
+ * 0 on success
+ * -EBUSY if another request is pending
+ * any URB submission error code
+ */
+static int atwrite_submit(struct cardstate *cs, unsigned char *buf, int len)
+{
+ struct bas_cardstate *ucs = cs->hw.bas;
+ int rc;
+
+ gig_dbg(DEBUG_USBREQ, "-------> HD_WRITE_ATMESSAGE (%d)", len);
+
+ if (update_basstate(ucs, BS_ATWRPEND, 0) & BS_ATWRPEND) {
+ dev_err(cs->dev,
+ "could not submit HD_WRITE_ATMESSAGE: URB busy\n");
+ return -EBUSY;
+ }
+
+ ucs->dr_cmd_out.bRequestType = OUT_VENDOR_REQ;
+ ucs->dr_cmd_out.bRequest = HD_WRITE_ATMESSAGE;
+ ucs->dr_cmd_out.wValue = 0;
+ ucs->dr_cmd_out.wIndex = 0;
+ ucs->dr_cmd_out.wLength = cpu_to_le16(len);
+ usb_fill_control_urb(ucs->urb_cmd_out, ucs->udev,
+ usb_sndctrlpipe(ucs->udev, 0),
+ (unsigned char*) &ucs->dr_cmd_out, buf, len,
+ write_command_callback, cs);
+ rc = usb_submit_urb(ucs->urb_cmd_out, GFP_ATOMIC);
+ if (unlikely(rc)) {
+ update_basstate(ucs, 0, BS_ATWRPEND);
+ dev_err(cs->dev, "could not submit HD_WRITE_ATMESSAGE: %s\n",
+ get_usb_rcmsg(rc));
+ return rc;
+ }
+
+ /* submitted successfully, start timeout if necessary */
+ if (!(update_basstate(ucs, BS_ATTIMER, BS_ATREADY) & BS_ATTIMER)) {
+ gig_dbg(DEBUG_OUTPUT, "setting ATREADY timeout of %d/10 secs",
+ ATRDY_TIMEOUT);
+ ucs->timer_atrdy.expires = jiffies + ATRDY_TIMEOUT * HZ / 10;
+ ucs->timer_atrdy.data = (unsigned long) cs;
+ ucs->timer_atrdy.function = atrdy_timeout;
+ add_timer(&ucs->timer_atrdy);
+ }
+ return 0;
+}
+
+/* start_cbsend
+ * start transmission of AT command queue if necessary
+ * parameter:
+ * cs controller state structure
+ * return value:
+ * 0 on success
+ * error code < 0 on error
+ */
+static int start_cbsend(struct cardstate *cs)
+{
+ struct cmdbuf_t *cb;
+ struct bas_cardstate *ucs = cs->hw.bas;
+ unsigned long flags;
+ int rc;
+ int retval = 0;
+
+ /* check if suspend requested */
+ if (ucs->basstate & BS_SUSPEND) {
+ gig_dbg(DEBUG_TRANSCMD|DEBUG_LOCKCMD, "suspending");
+ return -EHOSTUNREACH;
+ }
+
+ /* check if AT channel is open */
+ if (!(ucs->basstate & BS_ATOPEN)) {
+ gig_dbg(DEBUG_TRANSCMD|DEBUG_LOCKCMD, "AT channel not open");
+ rc = req_submit(cs->bcs, HD_OPEN_ATCHANNEL, 0, BAS_TIMEOUT);
+ if (rc < 0) {
+ /* flush command queue */
+ spin_lock_irqsave(&cs->cmdlock, flags);
+ while (cs->cmdbuf != NULL)
+ complete_cb(cs);
+ spin_unlock_irqrestore(&cs->cmdlock, flags);
+ }
+ return rc;
+ }
+
+ /* try to send first command in queue */
+ spin_lock_irqsave(&cs->cmdlock, flags);
+
+ while ((cb = cs->cmdbuf) != NULL && (ucs->basstate & BS_ATREADY)) {
+ ucs->retry_cmd_out = 0;
+ rc = atwrite_submit(cs, cb->buf, cb->len);
+ if (unlikely(rc)) {
+ retval = rc;
+ complete_cb(cs);
+ }
+ }
+
+ spin_unlock_irqrestore(&cs->cmdlock, flags);
+ return retval;
+}
+
+/* gigaset_write_cmd
+ * This function is called by the device independent part of the driver
+ * to transmit an AT command string to the Gigaset device.
+ * It encapsulates the device specific method for transmission over the
+ * direct USB connection to the base.
+ * The command string is added to the queue of commands to send, and
+ * USB transmission is started if necessary.
+ * parameters:
+ * cs controller state structure
+ * buf command string to send
+ * len number of bytes to send (max. IF_WRITEBUF)
+ * wake_tasklet tasklet to run when transmission is completed
+ * (NULL if none)
+ * return value:
+ * number of bytes queued on success
+ * error code < 0 on error
+ */
+static int gigaset_write_cmd(struct cardstate *cs,
+ const unsigned char *buf, int len,
+ struct tasklet_struct *wake_tasklet)
+{
+ struct cmdbuf_t *cb;
+ unsigned long flags;
+ int rc;
+
+ gigaset_dbg_buffer(cs->mstate != MS_LOCKED ?
+ DEBUG_TRANSCMD : DEBUG_LOCKCMD,
+ "CMD Transmit", len, buf);
+
+ if (len <= 0) {
+ /* nothing to do */
+ rc = 0;
+ goto notqueued;
+ }
+
+ if (len > IF_WRITEBUF)
+ len = IF_WRITEBUF;
+ if (!(cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC))) {
+ dev_err(cs->dev, "%s: out of memory\n", __func__);
+ rc = -ENOMEM;
+ goto notqueued;
+ }
+
+ memcpy(cb->buf, buf, len);
+ cb->len = len;
+ cb->offset = 0;
+ cb->next = NULL;
+ cb->wake_tasklet = wake_tasklet;
+
+ spin_lock_irqsave(&cs->cmdlock, flags);
+ cb->prev = cs->lastcmdbuf;
+ if (cs->lastcmdbuf)
+ cs->lastcmdbuf->next = cb;
+ else {
+ cs->cmdbuf = cb;
+ cs->curlen = len;
+ }
+ cs->cmdbytes += len;
+ cs->lastcmdbuf = cb;
+ spin_unlock_irqrestore(&cs->cmdlock, flags);
+
+ spin_lock_irqsave(&cs->lock, flags);
+ if (unlikely(!cs->connected)) {
+ spin_unlock_irqrestore(&cs->lock, flags);
+ gig_dbg(DEBUG_USBREQ, "%s: not connected", __func__);
+ /* flush command queue */
+ spin_lock_irqsave(&cs->cmdlock, flags);
+ while (cs->cmdbuf != NULL)
+ complete_cb(cs);
+ spin_unlock_irqrestore(&cs->cmdlock, flags);
+ return -ENODEV;
+ }
+ rc = start_cbsend(cs);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return rc < 0 ? rc : len;
+
+notqueued: /* request handled without queuing */
+ if (wake_tasklet)
+ tasklet_schedule(wake_tasklet);
+ return rc;
+}
+
+/* gigaset_write_room
+ * tty_driver.write_room interface routine
+ * return number of characters the driver will accept to be written via
+ * gigaset_write_cmd
+ * parameter:
+ * controller state structure
+ * return value:
+ * number of characters
+ */
+static int gigaset_write_room(struct cardstate *cs)
+{
+ return IF_WRITEBUF;
+}
+
+/* gigaset_chars_in_buffer
+ * tty_driver.chars_in_buffer interface routine
+ * return number of characters waiting to be sent
+ * parameter:
+ * controller state structure
+ * return value:
+ * number of characters
+ */
+static int gigaset_chars_in_buffer(struct cardstate *cs)
+{
+ return cs->cmdbytes;
+}
+
+/* gigaset_brkchars
+ * implementation of ioctl(GIGASET_BRKCHARS)
+ * parameter:
+ * controller state structure
+ * return value:
+ * -EINVAL (unimplemented function)
+ */
+static int gigaset_brkchars(struct cardstate *cs, const unsigned char buf[6])
+{
+ return -EINVAL;
+}
+
+
+/* Device Initialization/Shutdown */
+/* ============================== */
+
+/* Free hardware dependent part of the B channel structure
+ * parameter:
+ * bcs B channel structure
+ * return value:
+ * !=0 on success
+ */
+static int gigaset_freebcshw(struct bc_state *bcs)
+{
+ struct bas_bc_state *ubc = bcs->hw.bas;
+ int i;
+
+ if (!ubc)
+ return 0;
+
+ /* kill URBs and tasklets before freeing - better safe than sorry */
+ ubc->running = 0;
+ gig_dbg(DEBUG_INIT, "%s: killing iso URBs", __func__);
+ for (i = 0; i < BAS_OUTURBS; ++i) {
+ usb_kill_urb(ubc->isoouturbs[i].urb);
+ usb_free_urb(ubc->isoouturbs[i].urb);
+ }
+ for (i = 0; i < BAS_INURBS; ++i) {
+ usb_kill_urb(ubc->isoinurbs[i]);
+ usb_free_urb(ubc->isoinurbs[i]);
+ }
+ tasklet_kill(&ubc->sent_tasklet);
+ tasklet_kill(&ubc->rcvd_tasklet);
+ kfree(ubc->isooutbuf);
+ kfree(ubc);
+ bcs->hw.bas = NULL;
+ return 1;
+}
+
+/* Initialize hardware dependent part of the B channel structure
+ * parameter:
+ * bcs B channel structure
+ * return value:
+ * !=0 on success
+ */
+static int gigaset_initbcshw(struct bc_state *bcs)
+{
+ int i;
+ struct bas_bc_state *ubc;
+
+ bcs->hw.bas = ubc = kmalloc(sizeof(struct bas_bc_state), GFP_KERNEL);
+ if (!ubc) {
+ err("could not allocate bas_bc_state");
+ return 0;
+ }
+
+ ubc->running = 0;
+ atomic_set(&ubc->corrbytes, 0);
+ spin_lock_init(&ubc->isooutlock);
+ for (i = 0; i < BAS_OUTURBS; ++i) {
+ ubc->isoouturbs[i].urb = NULL;
+ ubc->isoouturbs[i].bcs = bcs;
+ }
+ ubc->isooutdone = ubc->isooutfree = ubc->isooutovfl = NULL;
+ ubc->numsub = 0;
+ if (!(ubc->isooutbuf = kmalloc(sizeof(struct isowbuf_t), GFP_KERNEL))) {
+ err("could not allocate isochronous output buffer");
+ kfree(ubc);
+ bcs->hw.bas = NULL;
+ return 0;
+ }
+ tasklet_init(&ubc->sent_tasklet,
+ &write_iso_tasklet, (unsigned long) bcs);
+
+ spin_lock_init(&ubc->isoinlock);
+ for (i = 0; i < BAS_INURBS; ++i)
+ ubc->isoinurbs[i] = NULL;
+ ubc->isoindone = NULL;
+ ubc->loststatus = -EINPROGRESS;
+ ubc->isoinlost = 0;
+ ubc->seqlen = 0;
+ ubc->inbyte = 0;
+ ubc->inbits = 0;
+ ubc->goodbytes = 0;
+ ubc->alignerrs = 0;
+ ubc->fcserrs = 0;
+ ubc->frameerrs = 0;
+ ubc->giants = 0;
+ ubc->runts = 0;
+ ubc->aborts = 0;
+ ubc->shared0s = 0;
+ ubc->stolen0s = 0;
+ tasklet_init(&ubc->rcvd_tasklet,
+ &read_iso_tasklet, (unsigned long) bcs);
+ return 1;
+}
+
+static void gigaset_reinitbcshw(struct bc_state *bcs)
+{
+ struct bas_bc_state *ubc = bcs->hw.bas;
+
+ bcs->hw.bas->running = 0;
+ atomic_set(&bcs->hw.bas->corrbytes, 0);
+ bcs->hw.bas->numsub = 0;
+ spin_lock_init(&ubc->isooutlock);
+ spin_lock_init(&ubc->isoinlock);
+ ubc->loststatus = -EINPROGRESS;
+}
+
+static void gigaset_freecshw(struct cardstate *cs)
+{
+ /* timers, URBs and rcvbuf are disposed of in disconnect */
+ kfree(cs->hw.bas);
+ cs->hw.bas = NULL;
+}
+
+static int gigaset_initcshw(struct cardstate *cs)
+{
+ struct bas_cardstate *ucs;
+
+ cs->hw.bas = ucs = kmalloc(sizeof *ucs, GFP_KERNEL);
+ if (!ucs)
+ return 0;
+
+ ucs->urb_cmd_in = NULL;
+ ucs->urb_cmd_out = NULL;
+ ucs->rcvbuf = NULL;
+ ucs->rcvbuf_size = 0;
+
+ spin_lock_init(&ucs->lock);
+ ucs->pending = 0;
+
+ ucs->basstate = 0;
+ init_timer(&ucs->timer_ctrl);
+ init_timer(&ucs->timer_atrdy);
+ init_timer(&ucs->timer_cmd_in);
+ init_waitqueue_head(&ucs->waitqueue);
+
+ return 1;
+}
+
+/* freeurbs
+ * unlink and deallocate all URBs unconditionally
+ * caller must make sure that no commands are still in progress
+ * parameter:
+ * cs controller state structure
+ */
+static void freeurbs(struct cardstate *cs)
+{
+ struct bas_cardstate *ucs = cs->hw.bas;
+ struct bas_bc_state *ubc;
+ int i, j;
+
+ gig_dbg(DEBUG_INIT, "%s: killing URBs", __func__);
+ for (j = 0; j < BAS_CHANNELS; ++j) {
+ ubc = cs->bcs[j].hw.bas;
+ for (i = 0; i < BAS_OUTURBS; ++i) {
+ usb_kill_urb(ubc->isoouturbs[i].urb);
+ usb_free_urb(ubc->isoouturbs[i].urb);
+ ubc->isoouturbs[i].urb = NULL;
+ }
+ for (i = 0; i < BAS_INURBS; ++i) {
+ usb_kill_urb(ubc->isoinurbs[i]);
+ usb_free_urb(ubc->isoinurbs[i]);
+ ubc->isoinurbs[i] = NULL;
+ }
+ }
+ usb_kill_urb(ucs->urb_int_in);
+ usb_free_urb(ucs->urb_int_in);
+ ucs->urb_int_in = NULL;
+ usb_kill_urb(ucs->urb_cmd_out);
+ usb_free_urb(ucs->urb_cmd_out);
+ ucs->urb_cmd_out = NULL;
+ usb_kill_urb(ucs->urb_cmd_in);
+ usb_free_urb(ucs->urb_cmd_in);
+ ucs->urb_cmd_in = NULL;
+ usb_kill_urb(ucs->urb_ctrl);
+ usb_free_urb(ucs->urb_ctrl);
+ ucs->urb_ctrl = NULL;
+}
+
+/* gigaset_probe
+ * This function is called when a new USB device is connected.
+ * It checks whether the new device is handled by this driver.
+ */
+static int gigaset_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_host_interface *hostif;
+ struct usb_device *udev = interface_to_usbdev(interface);
+ struct cardstate *cs = NULL;
+ struct bas_cardstate *ucs = NULL;
+ struct bas_bc_state *ubc;
+ struct usb_endpoint_descriptor *endpoint;
+ int i, j;
+ int rc;
+
+ gig_dbg(DEBUG_ANY,
+ "%s: Check if device matches .. (Vendor: 0x%x, Product: 0x%x)",
+ __func__, le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct));
+
+ /* set required alternate setting */
+ hostif = interface->cur_altsetting;
+ if (hostif->desc.bAlternateSetting != 3) {
+ gig_dbg(DEBUG_ANY,
+ "%s: wrong alternate setting %d - trying to switch",
+ __func__, hostif->desc.bAlternateSetting);
+ if (usb_set_interface(udev, hostif->desc.bInterfaceNumber, 3) < 0) {
+ dev_warn(&udev->dev, "usb_set_interface failed, "
+ "device %d interface %d altsetting %d\n",
+ udev->devnum, hostif->desc.bInterfaceNumber,
+ hostif->desc.bAlternateSetting);
+ return -ENODEV;
+ }
+ hostif = interface->cur_altsetting;
+ }
+
+ /* Reject application specific interfaces
+ */
+ if (hostif->desc.bInterfaceClass != 255) {
+ dev_warn(&udev->dev, "%s: bInterfaceClass == %d\n",
+ __func__, hostif->desc.bInterfaceClass);
+ return -ENODEV;
+ }
+
+ dev_info(&udev->dev,
+ "%s: Device matched (Vendor: 0x%x, Product: 0x%x)\n",
+ __func__, le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct));
+
+ /* allocate memory for our device state and intialize it */
+ cs = gigaset_initcs(driver, BAS_CHANNELS, 0, 0, cidmode,
+ GIGASET_MODULENAME);
+ if (!cs)
+ return -ENODEV;
+ ucs = cs->hw.bas;
+
+ /* save off device structure ptrs for later use */
+ usb_get_dev(udev);
+ ucs->udev = udev;
+ ucs->interface = interface;
+ cs->dev = &interface->dev;
+
+ /* allocate URBs:
+ * - one for the interrupt pipe
+ * - three for the different uses of the default control pipe
+ * - three for each isochronous pipe
+ */
+ if (!(ucs->urb_int_in = usb_alloc_urb(0, GFP_KERNEL)) ||
+ !(ucs->urb_cmd_in = usb_alloc_urb(0, GFP_KERNEL)) ||
+ !(ucs->urb_cmd_out = usb_alloc_urb(0, GFP_KERNEL)) ||
+ !(ucs->urb_ctrl = usb_alloc_urb(0, GFP_KERNEL)))
+ goto allocerr;
+
+ for (j = 0; j < BAS_CHANNELS; ++j) {
+ ubc = cs->bcs[j].hw.bas;
+ for (i = 0; i < BAS_OUTURBS; ++i)
+ if (!(ubc->isoouturbs[i].urb =
+ usb_alloc_urb(BAS_NUMFRAMES, GFP_KERNEL)))
+ goto allocerr;
+ for (i = 0; i < BAS_INURBS; ++i)
+ if (!(ubc->isoinurbs[i] =
+ usb_alloc_urb(BAS_NUMFRAMES, GFP_KERNEL)))
+ goto allocerr;
+ }
+
+ ucs->rcvbuf = NULL;
+ ucs->rcvbuf_size = 0;
+
+ /* Fill the interrupt urb and send it to the core */
+ endpoint = &hostif->endpoint[0].desc;
+ usb_fill_int_urb(ucs->urb_int_in, udev,
+ usb_rcvintpipe(udev,
+ (endpoint->bEndpointAddress) & 0x0f),
+ ucs->int_in_buf, 3, read_int_callback, cs,
+ endpoint->bInterval);
+ if ((rc = usb_submit_urb(ucs->urb_int_in, GFP_KERNEL)) != 0) {
+ dev_err(cs->dev, "could not submit interrupt URB: %s\n",
+ get_usb_rcmsg(rc));
+ goto error;
+ }
+
+ /* tell the device that the driver is ready */
+ if ((rc = req_submit(cs->bcs, HD_DEVICE_INIT_ACK, 0, 0)) != 0)
+ goto error;
+
+ /* tell common part that the device is ready */
+ if (startmode == SM_LOCKED)
+ cs->mstate = MS_LOCKED;
+
+ /* save address of controller structure */
+ usb_set_intfdata(interface, cs);
+
+ if (!gigaset_start(cs))
+ goto error;
+
+ return 0;
+
+allocerr:
+ dev_err(cs->dev, "could not allocate URBs\n");
+error:
+ freeurbs(cs);
+ usb_set_intfdata(interface, NULL);
+ gigaset_freecs(cs);
+ return -ENODEV;
+}
+
+/* gigaset_disconnect
+ * This function is called when the Gigaset base is unplugged.
+ */
+static void gigaset_disconnect(struct usb_interface *interface)
+{
+ struct cardstate *cs;
+ struct bas_cardstate *ucs;
+ int j;
+
+ cs = usb_get_intfdata(interface);
+
+ ucs = cs->hw.bas;
+
+ dev_info(cs->dev, "disconnecting Gigaset base\n");
+
+ /* mark base as not ready, all channels disconnected */
+ ucs->basstate = 0;
+
+ /* tell LL all channels are down */
+ for (j = 0; j < BAS_CHANNELS; ++j)
+ gigaset_bchannel_down(cs->bcs + j);
+
+ /* stop driver (common part) */
+ gigaset_stop(cs);
+
+ /* stop timers and URBs, free ressources */
+ del_timer_sync(&ucs->timer_ctrl);
+ del_timer_sync(&ucs->timer_atrdy);
+ del_timer_sync(&ucs->timer_cmd_in);
+ freeurbs(cs);
+ usb_set_intfdata(interface, NULL);
+ kfree(ucs->rcvbuf);
+ ucs->rcvbuf = NULL;
+ ucs->rcvbuf_size = 0;
+ usb_put_dev(ucs->udev);
+ ucs->interface = NULL;
+ ucs->udev = NULL;
+ cs->dev = NULL;
+ gigaset_freecs(cs);
+}
+
+/* gigaset_suspend
+ * This function is called before the USB connection is suspended.
+ */
+static int gigaset_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct cardstate *cs = usb_get_intfdata(intf);
+ struct bas_cardstate *ucs = cs->hw.bas;
+ int rc;
+
+ /* set suspend flag; this stops AT command/response traffic */
+ if (update_basstate(ucs, BS_SUSPEND, 0) & BS_SUSPEND) {
+ gig_dbg(DEBUG_SUSPEND, "already suspended");
+ return 0;
+ }
+
+ /* wait a bit for blocking conditions to go away */
+ rc = wait_event_timeout(ucs->waitqueue,
+ !(ucs->basstate &
+ (BS_B1OPEN|BS_B2OPEN|BS_ATRDPEND|BS_ATWRPEND)),
+ BAS_TIMEOUT*HZ/10);
+ gig_dbg(DEBUG_SUSPEND, "wait_event_timeout() -> %d", rc);
+
+ /* check for conditions preventing suspend */
+ if (ucs->basstate & (BS_B1OPEN|BS_B2OPEN|BS_ATRDPEND|BS_ATWRPEND)) {
+ dev_warn(cs->dev, "cannot suspend:\n");
+ if (ucs->basstate & BS_B1OPEN)
+ dev_warn(cs->dev, " B channel 1 open\n");
+ if (ucs->basstate & BS_B2OPEN)
+ dev_warn(cs->dev, " B channel 2 open\n");
+ if (ucs->basstate & BS_ATRDPEND)
+ dev_warn(cs->dev, " receiving AT reply\n");
+ if (ucs->basstate & BS_ATWRPEND)
+ dev_warn(cs->dev, " sending AT command\n");
+ update_basstate(ucs, 0, BS_SUSPEND);
+ return -EBUSY;
+ }
+
+ /* close AT channel if open */
+ if (ucs->basstate & BS_ATOPEN) {
+ gig_dbg(DEBUG_SUSPEND, "closing AT channel");
+ rc = req_submit(cs->bcs, HD_CLOSE_ATCHANNEL, 0, 0);
+ if (rc) {
+ update_basstate(ucs, 0, BS_SUSPEND);
+ return rc;
+ }
+ wait_event_timeout(ucs->waitqueue, !ucs->pending,
+ BAS_TIMEOUT*HZ/10);
+ /* in case of timeout, proceed anyway */
+ }
+
+ /* kill all URBs and timers that might still be pending */
+ usb_kill_urb(ucs->urb_ctrl);
+ usb_kill_urb(ucs->urb_int_in);
+ del_timer_sync(&ucs->timer_ctrl);
+
+ gig_dbg(DEBUG_SUSPEND, "suspend complete");
+ return 0;
+}
+
+/* gigaset_resume
+ * This function is called after the USB connection has been resumed.
+ */
+static int gigaset_resume(struct usb_interface *intf)
+{
+ struct cardstate *cs = usb_get_intfdata(intf);
+ struct bas_cardstate *ucs = cs->hw.bas;
+ int rc;
+
+ /* resubmit interrupt URB for spontaneous messages from base */
+ rc = usb_submit_urb(ucs->urb_int_in, GFP_KERNEL);
+ if (rc) {
+ dev_err(cs->dev, "could not resubmit interrupt URB: %s\n",
+ get_usb_rcmsg(rc));
+ return rc;
+ }
+
+ /* clear suspend flag to reallow activity */
+ update_basstate(ucs, 0, BS_SUSPEND);
+
+ gig_dbg(DEBUG_SUSPEND, "resume complete");
+ return 0;
+}
+
+/* gigaset_pre_reset
+ * This function is called before the USB connection is reset.
+ */
+static int gigaset_pre_reset(struct usb_interface *intf)
+{
+ /* handle just like suspend */
+ return gigaset_suspend(intf, PMSG_ON);
+}
+
+/* gigaset_post_reset
+ * This function is called after the USB connection has been reset.
+ */
+static int gigaset_post_reset(struct usb_interface *intf)
+{
+ /* FIXME: send HD_DEVICE_INIT_ACK? */
+
+ /* resume operations */
+ return gigaset_resume(intf);
+}
+
+
+static const struct gigaset_ops gigops = {
+ gigaset_write_cmd,
+ gigaset_write_room,
+ gigaset_chars_in_buffer,
+ gigaset_brkchars,
+ gigaset_init_bchannel,
+ gigaset_close_bchannel,
+ gigaset_initbcshw,
+ gigaset_freebcshw,
+ gigaset_reinitbcshw,
+ gigaset_initcshw,
+ gigaset_freecshw,
+ gigaset_set_modem_ctrl,
+ gigaset_baud_rate,
+ gigaset_set_line_ctrl,
+ gigaset_isoc_send_skb,
+ gigaset_isoc_input,
+};
+
+/* bas_gigaset_init
+ * This function is called after the kernel module is loaded.
+ */
+static int __init bas_gigaset_init(void)
+{
+ int result;
+
+ /* allocate memory for our driver state and intialize it */
+ if ((driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS,
+ GIGASET_MODULENAME, GIGASET_DEVNAME,
+ &gigops, THIS_MODULE)) == NULL)
+ goto error;
+
+ /* register this driver with the USB subsystem */
+ result = usb_register(&gigaset_usb_driver);
+ if (result < 0) {
+ err("usb_register failed (error %d)", -result);
+ goto error;
+ }
+
+ info(DRIVER_AUTHOR);
+ info(DRIVER_DESC);
+ return 0;
+
+error:
+ if (driver)
+ gigaset_freedriver(driver);
+ driver = NULL;
+ return -1;
+}
+
+/* bas_gigaset_exit
+ * This function is called before the kernel module is unloaded.
+ */
+static void __exit bas_gigaset_exit(void)
+{
+ struct bas_cardstate *ucs;
+ int i;
+
+ gigaset_blockdriver(driver); /* => probe will fail
+ * => no gigaset_start any more
+ */
+
+ /* stop all connected devices */
+ for (i = 0; i < driver->minors; i++) {
+ if (gigaset_shutdown(driver->cs + i) < 0)
+ continue; /* no device */
+ /* from now on, no isdn callback should be possible */
+
+ /* close all still open channels */
+ ucs = driver->cs[i].hw.bas;
+ if (ucs->basstate & BS_B1OPEN) {
+ gig_dbg(DEBUG_INIT, "closing B1 channel");
+ usb_control_msg(ucs->udev,
+ usb_sndctrlpipe(ucs->udev, 0),
+ HD_CLOSE_B1CHANNEL, OUT_VENDOR_REQ,
+ 0, 0, NULL, 0, BAS_TIMEOUT);
+ }
+ if (ucs->basstate & BS_B2OPEN) {
+ gig_dbg(DEBUG_INIT, "closing B2 channel");
+ usb_control_msg(ucs->udev,
+ usb_sndctrlpipe(ucs->udev, 0),
+ HD_CLOSE_B2CHANNEL, OUT_VENDOR_REQ,
+ 0, 0, NULL, 0, BAS_TIMEOUT);
+ }
+ if (ucs->basstate & BS_ATOPEN) {
+ gig_dbg(DEBUG_INIT, "closing AT channel");
+ usb_control_msg(ucs->udev,
+ usb_sndctrlpipe(ucs->udev, 0),
+ HD_CLOSE_ATCHANNEL, OUT_VENDOR_REQ,
+ 0, 0, NULL, 0, BAS_TIMEOUT);
+ }
+ ucs->basstate = 0;
+ }
+
+ /* deregister this driver with the USB subsystem */
+ usb_deregister(&gigaset_usb_driver);
+ /* this will call the disconnect-callback */
+ /* from now on, no disconnect/probe callback should be running */
+
+ gigaset_freedriver(driver);
+ driver = NULL;
+}
+
+
+module_init(bas_gigaset_init);
+module_exit(bas_gigaset_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/isdn/gigaset/common.c b/drivers/isdn/gigaset/common.c
new file mode 100644
index 0000000..9d3ce77
--- /dev/null
+++ b/drivers/isdn/gigaset/common.c
@@ -0,0 +1,1138 @@
+/*
+ * Stuff used by all variants of the driver
+ *
+ * Copyright (c) 2001 by Stefan Eilers,
+ * Hansjoerg Lipp <hjlipp@web.de>,
+ * Tilman Schmidt <tilman@imap.cc>.
+ *
+ * =====================================================================
+ * 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 of
+ * the License, or (at your option) any later version.
+ * =====================================================================
+ */
+
+#include "gigaset.h"
+#include <linux/ctype.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+/* Version Information */
+#define DRIVER_AUTHOR "Hansjoerg Lipp <hjlipp@web.de>, Tilman Schmidt <tilman@imap.cc>, Stefan Eilers"
+#define DRIVER_DESC "Driver for Gigaset 307x"
+
+/* Module parameters */
+int gigaset_debuglevel = DEBUG_DEFAULT;
+EXPORT_SYMBOL_GPL(gigaset_debuglevel);
+module_param_named(debug, gigaset_debuglevel, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(debug, "debug level");
+
+/* driver state flags */
+#define VALID_MINOR 0x01
+#define VALID_ID 0x02
+
+void gigaset_dbg_buffer(enum debuglevel level, const unsigned char *msg,
+ size_t len, const unsigned char *buf)
+{
+ unsigned char outbuf[80];
+ unsigned char c;
+ size_t space = sizeof outbuf - 1;
+ unsigned char *out = outbuf;
+ size_t numin = len;
+
+ while (numin--) {
+ c = *buf++;
+ if (c == '~' || c == '^' || c == '\\') {
+ if (!space--)
+ break;
+ *out++ = '\\';
+ }
+ if (c & 0x80) {
+ if (!space--)
+ break;
+ *out++ = '~';
+ c ^= 0x80;
+ }
+ if (c < 0x20 || c == 0x7f) {
+ if (!space--)
+ break;
+ *out++ = '^';
+ c ^= 0x40;
+ }
+ if (!space--)
+ break;
+ *out++ = c;
+ }
+ *out = 0;
+
+ gig_dbg(level, "%s (%u bytes): %s", msg, (unsigned) len, outbuf);
+}
+EXPORT_SYMBOL_GPL(gigaset_dbg_buffer);
+
+static int setflags(struct cardstate *cs, unsigned flags, unsigned delay)
+{
+ int r;
+
+ r = cs->ops->set_modem_ctrl(cs, cs->control_state, flags);
+ cs->control_state = flags;
+ if (r < 0)
+ return r;
+
+ if (delay) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(delay * HZ / 1000);
+ }
+
+ return 0;
+}
+
+int gigaset_enterconfigmode(struct cardstate *cs)
+{
+ int i, r;
+
+ cs->control_state = TIOCM_RTS; //FIXME
+
+ r = setflags(cs, TIOCM_DTR, 200);
+ if (r < 0)
+ goto error;
+ r = setflags(cs, 0, 200);
+ if (r < 0)
+ goto error;
+ for (i = 0; i < 5; ++i) {
+ r = setflags(cs, TIOCM_RTS, 100);
+ if (r < 0)
+ goto error;
+ r = setflags(cs, 0, 100);
+ if (r < 0)
+ goto error;
+ }
+ r = setflags(cs, TIOCM_RTS|TIOCM_DTR, 800);
+ if (r < 0)
+ goto error;
+
+ return 0;
+
+error:
+ dev_err(cs->dev, "error %d on setuartbits\n", -r);
+ cs->control_state = TIOCM_RTS|TIOCM_DTR; // FIXME is this a good value?
+ cs->ops->set_modem_ctrl(cs, 0, TIOCM_RTS|TIOCM_DTR);
+
+ return -1; //r
+}
+
+static int test_timeout(struct at_state_t *at_state)
+{
+ if (!at_state->timer_expires)
+ return 0;
+
+ if (--at_state->timer_expires) {
+ gig_dbg(DEBUG_MCMD, "decreased timer of %p to %lu",
+ at_state, at_state->timer_expires);
+ return 0;
+ }
+
+ if (!gigaset_add_event(at_state->cs, at_state, EV_TIMEOUT, NULL,
+ at_state->timer_index, NULL)) {
+ //FIXME what should we do?
+ }
+
+ return 1;
+}
+
+static void timer_tick(unsigned long data)
+{
+ struct cardstate *cs = (struct cardstate *) data;
+ unsigned long flags;
+ unsigned channel;
+ struct at_state_t *at_state;
+ int timeout = 0;
+
+ spin_lock_irqsave(&cs->lock, flags);
+
+ for (channel = 0; channel < cs->channels; ++channel)
+ if (test_timeout(&cs->bcs[channel].at_state))
+ timeout = 1;
+
+ if (test_timeout(&cs->at_state))
+ timeout = 1;
+
+ list_for_each_entry(at_state, &cs->temp_at_states, list)
+ if (test_timeout(at_state))
+ timeout = 1;
+
+ if (cs->running) {
+ mod_timer(&cs->timer, jiffies + msecs_to_jiffies(GIG_TICK));
+ if (timeout) {
+ gig_dbg(DEBUG_CMD, "scheduling timeout");
+ tasklet_schedule(&cs->event_tasklet);
+ }
+ }
+
+ spin_unlock_irqrestore(&cs->lock, flags);
+}
+
+int gigaset_get_channel(struct bc_state *bcs)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&bcs->cs->lock, flags);
+ if (bcs->use_count || !try_module_get(bcs->cs->driver->owner)) {
+ gig_dbg(DEBUG_ANY, "could not allocate channel %d",
+ bcs->channel);
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
+ return 0;
+ }
+ ++bcs->use_count;
+ bcs->busy = 1;
+ gig_dbg(DEBUG_ANY, "allocated channel %d", bcs->channel);
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
+ return 1;
+}
+
+void gigaset_free_channel(struct bc_state *bcs)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&bcs->cs->lock, flags);
+ if (!bcs->busy) {
+ gig_dbg(DEBUG_ANY, "could not free channel %d", bcs->channel);
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
+ return;
+ }
+ --bcs->use_count;
+ bcs->busy = 0;
+ module_put(bcs->cs->driver->owner);
+ gig_dbg(DEBUG_ANY, "freed channel %d", bcs->channel);
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
+}
+
+int gigaset_get_channels(struct cardstate *cs)
+{
+ unsigned long flags;
+ int i;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ for (i = 0; i < cs->channels; ++i)
+ if (cs->bcs[i].use_count) {
+ spin_unlock_irqrestore(&cs->lock, flags);
+ gig_dbg(DEBUG_ANY, "could not allocate all channels");
+ return 0;
+ }
+ for (i = 0; i < cs->channels; ++i)
+ ++cs->bcs[i].use_count;
+ spin_unlock_irqrestore(&cs->lock, flags);
+
+ gig_dbg(DEBUG_ANY, "allocated all channels");
+
+ return 1;
+}
+
+void gigaset_free_channels(struct cardstate *cs)
+{
+ unsigned long flags;
+ int i;
+
+ gig_dbg(DEBUG_ANY, "unblocking all channels");
+ spin_lock_irqsave(&cs->lock, flags);
+ for (i = 0; i < cs->channels; ++i)
+ --cs->bcs[i].use_count;
+ spin_unlock_irqrestore(&cs->lock, flags);
+}
+
+void gigaset_block_channels(struct cardstate *cs)
+{
+ unsigned long flags;
+ int i;
+
+ gig_dbg(DEBUG_ANY, "blocking all channels");
+ spin_lock_irqsave(&cs->lock, flags);
+ for (i = 0; i < cs->channels; ++i)
+ ++cs->bcs[i].use_count;
+ spin_unlock_irqrestore(&cs->lock, flags);
+}
+
+static void clear_events(struct cardstate *cs)
+{
+ struct event_t *ev;
+ unsigned head, tail;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cs->ev_lock, flags);
+
+ head = cs->ev_head;
+ tail = cs->ev_tail;
+
+ while (tail != head) {
+ ev = cs->events + head;
+ kfree(ev->ptr);
+ head = (head + 1) % MAX_EVENTS;
+ }
+
+ cs->ev_head = tail;
+
+ spin_unlock_irqrestore(&cs->ev_lock, flags);
+}
+
+struct event_t *gigaset_add_event(struct cardstate *cs,
+ struct at_state_t *at_state, int type,
+ void *ptr, int parameter, void *arg)
+{
+ unsigned long flags;
+ unsigned next, tail;
+ struct event_t *event = NULL;
+
+ spin_lock_irqsave(&cs->ev_lock, flags);
+
+ tail = cs->ev_tail;
+ next = (tail + 1) % MAX_EVENTS;
+ if (unlikely(next == cs->ev_head))
+ dev_err(cs->dev, "event queue full\n");
+ else {
+ event = cs->events + tail;
+ event->type = type;
+ event->at_state = at_state;
+ event->cid = -1;
+ event->ptr = ptr;
+ event->arg = arg;
+ event->parameter = parameter;
+ cs->ev_tail = next;
+ }
+
+ spin_unlock_irqrestore(&cs->ev_lock, flags);
+
+ return event;
+}
+EXPORT_SYMBOL_GPL(gigaset_add_event);
+
+static void free_strings(struct at_state_t *at_state)
+{
+ int i;
+
+ for (i = 0; i < STR_NUM; ++i) {
+ kfree(at_state->str_var[i]);
+ at_state->str_var[i] = NULL;
+ }
+}
+
+static void clear_at_state(struct at_state_t *at_state)
+{
+ free_strings(at_state);
+}
+
+static void dealloc_at_states(struct cardstate *cs)
+{
+ struct at_state_t *cur, *next;
+
+ list_for_each_entry_safe(cur, next, &cs->temp_at_states, list) {
+ list_del(&cur->list);
+ free_strings(cur);
+ kfree(cur);
+ }
+}
+
+static void gigaset_freebcs(struct bc_state *bcs)
+{
+ int i;
+
+ gig_dbg(DEBUG_INIT, "freeing bcs[%d]->hw", bcs->channel);
+ if (!bcs->cs->ops->freebcshw(bcs)) {
+ gig_dbg(DEBUG_INIT, "failed");
+ }
+
+ gig_dbg(DEBUG_INIT, "clearing bcs[%d]->at_state", bcs->channel);
+ clear_at_state(&bcs->at_state);
+ gig_dbg(DEBUG_INIT, "freeing bcs[%d]->skb", bcs->channel);
+
+ if (bcs->skb)
+ dev_kfree_skb(bcs->skb);
+ for (i = 0; i < AT_NUM; ++i) {
+ kfree(bcs->commands[i]);
+ bcs->commands[i] = NULL;
+ }
+}
+
+static struct cardstate *alloc_cs(struct gigaset_driver *drv)
+{
+ unsigned long flags;
+ unsigned i;
+ struct cardstate *cs;
+ struct cardstate *ret = NULL;
+
+ spin_lock_irqsave(&drv->lock, flags);
+ if (drv->blocked)
+ goto exit;
+ for (i = 0; i < drv->minors; ++i) {
+ cs = drv->cs + i;
+ if (!(cs->flags & VALID_MINOR)) {
+ cs->flags = VALID_MINOR;
+ ret = cs;
+ break;
+ }
+ }
+exit:
+ spin_unlock_irqrestore(&drv->lock, flags);
+ return ret;
+}
+
+static void free_cs(struct cardstate *cs)
+{
+ cs->flags = 0;
+}
+
+static void make_valid(struct cardstate *cs, unsigned mask)
+{
+ unsigned long flags;
+ struct gigaset_driver *drv = cs->driver;
+ spin_lock_irqsave(&drv->lock, flags);
+ cs->flags |= mask;
+ spin_unlock_irqrestore(&drv->lock, flags);
+}
+
+static void make_invalid(struct cardstate *cs, unsigned mask)
+{
+ unsigned long flags;
+ struct gigaset_driver *drv = cs->driver;
+ spin_lock_irqsave(&drv->lock, flags);
+ cs->flags &= ~mask;
+ spin_unlock_irqrestore(&drv->lock, flags);
+}
+
+void gigaset_freecs(struct cardstate *cs)
+{
+ int i;
+ unsigned long flags;
+
+ if (!cs)
+ return;
+
+ mutex_lock(&cs->mutex);
+
+ if (!cs->bcs)
+ goto f_cs;
+ if (!cs->inbuf)
+ goto f_bcs;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ cs->running = 0;
+ spin_unlock_irqrestore(&cs->lock, flags); /* event handler and timer are
+ not rescheduled below */
+
+ tasklet_kill(&cs->event_tasklet);
+ del_timer_sync(&cs->timer);
+
+ switch (cs->cs_init) {
+ default:
+ /* clear device sysfs */
+ gigaset_free_dev_sysfs(cs);
+
+ gigaset_if_free(cs);
+
+ gig_dbg(DEBUG_INIT, "clearing hw");
+ cs->ops->freecshw(cs);
+
+ //FIXME cmdbuf
+
+ /* fall through */
+ case 2: /* error in initcshw */
+ /* Deregister from LL */
+ make_invalid(cs, VALID_ID);
+ gig_dbg(DEBUG_INIT, "clearing iif");
+ gigaset_i4l_cmd(cs, ISDN_STAT_UNLOAD);
+
+ /* fall through */
+ case 1: /* error when regestering to LL */
+ gig_dbg(DEBUG_INIT, "clearing at_state");
+ clear_at_state(&cs->at_state);
+ dealloc_at_states(cs);
+
+ /* fall through */
+ case 0: /* error in one call to initbcs */
+ for (i = 0; i < cs->channels; ++i) {
+ gig_dbg(DEBUG_INIT, "clearing bcs[%d]", i);
+ gigaset_freebcs(cs->bcs + i);
+ }
+
+ clear_events(cs);
+ gig_dbg(DEBUG_INIT, "freeing inbuf");
+ kfree(cs->inbuf);
+ }
+f_bcs: gig_dbg(DEBUG_INIT, "freeing bcs[]");
+ kfree(cs->bcs);
+f_cs: gig_dbg(DEBUG_INIT, "freeing cs");
+ mutex_unlock(&cs->mutex);
+ free_cs(cs);
+}
+EXPORT_SYMBOL_GPL(gigaset_freecs);
+
+void gigaset_at_init(struct at_state_t *at_state, struct bc_state *bcs,
+ struct cardstate *cs, int cid)
+{
+ int i;
+
+ INIT_LIST_HEAD(&at_state->list);
+ at_state->waiting = 0;
+ at_state->getstring = 0;
+ at_state->pending_commands = 0;
+ at_state->timer_expires = 0;
+ at_state->timer_active = 0;
+ at_state->timer_index = 0;
+ at_state->seq_index = 0;
+ at_state->ConState = 0;
+ for (i = 0; i < STR_NUM; ++i)
+ at_state->str_var[i] = NULL;
+ at_state->int_var[VAR_ZDLE] = 0;
+ at_state->int_var[VAR_ZCTP] = -1;
+ at_state->int_var[VAR_ZSAU] = ZSAU_NULL;
+ at_state->cs = cs;
+ at_state->bcs = bcs;
+ at_state->cid = cid;
+ if (!cid)
+ at_state->replystruct = cs->tabnocid;
+ else
+ at_state->replystruct = cs->tabcid;
+}
+
+
+static void gigaset_inbuf_init(struct inbuf_t *inbuf, struct bc_state *bcs,
+ struct cardstate *cs, int inputstate)
+/* inbuf->read must be allocated before! */
+{
+ inbuf->head = 0;
+ inbuf->tail = 0;
+ inbuf->cs = cs;
+ inbuf->bcs = bcs; /*base driver: NULL*/
+ inbuf->rcvbuf = NULL;
+ inbuf->inputstate = inputstate;
+}
+
+/* append received bytes to inbuf */
+int gigaset_fill_inbuf(struct inbuf_t *inbuf, const unsigned char *src,
+ unsigned numbytes)
+{
+ unsigned n, head, tail, bytesleft;
+
+ gig_dbg(DEBUG_INTR, "received %u bytes", numbytes);
+
+ if (!numbytes)
+ return 0;
+
+ bytesleft = numbytes;
+ tail = inbuf->tail;
+ head = inbuf->head;
+ gig_dbg(DEBUG_INTR, "buffer state: %u -> %u", head, tail);
+
+ while (bytesleft) {
+ if (head > tail)
+ n = head - 1 - tail;
+ else if (head == 0)
+ n = (RBUFSIZE-1) - tail;
+ else
+ n = RBUFSIZE - tail;
+ if (!n) {
+ dev_err(inbuf->cs->dev,
+ "buffer overflow (%u bytes lost)\n",
+ bytesleft);
+ break;
+ }
+ if (n > bytesleft)
+ n = bytesleft;
+ memcpy(inbuf->data + tail, src, n);
+ bytesleft -= n;
+ tail = (tail + n) % RBUFSIZE;
+ src += n;
+ }
+ gig_dbg(DEBUG_INTR, "setting tail to %u", tail);
+ inbuf->tail = tail;
+ return numbytes != bytesleft;
+}
+EXPORT_SYMBOL_GPL(gigaset_fill_inbuf);
+
+/* Initialize the b-channel structure */
+static struct bc_state *gigaset_initbcs(struct bc_state *bcs,
+ struct cardstate *cs, int channel)
+{
+ int i;
+
+ bcs->tx_skb = NULL; //FIXME -> hw part
+
+ skb_queue_head_init(&bcs->squeue);
+
+ bcs->corrupted = 0;
+ bcs->trans_down = 0;
+ bcs->trans_up = 0;
+
+ gig_dbg(DEBUG_INIT, "setting up bcs[%d]->at_state", channel);
+ gigaset_at_init(&bcs->at_state, bcs, cs, -1);
+
+ bcs->rcvbytes = 0;
+
+#ifdef CONFIG_GIGASET_DEBUG
+ bcs->emptycount = 0;
+#endif
+
+ gig_dbg(DEBUG_INIT, "allocating bcs[%d]->skb", channel);
+ bcs->fcs = PPP_INITFCS;
+ bcs->inputstate = 0;
+ if (cs->ignoreframes) {
+ bcs->inputstate |= INS_skip_frame;
+ bcs->skb = NULL;
+ } else if ((bcs->skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL)
+ skb_reserve(bcs->skb, HW_HDR_LEN);
+ else {
+ warn("could not allocate skb");
+ bcs->inputstate |= INS_skip_frame;
+ }
+
+ bcs->channel = channel;
+ bcs->cs = cs;
+
+ bcs->chstate = 0;
+ bcs->use_count = 1;
+ bcs->busy = 0;
+ bcs->ignore = cs->ignoreframes;
+
+ for (i = 0; i < AT_NUM; ++i)
+ bcs->commands[i] = NULL;
+
+ gig_dbg(DEBUG_INIT, " setting up bcs[%d]->hw", channel);
+ if (cs->ops->initbcshw(bcs))
+ return bcs;
+
+ gig_dbg(DEBUG_INIT, " failed");
+
+ gig_dbg(DEBUG_INIT, " freeing bcs[%d]->skb", channel);
+ if (bcs->skb)
+ dev_kfree_skb(bcs->skb);
+
+ return NULL;
+}
+
+/* gigaset_initcs
+ * Allocate and initialize cardstate structure for Gigaset driver
+ * Calls hardware dependent gigaset_initcshw() function
+ * Calls B channel initialization function gigaset_initbcs() for each B channel
+ * parameters:
+ * drv hardware driver the device belongs to
+ * channels number of B channels supported by device
+ * onechannel !=0: B channel data and AT commands share one
+ * communication channel
+ * ==0: B channels have separate communication channels
+ * ignoreframes number of frames to ignore after setting up B channel
+ * cidmode !=0: start in CallID mode
+ * modulename name of driver module (used for I4L registration)
+ * return value:
+ * pointer to cardstate structure
+ */
+struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels,
+ int onechannel, int ignoreframes,
+ int cidmode, const char *modulename)
+{
+ struct cardstate *cs = NULL;
+ unsigned long flags;
+ int i;
+
+ gig_dbg(DEBUG_INIT, "allocating cs");
+ if (!(cs = alloc_cs(drv))) {
+ err("maximum number of devices exceeded");
+ return NULL;
+ }
+
+ gig_dbg(DEBUG_INIT, "allocating bcs[0..%d]", channels - 1);
+ cs->bcs = kmalloc(channels * sizeof(struct bc_state), GFP_KERNEL);
+ if (!cs->bcs) {
+ err("out of memory");
+ goto error;
+ }
+ gig_dbg(DEBUG_INIT, "allocating inbuf");
+ cs->inbuf = kmalloc(sizeof(struct inbuf_t), GFP_KERNEL);
+ if (!cs->inbuf) {
+ err("out of memory");
+ goto error;
+ }
+
+ cs->cs_init = 0;
+ cs->channels = channels;
+ cs->onechannel = onechannel;
+ cs->ignoreframes = ignoreframes;
+ INIT_LIST_HEAD(&cs->temp_at_states);
+ cs->running = 0;
+ init_timer(&cs->timer); /* clear next & prev */
+ spin_lock_init(&cs->ev_lock);
+ cs->ev_tail = 0;
+ cs->ev_head = 0;
+
+ tasklet_init(&cs->event_tasklet, &gigaset_handle_event,
+ (unsigned long) cs);
+ cs->commands_pending = 0;
+ cs->cur_at_seq = 0;
+ cs->gotfwver = -1;
+ cs->open_count = 0;
+ cs->dev = NULL;
+ cs->tty = NULL;
+ cs->tty_dev = NULL;
+ cs->cidmode = cidmode != 0;
+
+ //if(onechannel) { //FIXME
+ cs->tabnocid = gigaset_tab_nocid_m10x;
+ cs->tabcid = gigaset_tab_cid_m10x;
+ //} else {
+ // cs->tabnocid = gigaset_tab_nocid;
+ // cs->tabcid = gigaset_tab_cid;
+ //}
+
+ init_waitqueue_head(&cs->waitqueue);
+ cs->waiting = 0;
+
+ cs->mode = M_UNKNOWN;
+ cs->mstate = MS_UNINITIALIZED;
+
+ for (i = 0; i < channels; ++i) {
+ gig_dbg(DEBUG_INIT, "setting up bcs[%d].read", i);
+ if (!gigaset_initbcs(cs->bcs + i, cs, i)) {
+ err("could not allocate channel %d data", i);
+ goto error;
+ }
+ }
+
+ ++cs->cs_init;
+
+ gig_dbg(DEBUG_INIT, "setting up at_state");
+ spin_lock_init(&cs->lock);
+ gigaset_at_init(&cs->at_state, NULL, cs, 0);
+ cs->dle = 0;
+ cs->cbytes = 0;
+
+ gig_dbg(DEBUG_INIT, "setting up inbuf");
+ if (onechannel) { //FIXME distinction necessary?
+ gigaset_inbuf_init(cs->inbuf, cs->bcs, cs, INS_command);
+ } else
+ gigaset_inbuf_init(cs->inbuf, NULL, cs, INS_command);
+
+ cs->connected = 0;
+ cs->isdn_up = 0;
+
+ gig_dbg(DEBUG_INIT, "setting up cmdbuf");
+ cs->cmdbuf = cs->lastcmdbuf = NULL;
+ spin_lock_init(&cs->cmdlock);
+ cs->curlen = 0;
+ cs->cmdbytes = 0;
+
+ gig_dbg(DEBUG_INIT, "setting up iif");
+ if (!gigaset_register_to_LL(cs, modulename)) {
+ err("register_isdn failed");
+ goto error;
+ }
+
+ make_valid(cs, VALID_ID);
+ ++cs->cs_init;
+ gig_dbg(DEBUG_INIT, "setting up hw");
+ if (!cs->ops->initcshw(cs)) {
+ err("could not allocate device specific data");
+ goto error;
+ }
+
+ ++cs->cs_init;
+
+ /* set up character device */
+ gigaset_if_init(cs);
+
+ /* set up device sysfs */
+ gigaset_init_dev_sysfs(cs);
+
+ spin_lock_irqsave(&cs->lock, flags);
+ cs->running = 1;
+ spin_unlock_irqrestore(&cs->lock, flags);
+ setup_timer(&cs->timer, timer_tick, (unsigned long) cs);
+ cs->timer.expires = jiffies + msecs_to_jiffies(GIG_TICK);
+ /* FIXME: can jiffies increase too much until the timer is added?
+ * Same problem(?) with mod_timer() in timer_tick(). */
+ add_timer(&cs->timer);
+
+ gig_dbg(DEBUG_INIT, "cs initialized");
+ return cs;
+
+error:
+ gig_dbg(DEBUG_INIT, "failed");
+ gigaset_freecs(cs);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(gigaset_initcs);
+
+/* ReInitialize the b-channel structure on hangup */
+void gigaset_bcs_reinit(struct bc_state *bcs)
+{
+ struct sk_buff *skb;
+ struct cardstate *cs = bcs->cs;
+ unsigned long flags;
+
+ while ((skb = skb_dequeue(&bcs->squeue)) != NULL)
+ dev_kfree_skb(skb);
+
+ spin_lock_irqsave(&cs->lock, flags);
+ clear_at_state(&bcs->at_state);
+ bcs->at_state.ConState = 0;
+ bcs->at_state.timer_active = 0;
+ bcs->at_state.timer_expires = 0;
+ bcs->at_state.cid = -1; /* No CID defined */
+ spin_unlock_irqrestore(&cs->lock, flags);
+
+ bcs->inputstate = 0;
+
+#ifdef CONFIG_GIGASET_DEBUG
+ bcs->emptycount = 0;
+#endif
+
+ bcs->fcs = PPP_INITFCS;
+ bcs->chstate = 0;
+
+ bcs->ignore = cs->ignoreframes;
+ if (bcs->ignore)
+ bcs->inputstate |= INS_skip_frame;
+
+
+ cs->ops->reinitbcshw(bcs);
+}
+
+static void cleanup_cs(struct cardstate *cs)
+{
+ struct cmdbuf_t *cb, *tcb;
+ int i;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cs->lock, flags);
+
+ cs->mode = M_UNKNOWN;
+ cs->mstate = MS_UNINITIALIZED;
+
+ clear_at_state(&cs->at_state);
+ dealloc_at_states(cs);
+ free_strings(&cs->at_state);
+ gigaset_at_init(&cs->at_state, NULL, cs, 0);
+
+ kfree(cs->inbuf->rcvbuf);
+ cs->inbuf->rcvbuf = NULL;
+ cs->inbuf->inputstate = INS_command;
+ cs->inbuf->head = 0;
+ cs->inbuf->tail = 0;
+
+ cb = cs->cmdbuf;
+ while (cb) {
+ tcb = cb;
+ cb = cb->next;
+ kfree(tcb);
+ }
+ cs->cmdbuf = cs->lastcmdbuf = NULL;
+ cs->curlen = 0;
+ cs->cmdbytes = 0;
+ cs->gotfwver = -1;
+ cs->dle = 0;
+ cs->cur_at_seq = 0;
+ cs->commands_pending = 0;
+ cs->cbytes = 0;
+
+ spin_unlock_irqrestore(&cs->lock, flags);
+
+ for (i = 0; i < cs->channels; ++i) {
+ gigaset_freebcs(cs->bcs + i);
+ if (!gigaset_initbcs(cs->bcs + i, cs, i))
+ break; //FIXME error handling
+ }
+
+ if (cs->waiting) {
+ cs->cmd_result = -ENODEV;
+ cs->waiting = 0;
+ wake_up_interruptible(&cs->waitqueue);
+ }
+}
+
+
+int gigaset_start(struct cardstate *cs)
+{
+ unsigned long flags;
+
+ if (mutex_lock_interruptible(&cs->mutex))
+ return 0;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ cs->connected = 1;
+ spin_unlock_irqrestore(&cs->lock, flags);
+
+ if (cs->mstate != MS_LOCKED) {
+ cs->ops->set_modem_ctrl(cs, 0, TIOCM_DTR|TIOCM_RTS);
+ cs->ops->baud_rate(cs, B115200);
+ cs->ops->set_line_ctrl(cs, CS8);
+ cs->control_state = TIOCM_DTR|TIOCM_RTS;
+ } else {
+ //FIXME use some saved values?
+ }
+
+ cs->waiting = 1;
+
+ if (!gigaset_add_event(cs, &cs->at_state, EV_START, NULL, 0, NULL)) {
+ cs->waiting = 0;
+ //FIXME what should we do?
+ goto error;
+ }
+
+ gig_dbg(DEBUG_CMD, "scheduling START");
+ gigaset_schedule_event(cs);
+
+ wait_event(cs->waitqueue, !cs->waiting);
+
+ mutex_unlock(&cs->mutex);
+ return 1;
+
+error:
+ mutex_unlock(&cs->mutex);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(gigaset_start);
+
+/* gigaset_shutdown
+ * check if a device is associated to the cardstate structure and stop it
+ * return value: 0 if ok, -1 if no device was associated
+ */
+int gigaset_shutdown(struct cardstate *cs)
+{
+ mutex_lock(&cs->mutex);
+
+ if (!(cs->flags & VALID_MINOR)) {
+ mutex_unlock(&cs->mutex);
+ return -1;
+ }
+
+ cs->waiting = 1;
+
+ if (!gigaset_add_event(cs, &cs->at_state, EV_SHUTDOWN, NULL, 0, NULL)) {
+ //FIXME what should we do?
+ goto exit;
+ }
+
+ gig_dbg(DEBUG_CMD, "scheduling SHUTDOWN");
+ gigaset_schedule_event(cs);
+
+ wait_event(cs->waitqueue, !cs->waiting);
+
+ cleanup_cs(cs);
+
+exit:
+ mutex_unlock(&cs->mutex);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(gigaset_shutdown);
+
+void gigaset_stop(struct cardstate *cs)
+{
+ mutex_lock(&cs->mutex);
+
+ cs->waiting = 1;
+
+ if (!gigaset_add_event(cs, &cs->at_state, EV_STOP, NULL, 0, NULL)) {
+ //FIXME what should we do?
+ goto exit;
+ }
+
+ gig_dbg(DEBUG_CMD, "scheduling STOP");
+ gigaset_schedule_event(cs);
+
+ wait_event(cs->waitqueue, !cs->waiting);
+
+ cleanup_cs(cs);
+
+exit:
+ mutex_unlock(&cs->mutex);
+}
+EXPORT_SYMBOL_GPL(gigaset_stop);
+
+static LIST_HEAD(drivers);
+static DEFINE_SPINLOCK(driver_lock);
+
+struct cardstate *gigaset_get_cs_by_id(int id)
+{
+ unsigned long flags;
+ struct cardstate *ret = NULL;
+ struct cardstate *cs;
+ struct gigaset_driver *drv;
+ unsigned i;
+
+ spin_lock_irqsave(&driver_lock, flags);
+ list_for_each_entry(drv, &drivers, list) {
+ spin_lock(&drv->lock);
+ for (i = 0; i < drv->minors; ++i) {
+ cs = drv->cs + i;
+ if ((cs->flags & VALID_ID) && cs->myid == id) {
+ ret = cs;
+ break;
+ }
+ }
+ spin_unlock(&drv->lock);
+ if (ret)
+ break;
+ }
+ spin_unlock_irqrestore(&driver_lock, flags);
+ return ret;
+}
+
+void gigaset_debugdrivers(void)
+{
+ unsigned long flags;
+ static struct cardstate *cs;
+ struct gigaset_driver *drv;
+ unsigned i;
+
+ spin_lock_irqsave(&driver_lock, flags);
+ list_for_each_entry(drv, &drivers, list) {
+ gig_dbg(DEBUG_DRIVER, "driver %p", drv);
+ spin_lock(&drv->lock);
+ for (i = 0; i < drv->minors; ++i) {
+ gig_dbg(DEBUG_DRIVER, " index %u", i);
+ cs = drv->cs + i;
+ gig_dbg(DEBUG_DRIVER, " cardstate %p", cs);
+ gig_dbg(DEBUG_DRIVER, " flags 0x%02x", cs->flags);
+ gig_dbg(DEBUG_DRIVER, " minor_index %u",
+ cs->minor_index);
+ gig_dbg(DEBUG_DRIVER, " driver %p", cs->driver);
+ gig_dbg(DEBUG_DRIVER, " i4l id %d", cs->myid);
+ }
+ spin_unlock(&drv->lock);
+ }
+ spin_unlock_irqrestore(&driver_lock, flags);
+}
+
+static struct cardstate *gigaset_get_cs_by_minor(unsigned minor)
+{
+ unsigned long flags;
+ struct cardstate *ret = NULL;
+ struct gigaset_driver *drv;
+ unsigned index;
+
+ spin_lock_irqsave(&driver_lock, flags);
+ list_for_each_entry(drv, &drivers, list) {
+ if (minor < drv->minor || minor >= drv->minor + drv->minors)
+ continue;
+ index = minor - drv->minor;
+ spin_lock(&drv->lock);
+ if (drv->cs[index].flags & VALID_MINOR)
+ ret = drv->cs + index;
+ spin_unlock(&drv->lock);
+ if (ret)
+ break;
+ }
+ spin_unlock_irqrestore(&driver_lock, flags);
+ return ret;
+}
+
+struct cardstate *gigaset_get_cs_by_tty(struct tty_struct *tty)
+{
+ if (tty->index < 0 || tty->index >= tty->driver->num)
+ return NULL;
+ return gigaset_get_cs_by_minor(tty->index + tty->driver->minor_start);
+}
+
+void gigaset_freedriver(struct gigaset_driver *drv)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&driver_lock, flags);
+ list_del(&drv->list);
+ spin_unlock_irqrestore(&driver_lock, flags);
+
+ gigaset_if_freedriver(drv);
+
+ kfree(drv->cs);
+ kfree(drv);
+}
+EXPORT_SYMBOL_GPL(gigaset_freedriver);
+
+/* gigaset_initdriver
+ * Allocate and initialize gigaset_driver structure. Initialize interface.
+ * parameters:
+ * minor First minor number
+ * minors Number of minors this driver can handle
+ * procname Name of the driver
+ * devname Name of the device files (prefix without minor number)
+ * return value:
+ * Pointer to the gigaset_driver structure on success, NULL on failure.
+ */
+struct gigaset_driver *gigaset_initdriver(unsigned minor, unsigned minors,
+ const char *procname,
+ const char *devname,
+ const struct gigaset_ops *ops,
+ struct module *owner)
+{
+ struct gigaset_driver *drv;
+ unsigned long flags;
+ unsigned i;
+
+ drv = kmalloc(sizeof *drv, GFP_KERNEL);
+ if (!drv)
+ return NULL;
+
+ drv->have_tty = 0;
+ drv->minor = minor;
+ drv->minors = minors;
+ spin_lock_init(&drv->lock);
+ drv->blocked = 0;
+ drv->ops = ops;
+ drv->owner = owner;
+ INIT_LIST_HEAD(&drv->list);
+
+ drv->cs = kmalloc(minors * sizeof *drv->cs, GFP_KERNEL);
+ if (!drv->cs)
+ goto error;
+
+ for (i = 0; i < minors; ++i) {
+ drv->cs[i].flags = 0;
+ drv->cs[i].driver = drv;
+ drv->cs[i].ops = drv->ops;
+ drv->cs[i].minor_index = i;
+ mutex_init(&drv->cs[i].mutex);
+ }
+
+ gigaset_if_initdriver(drv, procname, devname);
+
+ spin_lock_irqsave(&driver_lock, flags);
+ list_add(&drv->list, &drivers);
+ spin_unlock_irqrestore(&driver_lock, flags);
+
+ return drv;
+
+error:
+ kfree(drv->cs);
+ kfree(drv);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(gigaset_initdriver);
+
+void gigaset_blockdriver(struct gigaset_driver *drv)
+{
+ drv->blocked = 1;
+}
+EXPORT_SYMBOL_GPL(gigaset_blockdriver);
+
+static int __init gigaset_init_module(void)
+{
+ /* in accordance with the principle of least astonishment,
+ * setting the 'debug' parameter to 1 activates a sensible
+ * set of default debug levels
+ */
+ if (gigaset_debuglevel == 1)
+ gigaset_debuglevel = DEBUG_DEFAULT;
+
+ info(DRIVER_AUTHOR);
+ info(DRIVER_DESC);
+ return 0;
+}
+
+static void __exit gigaset_exit_module(void)
+{
+}
+
+module_init(gigaset_init_module);
+module_exit(gigaset_exit_module);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/isdn/gigaset/ev-layer.c b/drivers/isdn/gigaset/ev-layer.c
new file mode 100644
index 0000000..5cbf64d
--- /dev/null
+++ b/drivers/isdn/gigaset/ev-layer.c
@@ -0,0 +1,1978 @@
+/*
+ * Stuff used by all variants of the driver
+ *
+ * Copyright (c) 2001 by Stefan Eilers,
+ * Hansjoerg Lipp <hjlipp@web.de>,
+ * Tilman Schmidt <tilman@imap.cc>.
+ *
+ * =====================================================================
+ * 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 of
+ * the License, or (at your option) any later version.
+ * =====================================================================
+ */
+
+#include "gigaset.h"
+
+/* ========================================================== */
+/* bit masks for pending commands */
+#define PC_DIAL 0x001
+#define PC_HUP 0x002
+#define PC_INIT 0x004
+#define PC_DLE0 0x008
+#define PC_DLE1 0x010
+#define PC_SHUTDOWN 0x020
+#define PC_ACCEPT 0x040
+#define PC_CID 0x080
+#define PC_NOCID 0x100
+#define PC_CIDMODE 0x200
+#define PC_UMMODE 0x400
+
+/* types of modem responses */
+#define RT_NOTHING 0
+#define RT_ZSAU 1
+#define RT_RING 2
+#define RT_NUMBER 3
+#define RT_STRING 4
+#define RT_HEX 5
+#define RT_ZCAU 6
+
+/* Possible ASCII responses */
+#define RSP_OK 0
+//#define RSP_BUSY 1
+//#define RSP_CONNECT 2
+#define RSP_ZGCI 3
+#define RSP_RING 4
+#define RSP_ZAOC 5
+#define RSP_ZCSTR 6
+#define RSP_ZCFGT 7
+#define RSP_ZCFG 8
+#define RSP_ZCCR 9
+#define RSP_EMPTY 10
+#define RSP_ZLOG 11
+#define RSP_ZCAU 12
+#define RSP_ZMWI 13
+#define RSP_ZABINFO 14
+#define RSP_ZSMLSTCHG 15
+#define RSP_VAR 100
+#define RSP_ZSAU (RSP_VAR + VAR_ZSAU)
+#define RSP_ZDLE (RSP_VAR + VAR_ZDLE)
+#define RSP_ZVLS (RSP_VAR + VAR_ZVLS)
+#define RSP_ZCTP (RSP_VAR + VAR_ZCTP)
+#define RSP_STR (RSP_VAR + VAR_NUM)
+#define RSP_NMBR (RSP_STR + STR_NMBR)
+#define RSP_ZCPN (RSP_STR + STR_ZCPN)
+#define RSP_ZCON (RSP_STR + STR_ZCON)
+#define RSP_ZBC (RSP_STR + STR_ZBC)
+#define RSP_ZHLC (RSP_STR + STR_ZHLC)
+#define RSP_ERROR -1 /* ERROR */
+#define RSP_WRONG_CID -2 /* unknown cid in cmd */
+//#define RSP_EMPTY -3
+#define RSP_UNKNOWN -4 /* unknown response */
+#define RSP_FAIL -5 /* internal error */
+#define RSP_INVAL -6 /* invalid response */
+
+#define RSP_NONE -19
+#define RSP_STRING -20
+#define RSP_NULL -21
+//#define RSP_RETRYFAIL -22
+//#define RSP_RETRY -23
+//#define RSP_SKIP -24
+#define RSP_INIT -27
+#define RSP_ANY -26
+#define RSP_LAST -28
+#define RSP_NODEV -9
+
+/* actions for process_response */
+#define ACT_NOTHING 0
+#define ACT_SETDLE1 1
+#define ACT_SETDLE0 2
+#define ACT_FAILINIT 3
+#define ACT_HUPMODEM 4
+#define ACT_CONFIGMODE 5
+#define ACT_INIT 6
+#define ACT_DLE0 7
+#define ACT_DLE1 8
+#define ACT_FAILDLE0 9
+#define ACT_FAILDLE1 10
+#define ACT_RING 11
+#define ACT_CID 12
+#define ACT_FAILCID 13
+#define ACT_SDOWN 14
+#define ACT_FAILSDOWN 15
+#define ACT_DEBUG 16
+#define ACT_WARN 17
+#define ACT_DIALING 18
+#define ACT_ABORTDIAL 19
+#define ACT_DISCONNECT 20
+#define ACT_CONNECT 21
+#define ACT_REMOTEREJECT 22
+#define ACT_CONNTIMEOUT 23
+#define ACT_REMOTEHUP 24
+#define ACT_ABORTHUP 25
+#define ACT_ICALL 26
+#define ACT_ACCEPTED 27
+#define ACT_ABORTACCEPT 28
+#define ACT_TIMEOUT 29
+#define ACT_GETSTRING 30
+#define ACT_SETVER 31
+#define ACT_FAILVER 32
+#define ACT_GOTVER 33
+#define ACT_TEST 34
+#define ACT_ERROR 35
+#define ACT_ABORTCID 36
+#define ACT_ZCAU 37
+#define ACT_NOTIFY_BC_DOWN 38
+#define ACT_NOTIFY_BC_UP 39
+#define ACT_DIAL 40
+#define ACT_ACCEPT 41
+#define ACT_PROTO_L2 42
+#define ACT_HUP 43
+#define ACT_IF_LOCK 44
+#define ACT_START 45
+#define ACT_STOP 46
+#define ACT_FAKEDLE0 47
+#define ACT_FAKEHUP 48
+#define ACT_FAKESDOWN 49
+#define ACT_SHUTDOWN 50
+#define ACT_PROC_CIDMODE 51
+#define ACT_UMODESET 52
+#define ACT_FAILUMODE 53
+#define ACT_CMODESET 54
+#define ACT_FAILCMODE 55
+#define ACT_IF_VER 56
+#define ACT_CMD 100
+
+/* at command sequences */
+#define SEQ_NONE 0
+#define SEQ_INIT 100
+#define SEQ_DLE0 200
+#define SEQ_DLE1 250
+#define SEQ_CID 300
+#define SEQ_NOCID 350
+#define SEQ_HUP 400
+#define SEQ_DIAL 600
+#define SEQ_ACCEPT 720
+#define SEQ_SHUTDOWN 500
+#define SEQ_CIDMODE 10
+#define SEQ_UMMODE 11
+
+
+// 100: init, 200: dle0, 250:dle1, 300: get cid (dial), 350: "hup" (no cid), 400: hup, 500: reset, 600: dial, 700: ring
+struct reply_t gigaset_tab_nocid_m10x[]= /* with dle mode */
+{
+ /* resp_code, min_ConState, max_ConState, parameter, new_ConState, timeout, action, command */
+
+ /* initialize device, set cid mode if possible */
+ //{RSP_INIT, -1, -1,100, 900, 0, {ACT_TEST}},
+ //{RSP_ERROR, 900,900, -1, 0, 0, {ACT_FAILINIT}},
+ //{RSP_OK, 900,900, -1, 100, INIT_TIMEOUT,
+ // {ACT_TIMEOUT}},
+
+ {RSP_INIT, -1, -1,SEQ_INIT, 100, INIT_TIMEOUT,
+ {ACT_TIMEOUT}}, /* wait until device is ready */
+
+ {EV_TIMEOUT, 100,100, -1, 101, 3, {0}, "Z\r"}, /* device in transparent mode? try to initialize it. */
+ {RSP_OK, 101,103, -1, 120, 5, {ACT_GETSTRING}, "+GMR\r"}, /* get version */
+
+ {EV_TIMEOUT, 101,101, -1, 102, 5, {0}, "Z\r"}, /* timeout => try once again. */
+ {RSP_ERROR, 101,101, -1, 102, 5, {0}, "Z\r"}, /* error => try once again. */
+
+ {EV_TIMEOUT, 102,102, -1, 108, 5, {ACT_SETDLE1}, "^SDLE=0\r"}, /* timeout => try again in DLE mode. */
+ {RSP_OK, 108,108, -1, 104,-1},
+ {RSP_ZDLE, 104,104, 0, 103, 5, {0}, "Z\r"},
+ {EV_TIMEOUT, 104,104, -1, 0, 0, {ACT_FAILINIT}},
+ {RSP_ERROR, 108,108, -1, 0, 0, {ACT_FAILINIT}},
+
+ {EV_TIMEOUT, 108,108, -1, 105, 2, {ACT_SETDLE0,
+ ACT_HUPMODEM,
+ ACT_TIMEOUT}}, /* still timeout => connection in unimodem mode? */
+ {EV_TIMEOUT, 105,105, -1, 103, 5, {0}, "Z\r"},
+
+ {RSP_ERROR, 102,102, -1, 107, 5, {0}, "^GETPRE\r"}, /* ERROR on ATZ => maybe in config mode? */
+ {RSP_OK, 107,107, -1, 0, 0, {ACT_CONFIGMODE}},
+ {RSP_ERROR, 107,107, -1, 0, 0, {ACT_FAILINIT}},
+ {EV_TIMEOUT, 107,107, -1, 0, 0, {ACT_FAILINIT}},
+
+ {RSP_ERROR, 103,103, -1, 0, 0, {ACT_FAILINIT}},
+ {EV_TIMEOUT, 103,103, -1, 0, 0, {ACT_FAILINIT}},
+
+ {RSP_STRING, 120,120, -1, 121,-1, {ACT_SETVER}},
+
+ {EV_TIMEOUT, 120,121, -1, 0, 0, {ACT_FAILVER, ACT_INIT}},
+ {RSP_ERROR, 120,121, -1, 0, 0, {ACT_FAILVER, ACT_INIT}},
+ {RSP_OK, 121,121, -1, 0, 0, {ACT_GOTVER, ACT_INIT}},
+#if 0
+ {EV_TIMEOUT, 120,121, -1, 130, 5, {ACT_FAILVER}, "^SGCI=1\r"},
+ {RSP_ERROR, 120,121, -1, 130, 5, {ACT_FAILVER}, "^SGCI=1\r"},
+ {RSP_OK, 121,121, -1, 130, 5, {ACT_GOTVER}, "^SGCI=1\r"},
+
+ {RSP_OK, 130,130, -1, 0, 0, {ACT_INIT}},
+ {RSP_ERROR, 130,130, -1, 0, 0, {ACT_FAILINIT}},
+ {EV_TIMEOUT, 130,130, -1, 0, 0, {ACT_FAILINIT}},
+#endif
+
+ /* leave dle mode */
+ {RSP_INIT, 0, 0,SEQ_DLE0, 201, 5, {0}, "^SDLE=0\r"},
+ {RSP_OK, 201,201, -1, 202,-1},
+ //{RSP_ZDLE, 202,202, 0, 202, 0, {ACT_ERROR}},//DELETE
+ {RSP_ZDLE, 202,202, 0, 0, 0, {ACT_DLE0}},
+ {RSP_NODEV, 200,249, -1, 0, 0, {ACT_FAKEDLE0}},
+ {RSP_ERROR, 200,249, -1, 0, 0, {ACT_FAILDLE0}},
+ {EV_TIMEOUT, 200,249, -1, 0, 0, {ACT_FAILDLE0}},
+
+ /* enter dle mode */
+ {RSP_INIT, 0, 0,SEQ_DLE1, 251, 5, {0}, "^SDLE=1\r"},
+ {RSP_OK, 251,251, -1, 252,-1},
+ {RSP_ZDLE, 252,252, 1, 0, 0, {ACT_DLE1}},
+ {RSP_ERROR, 250,299, -1, 0, 0, {ACT_FAILDLE1}},
+ {EV_TIMEOUT, 250,299, -1, 0, 0, {ACT_FAILDLE1}},
+
+ /* incoming call */
+ {RSP_RING, -1, -1, -1, -1,-1, {ACT_RING}},
+
+ /* get cid */
+ //{RSP_INIT, 0, 0,300, 901, 0, {ACT_TEST}},
+ //{RSP_ERROR, 901,901, -1, 0, 0, {ACT_FAILCID}},
+ //{RSP_OK, 901,901, -1, 301, 5, {0}, "^SGCI?\r"},
+
+ {RSP_INIT, 0, 0,SEQ_CID, 301, 5, {0}, "^SGCI?\r"},
+ {RSP_OK, 301,301, -1, 302,-1},
+ {RSP_ZGCI, 302,302, -1, 0, 0, {ACT_CID}},
+ {RSP_ERROR, 301,349, -1, 0, 0, {ACT_FAILCID}},
+ {EV_TIMEOUT, 301,349, -1, 0, 0, {ACT_FAILCID}},
+
+ /* enter cid mode */
+ {RSP_INIT, 0, 0,SEQ_CIDMODE, 150, 5, {0}, "^SGCI=1\r"},
+ {RSP_OK, 150,150, -1, 0, 0, {ACT_CMODESET}},
+ {RSP_ERROR, 150,150, -1, 0, 0, {ACT_FAILCMODE}},
+ {EV_TIMEOUT, 150,150, -1, 0, 0, {ACT_FAILCMODE}},
+
+ /* leave cid mode */
+ //{RSP_INIT, 0, 0,SEQ_UMMODE, 160, 5, {0}, "^SGCI=0\r"},
+ {RSP_INIT, 0, 0,SEQ_UMMODE, 160, 5, {0}, "Z\r"},
+ {RSP_OK, 160,160, -1, 0, 0, {ACT_UMODESET}},
+ {RSP_ERROR, 160,160, -1, 0, 0, {ACT_FAILUMODE}},
+ {EV_TIMEOUT, 160,160, -1, 0, 0, {ACT_FAILUMODE}},
+
+ /* abort getting cid */
+ {RSP_INIT, 0, 0,SEQ_NOCID, 0, 0, {ACT_ABORTCID}},
+
+ /* reset */
+#if 0
+ {RSP_INIT, 0, 0,SEQ_SHUTDOWN, 503, 5, {0}, "^SGCI=0\r"},
+ {RSP_OK, 503,503, -1, 504, 5, {0}, "Z\r"},
+#endif
+ {RSP_INIT, 0, 0,SEQ_SHUTDOWN, 504, 5, {0}, "Z\r"},
+ {RSP_OK, 504,504, -1, 0, 0, {ACT_SDOWN}},
+ {RSP_ERROR, 501,599, -1, 0, 0, {ACT_FAILSDOWN}},
+ {EV_TIMEOUT, 501,599, -1, 0, 0, {ACT_FAILSDOWN}},
+ {RSP_NODEV, 501,599, -1, 0, 0, {ACT_FAKESDOWN}},
+
+ {EV_PROC_CIDMODE,-1, -1, -1, -1,-1, {ACT_PROC_CIDMODE}}, //FIXME
+ {EV_IF_LOCK, -1, -1, -1, -1,-1, {ACT_IF_LOCK}}, //FIXME
+ {EV_IF_VER, -1, -1, -1, -1,-1, {ACT_IF_VER}}, //FIXME
+ {EV_START, -1, -1, -1, -1,-1, {ACT_START}}, //FIXME
+ {EV_STOP, -1, -1, -1, -1,-1, {ACT_STOP}}, //FIXME
+ {EV_SHUTDOWN, -1, -1, -1, -1,-1, {ACT_SHUTDOWN}}, //FIXME
+
+ /* misc. */
+ {RSP_EMPTY, -1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME
+ {RSP_ZCFGT, -1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME
+ {RSP_ZCFG, -1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME
+ {RSP_ZLOG, -1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME
+ {RSP_ZMWI, -1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME
+ {RSP_ZABINFO, -1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME
+ {RSP_ZSMLSTCHG,-1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME
+
+ {RSP_ZCAU, -1, -1, -1, -1,-1, {ACT_ZCAU}},
+ {RSP_NONE, -1, -1, -1, -1,-1, {ACT_DEBUG}},
+ {RSP_ANY, -1, -1, -1, -1,-1, {ACT_WARN}},
+ {RSP_LAST}
+};
+
+// 600: start dialing, 650: dial in progress, 800: connection is up, 700: ring, 400: hup, 750: accepted icall
+struct reply_t gigaset_tab_cid_m10x[] = /* for M10x */
+{
+ /* resp_code, min_ConState, max_ConState, parameter, new_ConState, timeout, action, command */
+
+ /* dial */
+ {EV_DIAL, -1, -1, -1, -1,-1, {ACT_DIAL}}, //FIXME
+ {RSP_INIT, 0, 0,SEQ_DIAL, 601, 5, {ACT_CMD+AT_BC}},
+ {RSP_OK, 601,601, -1, 602, 5, {ACT_CMD+AT_HLC}},
+ {RSP_NULL, 602,602, -1, 603, 5, {ACT_CMD+AT_PROTO}},
+ {RSP_OK, 602,602, -1, 603, 5, {ACT_CMD+AT_PROTO}},
+ {RSP_OK, 603,603, -1, 604, 5, {ACT_CMD+AT_TYPE}},
+ {RSP_OK, 604,604, -1, 605, 5, {ACT_CMD+AT_MSN}},
+ {RSP_OK, 605,605, -1, 606, 5, {ACT_CMD+AT_ISO}},
+ {RSP_NULL, 605,605, -1, 606, 5, {ACT_CMD+AT_ISO}},
+ {RSP_OK, 606,606, -1, 607, 5, {0}, "+VLS=17\r"}, /* set "Endgeraetemodus" */
+ {RSP_OK, 607,607, -1, 608,-1},
+ //{RSP_ZSAU, 608,608,ZSAU_PROCEEDING, 608, 0, {ACT_ERROR}},//DELETE
+ {RSP_ZSAU, 608,608,ZSAU_PROCEEDING, 609, 5, {ACT_CMD+AT_DIAL}},
+ {RSP_OK, 609,609, -1, 650, 0, {ACT_DIALING}},
+
+ {RSP_ZVLS, 608,608, 17, -1,-1, {ACT_DEBUG}},
+ {RSP_ZCTP, 609,609, -1, -1,-1, {ACT_DEBUG}},
+ {RSP_ZCPN, 609,609, -1, -1,-1, {ACT_DEBUG}},
+ {RSP_ERROR, 601,609, -1, 0, 0, {ACT_ABORTDIAL}},
+ {EV_TIMEOUT, 601,609, -1, 0, 0, {ACT_ABORTDIAL}},
+
+ /* dialing */
+ {RSP_ZCTP, 650,650, -1, -1,-1, {ACT_DEBUG}},
+ {RSP_ZCPN, 650,650, -1, -1,-1, {ACT_DEBUG}},
+ {RSP_ZSAU, 650,650,ZSAU_CALL_DELIVERED, -1,-1, {ACT_DEBUG}}, /* some devices don't send this */
+
+ /* connection established */
+ {RSP_ZSAU, 650,650,ZSAU_ACTIVE, 800,-1, {ACT_CONNECT}}, //FIXME -> DLE1
+ {RSP_ZSAU, 750,750,ZSAU_ACTIVE, 800,-1, {ACT_CONNECT}}, //FIXME -> DLE1
+
+ {EV_BC_OPEN, 800,800, -1, 800,-1, {ACT_NOTIFY_BC_UP}}, //FIXME new constate + timeout
+
+ /* remote hangup */
+ {RSP_ZSAU, 650,650,ZSAU_DISCONNECT_IND, 0, 0, {ACT_REMOTEREJECT}},
+ {RSP_ZSAU, 750,750,ZSAU_DISCONNECT_IND, 0, 0, {ACT_REMOTEHUP}},
+ {RSP_ZSAU, 800,800,ZSAU_DISCONNECT_IND, 0, 0, {ACT_REMOTEHUP}},
+
+ /* hangup */
+ {EV_HUP, -1, -1, -1, -1,-1, {ACT_HUP}}, //FIXME
+ {RSP_INIT, -1, -1,SEQ_HUP, 401, 5, {0}, "+VLS=0\r"}, /* hang up */ //-1,-1?
+ {RSP_OK, 401,401, -1, 402, 5},
+ {RSP_ZVLS, 402,402, 0, 403, 5},
+ {RSP_ZSAU, 403,403,ZSAU_DISCONNECT_REQ, -1,-1, {ACT_DEBUG}}, /* if not remote hup */
+ //{RSP_ZSAU, 403,403,ZSAU_NULL, 401, 0, {ACT_ERROR}}, //DELETE//FIXME -> DLE0 // should we do this _before_ hanging up for base driver?
+ {RSP_ZSAU, 403,403,ZSAU_NULL, 0, 0, {ACT_DISCONNECT}}, //FIXME -> DLE0 // should we do this _before_ hanging up for base driver?
+ {RSP_NODEV, 401,403, -1, 0, 0, {ACT_FAKEHUP}}, //FIXME -> DLE0 // should we do this _before_ hanging up for base driver?
+ {RSP_ERROR, 401,401, -1, 0, 0, {ACT_ABORTHUP}},
+ {EV_TIMEOUT, 401,403, -1, 0, 0, {ACT_ABORTHUP}},
+
+ {EV_BC_CLOSED, 0, 0, -1, 0,-1, {ACT_NOTIFY_BC_DOWN}}, //FIXME new constate + timeout
+
+ /* ring */
+ {RSP_ZBC, 700,700, -1, -1,-1, {0}},
+ {RSP_ZHLC, 700,700, -1, -1,-1, {0}},
+ {RSP_NMBR, 700,700, -1, -1,-1, {0}},
+ {RSP_ZCPN, 700,700, -1, -1,-1, {0}},
+ {RSP_ZCTP, 700,700, -1, -1,-1, {0}},
+ {EV_TIMEOUT, 700,700, -1, 720,720, {ACT_ICALL}},
+ {EV_BC_CLOSED,720,720, -1, 0,-1, {ACT_NOTIFY_BC_DOWN}},
+
+ /*accept icall*/
+ {EV_ACCEPT, -1, -1, -1, -1,-1, {ACT_ACCEPT}}, //FIXME
+ {RSP_INIT, 720,720,SEQ_ACCEPT, 721, 5, {ACT_CMD+AT_PROTO}},
+ {RSP_OK, 721,721, -1, 722, 5, {ACT_CMD+AT_ISO}},
+ {RSP_OK, 722,722, -1, 723, 5, {0}, "+VLS=17\r"}, /* set "Endgeraetemodus" */
+ {RSP_OK, 723,723, -1, 724, 5, {0}},
+ {RSP_ZVLS, 724,724, 17, 750,50, {ACT_ACCEPTED}},
+ {RSP_ERROR, 721,729, -1, 0, 0, {ACT_ABORTACCEPT}},
+ {EV_TIMEOUT, 721,729, -1, 0, 0, {ACT_ABORTACCEPT}},
+ {RSP_ZSAU, 700,729,ZSAU_NULL, 0, 0, {ACT_ABORTACCEPT}},
+ {RSP_ZSAU, 700,729,ZSAU_ACTIVE, 0, 0, {ACT_ABORTACCEPT}},
+ {RSP_ZSAU, 700,729,ZSAU_DISCONNECT_IND, 0, 0, {ACT_ABORTACCEPT}},
+
+ {EV_TIMEOUT, 750,750, -1, 0, 0, {ACT_CONNTIMEOUT}},
+
+ /* B channel closed (general case) */
+ {EV_BC_CLOSED, -1, -1, -1, -1,-1, {ACT_NOTIFY_BC_DOWN}}, //FIXME
+
+ /* misc. */
+ {EV_PROTO_L2, -1, -1, -1, -1,-1, {ACT_PROTO_L2}}, //FIXME
+
+ {RSP_ZCON, -1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME
+ {RSP_ZCCR, -1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME
+ {RSP_ZAOC, -1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME
+ {RSP_ZCSTR, -1, -1, -1, -1,-1, {ACT_DEBUG}}, //FIXME
+
+ {RSP_ZCAU, -1, -1, -1, -1,-1, {ACT_ZCAU}},
+ {RSP_NONE, -1, -1, -1, -1,-1, {ACT_DEBUG}},
+ {RSP_ANY, -1, -1, -1, -1,-1, {ACT_WARN}},
+ {RSP_LAST}
+};
+
+
+#if 0
+static struct reply_t tab_nocid[]= /* no dle mode */ //FIXME
+{
+ /* resp_code, min_ConState, max_ConState, parameter, new_ConState, timeout, action, command */
+
+ {RSP_ANY, -1, -1, -1, -1,-1, ACT_WARN, NULL},
+ {RSP_LAST,0,0,0,0,0,0}
+};
+
+static struct reply_t tab_cid[] = /* no dle mode */ //FIXME
+{
+ /* resp_code, min_ConState, max_ConState, parameter, new_ConState, timeout, action, command */
+
+ {RSP_ANY, -1, -1, -1, -1,-1, ACT_WARN, NULL},
+ {RSP_LAST,0,0,0,0,0,0}
+};
+#endif
+
+static const struct resp_type_t resp_type[] =
+{
+ /*{"", RSP_EMPTY, RT_NOTHING},*/
+ {"OK", RSP_OK, RT_NOTHING},
+ {"ERROR", RSP_ERROR, RT_NOTHING},
+ {"ZSAU", RSP_ZSAU, RT_ZSAU},
+ {"ZCAU", RSP_ZCAU, RT_ZCAU},
+ {"RING", RSP_RING, RT_RING},
+ {"ZGCI", RSP_ZGCI, RT_NUMBER},
+ {"ZVLS", RSP_ZVLS, RT_NUMBER},
+ {"ZCTP", RSP_ZCTP, RT_NUMBER},
+ {"ZDLE", RSP_ZDLE, RT_NUMBER},
+ {"ZCFGT", RSP_ZCFGT, RT_NUMBER},
+ {"ZCCR", RSP_ZCCR, RT_NUMBER},
+ {"ZMWI", RSP_ZMWI, RT_NUMBER},
+ {"ZHLC", RSP_ZHLC, RT_STRING},
+ {"ZBC", RSP_ZBC, RT_STRING},
+ {"NMBR", RSP_NMBR, RT_STRING},
+ {"ZCPN", RSP_ZCPN, RT_STRING},
+ {"ZCON", RSP_ZCON, RT_STRING},
+ {"ZAOC", RSP_ZAOC, RT_STRING},
+ {"ZCSTR", RSP_ZCSTR, RT_STRING},
+ {"ZCFG", RSP_ZCFG, RT_HEX},
+ {"ZLOG", RSP_ZLOG, RT_NOTHING},
+ {"ZABINFO", RSP_ZABINFO, RT_NOTHING},
+ {"ZSMLSTCHG", RSP_ZSMLSTCHG, RT_NOTHING},
+ {NULL,0,0}
+};
+
+/*
+ * Get integer from char-pointer
+ */
+static int isdn_getnum(char *p)
+{
+ int v = -1;
+
+ gig_dbg(DEBUG_TRANSCMD, "string: %s", p);
+
+ while (*p >= '0' && *p <= '9')
+ v = ((v < 0) ? 0 : (v * 10)) + (int) ((*p++) - '0');
+ if (*p)
+ v = -1; /* invalid Character */
+ return v;
+}
+
+/*
+ * Get integer from char-pointer
+ */
+static int isdn_gethex(char *p)
+{
+ int v = 0;
+ int c;
+
+ gig_dbg(DEBUG_TRANSCMD, "string: %s", p);
+
+ if (!*p)
+ return -1;
+
+ do {
+ if (v > (INT_MAX - 15) / 16)
+ return -1;
+ c = *p;
+ if (c >= '0' && c <= '9')
+ c -= '0';
+ else if (c >= 'a' && c <= 'f')
+ c -= 'a' - 10;
+ else if (c >= 'A' && c <= 'F')
+ c -= 'A' - 10;
+ else
+ return -1;
+ v = v * 16 + c;
+ } while (*++p);
+
+ return v;
+}
+
+/* retrieve CID from parsed response
+ * returns 0 if no CID, -1 if invalid CID, or CID value 1..65535
+ */
+static int cid_of_response(char *s)
+{
+ int cid;
+
+ if (s[-1] != ';')
+ return 0; /* no CID separator */
+ cid = isdn_getnum(s);
+ if (cid < 0)
+ return 0; /* CID not numeric */
+ if (cid < 1 || cid > 65535)
+ return -1; /* CID out of range */
+ return cid;
+ //FIXME is ;<digit>+ at end of non-CID response really impossible?
+}
+
+/* This function will be called via task queue from the callback handler.
+ * We received a modem response and have to handle it..
+ */
+void gigaset_handle_modem_response(struct cardstate *cs)
+{
+ unsigned char *argv[MAX_REC_PARAMS + 1];
+ int params;
+ int i, j;
+ const struct resp_type_t *rt;
+ int curarg;
+ unsigned long flags;
+ unsigned next, tail, head;
+ struct event_t *event;
+ int resp_code;
+ int param_type;
+ int abort;
+ size_t len;
+ int cid;
+ int rawstring;
+
+ len = cs->cbytes;
+ if (!len) {
+ /* ignore additional LFs/CRs (M10x config mode or cx100) */
+ gig_dbg(DEBUG_MCMD, "skipped EOL [%02X]", cs->respdata[len]);
+ return;
+ }
+ cs->respdata[len] = 0;
+ gig_dbg(DEBUG_TRANSCMD, "raw string: '%s'", cs->respdata);
+ argv[0] = cs->respdata;
+ params = 1;
+ if (cs->at_state.getstring) {
+ /* getstring only allowed without cid at the moment */
+ cs->at_state.getstring = 0;
+ rawstring = 1;
+ cid = 0;
+ } else {
+ /* parse line */
+ for (i = 0; i < len; i++)
+ switch (cs->respdata[i]) {
+ case ';':
+ case ',':
+ case '=':
+ if (params > MAX_REC_PARAMS) {
+ dev_warn(cs->dev,
+ "too many parameters in response\n");
+ /* need last parameter (might be CID) */
+ params--;
+ }
+ argv[params++] = cs->respdata + i + 1;
+ }
+
+ rawstring = 0;
+ cid = params > 1 ? cid_of_response(argv[params-1]) : 0;
+ if (cid < 0) {
+ gigaset_add_event(cs, &cs->at_state, RSP_INVAL,
+ NULL, 0, NULL);
+ return;
+ }
+
+ for (j = 1; j < params; ++j)
+ argv[j][-1] = 0;
+
+ gig_dbg(DEBUG_TRANSCMD, "CMD received: %s", argv[0]);
+ if (cid) {
+ --params;
+ gig_dbg(DEBUG_TRANSCMD, "CID: %s", argv[params]);
+ }
+ gig_dbg(DEBUG_TRANSCMD, "available params: %d", params - 1);
+ for (j = 1; j < params; j++)
+ gig_dbg(DEBUG_TRANSCMD, "param %d: %s", j, argv[j]);
+ }
+
+ spin_lock_irqsave(&cs->ev_lock, flags);
+ head = cs->ev_head;
+ tail = cs->ev_tail;
+
+ abort = 1;
+ curarg = 0;
+ while (curarg < params) {
+ next = (tail + 1) % MAX_EVENTS;
+ if (unlikely(next == head)) {
+ dev_err(cs->dev, "event queue full\n");
+ break;
+ }
+
+ event = cs->events + tail;
+ event->at_state = NULL;
+ event->cid = cid;
+ event->ptr = NULL;
+ event->arg = NULL;
+ tail = next;
+
+ if (rawstring) {
+ resp_code = RSP_STRING;
+ param_type = RT_STRING;
+ } else {
+ for (rt = resp_type; rt->response; ++rt)
+ if (!strcmp(argv[curarg], rt->response))
+ break;
+
+ if (!rt->response) {
+ event->type = RSP_UNKNOWN;
+ dev_warn(cs->dev,
+ "unknown modem response: %s\n",
+ argv[curarg]);
+ break;
+ }
+
+ resp_code = rt->resp_code;
+ param_type = rt->type;
+ ++curarg;
+ }
+
+ event->type = resp_code;
+
+ switch (param_type) {
+ case RT_NOTHING:
+ break;
+ case RT_RING:
+ if (!cid) {
+ dev_err(cs->dev,
+ "received RING without CID!\n");
+ event->type = RSP_INVAL;
+ abort = 1;
+ } else {
+ event->cid = 0;
+ event->parameter = cid;
+ abort = 0;
+ }
+ break;
+ case RT_ZSAU:
+ if (curarg >= params) {
+ event->parameter = ZSAU_NONE;
+ break;
+ }
+ if (!strcmp(argv[curarg], "OUTGOING_CALL_PROCEEDING"))
+ event->parameter = ZSAU_OUTGOING_CALL_PROCEEDING;
+ else if (!strcmp(argv[curarg], "CALL_DELIVERED"))
+ event->parameter = ZSAU_CALL_DELIVERED;
+ else if (!strcmp(argv[curarg], "ACTIVE"))
+ event->parameter = ZSAU_ACTIVE;
+ else if (!strcmp(argv[curarg], "DISCONNECT_IND"))
+ event->parameter = ZSAU_DISCONNECT_IND;
+ else if (!strcmp(argv[curarg], "NULL"))
+ event->parameter = ZSAU_NULL;
+ else if (!strcmp(argv[curarg], "DISCONNECT_REQ"))
+ event->parameter = ZSAU_DISCONNECT_REQ;
+ else {
+ event->parameter = ZSAU_UNKNOWN;
+ dev_warn(cs->dev,
+ "%s: unknown parameter %s after ZSAU\n",
+ __func__, argv[curarg]);
+ }
+ ++curarg;
+ break;
+ case RT_STRING:
+ if (curarg < params) {
+ event->ptr = kstrdup(argv[curarg], GFP_ATOMIC);
+ if (!event->ptr)
+ dev_err(cs->dev, "out of memory\n");
+ ++curarg;
+ }
+#ifdef CONFIG_GIGASET_DEBUG
+ if (!event->ptr)
+ gig_dbg(DEBUG_CMD, "string==NULL");
+ else
+ gig_dbg(DEBUG_CMD, "string==%s",
+ (char *) event->ptr);
+#endif
+ break;
+ case RT_ZCAU:
+ event->parameter = -1;
+ if (curarg + 1 < params) {
+ i = isdn_gethex(argv[curarg]);
+ j = isdn_gethex(argv[curarg + 1]);
+ if (i >= 0 && i < 256 && j >= 0 && j < 256)
+ event->parameter = (unsigned) i << 8
+ | j;
+ curarg += 2;
+ } else
+ curarg = params - 1;
+ break;
+ case RT_NUMBER:
+ case RT_HEX:
+ if (curarg < params) {
+ if (param_type == RT_HEX)
+ event->parameter =
+ isdn_gethex(argv[curarg]);
+ else
+ event->parameter =
+ isdn_getnum(argv[curarg]);
+ ++curarg;
+ } else
+ event->parameter = -1;
+#ifdef CONFIG_GIGASET_DEBUG
+ gig_dbg(DEBUG_CMD, "parameter==%d", event->parameter);
+#endif
+ break;
+ }
+
+ if (resp_code == RSP_ZDLE)
+ cs->dle = event->parameter;
+
+ if (abort)
+ break;
+ }
+
+ cs->ev_tail = tail;
+ spin_unlock_irqrestore(&cs->ev_lock, flags);
+
+ if (curarg != params)
+ gig_dbg(DEBUG_ANY,
+ "invalid number of processed parameters: %d/%d",
+ curarg, params);
+}
+EXPORT_SYMBOL_GPL(gigaset_handle_modem_response);
+
+/* disconnect
+ * process closing of connection associated with given AT state structure
+ */
+static void disconnect(struct at_state_t **at_state_p)
+{
+ unsigned long flags;
+ struct bc_state *bcs = (*at_state_p)->bcs;
+ struct cardstate *cs = (*at_state_p)->cs;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ ++(*at_state_p)->seq_index;
+
+ /* revert to selected idle mode */
+ if (!cs->cidmode) {
+ cs->at_state.pending_commands |= PC_UMMODE;
+ cs->commands_pending = 1;
+ gig_dbg(DEBUG_CMD, "Scheduling PC_UMMODE");
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+
+ if (bcs) {
+ /* B channel assigned: invoke hardware specific handler */
+ cs->ops->close_bchannel(bcs);
+ } else {
+ /* no B channel assigned: just deallocate */
+ spin_lock_irqsave(&cs->lock, flags);
+ list_del(&(*at_state_p)->list);
+ kfree(*at_state_p);
+ *at_state_p = NULL;
+ spin_unlock_irqrestore(&cs->lock, flags);
+ }
+}
+
+/* get_free_channel
+ * get a free AT state structure: either one of those associated with the
+ * B channels of the Gigaset device, or if none of those is available,
+ * a newly allocated one with bcs=NULL
+ * The structure should be freed by calling disconnect() after use.
+ */
+static inline struct at_state_t *get_free_channel(struct cardstate *cs,
+ int cid)
+/* cids: >0: siemens-cid
+ 0: without cid
+ -1: no cid assigned yet
+*/
+{
+ unsigned long flags;
+ int i;
+ struct at_state_t *ret;
+
+ for (i = 0; i < cs->channels; ++i)
+ if (gigaset_get_channel(cs->bcs + i)) {
+ ret = &cs->bcs[i].at_state;
+ ret->cid = cid;
+ return ret;
+ }
+
+ spin_lock_irqsave(&cs->lock, flags);
+ ret = kmalloc(sizeof(struct at_state_t), GFP_ATOMIC);
+ if (ret) {
+ gigaset_at_init(ret, NULL, cs, cid);
+ list_add(&ret->list, &cs->temp_at_states);
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return ret;
+}
+
+static void init_failed(struct cardstate *cs, int mode)
+{
+ int i;
+ struct at_state_t *at_state;
+
+ cs->at_state.pending_commands &= ~PC_INIT;
+ cs->mode = mode;
+ cs->mstate = MS_UNINITIALIZED;
+ gigaset_free_channels(cs);
+ for (i = 0; i < cs->channels; ++i) {
+ at_state = &cs->bcs[i].at_state;
+ if (at_state->pending_commands & PC_CID) {
+ at_state->pending_commands &= ~PC_CID;
+ at_state->pending_commands |= PC_NOCID;
+ cs->commands_pending = 1;
+ }
+ }
+}
+
+static void schedule_init(struct cardstate *cs, int state)
+{
+ if (cs->at_state.pending_commands & PC_INIT) {
+ gig_dbg(DEBUG_CMD, "not scheduling PC_INIT again");
+ return;
+ }
+ cs->mstate = state;
+ cs->mode = M_UNKNOWN;
+ gigaset_block_channels(cs);
+ cs->at_state.pending_commands |= PC_INIT;
+ cs->commands_pending = 1;
+ gig_dbg(DEBUG_CMD, "Scheduling PC_INIT");
+}
+
+/* Add "AT" to a command, add the cid, dle encode it, send the result to the
+ hardware. */
+static void send_command(struct cardstate *cs, const char *cmd, int cid,
+ int dle, gfp_t kmallocflags)
+{
+ size_t cmdlen, buflen;
+ char *cmdpos, *cmdbuf, *cmdtail;
+
+ cmdlen = strlen(cmd);
+ buflen = 11 + cmdlen;
+ if (unlikely(buflen <= cmdlen)) {
+ dev_err(cs->dev, "integer overflow in buflen\n");
+ return;
+ }
+
+ cmdbuf = kmalloc(buflen, kmallocflags);
+ if (unlikely(!cmdbuf)) {
+ dev_err(cs->dev, "out of memory\n");
+ return;
+ }
+
+ cmdpos = cmdbuf + 9;
+ cmdtail = cmdpos + cmdlen;
+ memcpy(cmdpos, cmd, cmdlen);
+
+ if (cid > 0 && cid <= 65535) {
+ do {
+ *--cmdpos = '0' + cid % 10;
+ cid /= 10;
+ ++cmdlen;
+ } while (cid);
+ }
+
+ cmdlen += 2;
+ *--cmdpos = 'T';
+ *--cmdpos = 'A';
+
+ if (dle) {
+ cmdlen += 4;
+ *--cmdpos = '(';
+ *--cmdpos = 0x10;
+ *cmdtail++ = 0x10;
+ *cmdtail++ = ')';
+ }
+
+ cs->ops->write_cmd(cs, cmdpos, cmdlen, NULL);
+ kfree(cmdbuf);
+}
+
+static struct at_state_t *at_state_from_cid(struct cardstate *cs, int cid)
+{
+ struct at_state_t *at_state;
+ int i;
+ unsigned long flags;
+
+ if (cid == 0)
+ return &cs->at_state;
+
+ for (i = 0; i < cs->channels; ++i)
+ if (cid == cs->bcs[i].at_state.cid)
+ return &cs->bcs[i].at_state;
+
+ spin_lock_irqsave(&cs->lock, flags);
+
+ list_for_each_entry(at_state, &cs->temp_at_states, list)
+ if (cid == at_state->cid) {
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return at_state;
+ }
+
+ spin_unlock_irqrestore(&cs->lock, flags);
+
+ return NULL;
+}
+
+static void bchannel_down(struct bc_state *bcs)
+{
+ if (bcs->chstate & CHS_B_UP) {
+ bcs->chstate &= ~CHS_B_UP;
+ gigaset_i4l_channel_cmd(bcs, ISDN_STAT_BHUP);
+ }
+
+ if (bcs->chstate & (CHS_D_UP | CHS_NOTIFY_LL)) {
+ bcs->chstate &= ~(CHS_D_UP | CHS_NOTIFY_LL);
+ gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DHUP);
+ }
+
+ gigaset_free_channel(bcs);
+
+ gigaset_bcs_reinit(bcs);
+}
+
+static void bchannel_up(struct bc_state *bcs)
+{
+ if (!(bcs->chstate & CHS_D_UP)) {
+ dev_notice(bcs->cs->dev, "%s: D channel not up\n", __func__);
+ bcs->chstate |= CHS_D_UP;
+ gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DCONN);
+ }
+
+ if (bcs->chstate & CHS_B_UP) {
+ dev_notice(bcs->cs->dev, "%s: B channel already up\n",
+ __func__);
+ return;
+ }
+
+ bcs->chstate |= CHS_B_UP;
+ gigaset_i4l_channel_cmd(bcs, ISDN_STAT_BCONN);
+}
+
+static void start_dial(struct at_state_t *at_state, void *data, unsigned seq_index)
+{
+ struct bc_state *bcs = at_state->bcs;
+ struct cardstate *cs = at_state->cs;
+ int retval;
+ unsigned long flags;
+
+ bcs->chstate |= CHS_NOTIFY_LL;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ if (at_state->seq_index != seq_index) {
+ spin_unlock_irqrestore(&cs->lock, flags);
+ goto error;
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+
+ retval = gigaset_isdn_setup_dial(at_state, data);
+ if (retval != 0)
+ goto error;
+
+
+ at_state->pending_commands |= PC_CID;
+ gig_dbg(DEBUG_CMD, "Scheduling PC_CID");
+ cs->commands_pending = 1;
+ return;
+
+error:
+ at_state->pending_commands |= PC_NOCID;
+ gig_dbg(DEBUG_CMD, "Scheduling PC_NOCID");
+ cs->commands_pending = 1;
+ return;
+}
+
+static void start_accept(struct at_state_t *at_state)
+{
+ struct cardstate *cs = at_state->cs;
+ int retval;
+
+ retval = gigaset_isdn_setup_accept(at_state);
+
+ if (retval == 0) {
+ at_state->pending_commands |= PC_ACCEPT;
+ gig_dbg(DEBUG_CMD, "Scheduling PC_ACCEPT");
+ cs->commands_pending = 1;
+ } else {
+ /* error reset */
+ at_state->pending_commands |= PC_HUP;
+ gig_dbg(DEBUG_CMD, "Scheduling PC_HUP");
+ cs->commands_pending = 1;
+ }
+}
+
+static void do_start(struct cardstate *cs)
+{
+ gigaset_free_channels(cs);
+
+ if (cs->mstate != MS_LOCKED)
+ schedule_init(cs, MS_INIT);
+
+ cs->isdn_up = 1;
+ gigaset_i4l_cmd(cs, ISDN_STAT_RUN);
+ // FIXME: not in locked mode
+ // FIXME 2: only after init sequence
+
+ cs->waiting = 0;
+ wake_up(&cs->waitqueue);
+}
+
+static void finish_shutdown(struct cardstate *cs)
+{
+ if (cs->mstate != MS_LOCKED) {
+ cs->mstate = MS_UNINITIALIZED;
+ cs->mode = M_UNKNOWN;
+ }
+
+ /* Tell the LL that the device is not available .. */
+ if (cs->isdn_up) {
+ cs->isdn_up = 0;
+ gigaset_i4l_cmd(cs, ISDN_STAT_STOP);
+ }
+
+ /* The rest is done by cleanup_cs () in user mode. */
+
+ cs->cmd_result = -ENODEV;
+ cs->waiting = 0;
+ wake_up(&cs->waitqueue);
+}
+
+static void do_shutdown(struct cardstate *cs)
+{
+ gigaset_block_channels(cs);
+
+ if (cs->mstate == MS_READY) {
+ cs->mstate = MS_SHUTDOWN;
+ cs->at_state.pending_commands |= PC_SHUTDOWN;
+ cs->commands_pending = 1;
+ gig_dbg(DEBUG_CMD, "Scheduling PC_SHUTDOWN");
+ } else
+ finish_shutdown(cs);
+}
+
+static void do_stop(struct cardstate *cs)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ cs->connected = 0;
+ spin_unlock_irqrestore(&cs->lock, flags);
+
+ do_shutdown(cs);
+}
+
+/* Entering cid mode or getting a cid failed:
+ * try to initialize the device and try again.
+ *
+ * channel >= 0: getting cid for the channel failed
+ * channel < 0: entering cid mode failed
+ *
+ * returns 0 on failure
+ */
+static int reinit_and_retry(struct cardstate *cs, int channel)
+{
+ int i;
+
+ if (--cs->retry_count <= 0)
+ return 0;
+
+ for (i = 0; i < cs->channels; ++i)
+ if (cs->bcs[i].at_state.cid > 0)
+ return 0;
+
+ if (channel < 0)
+ dev_warn(cs->dev,
+ "Could not enter cid mode. Reinit device and try again.\n");
+ else {
+ dev_warn(cs->dev,
+ "Could not get a call id. Reinit device and try again.\n");
+ cs->bcs[channel].at_state.pending_commands |= PC_CID;
+ }
+ schedule_init(cs, MS_INIT);
+ return 1;
+}
+
+static int at_state_invalid(struct cardstate *cs,
+ struct at_state_t *test_ptr)
+{
+ unsigned long flags;
+ unsigned channel;
+ struct at_state_t *at_state;
+ int retval = 0;
+
+ spin_lock_irqsave(&cs->lock, flags);
+
+ if (test_ptr == &cs->at_state)
+ goto exit;
+
+ list_for_each_entry(at_state, &cs->temp_at_states, list)
+ if (at_state == test_ptr)
+ goto exit;
+
+ for (channel = 0; channel < cs->channels; ++channel)
+ if (&cs->bcs[channel].at_state == test_ptr)
+ goto exit;
+
+ retval = 1;
+exit:
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return retval;
+}
+
+static void handle_icall(struct cardstate *cs, struct bc_state *bcs,
+ struct at_state_t **p_at_state)
+{
+ int retval;
+ struct at_state_t *at_state = *p_at_state;
+
+ retval = gigaset_isdn_icall(at_state);
+ switch (retval) {
+ case ICALL_ACCEPT:
+ break;
+ default:
+ dev_err(cs->dev, "internal error: disposition=%d\n", retval);
+ /* --v-- fall through --v-- */
+ case ICALL_IGNORE:
+ case ICALL_REJECT:
+ /* hang up actively
+ * Device doc says that would reject the call.
+ * In fact it doesn't.
+ */
+ at_state->pending_commands |= PC_HUP;
+ cs->commands_pending = 1;
+ break;
+ }
+}
+
+static int do_lock(struct cardstate *cs)
+{
+ int mode;
+ int i;
+
+ switch (cs->mstate) {
+ case MS_UNINITIALIZED:
+ case MS_READY:
+ if (cs->cur_at_seq || !list_empty(&cs->temp_at_states) ||
+ cs->at_state.pending_commands)
+ return -EBUSY;
+
+ for (i = 0; i < cs->channels; ++i)
+ if (cs->bcs[i].at_state.pending_commands)
+ return -EBUSY;
+
+ if (!gigaset_get_channels(cs))
+ return -EBUSY;
+
+ break;
+ case MS_LOCKED:
+ //retval = -EACCES;
+ break;
+ default:
+ return -EBUSY;
+ }
+
+ mode = cs->mode;
+ cs->mstate = MS_LOCKED;
+ cs->mode = M_UNKNOWN;
+
+ return mode;
+}
+
+static int do_unlock(struct cardstate *cs)
+{
+ if (cs->mstate != MS_LOCKED)
+ return -EINVAL;
+
+ cs->mstate = MS_UNINITIALIZED;
+ cs->mode = M_UNKNOWN;
+ gigaset_free_channels(cs);
+ if (cs->connected)
+ schedule_init(cs, MS_INIT);
+
+ return 0;
+}
+
+static void do_action(int action, struct cardstate *cs,
+ struct bc_state *bcs,
+ struct at_state_t **p_at_state, char **pp_command,
+ int *p_genresp, int *p_resp_code,
+ struct event_t *ev)
+{
+ struct at_state_t *at_state = *p_at_state;
+ struct at_state_t *at_state2;
+ unsigned long flags;
+
+ int channel;
+
+ unsigned char *s, *e;
+ int i;
+ unsigned long val;
+
+ switch (action) {
+ case ACT_NOTHING:
+ break;
+ case ACT_TIMEOUT:
+ at_state->waiting = 1;
+ break;
+ case ACT_INIT:
+ cs->at_state.pending_commands &= ~PC_INIT;
+ cs->cur_at_seq = SEQ_NONE;
+ cs->mode = M_UNIMODEM;
+ spin_lock_irqsave(&cs->lock, flags);
+ if (!cs->cidmode) {
+ spin_unlock_irqrestore(&cs->lock, flags);
+ gigaset_free_channels(cs);
+ cs->mstate = MS_READY;
+ break;
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+ cs->at_state.pending_commands |= PC_CIDMODE;
+ cs->commands_pending = 1;
+ gig_dbg(DEBUG_CMD, "Scheduling PC_CIDMODE");
+ break;
+ case ACT_FAILINIT:
+ dev_warn(cs->dev, "Could not initialize the device.\n");
+ cs->dle = 0;
+ init_failed(cs, M_UNKNOWN);
+ cs->cur_at_seq = SEQ_NONE;
+ break;
+ case ACT_CONFIGMODE:
+ init_failed(cs, M_CONFIG);
+ cs->cur_at_seq = SEQ_NONE;
+ break;
+ case ACT_SETDLE1:
+ cs->dle = 1;
+ /* cs->inbuf[0].inputstate |= INS_command | INS_DLE_command; */
+ cs->inbuf[0].inputstate &=
+ ~(INS_command | INS_DLE_command);
+ break;
+ case ACT_SETDLE0:
+ cs->dle = 0;
+ cs->inbuf[0].inputstate =
+ (cs->inbuf[0].inputstate & ~INS_DLE_command)
+ | INS_command;
+ break;
+ case ACT_CMODESET:
+ if (cs->mstate == MS_INIT || cs->mstate == MS_RECOVER) {
+ gigaset_free_channels(cs);
+ cs->mstate = MS_READY;
+ }
+ cs->mode = M_CID;
+ cs->cur_at_seq = SEQ_NONE;
+ break;
+ case ACT_UMODESET:
+ cs->mode = M_UNIMODEM;
+ cs->cur_at_seq = SEQ_NONE;
+ break;
+ case ACT_FAILCMODE:
+ cs->cur_at_seq = SEQ_NONE;
+ if (cs->mstate == MS_INIT || cs->mstate == MS_RECOVER) {
+ init_failed(cs, M_UNKNOWN);
+ break;
+ }
+ if (!reinit_and_retry(cs, -1))
+ schedule_init(cs, MS_RECOVER);
+ break;
+ case ACT_FAILUMODE:
+ cs->cur_at_seq = SEQ_NONE;
+ schedule_init(cs, MS_RECOVER);
+ break;
+ case ACT_HUPMODEM:
+ /* send "+++" (hangup in unimodem mode) */
+ if (cs->connected)
+ cs->ops->write_cmd(cs, "+++", 3, NULL);
+ break;
+ case ACT_RING:
+ /* get fresh AT state structure for new CID */
+ at_state2 = get_free_channel(cs, ev->parameter);
+ if (!at_state2) {
+ dev_warn(cs->dev,
+ "RING ignored: could not allocate channel structure\n");
+ break;
+ }
+
+ /* initialize AT state structure
+ * note that bcs may be NULL if no B channel is free
+ */
+ at_state2->ConState = 700;
+ kfree(at_state2->str_var[STR_NMBR]);
+ at_state2->str_var[STR_NMBR] = NULL;
+ kfree(at_state2->str_var[STR_ZCPN]);
+ at_state2->str_var[STR_ZCPN] = NULL;
+ kfree(at_state2->str_var[STR_ZBC]);
+ at_state2->str_var[STR_ZBC] = NULL;
+ kfree(at_state2->str_var[STR_ZHLC]);
+ at_state2->str_var[STR_ZHLC] = NULL;
+ at_state2->int_var[VAR_ZCTP] = -1;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ at_state2->timer_expires = RING_TIMEOUT;
+ at_state2->timer_active = 1;
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case ACT_ICALL:
+ handle_icall(cs, bcs, p_at_state);
+ break;
+ case ACT_FAILSDOWN:
+ dev_warn(cs->dev, "Could not shut down the device.\n");
+ /* fall through */
+ case ACT_FAKESDOWN:
+ case ACT_SDOWN:
+ cs->cur_at_seq = SEQ_NONE;
+ finish_shutdown(cs);
+ break;
+ case ACT_CONNECT:
+ if (cs->onechannel) {
+ at_state->pending_commands |= PC_DLE1;
+ cs->commands_pending = 1;
+ break;
+ }
+ bcs->chstate |= CHS_D_UP;
+ gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DCONN);
+ cs->ops->init_bchannel(bcs);
+ break;
+ case ACT_DLE1:
+ cs->cur_at_seq = SEQ_NONE;
+ bcs = cs->bcs + cs->curchannel;
+
+ bcs->chstate |= CHS_D_UP;
+ gigaset_i4l_channel_cmd(bcs, ISDN_STAT_DCONN);
+ cs->ops->init_bchannel(bcs);
+ break;
+ case ACT_FAKEHUP:
+ at_state->int_var[VAR_ZSAU] = ZSAU_NULL;
+ /* fall through */
+ case ACT_DISCONNECT:
+ cs->cur_at_seq = SEQ_NONE;
+ at_state->cid = -1;
+ if (bcs && cs->onechannel && cs->dle) {
+ /* Check for other open channels not needed:
+ * DLE only used for M10x with one B channel.
+ */
+ at_state->pending_commands |= PC_DLE0;
+ cs->commands_pending = 1;
+ } else
+ disconnect(p_at_state);
+ break;
+ case ACT_FAKEDLE0:
+ at_state->int_var[VAR_ZDLE] = 0;
+ cs->dle = 0;
+ /* fall through */
+ case ACT_DLE0:
+ cs->cur_at_seq = SEQ_NONE;
+ at_state2 = &cs->bcs[cs->curchannel].at_state;
+ disconnect(&at_state2);
+ break;
+ case ACT_ABORTHUP:
+ cs->cur_at_seq = SEQ_NONE;
+ dev_warn(cs->dev, "Could not hang up.\n");
+ at_state->cid = -1;
+ if (bcs && cs->onechannel)
+ at_state->pending_commands |= PC_DLE0;
+ else
+ disconnect(p_at_state);
+ schedule_init(cs, MS_RECOVER);
+ break;
+ case ACT_FAILDLE0:
+ cs->cur_at_seq = SEQ_NONE;
+ dev_warn(cs->dev, "Could not leave DLE mode.\n");
+ at_state2 = &cs->bcs[cs->curchannel].at_state;
+ disconnect(&at_state2);
+ schedule_init(cs, MS_RECOVER);
+ break;
+ case ACT_FAILDLE1:
+ cs->cur_at_seq = SEQ_NONE;
+ dev_warn(cs->dev,
+ "Could not enter DLE mode. Trying to hang up.\n");
+ channel = cs->curchannel;
+ cs->bcs[channel].at_state.pending_commands |= PC_HUP;
+ cs->commands_pending = 1;
+ break;
+
+ case ACT_CID: /* got cid; start dialing */
+ cs->cur_at_seq = SEQ_NONE;
+ channel = cs->curchannel;
+ if (ev->parameter > 0 && ev->parameter <= 65535) {
+ cs->bcs[channel].at_state.cid = ev->parameter;
+ cs->bcs[channel].at_state.pending_commands |=
+ PC_DIAL;
+ cs->commands_pending = 1;
+ break;
+ }
+ /* fall through */
+ case ACT_FAILCID:
+ cs->cur_at_seq = SEQ_NONE;
+ channel = cs->curchannel;
+ if (!reinit_and_retry(cs, channel)) {
+ dev_warn(cs->dev,
+ "Could not get a call ID. Cannot dial.\n");
+ at_state2 = &cs->bcs[channel].at_state;
+ disconnect(&at_state2);
+ }
+ break;
+ case ACT_ABORTCID:
+ cs->cur_at_seq = SEQ_NONE;
+ at_state2 = &cs->bcs[cs->curchannel].at_state;
+ disconnect(&at_state2);
+ break;
+
+ case ACT_DIALING:
+ case ACT_ACCEPTED:
+ cs->cur_at_seq = SEQ_NONE;
+ break;
+
+ case ACT_ABORTACCEPT: /* hangup/error/timeout during ICALL processing */
+ disconnect(p_at_state);
+ break;
+
+ case ACT_ABORTDIAL: /* error/timeout during dial preparation */
+ cs->cur_at_seq = SEQ_NONE;
+ at_state->pending_commands |= PC_HUP;
+ cs->commands_pending = 1;
+ break;
+
+ case ACT_REMOTEREJECT: /* DISCONNECT_IND after dialling */
+ case ACT_CONNTIMEOUT: /* timeout waiting for ZSAU=ACTIVE */
+ case ACT_REMOTEHUP: /* DISCONNECT_IND with established connection */
+ at_state->pending_commands |= PC_HUP;
+ cs->commands_pending = 1;
+ break;
+ case ACT_GETSTRING: /* warning: RING, ZDLE, ...
+ are not handled properly anymore */
+ at_state->getstring = 1;
+ break;
+ case ACT_SETVER:
+ if (!ev->ptr) {
+ *p_genresp = 1;
+ *p_resp_code = RSP_ERROR;
+ break;
+ }
+ s = ev->ptr;
+
+ if (!strcmp(s, "OK")) {
+ *p_genresp = 1;
+ *p_resp_code = RSP_ERROR;
+ break;
+ }
+
+ for (i = 0; i < 4; ++i) {
+ val = simple_strtoul(s, (char **) &e, 10);
+ if (val > INT_MAX || e == s)
+ break;
+ if (i == 3) {
+ if (*e)
+ break;
+ } else if (*e != '.')
+ break;
+ else
+ s = e + 1;
+ cs->fwver[i] = val;
+ }
+ if (i != 4) {
+ *p_genresp = 1;
+ *p_resp_code = RSP_ERROR;
+ break;
+ }
+ /*at_state->getstring = 1;*/
+ cs->gotfwver = 0;
+ break;
+ case ACT_GOTVER:
+ if (cs->gotfwver == 0) {
+ cs->gotfwver = 1;
+ gig_dbg(DEBUG_ANY,
+ "firmware version %02d.%03d.%02d.%02d",
+ cs->fwver[0], cs->fwver[1],
+ cs->fwver[2], cs->fwver[3]);
+ break;
+ }
+ /* fall through */
+ case ACT_FAILVER:
+ cs->gotfwver = -1;
+ dev_err(cs->dev, "could not read firmware version.\n");
+ break;
+#ifdef CONFIG_GIGASET_DEBUG
+ case ACT_ERROR:
+ *p_genresp = 1;
+ *p_resp_code = RSP_ERROR;
+ break;
+ case ACT_TEST:
+ {
+ static int count = 3; //2; //1;
+ *p_genresp = 1;
+ *p_resp_code = count ? RSP_ERROR : RSP_OK;
+ if (count > 0)
+ --count;
+ }
+ break;
+#endif
+ case ACT_DEBUG:
+ gig_dbg(DEBUG_ANY, "%s: resp_code %d in ConState %d",
+ __func__, ev->type, at_state->ConState);
+ break;
+ case ACT_WARN:
+ dev_warn(cs->dev, "%s: resp_code %d in ConState %d!\n",
+ __func__, ev->type, at_state->ConState);
+ break;
+ case ACT_ZCAU:
+ dev_warn(cs->dev, "cause code %04x in connection state %d.\n",
+ ev->parameter, at_state->ConState);
+ break;
+
+ /* events from the LL */
+ case ACT_DIAL:
+ start_dial(at_state, ev->ptr, ev->parameter);
+ break;
+ case ACT_ACCEPT:
+ start_accept(at_state);
+ break;
+ case ACT_PROTO_L2:
+ gig_dbg(DEBUG_CMD, "set protocol to %u",
+ (unsigned) ev->parameter);
+ at_state->bcs->proto2 = ev->parameter;
+ break;
+ case ACT_HUP:
+ at_state->pending_commands |= PC_HUP;
+ cs->commands_pending = 1;
+ gig_dbg(DEBUG_CMD, "Scheduling PC_HUP");
+ break;
+
+ /* hotplug events */
+ case ACT_STOP:
+ do_stop(cs);
+ break;
+ case ACT_START:
+ do_start(cs);
+ break;
+
+ /* events from the interface */ // FIXME without ACT_xxxx?
+ case ACT_IF_LOCK:
+ cs->cmd_result = ev->parameter ? do_lock(cs) : do_unlock(cs);
+ cs->waiting = 0;
+ wake_up(&cs->waitqueue);
+ break;
+ case ACT_IF_VER:
+ if (ev->parameter != 0)
+ cs->cmd_result = -EINVAL;
+ else if (cs->gotfwver != 1) {
+ cs->cmd_result = -ENOENT;
+ } else {
+ memcpy(ev->arg, cs->fwver, sizeof cs->fwver);
+ cs->cmd_result = 0;
+ }
+ cs->waiting = 0;
+ wake_up(&cs->waitqueue);
+ break;
+
+ /* events from the proc file system */ // FIXME without ACT_xxxx?
+ case ACT_PROC_CIDMODE:
+ spin_lock_irqsave(&cs->lock, flags);
+ if (ev->parameter != cs->cidmode) {
+ cs->cidmode = ev->parameter;
+ if (ev->parameter) {
+ cs->at_state.pending_commands |= PC_CIDMODE;
+ gig_dbg(DEBUG_CMD, "Scheduling PC_CIDMODE");
+ } else {
+ cs->at_state.pending_commands |= PC_UMMODE;
+ gig_dbg(DEBUG_CMD, "Scheduling PC_UMMODE");
+ }
+ cs->commands_pending = 1;
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+ cs->waiting = 0;
+ wake_up(&cs->waitqueue);
+ break;
+
+ /* events from the hardware drivers */
+ case ACT_NOTIFY_BC_DOWN:
+ bchannel_down(bcs);
+ break;
+ case ACT_NOTIFY_BC_UP:
+ bchannel_up(bcs);
+ break;
+ case ACT_SHUTDOWN:
+ do_shutdown(cs);
+ break;
+
+
+ default:
+ if (action >= ACT_CMD && action < ACT_CMD + AT_NUM) {
+ *pp_command = at_state->bcs->commands[action - ACT_CMD];
+ if (!*pp_command) {
+ *p_genresp = 1;
+ *p_resp_code = RSP_NULL;
+ }
+ } else
+ dev_err(cs->dev, "%s: action==%d!\n", __func__, action);
+ }
+}
+
+/* State machine to do the calling and hangup procedure */
+static void process_event(struct cardstate *cs, struct event_t *ev)
+{
+ struct bc_state *bcs;
+ char *p_command = NULL;
+ struct reply_t *rep;
+ int rcode;
+ int genresp = 0;
+ int resp_code = RSP_ERROR;
+ int sendcid;
+ struct at_state_t *at_state;
+ int index;
+ int curact;
+ unsigned long flags;
+
+ if (ev->cid >= 0) {
+ at_state = at_state_from_cid(cs, ev->cid);
+ if (!at_state) {
+ gigaset_add_event(cs, &cs->at_state, RSP_WRONG_CID,
+ NULL, 0, NULL);
+ return;
+ }
+ } else {
+ at_state = ev->at_state;
+ if (at_state_invalid(cs, at_state)) {
+ gig_dbg(DEBUG_ANY, "event for invalid at_state %p",
+ at_state);
+ return;
+ }
+ }
+
+ gig_dbg(DEBUG_CMD, "connection state %d, event %d",
+ at_state->ConState, ev->type);
+
+ bcs = at_state->bcs;
+ sendcid = at_state->cid;
+
+ /* Setting the pointer to the dial array */
+ rep = at_state->replystruct;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ if (ev->type == EV_TIMEOUT) {
+ if (ev->parameter != at_state->timer_index
+ || !at_state->timer_active) {
+ ev->type = RSP_NONE; /* old timeout */
+ gig_dbg(DEBUG_ANY, "old timeout");
+ } else if (!at_state->waiting)
+ gig_dbg(DEBUG_ANY, "timeout occurred");
+ else
+ gig_dbg(DEBUG_ANY, "stopped waiting");
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+
+ /* if the response belongs to a variable in at_state->int_var[VAR_XXXX]
+ or at_state->str_var[STR_XXXX], set it */
+ if (ev->type >= RSP_VAR && ev->type < RSP_VAR + VAR_NUM) {
+ index = ev->type - RSP_VAR;
+ at_state->int_var[index] = ev->parameter;
+ } else if (ev->type >= RSP_STR && ev->type < RSP_STR + STR_NUM) {
+ index = ev->type - RSP_STR;
+ kfree(at_state->str_var[index]);
+ at_state->str_var[index] = ev->ptr;
+ ev->ptr = NULL; /* prevent process_events() from
+ deallocating ptr */
+ }
+
+ if (ev->type == EV_TIMEOUT || ev->type == RSP_STRING)
+ at_state->getstring = 0;
+
+ /* Search row in dial array which matches modem response and current
+ constate */
+ for (;; rep++) {
+ rcode = rep->resp_code;
+ if (rcode == RSP_LAST) {
+ /* found nothing...*/
+ dev_warn(cs->dev, "%s: rcode=RSP_LAST: "
+ "resp_code %d in ConState %d!\n",
+ __func__, ev->type, at_state->ConState);
+ return;
+ }
+ if ((rcode == RSP_ANY || rcode == ev->type)
+ && ((int) at_state->ConState >= rep->min_ConState)
+ && (rep->max_ConState < 0
+ || (int) at_state->ConState <= rep->max_ConState)
+ && (rep->parameter < 0 || rep->parameter == ev->parameter))
+ break;
+ }
+
+ p_command = rep->command;
+
+ at_state->waiting = 0;
+ for (curact = 0; curact < MAXACT; ++curact) {
+ /* The row tells us what we should do ..
+ */
+ do_action(rep->action[curact], cs, bcs, &at_state, &p_command, &genresp, &resp_code, ev);
+ if (!at_state)
+ break; /* may be freed after disconnect */
+ }
+
+ if (at_state) {
+ /* Jump to the next con-state regarding the array */
+ if (rep->new_ConState >= 0)
+ at_state->ConState = rep->new_ConState;
+
+ if (genresp) {
+ spin_lock_irqsave(&cs->lock, flags);
+ at_state->timer_expires = 0; //FIXME
+ at_state->timer_active = 0; //FIXME
+ spin_unlock_irqrestore(&cs->lock, flags);
+ gigaset_add_event(cs, at_state, resp_code, NULL, 0, NULL);
+ } else {
+ /* Send command to modem if not NULL... */
+ if (p_command/*rep->command*/) {
+ if (cs->connected)
+ send_command(cs, p_command,
+ sendcid, cs->dle,
+ GFP_ATOMIC);
+ else
+ gigaset_add_event(cs, at_state,
+ RSP_NODEV,
+ NULL, 0, NULL);
+ }
+
+ spin_lock_irqsave(&cs->lock, flags);
+ if (!rep->timeout) {
+ at_state->timer_expires = 0;
+ at_state->timer_active = 0;
+ } else if (rep->timeout > 0) { /* new timeout */
+ at_state->timer_expires = rep->timeout * 10;
+ at_state->timer_active = 1;
+ ++at_state->timer_index;
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+ }
+ }
+}
+
+static void schedule_sequence(struct cardstate *cs,
+ struct at_state_t *at_state, int sequence)
+{
+ cs->cur_at_seq = sequence;
+ gigaset_add_event(cs, at_state, RSP_INIT, NULL, sequence, NULL);
+}
+
+static void process_command_flags(struct cardstate *cs)
+{
+ struct at_state_t *at_state = NULL;
+ struct bc_state *bcs;
+ int i;
+ int sequence;
+ unsigned long flags;
+
+ cs->commands_pending = 0;
+
+ if (cs->cur_at_seq) {
+ gig_dbg(DEBUG_CMD, "not searching scheduled commands: busy");
+ return;
+ }
+
+ gig_dbg(DEBUG_CMD, "searching scheduled commands");
+
+ sequence = SEQ_NONE;
+
+ /* clear pending_commands and hangup channels on shutdown */
+ if (cs->at_state.pending_commands & PC_SHUTDOWN) {
+ cs->at_state.pending_commands &= ~PC_CIDMODE;
+ for (i = 0; i < cs->channels; ++i) {
+ bcs = cs->bcs + i;
+ at_state = &bcs->at_state;
+ at_state->pending_commands &=
+ ~(PC_DLE1 | PC_ACCEPT | PC_DIAL);
+ if (at_state->cid > 0)
+ at_state->pending_commands |= PC_HUP;
+ if (at_state->pending_commands & PC_CID) {
+ at_state->pending_commands |= PC_NOCID;
+ at_state->pending_commands &= ~PC_CID;
+ }
+ }
+ }
+
+ /* clear pending_commands and hangup channels on reset */
+ if (cs->at_state.pending_commands & PC_INIT) {
+ cs->at_state.pending_commands &= ~PC_CIDMODE;
+ for (i = 0; i < cs->channels; ++i) {
+ bcs = cs->bcs + i;
+ at_state = &bcs->at_state;
+ at_state->pending_commands &=
+ ~(PC_DLE1 | PC_ACCEPT | PC_DIAL);
+ if (at_state->cid > 0)
+ at_state->pending_commands |= PC_HUP;
+ if (cs->mstate == MS_RECOVER) {
+ if (at_state->pending_commands & PC_CID) {
+ at_state->pending_commands |= PC_NOCID;
+ at_state->pending_commands &= ~PC_CID;
+ }
+ }
+ }
+ }
+
+ /* only switch back to unimodem mode, if no commands are pending and no channels are up */
+ spin_lock_irqsave(&cs->lock, flags);
+ if (cs->at_state.pending_commands == PC_UMMODE
+ && !cs->cidmode
+ && list_empty(&cs->temp_at_states)
+ && cs->mode == M_CID) {
+ sequence = SEQ_UMMODE;
+ at_state = &cs->at_state;
+ for (i = 0; i < cs->channels; ++i) {
+ bcs = cs->bcs + i;
+ if (bcs->at_state.pending_commands ||
+ bcs->at_state.cid > 0) {
+ sequence = SEQ_NONE;
+ break;
+ }
+ }
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+ cs->at_state.pending_commands &= ~PC_UMMODE;
+ if (sequence != SEQ_NONE) {
+ schedule_sequence(cs, at_state, sequence);
+ return;
+ }
+
+ for (i = 0; i < cs->channels; ++i) {
+ bcs = cs->bcs + i;
+ if (bcs->at_state.pending_commands & PC_HUP) {
+ bcs->at_state.pending_commands &= ~PC_HUP;
+ if (bcs->at_state.pending_commands & PC_CID) {
+ /* not yet dialing: PC_NOCID is sufficient */
+ bcs->at_state.pending_commands |= PC_NOCID;
+ bcs->at_state.pending_commands &= ~PC_CID;
+ } else {
+ schedule_sequence(cs, &bcs->at_state, SEQ_HUP);
+ return;
+ }
+ }
+ if (bcs->at_state.pending_commands & PC_NOCID) {
+ bcs->at_state.pending_commands &= ~PC_NOCID;
+ cs->curchannel = bcs->channel;
+ schedule_sequence(cs, &cs->at_state, SEQ_NOCID);
+ return;
+ } else if (bcs->at_state.pending_commands & PC_DLE0) {
+ bcs->at_state.pending_commands &= ~PC_DLE0;
+ cs->curchannel = bcs->channel;
+ schedule_sequence(cs, &cs->at_state, SEQ_DLE0);
+ return;
+ }
+ }
+
+ list_for_each_entry(at_state, &cs->temp_at_states, list)
+ if (at_state->pending_commands & PC_HUP) {
+ at_state->pending_commands &= ~PC_HUP;
+ schedule_sequence(cs, at_state, SEQ_HUP);
+ return;
+ }
+
+ if (cs->at_state.pending_commands & PC_INIT) {
+ cs->at_state.pending_commands &= ~PC_INIT;
+ cs->dle = 0; //FIXME
+ cs->inbuf->inputstate = INS_command;
+ //FIXME reset card state (or -> LOCK0)?
+ schedule_sequence(cs, &cs->at_state, SEQ_INIT);
+ return;
+ }
+ if (cs->at_state.pending_commands & PC_SHUTDOWN) {
+ cs->at_state.pending_commands &= ~PC_SHUTDOWN;
+ schedule_sequence(cs, &cs->at_state, SEQ_SHUTDOWN);
+ return;
+ }
+ if (cs->at_state.pending_commands & PC_CIDMODE) {
+ cs->at_state.pending_commands &= ~PC_CIDMODE;
+ if (cs->mode == M_UNIMODEM) {
+ cs->retry_count = 1;
+ schedule_sequence(cs, &cs->at_state, SEQ_CIDMODE);
+ return;
+ }
+ }
+
+ for (i = 0; i < cs->channels; ++i) {
+ bcs = cs->bcs + i;
+ if (bcs->at_state.pending_commands & PC_DLE1) {
+ bcs->at_state.pending_commands &= ~PC_DLE1;
+ cs->curchannel = bcs->channel;
+ schedule_sequence(cs, &cs->at_state, SEQ_DLE1);
+ return;
+ }
+ if (bcs->at_state.pending_commands & PC_ACCEPT) {
+ bcs->at_state.pending_commands &= ~PC_ACCEPT;
+ schedule_sequence(cs, &bcs->at_state, SEQ_ACCEPT);
+ return;
+ }
+ if (bcs->at_state.pending_commands & PC_DIAL) {
+ bcs->at_state.pending_commands &= ~PC_DIAL;
+ schedule_sequence(cs, &bcs->at_state, SEQ_DIAL);
+ return;
+ }
+ if (bcs->at_state.pending_commands & PC_CID) {
+ switch (cs->mode) {
+ case M_UNIMODEM:
+ cs->at_state.pending_commands |= PC_CIDMODE;
+ gig_dbg(DEBUG_CMD, "Scheduling PC_CIDMODE");
+ cs->commands_pending = 1;
+ return;
+#ifdef GIG_MAYINITONDIAL
+ case M_UNKNOWN:
+ schedule_init(cs, MS_INIT);
+ return;
+#endif
+ }
+ bcs->at_state.pending_commands &= ~PC_CID;
+ cs->curchannel = bcs->channel;
+#ifdef GIG_RETRYCID
+ cs->retry_count = 2;
+#else
+ cs->retry_count = 1;
+#endif
+ schedule_sequence(cs, &cs->at_state, SEQ_CID);
+ return;
+ }
+ }
+}
+
+static void process_events(struct cardstate *cs)
+{
+ struct event_t *ev;
+ unsigned head, tail;
+ int i;
+ int check_flags = 0;
+ int was_busy;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cs->ev_lock, flags);
+ head = cs->ev_head;
+
+ for (i = 0; i < 2 * MAX_EVENTS; ++i) {
+ tail = cs->ev_tail;
+ if (tail == head) {
+ if (!check_flags && !cs->commands_pending)
+ break;
+ check_flags = 0;
+ spin_unlock_irqrestore(&cs->ev_lock, flags);
+ process_command_flags(cs);
+ spin_lock_irqsave(&cs->ev_lock, flags);
+ tail = cs->ev_tail;
+ if (tail == head) {
+ if (!cs->commands_pending)
+ break;
+ continue;
+ }
+ }
+
+ ev = cs->events + head;
+ was_busy = cs->cur_at_seq != SEQ_NONE;
+ spin_unlock_irqrestore(&cs->ev_lock, flags);
+ process_event(cs, ev);
+ spin_lock_irqsave(&cs->ev_lock, flags);
+ kfree(ev->ptr);
+ ev->ptr = NULL;
+ if (was_busy && cs->cur_at_seq == SEQ_NONE)
+ check_flags = 1;
+
+ head = (head + 1) % MAX_EVENTS;
+ cs->ev_head = head;
+ }
+
+ spin_unlock_irqrestore(&cs->ev_lock, flags);
+
+ if (i == 2 * MAX_EVENTS) {
+ dev_err(cs->dev,
+ "infinite loop in process_events; aborting.\n");
+ }
+}
+
+/* tasklet scheduled on any event received from the Gigaset device
+ * parameter:
+ * data ISDN controller state structure
+ */
+void gigaset_handle_event(unsigned long data)
+{
+ struct cardstate *cs = (struct cardstate *) data;
+
+ /* handle incoming data on control/common channel */
+ if (cs->inbuf->head != cs->inbuf->tail) {
+ gig_dbg(DEBUG_INTR, "processing new data");
+ cs->ops->handle_input(cs->inbuf);
+ }
+
+ process_events(cs);
+}
diff --git a/drivers/isdn/gigaset/gigaset.h b/drivers/isdn/gigaset/gigaset.h
new file mode 100644
index 0000000..0037529
--- /dev/null
+++ b/drivers/isdn/gigaset/gigaset.h
@@ -0,0 +1,884 @@
+/*
+ * Siemens Gigaset 307x driver
+ * Common header file for all connection variants
+ *
+ * Written by Stefan Eilers
+ * and Hansjoerg Lipp <hjlipp@web.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 of
+ * the License, or (at your option) any later version.
+ * =====================================================================
+ */
+
+#ifndef GIGASET_H
+#define GIGASET_H
+
+#include <linux/kernel.h>
+#include <linux/compiler.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/isdnif.h>
+#include <linux/usb.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/ppp_defs.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/list.h>
+#include <asm/atomic.h>
+
+#define GIG_VERSION {0,5,0,0}
+#define GIG_COMPAT {0,4,0,0}
+
+#define MAX_REC_PARAMS 10 /* Max. number of params in response string */
+#define MAX_RESP_SIZE 512 /* Max. size of a response string */
+#define HW_HDR_LEN 2 /* Header size used to store ack info */
+
+#define MAX_EVENTS 64 /* size of event queue */
+
+#define RBUFSIZE 8192
+#define SBUFSIZE 4096 /* sk_buff payload size */
+
+#define TRANSBUFSIZE 768 /* bytes per skb for transparent receive */
+#define MAX_BUF_SIZE (SBUFSIZE - 2) /* Max. size of a data packet from LL */
+
+/* compile time options */
+#define GIG_MAJOR 0
+
+#define GIG_MAYINITONDIAL
+#define GIG_RETRYCID
+#define GIG_X75
+
+#define GIG_TICK 100 /* in milliseconds */
+
+/* timeout values (unit: 1 sec) */
+#define INIT_TIMEOUT 1
+
+/* timeout values (unit: 0.1 sec) */
+#define RING_TIMEOUT 3 /* for additional parameters to RING */
+#define BAS_TIMEOUT 20 /* for response to Base USB ops */
+#define ATRDY_TIMEOUT 3 /* for HD_READY_SEND_ATDATA */
+
+#define BAS_RETRY 3 /* max. retries for base USB ops */
+
+#define MAXACT 3
+
+extern int gigaset_debuglevel; /* "needs" cast to (enum debuglevel) */
+
+/* debug flags, combine by adding/bitwise OR */
+enum debuglevel {
+ DEBUG_INTR = 0x00008, /* interrupt processing */
+ DEBUG_CMD = 0x00020, /* sent/received LL commands */
+ DEBUG_STREAM = 0x00040, /* application data stream I/O events */
+ DEBUG_STREAM_DUMP = 0x00080, /* application data stream content */
+ DEBUG_LLDATA = 0x00100, /* sent/received LL data */
+ DEBUG_DRIVER = 0x00400, /* driver structure */
+ DEBUG_HDLC = 0x00800, /* M10x HDLC processing */
+ DEBUG_WRITE = 0x01000, /* M105 data write */
+ DEBUG_TRANSCMD = 0x02000, /* AT-COMMANDS+RESPONSES */
+ DEBUG_MCMD = 0x04000, /* COMMANDS THAT ARE SENT VERY OFTEN */
+ DEBUG_INIT = 0x08000, /* (de)allocation+initialization of data
+ structures */
+ DEBUG_SUSPEND = 0x10000, /* suspend/resume processing */
+ DEBUG_OUTPUT = 0x20000, /* output to device */
+ DEBUG_ISO = 0x40000, /* isochronous transfers */
+ DEBUG_IF = 0x80000, /* character device operations */
+ DEBUG_USBREQ = 0x100000, /* USB communication (except payload
+ data) */
+ DEBUG_LOCKCMD = 0x200000, /* AT commands and responses when
+ MS_LOCKED */
+
+ DEBUG_ANY = 0x3fffff, /* print message if any of the others is
+ activated */
+};
+
+/* Kernel message macros for situations where dev_printk and friends cannot be
+ * used for lack of reliable access to a device structure.
+ * linux/usb.h already contains these but in an obsolete form which clutters
+ * the log needlessly, and according to the USB maintainer those should be
+ * removed rather than fixed anyway.
+ */
+#undef err
+#undef info
+#undef warn
+
+#define err(format, arg...) printk(KERN_ERR KBUILD_MODNAME ": " \
+ format "\n" , ## arg)
+#define info(format, arg...) printk(KERN_INFO KBUILD_MODNAME ": " \
+ format "\n" , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING KBUILD_MODNAME ": " \
+ format "\n" , ## arg)
+
+#ifdef CONFIG_GIGASET_DEBUG
+
+#define gig_dbg(level, format, arg...) \
+ do { \
+ if (unlikely(((enum debuglevel)gigaset_debuglevel) & (level))) \
+ printk(KERN_DEBUG KBUILD_MODNAME ": " format "\n", \
+ ## arg); \
+ } while (0)
+#define DEBUG_DEFAULT (DEBUG_TRANSCMD | DEBUG_CMD | DEBUG_USBREQ)
+
+#else
+
+#define gig_dbg(level, format, arg...) do {} while (0)
+#define DEBUG_DEFAULT 0
+
+#endif
+
+void gigaset_dbg_buffer(enum debuglevel level, const unsigned char *msg,
+ size_t len, const unsigned char *buf);
+
+/* connection state */
+#define ZSAU_NONE 0
+#define ZSAU_DISCONNECT_IND 4
+#define ZSAU_OUTGOING_CALL_PROCEEDING 1
+#define ZSAU_PROCEEDING 1
+#define ZSAU_CALL_DELIVERED 2
+#define ZSAU_ACTIVE 3
+#define ZSAU_NULL 5
+#define ZSAU_DISCONNECT_REQ 6
+#define ZSAU_UNKNOWN -1
+
+/* USB control transfer requests */
+#define OUT_VENDOR_REQ (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT)
+#define IN_VENDOR_REQ (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT)
+
+/* int-in-events 3070 */
+#define HD_B1_FLOW_CONTROL 0x80
+#define HD_B2_FLOW_CONTROL 0x81
+#define HD_RECEIVEATDATA_ACK (0x35) // 3070
+ // att: HD_RECEIVE>>AT<<DATA_ACK
+#define HD_READY_SEND_ATDATA (0x36) // 3070
+#define HD_OPEN_ATCHANNEL_ACK (0x37) // 3070
+#define HD_CLOSE_ATCHANNEL_ACK (0x38) // 3070
+#define HD_DEVICE_INIT_OK (0x11) // ISurf USB + 3070
+#define HD_OPEN_B1CHANNEL_ACK (0x51) // ISurf USB + 3070
+#define HD_OPEN_B2CHANNEL_ACK (0x52) // ISurf USB + 3070
+#define HD_CLOSE_B1CHANNEL_ACK (0x53) // ISurf USB + 3070
+#define HD_CLOSE_B2CHANNEL_ACK (0x54) // ISurf USB + 3070
+// Powermangment
+#define HD_SUSPEND_END (0x61) // ISurf USB
+// Configuration
+#define HD_RESET_INTERRUPT_PIPE_ACK (0xFF) // ISurf USB + 3070
+
+/* control requests 3070 */
+#define HD_OPEN_B1CHANNEL (0x23) // ISurf USB + 3070
+#define HD_CLOSE_B1CHANNEL (0x24) // ISurf USB + 3070
+#define HD_OPEN_B2CHANNEL (0x25) // ISurf USB + 3070
+#define HD_CLOSE_B2CHANNEL (0x26) // ISurf USB + 3070
+#define HD_RESET_INTERRUPT_PIPE (0x27) // ISurf USB + 3070
+#define HD_DEVICE_INIT_ACK (0x34) // ISurf USB + 3070
+#define HD_WRITE_ATMESSAGE (0x12) // 3070
+#define HD_READ_ATMESSAGE (0x13) // 3070
+#define HD_OPEN_ATCHANNEL (0x28) // 3070
+#define HD_CLOSE_ATCHANNEL (0x29) // 3070
+
+/* number of B channels supported by base driver */
+#define BAS_CHANNELS 2
+
+/* USB frames for isochronous transfer */
+#define BAS_FRAMETIME 1 /* number of milliseconds between frames */
+#define BAS_NUMFRAMES 8 /* number of frames per URB */
+#define BAS_MAXFRAME 16 /* allocated bytes per frame */
+#define BAS_NORMFRAME 8 /* send size without flow control */
+#define BAS_HIGHFRAME 10 /* " " with positive flow control */
+#define BAS_LOWFRAME 5 /* " " with negative flow control */
+#define BAS_CORRFRAMES 4 /* flow control multiplicator */
+
+#define BAS_INBUFSIZE (BAS_MAXFRAME * BAS_NUMFRAMES)
+ /* size of isoc in buf per URB */
+#define BAS_OUTBUFSIZE 4096 /* size of common isoc out buffer */
+#define BAS_OUTBUFPAD BAS_MAXFRAME /* size of pad area for isoc out buf */
+
+#define BAS_INURBS 3
+#define BAS_OUTURBS 3
+
+/* variable commands in struct bc_state */
+#define AT_ISO 0
+#define AT_DIAL 1
+#define AT_MSN 2
+#define AT_BC 3
+#define AT_PROTO 4
+#define AT_TYPE 5
+#define AT_HLC 6
+#define AT_NUM 7
+
+/* variables in struct at_state_t */
+#define VAR_ZSAU 0
+#define VAR_ZDLE 1
+#define VAR_ZVLS 2
+#define VAR_ZCTP 3
+#define VAR_NUM 4
+
+#define STR_NMBR 0
+#define STR_ZCPN 1
+#define STR_ZCON 2
+#define STR_ZBC 3
+#define STR_ZHLC 4
+#define STR_NUM 5
+
+#define EV_TIMEOUT -105
+#define EV_IF_VER -106
+#define EV_PROC_CIDMODE -107
+#define EV_SHUTDOWN -108
+#define EV_START -110
+#define EV_STOP -111
+#define EV_IF_LOCK -112
+#define EV_PROTO_L2 -113
+#define EV_ACCEPT -114
+#define EV_DIAL -115
+#define EV_HUP -116
+#define EV_BC_OPEN -117
+#define EV_BC_CLOSED -118
+
+/* input state */
+#define INS_command 0x0001
+#define INS_DLE_char 0x0002
+#define INS_byte_stuff 0x0004
+#define INS_have_data 0x0008
+#define INS_skip_frame 0x0010
+#define INS_DLE_command 0x0020
+#define INS_flag_hunt 0x0040
+
+/* channel state */
+#define CHS_D_UP 0x01
+#define CHS_B_UP 0x02
+#define CHS_NOTIFY_LL 0x04
+
+#define ICALL_REJECT 0
+#define ICALL_ACCEPT 1
+#define ICALL_IGNORE 2
+
+/* device state */
+#define MS_UNINITIALIZED 0
+#define MS_INIT 1
+#define MS_LOCKED 2
+#define MS_SHUTDOWN 3
+#define MS_RECOVER 4
+#define MS_READY 5
+
+/* mode */
+#define M_UNKNOWN 0
+#define M_CONFIG 1
+#define M_UNIMODEM 2
+#define M_CID 3
+
+/* start mode */
+#define SM_LOCKED 0
+#define SM_ISDN 1 /* default */
+
+struct gigaset_ops;
+struct gigaset_driver;
+
+struct usb_cardstate;
+struct ser_cardstate;
+struct bas_cardstate;
+
+struct bc_state;
+struct usb_bc_state;
+struct ser_bc_state;
+struct bas_bc_state;
+
+struct reply_t {
+ int resp_code; /* RSP_XXXX */
+ int min_ConState; /* <0 => ignore */
+ int max_ConState; /* <0 => ignore */
+ int parameter; /* e.g. ZSAU_XXXX <0: ignore*/
+ int new_ConState; /* <0 => ignore */
+ int timeout; /* >0 => *HZ; <=0 => TOUT_XXXX*/
+ int action[MAXACT]; /* ACT_XXXX */
+ char *command; /* NULL==none */
+};
+
+extern struct reply_t gigaset_tab_cid_m10x[];
+extern struct reply_t gigaset_tab_nocid_m10x[];
+
+struct inbuf_t {
+ unsigned char *rcvbuf; /* usb-gigaset receive buffer */
+ struct bc_state *bcs;
+ struct cardstate *cs;
+ int inputstate;
+ int head, tail;
+ unsigned char data[RBUFSIZE];
+};
+
+/* isochronous write buffer structure
+ * circular buffer with pad area for extraction of complete USB frames
+ * - data[read..nextread-1] is valid data already submitted to the USB subsystem
+ * - data[nextread..write-1] is valid data yet to be sent
+ * - data[write] is the next byte to write to
+ * - in byte-oriented L2 procotols, it is completely free
+ * - in bit-oriented L2 procotols, it may contain a partial byte of valid data
+ * - data[write+1..read-1] is free
+ * - wbits is the number of valid data bits in data[write], starting at the LSB
+ * - writesem is the semaphore for writing to the buffer:
+ * if writesem <= 0, data[write..read-1] is currently being written to
+ * - idle contains the byte value to repeat when the end of valid data is
+ * reached; if nextread==write (buffer contains no data to send), either the
+ * BAS_OUTBUFPAD bytes immediately before data[write] (if
+ * write>=BAS_OUTBUFPAD) or those of the pad area (if write<BAS_OUTBUFPAD)
+ * are also filled with that value
+ */
+struct isowbuf_t {
+ int read;
+ int nextread;
+ int write;
+ atomic_t writesem;
+ int wbits;
+ unsigned char data[BAS_OUTBUFSIZE + BAS_OUTBUFPAD];
+ unsigned char idle;
+};
+
+/* isochronous write URB context structure
+ * data to be stored along with the URB and retrieved when it is returned
+ * as completed by the USB subsystem
+ * - urb: pointer to the URB itself
+ * - bcs: pointer to the B Channel control structure
+ * - limit: end of write buffer area covered by this URB
+ * - status: URB completion status
+ */
+struct isow_urbctx_t {
+ struct urb *urb;
+ struct bc_state *bcs;
+ int limit;
+ int status;
+};
+
+/* AT state structure
+ * data associated with the state of an ISDN connection, whether or not
+ * it is currently assigned a B channel
+ */
+struct at_state_t {
+ struct list_head list;
+ int waiting;
+ int getstring;
+ unsigned timer_index;
+ unsigned long timer_expires;
+ int timer_active;
+ unsigned int ConState; /* State of connection */
+ struct reply_t *replystruct;
+ int cid;
+ int int_var[VAR_NUM]; /* see VAR_XXXX */
+ char *str_var[STR_NUM]; /* see STR_XXXX */
+ unsigned pending_commands; /* see PC_XXXX */
+ unsigned seq_index;
+
+ struct cardstate *cs;
+ struct bc_state *bcs;
+};
+
+struct resp_type_t {
+ unsigned char *response;
+ int resp_code; /* RSP_XXXX */
+ int type; /* RT_XXXX */
+};
+
+struct event_t {
+ int type;
+ void *ptr, *arg;
+ int parameter;
+ int cid;
+ struct at_state_t *at_state;
+};
+
+/* This buffer holds all information about the used B-Channel */
+struct bc_state {
+ struct sk_buff *tx_skb; /* Current transfer buffer to modem */
+ struct sk_buff_head squeue; /* B-Channel send Queue */
+
+ /* Variables for debugging .. */
+ int corrupted; /* Counter for corrupted packages */
+ int trans_down; /* Counter of packages (downstream) */
+ int trans_up; /* Counter of packages (upstream) */
+
+ struct at_state_t at_state;
+ unsigned long rcvbytes;
+
+ __u16 fcs;
+ struct sk_buff *skb;
+ int inputstate; /* see INS_XXXX */
+
+ int channel;
+
+ struct cardstate *cs;
+
+ unsigned chstate; /* bitmap (CHS_*) */
+ int ignore;
+ unsigned proto2; /* Layer 2 protocol (ISDN_PROTO_L2_*) */
+ char *commands[AT_NUM]; /* see AT_XXXX */
+
+#ifdef CONFIG_GIGASET_DEBUG
+ int emptycount;
+#endif
+ int busy;
+ int use_count;
+
+ /* private data of hardware drivers */
+ union {
+ struct ser_bc_state *ser; /* serial hardware driver */
+ struct usb_bc_state *usb; /* usb hardware driver (m105) */
+ struct bas_bc_state *bas; /* usb hardware driver (base) */
+ } hw;
+};
+
+struct cardstate {
+ struct gigaset_driver *driver;
+ unsigned minor_index;
+ struct device *dev;
+ struct device *tty_dev;
+ unsigned flags;
+
+ const struct gigaset_ops *ops;
+
+ /* Stuff to handle communication */
+ wait_queue_head_t waitqueue;
+ int waiting;
+ int mode; /* see M_XXXX */
+ int mstate; /* Modem state: see MS_XXXX */
+ /* only changed by the event layer */
+ int cmd_result;
+
+ int channels;
+ struct bc_state *bcs; /* Array of struct bc_state */
+
+ int onechannel; /* data and commands transmitted in one
+ stream (M10x) */
+
+ spinlock_t lock;
+ struct at_state_t at_state; /* at_state_t for cid == 0 */
+ struct list_head temp_at_states;/* list of temporary "struct
+ at_state_t"s without B channel */
+
+ struct inbuf_t *inbuf;
+
+ struct cmdbuf_t *cmdbuf, *lastcmdbuf;
+ spinlock_t cmdlock;
+ unsigned curlen, cmdbytes;
+
+ unsigned open_count;
+ struct tty_struct *tty;
+ struct tasklet_struct if_wake_tasklet;
+ unsigned control_state;
+
+ unsigned fwver[4];
+ int gotfwver;
+
+ unsigned running; /* !=0 if events are handled */
+ unsigned connected; /* !=0 if hardware is connected */
+ unsigned isdn_up; /* !=0 after ISDN_STAT_RUN */
+
+ unsigned cidmode;
+
+ int myid; /* id for communication with LL */
+ isdn_if iif;
+
+ struct reply_t *tabnocid;
+ struct reply_t *tabcid;
+ int cs_init;
+ int ignoreframes; /* frames to ignore after setting up the
+ B channel */
+ struct mutex mutex; /* locks this structure:
+ * connected is not changed,
+ * hardware_up is not changed,
+ * MState is not changed to or from
+ * MS_LOCKED */
+
+ struct timer_list timer;
+ int retry_count;
+ int dle; /* !=0 if modem commands/responses are
+ dle encoded */
+ int cur_at_seq; /* sequence of AT commands being
+ processed */
+ int curchannel; /* channel those commands are meant
+ for */
+ int commands_pending; /* flag(s) in xxx.commands_pending have
+ been set */
+ struct tasklet_struct event_tasklet;
+ /* tasklet for serializing AT commands.
+ * Scheduled
+ * -> for modem reponses (and
+ * incoming data for M10x)
+ * -> on timeout
+ * -> after setting bits in
+ * xxx.at_state.pending_command
+ * (e.g. command from LL) */
+ struct tasklet_struct write_tasklet;
+ /* tasklet for serial output
+ * (not used in base driver) */
+
+ /* event queue */
+ struct event_t events[MAX_EVENTS];
+ unsigned ev_tail, ev_head;
+ spinlock_t ev_lock;
+
+ /* current modem response */
+ unsigned char respdata[MAX_RESP_SIZE];
+ unsigned cbytes;
+
+ /* private data of hardware drivers */
+ union {
+ struct usb_cardstate *usb; /* USB hardware driver (m105) */
+ struct ser_cardstate *ser; /* serial hardware driver */
+ struct bas_cardstate *bas; /* USB hardware driver (base) */
+ } hw;
+};
+
+struct gigaset_driver {
+ struct list_head list;
+ spinlock_t lock; /* locks minor tables and blocked */
+ struct tty_driver *tty;
+ unsigned have_tty;
+ unsigned minor;
+ unsigned minors;
+ struct cardstate *cs;
+ int blocked;
+
+ const struct gigaset_ops *ops;
+ struct module *owner;
+};
+
+struct cmdbuf_t {
+ struct cmdbuf_t *next, *prev;
+ int len, offset;
+ struct tasklet_struct *wake_tasklet;
+ unsigned char buf[0];
+};
+
+struct bas_bc_state {
+ /* isochronous output state */
+ int running;
+ atomic_t corrbytes;
+ spinlock_t isooutlock;
+ struct isow_urbctx_t isoouturbs[BAS_OUTURBS];
+ struct isow_urbctx_t *isooutdone, *isooutfree, *isooutovfl;
+ struct isowbuf_t *isooutbuf;
+ unsigned numsub; /* submitted URB counter
+ (for diagnostic messages only) */
+ struct tasklet_struct sent_tasklet;
+
+ /* isochronous input state */
+ spinlock_t isoinlock;
+ struct urb *isoinurbs[BAS_INURBS];
+ unsigned char isoinbuf[BAS_INBUFSIZE * BAS_INURBS];
+ struct urb *isoindone; /* completed isoc read URB */
+ int isoinstatus; /* status of completed URB */
+ int loststatus; /* status of dropped URB */
+ unsigned isoinlost; /* number of bytes lost */
+ /* state of bit unstuffing algorithm
+ (in addition to BC_state.inputstate) */
+ unsigned seqlen; /* number of '1' bits not yet
+ unstuffed */
+ unsigned inbyte, inbits; /* collected bits for next byte */
+ /* statistics */
+ unsigned goodbytes; /* bytes correctly received */
+ unsigned alignerrs; /* frames with incomplete byte at end */
+ unsigned fcserrs; /* FCS errors */
+ unsigned frameerrs; /* framing errors */
+ unsigned giants; /* long frames */
+ unsigned runts; /* short frames */
+ unsigned aborts; /* HDLC aborts */
+ unsigned shared0s; /* '0' bits shared between flags */
+ unsigned stolen0s; /* '0' stuff bits also serving as
+ leading flag bits */
+ struct tasklet_struct rcvd_tasklet;
+};
+
+struct gigaset_ops {
+ /* Called from ev-layer.c/interface.c for sending AT commands to the
+ device */
+ int (*write_cmd)(struct cardstate *cs,
+ const unsigned char *buf, int len,
+ struct tasklet_struct *wake_tasklet);
+
+ /* Called from interface.c for additional device control */
+ int (*write_room)(struct cardstate *cs);
+ int (*chars_in_buffer)(struct cardstate *cs);
+ int (*brkchars)(struct cardstate *cs, const unsigned char buf[6]);
+
+ /* Called from ev-layer.c after setting up connection
+ * Should call gigaset_bchannel_up(), when finished. */
+ int (*init_bchannel)(struct bc_state *bcs);
+
+ /* Called from ev-layer.c after hanging up
+ * Should call gigaset_bchannel_down(), when finished. */
+ int (*close_bchannel)(struct bc_state *bcs);
+
+ /* Called by gigaset_initcs() for setting up bcs->hw.xxx */
+ int (*initbcshw)(struct bc_state *bcs);
+
+ /* Called by gigaset_freecs() for freeing bcs->hw.xxx */
+ int (*freebcshw)(struct bc_state *bcs);
+
+ /* Called by gigaset_bchannel_down() for resetting bcs->hw.xxx */
+ void (*reinitbcshw)(struct bc_state *bcs);
+
+ /* Called by gigaset_initcs() for setting up cs->hw.xxx */
+ int (*initcshw)(struct cardstate *cs);
+
+ /* Called by gigaset_freecs() for freeing cs->hw.xxx */
+ void (*freecshw)(struct cardstate *cs);
+
+ /* Called from common.c/interface.c for additional serial port
+ control */
+ int (*set_modem_ctrl)(struct cardstate *cs, unsigned old_state,
+ unsigned new_state);
+ int (*baud_rate)(struct cardstate *cs, unsigned cflag);
+ int (*set_line_ctrl)(struct cardstate *cs, unsigned cflag);
+
+ /* Called from i4l.c to put an skb into the send-queue. */
+ int (*send_skb)(struct bc_state *bcs, struct sk_buff *skb);
+
+ /* Called from ev-layer.c to process a block of data
+ * received through the common/control channel. */
+ void (*handle_input)(struct inbuf_t *inbuf);
+
+};
+
+/* = Common structures and definitions ======================================= */
+
+/* Parser states for DLE-Event:
+ * <DLE-EVENT>: <DLE_FLAG> "X" <EVENT> <DLE_FLAG> "."
+ * <DLE_FLAG>: 0x10
+ * <EVENT>: ((a-z)* | (A-Z)* | (0-10)*)+
+ */
+#define DLE_FLAG 0x10
+
+/* ===========================================================================
+ * Functions implemented in asyncdata.c
+ */
+
+/* Called from i4l.c to put an skb into the send-queue.
+ * After sending gigaset_skb_sent() should be called. */
+int gigaset_m10x_send_skb(struct bc_state *bcs, struct sk_buff *skb);
+
+/* Called from ev-layer.c to process a block of data
+ * received through the common/control channel. */
+void gigaset_m10x_input(struct inbuf_t *inbuf);
+
+/* ===========================================================================
+ * Functions implemented in isocdata.c
+ */
+
+/* Called from i4l.c to put an skb into the send-queue.
+ * After sending gigaset_skb_sent() should be called. */
+int gigaset_isoc_send_skb(struct bc_state *bcs, struct sk_buff *skb);
+
+/* Called from ev-layer.c to process a block of data
+ * received through the common/control channel. */
+void gigaset_isoc_input(struct inbuf_t *inbuf);
+
+/* Called from bas-gigaset.c to process a block of data
+ * received through the isochronous channel */
+void gigaset_isoc_receive(unsigned char *src, unsigned count,
+ struct bc_state *bcs);
+
+/* Called from bas-gigaset.c to put a block of data
+ * into the isochronous output buffer */
+int gigaset_isoc_buildframe(struct bc_state *bcs, unsigned char *in, int len);
+
+/* Called from bas-gigaset.c to initialize the isochronous output buffer */
+void gigaset_isowbuf_init(struct isowbuf_t *iwb, unsigned char idle);
+
+/* Called from bas-gigaset.c to retrieve a block of bytes for sending */
+int gigaset_isowbuf_getbytes(struct isowbuf_t *iwb, int size);
+
+/* ===========================================================================
+ * Functions implemented in i4l.c/gigaset.h
+ */
+
+/* Called by gigaset_initcs() for setting up with the isdn4linux subsystem */
+int gigaset_register_to_LL(struct cardstate *cs, const char *isdnid);
+
+/* Called from xxx-gigaset.c to indicate completion of sending an skb */
+void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb);
+
+/* Called from common.c/ev-layer.c to indicate events relevant to the LL */
+int gigaset_isdn_icall(struct at_state_t *at_state);
+int gigaset_isdn_setup_accept(struct at_state_t *at_state);
+int gigaset_isdn_setup_dial(struct at_state_t *at_state, void *data);
+
+void gigaset_i4l_cmd(struct cardstate *cs, int cmd);
+void gigaset_i4l_channel_cmd(struct bc_state *bcs, int cmd);
+
+
+static inline void gigaset_isdn_rcv_err(struct bc_state *bcs)
+{
+ isdn_ctrl response;
+
+ /* error -> LL */
+ gig_dbg(DEBUG_CMD, "sending L1ERR");
+ response.driver = bcs->cs->myid;
+ response.command = ISDN_STAT_L1ERR;
+ response.arg = bcs->channel;
+ response.parm.errcode = ISDN_STAT_L1ERR_RECV;
+ bcs->cs->iif.statcallb(&response);
+}
+
+/* ===========================================================================
+ * Functions implemented in ev-layer.c
+ */
+
+/* tasklet called from common.c to process queued events */
+void gigaset_handle_event(unsigned long data);
+
+/* called from isocdata.c / asyncdata.c
+ * when a complete modem response line has been received */
+void gigaset_handle_modem_response(struct cardstate *cs);
+
+/* ===========================================================================
+ * Functions implemented in proc.c
+ */
+
+/* initialize sysfs for device */
+void gigaset_init_dev_sysfs(struct cardstate *cs);
+void gigaset_free_dev_sysfs(struct cardstate *cs);
+
+/* ===========================================================================
+ * Functions implemented in common.c/gigaset.h
+ */
+
+void gigaset_bcs_reinit(struct bc_state *bcs);
+void gigaset_at_init(struct at_state_t *at_state, struct bc_state *bcs,
+ struct cardstate *cs, int cid);
+int gigaset_get_channel(struct bc_state *bcs);
+void gigaset_free_channel(struct bc_state *bcs);
+int gigaset_get_channels(struct cardstate *cs);
+void gigaset_free_channels(struct cardstate *cs);
+void gigaset_block_channels(struct cardstate *cs);
+
+/* Allocate and initialize driver structure. */
+struct gigaset_driver *gigaset_initdriver(unsigned minor, unsigned minors,
+ const char *procname,
+ const char *devname,
+ const struct gigaset_ops *ops,
+ struct module *owner);
+
+/* Deallocate driver structure. */
+void gigaset_freedriver(struct gigaset_driver *drv);
+void gigaset_debugdrivers(void);
+struct cardstate *gigaset_get_cs_by_tty(struct tty_struct *tty);
+struct cardstate *gigaset_get_cs_by_id(int id);
+void gigaset_blockdriver(struct gigaset_driver *drv);
+
+/* Allocate and initialize card state. Calls hardware dependent
+ gigaset_init[b]cs(). */
+struct cardstate *gigaset_initcs(struct gigaset_driver *drv, int channels,
+ int onechannel, int ignoreframes,
+ int cidmode, const char *modulename);
+
+/* Free card state. Calls hardware dependent gigaset_free[b]cs(). */
+void gigaset_freecs(struct cardstate *cs);
+
+/* Tell common.c that hardware and driver are ready. */
+int gigaset_start(struct cardstate *cs);
+
+/* Tell common.c that the device is not present any more. */
+void gigaset_stop(struct cardstate *cs);
+
+/* Tell common.c that the driver is being unloaded. */
+int gigaset_shutdown(struct cardstate *cs);
+
+/* Tell common.c that an skb has been sent. */
+void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb);
+
+/* Append event to the queue.
+ * Returns NULL on failure or a pointer to the event on success.
+ * ptr must be kmalloc()ed (and not be freed by the caller).
+ */
+struct event_t *gigaset_add_event(struct cardstate *cs,
+ struct at_state_t *at_state, int type,
+ void *ptr, int parameter, void *arg);
+
+/* Called on CONFIG1 command from frontend. */
+int gigaset_enterconfigmode(struct cardstate *cs); //0: success <0: errorcode
+
+/* cs->lock must not be locked */
+static inline void gigaset_schedule_event(struct cardstate *cs)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&cs->lock, flags);
+ if (cs->running)
+ tasklet_schedule(&cs->event_tasklet);
+ spin_unlock_irqrestore(&cs->lock, flags);
+}
+
+/* Tell common.c that B channel has been closed. */
+/* cs->lock must not be locked */
+static inline void gigaset_bchannel_down(struct bc_state *bcs)
+{
+ gigaset_add_event(bcs->cs, &bcs->at_state, EV_BC_CLOSED, NULL, 0, NULL);
+
+ gig_dbg(DEBUG_CMD, "scheduling BC_CLOSED");
+ gigaset_schedule_event(bcs->cs);
+}
+
+/* Tell common.c that B channel has been opened. */
+/* cs->lock must not be locked */
+static inline void gigaset_bchannel_up(struct bc_state *bcs)
+{
+ gigaset_add_event(bcs->cs, &bcs->at_state, EV_BC_OPEN, NULL, 0, NULL);
+
+ gig_dbg(DEBUG_CMD, "scheduling BC_OPEN");
+ gigaset_schedule_event(bcs->cs);
+}
+
+/* handling routines for sk_buff */
+/* ============================= */
+
+/* pass received skb to LL
+ * Warning: skb must not be accessed anymore!
+ */
+static inline void gigaset_rcv_skb(struct sk_buff *skb,
+ struct cardstate *cs,
+ struct bc_state *bcs)
+{
+ cs->iif.rcvcallb_skb(cs->myid, bcs->channel, skb);
+ bcs->trans_down++;
+}
+
+/* handle reception of corrupted skb
+ * Warning: skb must not be accessed anymore!
+ */
+static inline void gigaset_rcv_error(struct sk_buff *procskb,
+ struct cardstate *cs,
+ struct bc_state *bcs)
+{
+ if (procskb)
+ dev_kfree_skb(procskb);
+
+ if (bcs->ignore)
+ --bcs->ignore;
+ else {
+ ++bcs->corrupted;
+ gigaset_isdn_rcv_err(bcs);
+ }
+}
+
+/* append received bytes to inbuf */
+int gigaset_fill_inbuf(struct inbuf_t *inbuf, const unsigned char *src,
+ unsigned numbytes);
+
+/* ===========================================================================
+ * Functions implemented in interface.c
+ */
+
+/* initialize interface */
+void gigaset_if_initdriver(struct gigaset_driver *drv, const char *procname,
+ const char *devname);
+/* release interface */
+void gigaset_if_freedriver(struct gigaset_driver *drv);
+/* add minor */
+void gigaset_if_init(struct cardstate *cs);
+/* remove minor */
+void gigaset_if_free(struct cardstate *cs);
+/* device received data */
+void gigaset_if_receive(struct cardstate *cs,
+ unsigned char *buffer, size_t len);
+
+#endif
diff --git a/drivers/isdn/gigaset/i4l.c b/drivers/isdn/gigaset/i4l.c
new file mode 100644
index 0000000..3c127a8
--- /dev/null
+++ b/drivers/isdn/gigaset/i4l.c
@@ -0,0 +1,576 @@
+/*
+ * Stuff used by all variants of the driver
+ *
+ * Copyright (c) 2001 by Stefan Eilers,
+ * Hansjoerg Lipp <hjlipp@web.de>,
+ * Tilman Schmidt <tilman@imap.cc>.
+ *
+ * =====================================================================
+ * 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 of
+ * the License, or (at your option) any later version.
+ * =====================================================================
+ */
+
+#include "gigaset.h"
+
+/* == Handling of I4L IO =====================================================*/
+
+/* writebuf_from_LL
+ * called by LL to transmit data on an open channel
+ * inserts the buffer data into the send queue and starts the transmission
+ * Note that this operation must not sleep!
+ * When the buffer is processed completely, gigaset_skb_sent() should be called.
+ * parameters:
+ * driverID driver ID as assigned by LL
+ * channel channel number
+ * ack if != 0 LL wants to be notified on completion via
+ * statcallb(ISDN_STAT_BSENT)
+ * skb skb containing data to send
+ * return value:
+ * number of accepted bytes
+ * 0 if temporarily unable to accept data (out of buffer space)
+ * <0 on error (eg. -EINVAL)
+ */
+static int writebuf_from_LL(int driverID, int channel, int ack,
+ struct sk_buff *skb)
+{
+ struct cardstate *cs;
+ struct bc_state *bcs;
+ unsigned len;
+ unsigned skblen;
+
+ if (!(cs = gigaset_get_cs_by_id(driverID))) {
+ err("%s: invalid driver ID (%d)", __func__, driverID);
+ return -ENODEV;
+ }
+ if (channel < 0 || channel >= cs->channels) {
+ dev_err(cs->dev, "%s: invalid channel ID (%d)\n",
+ __func__, channel);
+ return -ENODEV;
+ }
+ bcs = &cs->bcs[channel];
+ len = skb->len;
+
+ gig_dbg(DEBUG_LLDATA,
+ "Receiving data from LL (id: %d, ch: %d, ack: %d, sz: %d)",
+ driverID, channel, ack, len);
+
+ if (!len) {
+ if (ack)
+ dev_notice(cs->dev, "%s: not ACKing empty packet\n",
+ __func__);
+ return 0;
+ }
+ if (len > MAX_BUF_SIZE) {
+ dev_err(cs->dev, "%s: packet too large (%d bytes)\n",
+ __func__, len);
+ return -EINVAL;
+ }
+
+ skblen = ack ? len : 0;
+ skb->head[0] = skblen & 0xff;
+ skb->head[1] = skblen >> 8;
+ gig_dbg(DEBUG_MCMD, "skb: len=%u, skblen=%u: %02x %02x",
+ len, skblen, (unsigned) skb->head[0], (unsigned) skb->head[1]);
+
+ /* pass to device-specific module */
+ return cs->ops->send_skb(bcs, skb);
+}
+
+void gigaset_skb_sent(struct bc_state *bcs, struct sk_buff *skb)
+{
+ unsigned len;
+ isdn_ctrl response;
+
+ ++bcs->trans_up;
+
+ if (skb->len)
+ dev_warn(bcs->cs->dev, "%s: skb->len==%d\n",
+ __func__, skb->len);
+
+ len = (unsigned char) skb->head[0] |
+ (unsigned) (unsigned char) skb->head[1] << 8;
+ if (len) {
+ gig_dbg(DEBUG_MCMD, "ACKing to LL (id: %d, ch: %d, sz: %u)",
+ bcs->cs->myid, bcs->channel, len);
+
+ response.driver = bcs->cs->myid;
+ response.command = ISDN_STAT_BSENT;
+ response.arg = bcs->channel;
+ response.parm.length = len;
+ bcs->cs->iif.statcallb(&response);
+ }
+}
+EXPORT_SYMBOL_GPL(gigaset_skb_sent);
+
+/* This function will be called by LL to send commands
+ * NOTE: LL ignores the returned value, for commands other than ISDN_CMD_IOCTL,
+ * so don't put too much effort into it.
+ */
+static int command_from_LL(isdn_ctrl *cntrl)
+{
+ struct cardstate *cs = gigaset_get_cs_by_id(cntrl->driver);
+ struct bc_state *bcs;
+ int retval = 0;
+ struct setup_parm *sp;
+
+ gigaset_debugdrivers();
+
+ if (!cs) {
+ err("%s: invalid driver ID (%d)", __func__, cntrl->driver);
+ return -ENODEV;
+ }
+
+ switch (cntrl->command) {
+ case ISDN_CMD_IOCTL:
+ gig_dbg(DEBUG_ANY, "ISDN_CMD_IOCTL (driver: %d, arg: %ld)",
+ cntrl->driver, cntrl->arg);
+
+ dev_warn(cs->dev, "ISDN_CMD_IOCTL not supported\n");
+ return -EINVAL;
+
+ case ISDN_CMD_DIAL:
+ gig_dbg(DEBUG_ANY,
+ "ISDN_CMD_DIAL (driver: %d, ch: %ld, "
+ "phone: %s, ownmsn: %s, si1: %d, si2: %d)",
+ cntrl->driver, cntrl->arg,
+ cntrl->parm.setup.phone, cntrl->parm.setup.eazmsn,
+ cntrl->parm.setup.si1, cntrl->parm.setup.si2);
+
+ if (cntrl->arg >= cs->channels) {
+ dev_err(cs->dev,
+ "ISDN_CMD_DIAL: invalid channel (%d)\n",
+ (int) cntrl->arg);
+ return -EINVAL;
+ }
+
+ bcs = cs->bcs + cntrl->arg;
+
+ if (!gigaset_get_channel(bcs)) {
+ dev_err(cs->dev, "ISDN_CMD_DIAL: channel not free\n");
+ return -EBUSY;
+ }
+
+ sp = kmalloc(sizeof *sp, GFP_ATOMIC);
+ if (!sp) {
+ gigaset_free_channel(bcs);
+ dev_err(cs->dev, "ISDN_CMD_DIAL: out of memory\n");
+ return -ENOMEM;
+ }
+ *sp = cntrl->parm.setup;
+
+ if (!gigaset_add_event(cs, &bcs->at_state, EV_DIAL, sp,
+ bcs->at_state.seq_index, NULL)) {
+ //FIXME what should we do?
+ kfree(sp);
+ gigaset_free_channel(bcs);
+ return -ENOMEM;
+ }
+
+ gig_dbg(DEBUG_CMD, "scheduling DIAL");
+ gigaset_schedule_event(cs);
+ break;
+ case ISDN_CMD_ACCEPTD: //FIXME
+ gig_dbg(DEBUG_ANY, "ISDN_CMD_ACCEPTD");
+
+ if (cntrl->arg >= cs->channels) {
+ dev_err(cs->dev,
+ "ISDN_CMD_ACCEPTD: invalid channel (%d)\n",
+ (int) cntrl->arg);
+ return -EINVAL;
+ }
+
+ if (!gigaset_add_event(cs, &cs->bcs[cntrl->arg].at_state,
+ EV_ACCEPT, NULL, 0, NULL)) {
+ //FIXME what should we do?
+ return -ENOMEM;
+ }
+
+ gig_dbg(DEBUG_CMD, "scheduling ACCEPT");
+ gigaset_schedule_event(cs);
+
+ break;
+ case ISDN_CMD_ACCEPTB:
+ gig_dbg(DEBUG_ANY, "ISDN_CMD_ACCEPTB");
+ break;
+ case ISDN_CMD_HANGUP:
+ gig_dbg(DEBUG_ANY, "ISDN_CMD_HANGUP (ch: %d)",
+ (int) cntrl->arg);
+
+ if (cntrl->arg >= cs->channels) {
+ dev_err(cs->dev,
+ "ISDN_CMD_HANGUP: invalid channel (%d)\n",
+ (int) cntrl->arg);
+ return -EINVAL;
+ }
+
+ if (!gigaset_add_event(cs, &cs->bcs[cntrl->arg].at_state,
+ EV_HUP, NULL, 0, NULL)) {
+ //FIXME what should we do?
+ return -ENOMEM;
+ }
+
+ gig_dbg(DEBUG_CMD, "scheduling HUP");
+ gigaset_schedule_event(cs);
+
+ break;
+ case ISDN_CMD_CLREAZ: /* Do not signal incoming signals */ //FIXME
+ gig_dbg(DEBUG_ANY, "ISDN_CMD_CLREAZ");
+ break;
+ case ISDN_CMD_SETEAZ: /* Signal incoming calls for given MSN */ //FIXME
+ gig_dbg(DEBUG_ANY,
+ "ISDN_CMD_SETEAZ (id: %d, ch: %ld, number: %s)",
+ cntrl->driver, cntrl->arg, cntrl->parm.num);
+ break;
+ case ISDN_CMD_SETL2: /* Set L2 to given protocol */
+ gig_dbg(DEBUG_ANY, "ISDN_CMD_SETL2 (ch: %ld, proto: %lx)",
+ cntrl->arg & 0xff, (cntrl->arg >> 8));
+
+ if ((cntrl->arg & 0xff) >= cs->channels) {
+ dev_err(cs->dev,
+ "ISDN_CMD_SETL2: invalid channel (%d)\n",
+ (int) cntrl->arg & 0xff);
+ return -EINVAL;
+ }
+
+ if (!gigaset_add_event(cs, &cs->bcs[cntrl->arg & 0xff].at_state,
+ EV_PROTO_L2, NULL, cntrl->arg >> 8,
+ NULL)) {
+ //FIXME what should we do?
+ return -ENOMEM;
+ }
+
+ gig_dbg(DEBUG_CMD, "scheduling PROTO_L2");
+ gigaset_schedule_event(cs);
+ break;
+ case ISDN_CMD_SETL3: /* Set L3 to given protocol */
+ gig_dbg(DEBUG_ANY, "ISDN_CMD_SETL3 (ch: %ld, proto: %lx)",
+ cntrl->arg & 0xff, (cntrl->arg >> 8));
+
+ if ((cntrl->arg & 0xff) >= cs->channels) {
+ dev_err(cs->dev,
+ "ISDN_CMD_SETL3: invalid channel (%d)\n",
+ (int) cntrl->arg & 0xff);
+ return -EINVAL;
+ }
+
+ if (cntrl->arg >> 8 != ISDN_PROTO_L3_TRANS) {
+ dev_err(cs->dev,
+ "ISDN_CMD_SETL3: invalid protocol %lu\n",
+ cntrl->arg >> 8);
+ return -EINVAL;
+ }
+
+ break;
+ case ISDN_CMD_PROCEED:
+ gig_dbg(DEBUG_ANY, "ISDN_CMD_PROCEED"); //FIXME
+ break;
+ case ISDN_CMD_ALERT:
+ gig_dbg(DEBUG_ANY, "ISDN_CMD_ALERT"); //FIXME
+ if (cntrl->arg >= cs->channels) {
+ dev_err(cs->dev,
+ "ISDN_CMD_ALERT: invalid channel (%d)\n",
+ (int) cntrl->arg);
+ return -EINVAL;
+ }
+ //bcs = cs->bcs + cntrl->arg;
+ //bcs->proto2 = -1;
+ // FIXME
+ break;
+ case ISDN_CMD_REDIR:
+ gig_dbg(DEBUG_ANY, "ISDN_CMD_REDIR"); //FIXME
+ break;
+ case ISDN_CMD_PROT_IO:
+ gig_dbg(DEBUG_ANY, "ISDN_CMD_PROT_IO");
+ break;
+ case ISDN_CMD_FAXCMD:
+ gig_dbg(DEBUG_ANY, "ISDN_CMD_FAXCMD");
+ break;
+ case ISDN_CMD_GETL2:
+ gig_dbg(DEBUG_ANY, "ISDN_CMD_GETL2");
+ break;
+ case ISDN_CMD_GETL3:
+ gig_dbg(DEBUG_ANY, "ISDN_CMD_GETL3");
+ break;
+ case ISDN_CMD_GETEAZ:
+ gig_dbg(DEBUG_ANY, "ISDN_CMD_GETEAZ");
+ break;
+ case ISDN_CMD_SETSIL:
+ gig_dbg(DEBUG_ANY, "ISDN_CMD_SETSIL");
+ break;
+ case ISDN_CMD_GETSIL:
+ gig_dbg(DEBUG_ANY, "ISDN_CMD_GETSIL");
+ break;
+ default:
+ dev_err(cs->dev, "unknown command %d from LL\n",
+ cntrl->command);
+ return -EINVAL;
+ }
+
+ return retval;
+}
+
+void gigaset_i4l_cmd(struct cardstate *cs, int cmd)
+{
+ isdn_ctrl command;
+
+ command.driver = cs->myid;
+ command.command = cmd;
+ command.arg = 0;
+ cs->iif.statcallb(&command);
+}
+
+void gigaset_i4l_channel_cmd(struct bc_state *bcs, int cmd)
+{
+ isdn_ctrl command;
+
+ command.driver = bcs->cs->myid;
+ command.command = cmd;
+ command.arg = bcs->channel;
+ bcs->cs->iif.statcallb(&command);
+}
+
+int gigaset_isdn_setup_dial(struct at_state_t *at_state, void *data)
+{
+ struct bc_state *bcs = at_state->bcs;
+ unsigned proto;
+ const char *bc;
+ size_t length[AT_NUM];
+ size_t l;
+ int i;
+ struct setup_parm *sp = data;
+
+ switch (bcs->proto2) {
+ case ISDN_PROTO_L2_HDLC:
+ proto = 1; /* 0: Bitsynchron, 1: HDLC, 2: voice */
+ break;
+ case ISDN_PROTO_L2_TRANS:
+ proto = 2; /* 0: Bitsynchron, 1: HDLC, 2: voice */
+ break;
+ default:
+ dev_err(bcs->cs->dev, "%s: invalid L2 protocol: %u\n",
+ __func__, bcs->proto2);
+ return -EINVAL;
+ }
+
+ switch (sp->si1) {
+ case 1: /* audio */
+ bc = "9090A3"; /* 3.1 kHz audio, A-law */
+ break;
+ case 7: /* data */
+ default: /* hope the app knows what it is doing */
+ bc = "8890"; /* unrestricted digital information */
+ }
+ //FIXME add missing si1 values from 1TR6, inspect si2, set HLC/LLC
+
+ length[AT_DIAL ] = 1 + strlen(sp->phone) + 1 + 1;
+ l = strlen(sp->eazmsn);
+ length[AT_MSN ] = l ? 6 + l + 1 + 1 : 0;
+ length[AT_BC ] = 5 + strlen(bc) + 1 + 1;
+ length[AT_PROTO] = 6 + 1 + 1 + 1; /* proto: 1 character */
+ length[AT_ISO ] = 6 + 1 + 1 + 1; /* channel: 1 character */
+ length[AT_TYPE ] = 6 + 1 + 1 + 1; /* call type: 1 character */
+ length[AT_HLC ] = 0;
+
+ for (i = 0; i < AT_NUM; ++i) {
+ kfree(bcs->commands[i]);
+ bcs->commands[i] = NULL;
+ if (length[i] &&
+ !(bcs->commands[i] = kmalloc(length[i], GFP_ATOMIC))) {
+ dev_err(bcs->cs->dev, "out of memory\n");
+ return -ENOMEM;
+ }
+ }
+
+ /* type = 1: extern, 0: intern, 2: recall, 3: door, 4: centrex */
+ if (sp->phone[0] == '*' && sp->phone[1] == '*') {
+ /* internal call: translate ** prefix to CTP value */
+ snprintf(bcs->commands[AT_DIAL], length[AT_DIAL],
+ "D%s\r", sp->phone+2);
+ strncpy(bcs->commands[AT_TYPE], "^SCTP=0\r", length[AT_TYPE]);
+ } else {
+ snprintf(bcs->commands[AT_DIAL], length[AT_DIAL],
+ "D%s\r", sp->phone);
+ strncpy(bcs->commands[AT_TYPE], "^SCTP=1\r", length[AT_TYPE]);
+ }
+
+ if (bcs->commands[AT_MSN])
+ snprintf(bcs->commands[AT_MSN], length[AT_MSN],
+ "^SMSN=%s\r", sp->eazmsn);
+ snprintf(bcs->commands[AT_BC ], length[AT_BC ],
+ "^SBC=%s\r", bc);
+ snprintf(bcs->commands[AT_PROTO], length[AT_PROTO],
+ "^SBPR=%u\r", proto);
+ snprintf(bcs->commands[AT_ISO ], length[AT_ISO ],
+ "^SISO=%u\r", (unsigned)bcs->channel + 1);
+
+ return 0;
+}
+
+int gigaset_isdn_setup_accept(struct at_state_t *at_state)
+{
+ unsigned proto;
+ size_t length[AT_NUM];
+ int i;
+ struct bc_state *bcs = at_state->bcs;
+
+ switch (bcs->proto2) {
+ case ISDN_PROTO_L2_HDLC:
+ proto = 1; /* 0: Bitsynchron, 1: HDLC, 2: voice */
+ break;
+ case ISDN_PROTO_L2_TRANS:
+ proto = 2; /* 0: Bitsynchron, 1: HDLC, 2: voice */
+ break;
+ default:
+ dev_err(at_state->cs->dev, "%s: invalid protocol: %u\n",
+ __func__, bcs->proto2);
+ return -EINVAL;
+ }
+
+ length[AT_DIAL ] = 0;
+ length[AT_MSN ] = 0;
+ length[AT_BC ] = 0;
+ length[AT_PROTO] = 6 + 1 + 1 + 1; /* proto: 1 character */
+ length[AT_ISO ] = 6 + 1 + 1 + 1; /* channel: 1 character */
+ length[AT_TYPE ] = 0;
+ length[AT_HLC ] = 0;
+
+ for (i = 0; i < AT_NUM; ++i) {
+ kfree(bcs->commands[i]);
+ bcs->commands[i] = NULL;
+ if (length[i] &&
+ !(bcs->commands[i] = kmalloc(length[i], GFP_ATOMIC))) {
+ dev_err(at_state->cs->dev, "out of memory\n");
+ return -ENOMEM;
+ }
+ }
+
+ snprintf(bcs->commands[AT_PROTO], length[AT_PROTO],
+ "^SBPR=%u\r", proto);
+ snprintf(bcs->commands[AT_ISO ], length[AT_ISO ],
+ "^SISO=%u\r", (unsigned) bcs->channel + 1);
+
+ return 0;
+}
+
+int gigaset_isdn_icall(struct at_state_t *at_state)
+{
+ struct cardstate *cs = at_state->cs;
+ struct bc_state *bcs = at_state->bcs;
+ isdn_ctrl response;
+ int retval;
+
+ /* fill ICALL structure */
+ response.parm.setup.si1 = 0; /* default: unknown */
+ response.parm.setup.si2 = 0;
+ response.parm.setup.screen = 0; //FIXME how to set these?
+ response.parm.setup.plan = 0;
+ if (!at_state->str_var[STR_ZBC]) {
+ /* no BC (internal call): assume speech, A-law */
+ response.parm.setup.si1 = 1;
+ } else if (!strcmp(at_state->str_var[STR_ZBC], "8890")) {
+ /* unrestricted digital information */
+ response.parm.setup.si1 = 7;
+ } else if (!strcmp(at_state->str_var[STR_ZBC], "8090A3")) {
+ /* speech, A-law */
+ response.parm.setup.si1 = 1;
+ } else if (!strcmp(at_state->str_var[STR_ZBC], "9090A3")) {
+ /* 3,1 kHz audio, A-law */
+ response.parm.setup.si1 = 1;
+ response.parm.setup.si2 = 2;
+ } else {
+ dev_warn(cs->dev, "RING ignored - unsupported BC %s\n",
+ at_state->str_var[STR_ZBC]);
+ return ICALL_IGNORE;
+ }
+ if (at_state->str_var[STR_NMBR]) {
+ strncpy(response.parm.setup.phone, at_state->str_var[STR_NMBR],
+ sizeof response.parm.setup.phone - 1);
+ response.parm.setup.phone[sizeof response.parm.setup.phone - 1] = 0;
+ } else
+ response.parm.setup.phone[0] = 0;
+ if (at_state->str_var[STR_ZCPN]) {
+ strncpy(response.parm.setup.eazmsn, at_state->str_var[STR_ZCPN],
+ sizeof response.parm.setup.eazmsn - 1);
+ response.parm.setup.eazmsn[sizeof response.parm.setup.eazmsn - 1] = 0;
+ } else
+ response.parm.setup.eazmsn[0] = 0;
+
+ if (!bcs) {
+ dev_notice(cs->dev, "no channel for incoming call\n");
+ response.command = ISDN_STAT_ICALLW;
+ response.arg = 0; //FIXME
+ } else {
+ gig_dbg(DEBUG_CMD, "Sending ICALL");
+ response.command = ISDN_STAT_ICALL;
+ response.arg = bcs->channel; //FIXME
+ }
+ response.driver = cs->myid;
+ retval = cs->iif.statcallb(&response);
+ gig_dbg(DEBUG_CMD, "Response: %d", retval);
+ switch (retval) {
+ case 0: /* no takers */
+ return ICALL_IGNORE;
+ case 1: /* alerting */
+ bcs->chstate |= CHS_NOTIFY_LL;
+ return ICALL_ACCEPT;
+ case 2: /* reject */
+ return ICALL_REJECT;
+ case 3: /* incomplete */
+ dev_warn(cs->dev,
+ "LL requested unsupported feature: Incomplete Number\n");
+ return ICALL_IGNORE;
+ case 4: /* proceeding */
+ /* Gigaset will send ALERTING anyway.
+ * There doesn't seem to be a way to avoid this.
+ */
+ return ICALL_ACCEPT;
+ case 5: /* deflect */
+ dev_warn(cs->dev,
+ "LL requested unsupported feature: Call Deflection\n");
+ return ICALL_IGNORE;
+ default:
+ dev_err(cs->dev, "LL error %d on ICALL\n", retval);
+ return ICALL_IGNORE;
+ }
+}
+
+/* Set Callback function pointer */
+int gigaset_register_to_LL(struct cardstate *cs, const char *isdnid)
+{
+ isdn_if *iif = &cs->iif;
+
+ gig_dbg(DEBUG_ANY, "Register driver capabilities to LL");
+
+ //iif->id[sizeof(iif->id) - 1]=0;
+ //strncpy(iif->id, isdnid, sizeof(iif->id) - 1);
+ if (snprintf(iif->id, sizeof iif->id, "%s_%u", isdnid, cs->minor_index)
+ >= sizeof iif->id)
+ return -ENOMEM; //FIXME EINVAL/...??
+
+ iif->owner = THIS_MODULE;
+ iif->channels = cs->channels;
+ iif->maxbufsize = MAX_BUF_SIZE;
+ iif->features = ISDN_FEATURE_L2_TRANS |
+ ISDN_FEATURE_L2_HDLC |
+#ifdef GIG_X75
+ ISDN_FEATURE_L2_X75I |
+#endif
+ ISDN_FEATURE_L3_TRANS |
+ ISDN_FEATURE_P_EURO;
+ iif->hl_hdrlen = HW_HDR_LEN; /* Area for storing ack */
+ iif->command = command_from_LL;
+ iif->writebuf_skb = writebuf_from_LL;
+ iif->writecmd = NULL; /* Don't support isdnctrl */
+ iif->readstat = NULL; /* Don't support isdnctrl */
+ iif->rcvcallb_skb = NULL; /* Will be set by LL */
+ iif->statcallb = NULL; /* Will be set by LL */
+
+ if (!register_isdn(iif))
+ return 0;
+
+ cs->myid = iif->channels; /* Set my device id */
+ return 1;
+}
diff --git a/drivers/isdn/gigaset/interface.c b/drivers/isdn/gigaset/interface.c
new file mode 100644
index 0000000..521951a
--- /dev/null
+++ b/drivers/isdn/gigaset/interface.c
@@ -0,0 +1,726 @@
+/*
+ * interface to user space for the gigaset driver
+ *
+ * Copyright (c) 2004 by Hansjoerg Lipp <hjlipp@web.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 of
+ * the License, or (at your option) any later version.
+ * =====================================================================
+ */
+
+#include "gigaset.h"
+#include <linux/gigaset_dev.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+
+/*** our ioctls ***/
+
+static int if_lock(struct cardstate *cs, int *arg)
+{
+ int cmd = *arg;
+
+ gig_dbg(DEBUG_IF, "%u: if_lock (%d)", cs->minor_index, cmd);
+
+ if (cmd > 1)
+ return -EINVAL;
+
+ if (cmd < 0) {
+ *arg = cs->mstate == MS_LOCKED;
+ return 0;
+ }
+
+ if (!cmd && cs->mstate == MS_LOCKED && cs->connected) {
+ cs->ops->set_modem_ctrl(cs, 0, TIOCM_DTR|TIOCM_RTS);
+ cs->ops->baud_rate(cs, B115200);
+ cs->ops->set_line_ctrl(cs, CS8);
+ cs->control_state = TIOCM_DTR|TIOCM_RTS;
+ }
+
+ cs->waiting = 1;
+ if (!gigaset_add_event(cs, &cs->at_state, EV_IF_LOCK,
+ NULL, cmd, NULL)) {
+ cs->waiting = 0;
+ return -ENOMEM;
+ }
+
+ gig_dbg(DEBUG_CMD, "scheduling IF_LOCK");
+ gigaset_schedule_event(cs);
+
+ wait_event(cs->waitqueue, !cs->waiting);
+
+ if (cs->cmd_result >= 0) {
+ *arg = cs->cmd_result;
+ return 0;
+ }
+
+ return cs->cmd_result;
+}
+
+static int if_version(struct cardstate *cs, unsigned arg[4])
+{
+ static const unsigned version[4] = GIG_VERSION;
+ static const unsigned compat[4] = GIG_COMPAT;
+ unsigned cmd = arg[0];
+
+ gig_dbg(DEBUG_IF, "%u: if_version (%d)", cs->minor_index, cmd);
+
+ switch (cmd) {
+ case GIGVER_DRIVER:
+ memcpy(arg, version, sizeof version);
+ return 0;
+ case GIGVER_COMPAT:
+ memcpy(arg, compat, sizeof compat);
+ return 0;
+ case GIGVER_FWBASE:
+ cs->waiting = 1;
+ if (!gigaset_add_event(cs, &cs->at_state, EV_IF_VER,
+ NULL, 0, arg)) {
+ cs->waiting = 0;
+ return -ENOMEM;
+ }
+
+ gig_dbg(DEBUG_CMD, "scheduling IF_VER");
+ gigaset_schedule_event(cs);
+
+ wait_event(cs->waitqueue, !cs->waiting);
+
+ if (cs->cmd_result >= 0)
+ return 0;
+
+ return cs->cmd_result;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int if_config(struct cardstate *cs, int *arg)
+{
+ gig_dbg(DEBUG_IF, "%u: if_config (%d)", cs->minor_index, *arg);
+
+ if (*arg != 1)
+ return -EINVAL;
+
+ if (cs->mstate != MS_LOCKED)
+ return -EBUSY;
+
+ if (!cs->connected) {
+ err("not connected!");
+ return -ENODEV;
+ }
+
+ *arg = 0;
+ return gigaset_enterconfigmode(cs);
+}
+
+/*** the terminal driver ***/
+/* stolen from usbserial and some other tty drivers */
+
+static int if_open(struct tty_struct *tty, struct file *filp);
+static void if_close(struct tty_struct *tty, struct file *filp);
+static int if_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg);
+static int if_write_room(struct tty_struct *tty);
+static int if_chars_in_buffer(struct tty_struct *tty);
+static void if_throttle(struct tty_struct *tty);
+static void if_unthrottle(struct tty_struct *tty);
+static void if_set_termios(struct tty_struct *tty, struct ktermios *old);
+static int if_tiocmget(struct tty_struct *tty, struct file *file);
+static int if_tiocmset(struct tty_struct *tty, struct file *file,
+ unsigned int set, unsigned int clear);
+static int if_write(struct tty_struct *tty,
+ const unsigned char *buf, int count);
+
+static const struct tty_operations if_ops = {
+ .open = if_open,
+ .close = if_close,
+ .ioctl = if_ioctl,
+ .write = if_write,
+ .write_room = if_write_room,
+ .chars_in_buffer = if_chars_in_buffer,
+ .set_termios = if_set_termios,
+ .throttle = if_throttle,
+ .unthrottle = if_unthrottle,
+#if 0
+ .break_ctl = serial_break,
+#endif
+ .tiocmget = if_tiocmget,
+ .tiocmset = if_tiocmset,
+};
+
+static int if_open(struct tty_struct *tty, struct file *filp)
+{
+ struct cardstate *cs;
+ unsigned long flags;
+
+ gig_dbg(DEBUG_IF, "%d+%d: %s()",
+ tty->driver->minor_start, tty->index, __func__);
+
+ tty->driver_data = NULL;
+
+ cs = gigaset_get_cs_by_tty(tty);
+ if (!cs || !try_module_get(cs->driver->owner))
+ return -ENODEV;
+
+ if (mutex_lock_interruptible(&cs->mutex))
+ return -ERESTARTSYS; // FIXME -EINTR?
+ tty->driver_data = cs;
+
+ ++cs->open_count;
+
+ if (cs->open_count == 1) {
+ spin_lock_irqsave(&cs->lock, flags);
+ cs->tty = tty;
+ spin_unlock_irqrestore(&cs->lock, flags);
+ tty->low_latency = 1; //FIXME test
+ }
+
+ mutex_unlock(&cs->mutex);
+ return 0;
+}
+
+static void if_close(struct tty_struct *tty, struct file *filp)
+{
+ struct cardstate *cs;
+ unsigned long flags;
+
+ cs = (struct cardstate *) tty->driver_data;
+ if (!cs) {
+ err("cs==NULL in %s", __func__);
+ return;
+ }
+
+ gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
+
+ mutex_lock(&cs->mutex);
+
+ if (!cs->open_count)
+ dev_warn(cs->dev, "%s: device not opened\n", __func__);
+ else {
+ if (!--cs->open_count) {
+ spin_lock_irqsave(&cs->lock, flags);
+ cs->tty = NULL;
+ spin_unlock_irqrestore(&cs->lock, flags);
+ }
+ }
+
+ mutex_unlock(&cs->mutex);
+
+ module_put(cs->driver->owner);
+}
+
+static int if_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct cardstate *cs;
+ int retval = -ENODEV;
+ int int_arg;
+ unsigned char buf[6];
+ unsigned version[4];
+
+ cs = (struct cardstate *) tty->driver_data;
+ if (!cs) {
+ err("cs==NULL in %s", __func__);
+ return -ENODEV;
+ }
+
+ gig_dbg(DEBUG_IF, "%u: %s(0x%x)", cs->minor_index, __func__, cmd);
+
+ if (mutex_lock_interruptible(&cs->mutex))
+ return -ERESTARTSYS; // FIXME -EINTR?
+
+ if (!cs->open_count)
+ dev_warn(cs->dev, "%s: device not opened\n", __func__);
+ else {
+ retval = 0;
+ switch (cmd) {
+ case GIGASET_REDIR:
+ retval = get_user(int_arg, (int __user *) arg);
+ if (retval >= 0)
+ retval = if_lock(cs, &int_arg);
+ if (retval >= 0)
+ retval = put_user(int_arg, (int __user *) arg);
+ break;
+ case GIGASET_CONFIG:
+ retval = get_user(int_arg, (int __user *) arg);
+ if (retval >= 0)
+ retval = if_config(cs, &int_arg);
+ if (retval >= 0)
+ retval = put_user(int_arg, (int __user *) arg);
+ break;
+ case GIGASET_BRKCHARS:
+ //FIXME test if MS_LOCKED
+ if (!cs->connected) {
+ gig_dbg(DEBUG_ANY,
+ "can't communicate with unplugged device");
+ retval = -ENODEV;
+ break;
+ }
+ retval = copy_from_user(&buf,
+ (const unsigned char __user *) arg, 6)
+ ? -EFAULT : 0;
+ if (retval >= 0) {
+ gigaset_dbg_buffer(DEBUG_IF, "GIGASET_BRKCHARS",
+ 6, (const unsigned char *) arg);
+ retval = cs->ops->brkchars(cs, buf);
+ }
+ break;
+ case GIGASET_VERSION:
+ retval = copy_from_user(version,
+ (unsigned __user *) arg, sizeof version)
+ ? -EFAULT : 0;
+ if (retval >= 0)
+ retval = if_version(cs, version);
+ if (retval >= 0)
+ retval = copy_to_user((unsigned __user *) arg,
+ version, sizeof version)
+ ? -EFAULT : 0;
+ break;
+ default:
+ gig_dbg(DEBUG_ANY, "%s: arg not supported - 0x%04x",
+ __func__, cmd);
+ retval = -ENOIOCTLCMD;
+ }
+ }
+
+ mutex_unlock(&cs->mutex);
+
+ return retval;
+}
+
+static int if_tiocmget(struct tty_struct *tty, struct file *file)
+{
+ struct cardstate *cs;
+ int retval;
+
+ cs = (struct cardstate *) tty->driver_data;
+ if (!cs) {
+ err("cs==NULL in %s", __func__);
+ return -ENODEV;
+ }
+
+ gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
+
+ if (mutex_lock_interruptible(&cs->mutex))
+ return -ERESTARTSYS; // FIXME -EINTR?
+
+ // FIXME read from device?
+ retval = cs->control_state & (TIOCM_RTS|TIOCM_DTR);
+
+ mutex_unlock(&cs->mutex);
+
+ return retval;
+}
+
+static int if_tiocmset(struct tty_struct *tty, struct file *file,
+ unsigned int set, unsigned int clear)
+{
+ struct cardstate *cs;
+ int retval;
+ unsigned mc;
+
+ cs = (struct cardstate *) tty->driver_data;
+ if (!cs) {
+ err("cs==NULL in %s", __func__);
+ return -ENODEV;
+ }
+
+ gig_dbg(DEBUG_IF, "%u: %s(0x%x, 0x%x)",
+ cs->minor_index, __func__, set, clear);
+
+ if (mutex_lock_interruptible(&cs->mutex))
+ return -ERESTARTSYS; // FIXME -EINTR?
+
+ if (!cs->connected) {
+ gig_dbg(DEBUG_ANY, "can't communicate with unplugged device");
+ retval = -ENODEV;
+ } else {
+ mc = (cs->control_state | set) & ~clear & (TIOCM_RTS|TIOCM_DTR);
+ retval = cs->ops->set_modem_ctrl(cs, cs->control_state, mc);
+ cs->control_state = mc;
+ }
+
+ mutex_unlock(&cs->mutex);
+
+ return retval;
+}
+
+static int if_write(struct tty_struct *tty, const unsigned char *buf, int count)
+{
+ struct cardstate *cs;
+ int retval = -ENODEV;
+
+ cs = (struct cardstate *) tty->driver_data;
+ if (!cs) {
+ err("cs==NULL in %s", __func__);
+ return -ENODEV;
+ }
+
+ gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
+
+ if (mutex_lock_interruptible(&cs->mutex))
+ return -ERESTARTSYS; // FIXME -EINTR?
+
+ if (!cs->open_count)
+ dev_warn(cs->dev, "%s: device not opened\n", __func__);
+ else if (cs->mstate != MS_LOCKED) {
+ dev_warn(cs->dev, "can't write to unlocked device\n");
+ retval = -EBUSY;
+ } else if (!cs->connected) {
+ gig_dbg(DEBUG_ANY, "can't write to unplugged device");
+ retval = -EBUSY; //FIXME
+ } else {
+ retval = cs->ops->write_cmd(cs, buf, count,
+ &cs->if_wake_tasklet);
+ }
+
+ mutex_unlock(&cs->mutex);
+
+ return retval;
+}
+
+static int if_write_room(struct tty_struct *tty)
+{
+ struct cardstate *cs;
+ int retval = -ENODEV;
+
+ cs = (struct cardstate *) tty->driver_data;
+ if (!cs) {
+ err("cs==NULL in %s", __func__);
+ return -ENODEV;
+ }
+
+ gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
+
+ if (mutex_lock_interruptible(&cs->mutex))
+ return -ERESTARTSYS; // FIXME -EINTR?
+
+ if (!cs->open_count)
+ dev_warn(cs->dev, "%s: device not opened\n", __func__);
+ else if (cs->mstate != MS_LOCKED) {
+ dev_warn(cs->dev, "can't write to unlocked device\n");
+ retval = -EBUSY;
+ } else if (!cs->connected) {
+ gig_dbg(DEBUG_ANY, "can't write to unplugged device");
+ retval = -EBUSY; //FIXME
+ } else
+ retval = cs->ops->write_room(cs);
+
+ mutex_unlock(&cs->mutex);
+
+ return retval;
+}
+
+static int if_chars_in_buffer(struct tty_struct *tty)
+{
+ struct cardstate *cs;
+ int retval = -ENODEV;
+
+ cs = (struct cardstate *) tty->driver_data;
+ if (!cs) {
+ err("cs==NULL in %s", __func__);
+ return -ENODEV;
+ }
+
+ gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
+
+ if (mutex_lock_interruptible(&cs->mutex))
+ return -ERESTARTSYS; // FIXME -EINTR?
+
+ if (!cs->open_count)
+ dev_warn(cs->dev, "%s: device not opened\n", __func__);
+ else if (cs->mstate != MS_LOCKED) {
+ dev_warn(cs->dev, "can't write to unlocked device\n");
+ retval = -EBUSY;
+ } else if (!cs->connected) {
+ gig_dbg(DEBUG_ANY, "can't write to unplugged device");
+ retval = -EBUSY; //FIXME
+ } else
+ retval = cs->ops->chars_in_buffer(cs);
+
+ mutex_unlock(&cs->mutex);
+
+ return retval;
+}
+
+static void if_throttle(struct tty_struct *tty)
+{
+ struct cardstate *cs;
+
+ cs = (struct cardstate *) tty->driver_data;
+ if (!cs) {
+ err("cs==NULL in %s", __func__);
+ return;
+ }
+
+ gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
+
+ mutex_lock(&cs->mutex);
+
+ if (!cs->open_count)
+ dev_warn(cs->dev, "%s: device not opened\n", __func__);
+ else {
+ //FIXME
+ }
+
+ mutex_unlock(&cs->mutex);
+}
+
+static void if_unthrottle(struct tty_struct *tty)
+{
+ struct cardstate *cs;
+
+ cs = (struct cardstate *) tty->driver_data;
+ if (!cs) {
+ err("cs==NULL in %s", __func__);
+ return;
+ }
+
+ gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
+
+ mutex_lock(&cs->mutex);
+
+ if (!cs->open_count)
+ dev_warn(cs->dev, "%s: device not opened\n", __func__);
+ else {
+ //FIXME
+ }
+
+ mutex_unlock(&cs->mutex);
+}
+
+static void if_set_termios(struct tty_struct *tty, struct ktermios *old)
+{
+ struct cardstate *cs;
+ unsigned int iflag;
+ unsigned int cflag;
+ unsigned int old_cflag;
+ unsigned int control_state, new_state;
+
+ cs = (struct cardstate *) tty->driver_data;
+ if (!cs) {
+ err("cs==NULL in %s", __func__);
+ return;
+ }
+
+ gig_dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __func__);
+
+ mutex_lock(&cs->mutex);
+
+ if (!cs->open_count) {
+ dev_warn(cs->dev, "%s: device not opened\n", __func__);
+ goto out;
+ }
+
+ if (!cs->connected) {
+ gig_dbg(DEBUG_ANY, "can't communicate with unplugged device");
+ goto out;
+ }
+
+ // stolen from mct_u232.c
+ iflag = tty->termios->c_iflag;
+ cflag = tty->termios->c_cflag;
+ old_cflag = old ? old->c_cflag : cflag; //FIXME?
+ gig_dbg(DEBUG_IF, "%u: iflag %x cflag %x old %x",
+ cs->minor_index, iflag, cflag, old_cflag);
+
+ /* get a local copy of the current port settings */
+ control_state = cs->control_state;
+
+ /*
+ * Update baud rate.
+ * Do not attempt to cache old rates and skip settings,
+ * disconnects screw such tricks up completely.
+ * Premature optimization is the root of all evil.
+ */
+
+ /* reassert DTR and (maybe) RTS on transition from B0 */
+ if ((old_cflag & CBAUD) == B0) {
+ new_state = control_state | TIOCM_DTR;
+ /* don't set RTS if using hardware flow control */
+ if (!(old_cflag & CRTSCTS))
+ new_state |= TIOCM_RTS;
+ gig_dbg(DEBUG_IF, "%u: from B0 - set DTR%s",
+ cs->minor_index,
+ (new_state & TIOCM_RTS) ? " only" : "/RTS");
+ cs->ops->set_modem_ctrl(cs, control_state, new_state);
+ control_state = new_state;
+ }
+
+ cs->ops->baud_rate(cs, cflag & CBAUD);
+
+ if ((cflag & CBAUD) == B0) {
+ /* Drop RTS and DTR */
+ gig_dbg(DEBUG_IF, "%u: to B0 - drop DTR/RTS", cs->minor_index);
+ new_state = control_state & ~(TIOCM_DTR | TIOCM_RTS);
+ cs->ops->set_modem_ctrl(cs, control_state, new_state);
+ control_state = new_state;
+ }
+
+ /*
+ * Update line control register (LCR)
+ */
+
+ cs->ops->set_line_ctrl(cs, cflag);
+
+#if 0
+ //FIXME this hangs M101 [ts 2005-03-09]
+ //FIXME do we need this?
+ /*
+ * Set flow control: well, I do not really now how to handle DTR/RTS.
+ * Just do what we have seen with SniffUSB on Win98.
+ */
+ /* Drop DTR/RTS if no flow control otherwise assert */
+ gig_dbg(DEBUG_IF, "%u: control_state %x",
+ cs->minor_index, control_state);
+ new_state = control_state;
+ if ((iflag & IXOFF) || (iflag & IXON) || (cflag & CRTSCTS))
+ new_state |= TIOCM_DTR | TIOCM_RTS;
+ else
+ new_state &= ~(TIOCM_DTR | TIOCM_RTS);
+ if (new_state != control_state) {
+ gig_dbg(DEBUG_IF, "%u: new_state %x",
+ cs->minor_index, new_state);
+ gigaset_set_modem_ctrl(cs, control_state, new_state);
+ control_state = new_state;
+ }
+#endif
+
+ /* save off the modified port settings */
+ cs->control_state = control_state;
+
+out:
+ mutex_unlock(&cs->mutex);
+}
+
+
+/* wakeup tasklet for the write operation */
+static void if_wake(unsigned long data)
+{
+ struct cardstate *cs = (struct cardstate *) data;
+
+ if (cs->tty)
+ tty_wakeup(cs->tty);
+}
+
+/*** interface to common ***/
+
+void gigaset_if_init(struct cardstate *cs)
+{
+ struct gigaset_driver *drv;
+
+ drv = cs->driver;
+ if (!drv->have_tty)
+ return;
+
+ tasklet_init(&cs->if_wake_tasklet, &if_wake, (unsigned long) cs);
+
+ mutex_lock(&cs->mutex);
+ cs->tty_dev = tty_register_device(drv->tty, cs->minor_index, NULL);
+
+ if (!IS_ERR(cs->tty_dev))
+ dev_set_drvdata(cs->tty_dev, cs);
+ else {
+ dev_warn(cs->dev,
+ "could not register device to the tty subsystem\n");
+ cs->tty_dev = NULL;
+ }
+ mutex_unlock(&cs->mutex);
+}
+
+void gigaset_if_free(struct cardstate *cs)
+{
+ struct gigaset_driver *drv;
+
+ drv = cs->driver;
+ if (!drv->have_tty)
+ return;
+
+ tasklet_disable(&cs->if_wake_tasklet);
+ tasklet_kill(&cs->if_wake_tasklet);
+ cs->tty_dev = NULL;
+ tty_unregister_device(drv->tty, cs->minor_index);
+}
+
+void gigaset_if_receive(struct cardstate *cs,
+ unsigned char *buffer, size_t len)
+{
+ unsigned long flags;
+ struct tty_struct *tty;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ if ((tty = cs->tty) == NULL)
+ gig_dbg(DEBUG_ANY, "receive on closed device");
+ else {
+ tty_buffer_request_room(tty, len);
+ tty_insert_flip_string(tty, buffer, len);
+ tty_flip_buffer_push(tty);
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+}
+EXPORT_SYMBOL_GPL(gigaset_if_receive);
+
+/* gigaset_if_initdriver
+ * Initialize tty interface.
+ * parameters:
+ * drv Driver
+ * procname Name of the driver (e.g. for /proc/tty/drivers)
+ * devname Name of the device files (prefix without minor number)
+ */
+void gigaset_if_initdriver(struct gigaset_driver *drv, const char *procname,
+ const char *devname)
+{
+ unsigned minors = drv->minors;
+ int ret;
+ struct tty_driver *tty;
+
+ drv->have_tty = 0;
+
+ if ((drv->tty = alloc_tty_driver(minors)) == NULL)
+ goto enomem;
+ tty = drv->tty;
+
+ tty->magic = TTY_DRIVER_MAGIC,
+ tty->major = GIG_MAJOR,
+ tty->type = TTY_DRIVER_TYPE_SERIAL,
+ tty->subtype = SERIAL_TYPE_NORMAL,
+ tty->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+
+ tty->driver_name = procname;
+ tty->name = devname;
+ tty->minor_start = drv->minor;
+ tty->num = drv->minors;
+
+ tty->owner = THIS_MODULE;
+
+ tty->init_termios = tty_std_termios; //FIXME
+ tty->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; //FIXME
+ tty_set_operations(tty, &if_ops);
+
+ ret = tty_register_driver(tty);
+ if (ret < 0) {
+ warn("failed to register tty driver (error %d)", ret);
+ goto error;
+ }
+ gig_dbg(DEBUG_IF, "tty driver initialized");
+ drv->have_tty = 1;
+ return;
+
+enomem:
+ warn("could not allocate tty structures");
+error:
+ if (drv->tty)
+ put_tty_driver(drv->tty);
+}
+
+void gigaset_if_freedriver(struct gigaset_driver *drv)
+{
+ if (!drv->have_tty)
+ return;
+
+ drv->have_tty = 0;
+ tty_unregister_driver(drv->tty);
+ put_tty_driver(drv->tty);
+}
diff --git a/drivers/isdn/gigaset/isocdata.c b/drivers/isdn/gigaset/isocdata.c
new file mode 100644
index 0000000..fbce522
--- /dev/null
+++ b/drivers/isdn/gigaset/isocdata.c
@@ -0,0 +1,1011 @@
+/*
+ * Common data handling layer for bas_gigaset
+ *
+ * Copyright (c) 2005 by Tilman Schmidt <tilman@imap.cc>,
+ * Hansjoerg Lipp <hjlipp@web.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 of
+ * the License, or (at your option) any later version.
+ * =====================================================================
+ */
+
+#include "gigaset.h"
+#include <linux/crc-ccitt.h>
+#include <linux/bitrev.h>
+
+/* access methods for isowbuf_t */
+/* ============================ */
+
+/* initialize buffer structure
+ */
+void gigaset_isowbuf_init(struct isowbuf_t *iwb, unsigned char idle)
+{
+ iwb->read = 0;
+ iwb->nextread = 0;
+ iwb->write = 0;
+ atomic_set(&iwb->writesem, 1);
+ iwb->wbits = 0;
+ iwb->idle = idle;
+ memset(iwb->data + BAS_OUTBUFSIZE, idle, BAS_OUTBUFPAD);
+}
+
+/* compute number of bytes which can be appended to buffer
+ * so that there is still room to append a maximum frame of flags
+ */
+static inline int isowbuf_freebytes(struct isowbuf_t *iwb)
+{
+ int read, write, freebytes;
+
+ read = iwb->read;
+ write = iwb->write;
+ if ((freebytes = read - write) > 0) {
+ /* no wraparound: need padding space within regular area */
+ return freebytes - BAS_OUTBUFPAD;
+ } else if (read < BAS_OUTBUFPAD) {
+ /* wraparound: can use space up to end of regular area */
+ return BAS_OUTBUFSIZE - write;
+ } else {
+ /* following the wraparound yields more space */
+ return freebytes + BAS_OUTBUFSIZE - BAS_OUTBUFPAD;
+ }
+}
+
+/* compare two offsets within the buffer
+ * The buffer is seen as circular, with the read position as start
+ * returns -1/0/1 if position a </=/> position b without crossing 'read'
+ */
+static inline int isowbuf_poscmp(struct isowbuf_t *iwb, int a, int b)
+{
+ int read;
+ if (a == b)
+ return 0;
+ read = iwb->read;
+ if (a < b) {
+ if (a < read && read <= b)
+ return +1;
+ else
+ return -1;
+ } else {
+ if (b < read && read <= a)
+ return -1;
+ else
+ return +1;
+ }
+}
+
+/* start writing
+ * acquire the write semaphore
+ * return true if acquired, false if busy
+ */
+static inline int isowbuf_startwrite(struct isowbuf_t *iwb)
+{
+ if (!atomic_dec_and_test(&iwb->writesem)) {
+ atomic_inc(&iwb->writesem);
+ gig_dbg(DEBUG_ISO, "%s: couldn't acquire iso write semaphore",
+ __func__);
+ return 0;
+ }
+#ifdef CONFIG_GIGASET_DEBUG
+ gig_dbg(DEBUG_ISO,
+ "%s: acquired iso write semaphore, data[write]=%02x, nbits=%d",
+ __func__, iwb->data[iwb->write], iwb->wbits);
+#endif
+ return 1;
+}
+
+/* finish writing
+ * release the write semaphore
+ * returns the current write position
+ */
+static inline int isowbuf_donewrite(struct isowbuf_t *iwb)
+{
+ int write = iwb->write;
+ atomic_inc(&iwb->writesem);
+ return write;
+}
+
+/* append bits to buffer without any checks
+ * - data contains bits to append, starting at LSB
+ * - nbits is number of bits to append (0..24)
+ * must be called with the write semaphore held
+ * If more than nbits bits are set in data, the extraneous bits are set in the
+ * buffer too, but the write position is only advanced by nbits.
+ */
+static inline void isowbuf_putbits(struct isowbuf_t *iwb, u32 data, int nbits)
+{
+ int write = iwb->write;
+ data <<= iwb->wbits;
+ data |= iwb->data[write];
+ nbits += iwb->wbits;
+ while (nbits >= 8) {
+ iwb->data[write++] = data & 0xff;
+ write %= BAS_OUTBUFSIZE;
+ data >>= 8;
+ nbits -= 8;
+ }
+ iwb->wbits = nbits;
+ iwb->data[write] = data & 0xff;
+ iwb->write = write;
+}
+
+/* put final flag on HDLC bitstream
+ * also sets the idle fill byte to the correspondingly shifted flag pattern
+ * must be called with the write semaphore held
+ */
+static inline void isowbuf_putflag(struct isowbuf_t *iwb)
+{
+ int write;
+
+ /* add two flags, thus reliably covering one byte */
+ isowbuf_putbits(iwb, 0x7e7e, 8);
+ /* recover the idle flag byte */
+ write = iwb->write;
+ iwb->idle = iwb->data[write];
+ gig_dbg(DEBUG_ISO, "idle fill byte %02x", iwb->idle);
+ /* mask extraneous bits in buffer */
+ iwb->data[write] &= (1 << iwb->wbits) - 1;
+}
+
+/* retrieve a block of bytes for sending
+ * The requested number of bytes is provided as a contiguous block.
+ * If necessary, the frame is filled to the requested number of bytes
+ * with the idle value.
+ * returns offset to frame, < 0 on busy or error
+ */
+int gigaset_isowbuf_getbytes(struct isowbuf_t *iwb, int size)
+{
+ int read, write, limit, src, dst;
+ unsigned char pbyte;
+
+ read = iwb->nextread;
+ write = iwb->write;
+ if (likely(read == write)) {
+ /* return idle frame */
+ return read < BAS_OUTBUFPAD ?
+ BAS_OUTBUFSIZE : read - BAS_OUTBUFPAD;
+ }
+
+ limit = read + size;
+ gig_dbg(DEBUG_STREAM, "%s: read=%d write=%d limit=%d",
+ __func__, read, write, limit);
+#ifdef CONFIG_GIGASET_DEBUG
+ if (unlikely(size < 0 || size > BAS_OUTBUFPAD)) {
+ err("invalid size %d", size);
+ return -EINVAL;
+ }
+ src = iwb->read;
+ if (unlikely(limit > BAS_OUTBUFSIZE + BAS_OUTBUFPAD ||
+ (read < src && limit >= src))) {
+ err("isoc write buffer frame reservation violated");
+ return -EFAULT;
+ }
+#endif
+
+ if (read < write) {
+ /* no wraparound in valid data */
+ if (limit >= write) {
+ /* append idle frame */
+ if (!isowbuf_startwrite(iwb))
+ return -EBUSY;
+ /* write position could have changed */
+ write = iwb->write;
+ if (limit >= write) {
+ pbyte = iwb->data[write]; /* save
+ partial byte */
+ limit = write + BAS_OUTBUFPAD;
+ gig_dbg(DEBUG_STREAM,
+ "%s: filling %d->%d with %02x",
+ __func__, write, limit, iwb->idle);
+ if (write + BAS_OUTBUFPAD < BAS_OUTBUFSIZE)
+ memset(iwb->data + write, iwb->idle,
+ BAS_OUTBUFPAD);
+ else {
+ /* wraparound, fill entire pad area */
+ memset(iwb->data + write, iwb->idle,
+ BAS_OUTBUFSIZE + BAS_OUTBUFPAD
+ - write);
+ limit = 0;
+ }
+ gig_dbg(DEBUG_STREAM,
+ "%s: restoring %02x at %d",
+ __func__, pbyte, limit);
+ iwb->data[limit] = pbyte; /* restore
+ partial byte */
+ iwb->write = limit;
+ }
+ isowbuf_donewrite(iwb);
+ }
+ } else {
+ /* valid data wraparound */
+ if (limit >= BAS_OUTBUFSIZE) {
+ /* copy wrapped part into pad area */
+ src = 0;
+ dst = BAS_OUTBUFSIZE;
+ while (dst < limit && src < write)
+ iwb->data[dst++] = iwb->data[src++];
+ if (dst <= limit) {
+ /* fill pad area with idle byte */
+ memset(iwb->data + dst, iwb->idle,
+ BAS_OUTBUFSIZE + BAS_OUTBUFPAD - dst);
+ }
+ limit = src;
+ }
+ }
+ iwb->nextread = limit;
+ return read;
+}
+
+/* dump_bytes
+ * write hex bytes to syslog for debugging
+ */
+static inline void dump_bytes(enum debuglevel level, const char *tag,
+ unsigned char *bytes, int count)
+{
+#ifdef CONFIG_GIGASET_DEBUG
+ unsigned char c;
+ static char dbgline[3 * 32 + 1];
+ int i = 0;
+ while (count-- > 0) {
+ if (i > sizeof(dbgline) - 4) {
+ dbgline[i] = '\0';
+ gig_dbg(level, "%s:%s", tag, dbgline);
+ i = 0;
+ }
+ c = *bytes++;
+ dbgline[i] = (i && !(i % 12)) ? '-' : ' ';
+ i++;
+ dbgline[i++] = hex_asc_hi(c);
+ dbgline[i++] = hex_asc_lo(c);
+ }
+ dbgline[i] = '\0';
+ gig_dbg(level, "%s:%s", tag, dbgline);
+#endif
+}
+
+/*============================================================================*/
+
+/* bytewise HDLC bitstuffing via table lookup
+ * lookup table: 5 subtables for 0..4 preceding consecutive '1' bits
+ * index: 256*(number of preceding '1' bits) + (next byte to stuff)
+ * value: bit 9.. 0 = result bits
+ * bit 12..10 = number of trailing '1' bits in result
+ * bit 14..13 = number of bits added by stuffing
+ */
+static const u16 stufftab[5 * 256] = {
+// previous 1s = 0:
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x201f,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x203e, 0x205f,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x209f,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x207c, 0x207d, 0x20be, 0x20df,
+ 0x0480, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487, 0x0488, 0x0489, 0x048a, 0x048b, 0x048c, 0x048d, 0x048e, 0x048f,
+ 0x0490, 0x0491, 0x0492, 0x0493, 0x0494, 0x0495, 0x0496, 0x0497, 0x0498, 0x0499, 0x049a, 0x049b, 0x049c, 0x049d, 0x049e, 0x251f,
+ 0x04a0, 0x04a1, 0x04a2, 0x04a3, 0x04a4, 0x04a5, 0x04a6, 0x04a7, 0x04a8, 0x04a9, 0x04aa, 0x04ab, 0x04ac, 0x04ad, 0x04ae, 0x04af,
+ 0x04b0, 0x04b1, 0x04b2, 0x04b3, 0x04b4, 0x04b5, 0x04b6, 0x04b7, 0x04b8, 0x04b9, 0x04ba, 0x04bb, 0x04bc, 0x04bd, 0x253e, 0x255f,
+ 0x08c0, 0x08c1, 0x08c2, 0x08c3, 0x08c4, 0x08c5, 0x08c6, 0x08c7, 0x08c8, 0x08c9, 0x08ca, 0x08cb, 0x08cc, 0x08cd, 0x08ce, 0x08cf,
+ 0x08d0, 0x08d1, 0x08d2, 0x08d3, 0x08d4, 0x08d5, 0x08d6, 0x08d7, 0x08d8, 0x08d9, 0x08da, 0x08db, 0x08dc, 0x08dd, 0x08de, 0x299f,
+ 0x0ce0, 0x0ce1, 0x0ce2, 0x0ce3, 0x0ce4, 0x0ce5, 0x0ce6, 0x0ce7, 0x0ce8, 0x0ce9, 0x0cea, 0x0ceb, 0x0cec, 0x0ced, 0x0cee, 0x0cef,
+ 0x10f0, 0x10f1, 0x10f2, 0x10f3, 0x10f4, 0x10f5, 0x10f6, 0x10f7, 0x20f8, 0x20f9, 0x20fa, 0x20fb, 0x257c, 0x257d, 0x29be, 0x2ddf,
+
+// previous 1s = 1:
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x200f,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x202f,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x204f,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x203e, 0x206f,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x208f,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x20af,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x20cf,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x207c, 0x207d, 0x20be, 0x20ef,
+ 0x0480, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487, 0x0488, 0x0489, 0x048a, 0x048b, 0x048c, 0x048d, 0x048e, 0x250f,
+ 0x0490, 0x0491, 0x0492, 0x0493, 0x0494, 0x0495, 0x0496, 0x0497, 0x0498, 0x0499, 0x049a, 0x049b, 0x049c, 0x049d, 0x049e, 0x252f,
+ 0x04a0, 0x04a1, 0x04a2, 0x04a3, 0x04a4, 0x04a5, 0x04a6, 0x04a7, 0x04a8, 0x04a9, 0x04aa, 0x04ab, 0x04ac, 0x04ad, 0x04ae, 0x254f,
+ 0x04b0, 0x04b1, 0x04b2, 0x04b3, 0x04b4, 0x04b5, 0x04b6, 0x04b7, 0x04b8, 0x04b9, 0x04ba, 0x04bb, 0x04bc, 0x04bd, 0x253e, 0x256f,
+ 0x08c0, 0x08c1, 0x08c2, 0x08c3, 0x08c4, 0x08c5, 0x08c6, 0x08c7, 0x08c8, 0x08c9, 0x08ca, 0x08cb, 0x08cc, 0x08cd, 0x08ce, 0x298f,
+ 0x08d0, 0x08d1, 0x08d2, 0x08d3, 0x08d4, 0x08d5, 0x08d6, 0x08d7, 0x08d8, 0x08d9, 0x08da, 0x08db, 0x08dc, 0x08dd, 0x08de, 0x29af,
+ 0x0ce0, 0x0ce1, 0x0ce2, 0x0ce3, 0x0ce4, 0x0ce5, 0x0ce6, 0x0ce7, 0x0ce8, 0x0ce9, 0x0cea, 0x0ceb, 0x0cec, 0x0ced, 0x0cee, 0x2dcf,
+ 0x10f0, 0x10f1, 0x10f2, 0x10f3, 0x10f4, 0x10f5, 0x10f6, 0x10f7, 0x20f8, 0x20f9, 0x20fa, 0x20fb, 0x257c, 0x257d, 0x29be, 0x31ef,
+
+// previous 1s = 2:
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x2007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x2017,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x2027, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x2037,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x2047, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x2057,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x2067, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x203e, 0x2077,
+ 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x2087, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x2097,
+ 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x20a7, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x20b7,
+ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x20c7, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x20d7,
+ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x20e7, 0x0078, 0x0079, 0x007a, 0x007b, 0x207c, 0x207d, 0x20be, 0x20f7,
+ 0x0480, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x2507, 0x0488, 0x0489, 0x048a, 0x048b, 0x048c, 0x048d, 0x048e, 0x2517,
+ 0x0490, 0x0491, 0x0492, 0x0493, 0x0494, 0x0495, 0x0496, 0x2527, 0x0498, 0x0499, 0x049a, 0x049b, 0x049c, 0x049d, 0x049e, 0x2537,
+ 0x04a0, 0x04a1, 0x04a2, 0x04a3, 0x04a4, 0x04a5, 0x04a6, 0x2547, 0x04a8, 0x04a9, 0x04aa, 0x04ab, 0x04ac, 0x04ad, 0x04ae, 0x2557,
+ 0x04b0, 0x04b1, 0x04b2, 0x04b3, 0x04b4, 0x04b5, 0x04b6, 0x2567, 0x04b8, 0x04b9, 0x04ba, 0x04bb, 0x04bc, 0x04bd, 0x253e, 0x2577,
+ 0x08c0, 0x08c1, 0x08c2, 0x08c3, 0x08c4, 0x08c5, 0x08c6, 0x2987, 0x08c8, 0x08c9, 0x08ca, 0x08cb, 0x08cc, 0x08cd, 0x08ce, 0x2997,
+ 0x08d0, 0x08d1, 0x08d2, 0x08d3, 0x08d4, 0x08d5, 0x08d6, 0x29a7, 0x08d8, 0x08d9, 0x08da, 0x08db, 0x08dc, 0x08dd, 0x08de, 0x29b7,
+ 0x0ce0, 0x0ce1, 0x0ce2, 0x0ce3, 0x0ce4, 0x0ce5, 0x0ce6, 0x2dc7, 0x0ce8, 0x0ce9, 0x0cea, 0x0ceb, 0x0cec, 0x0ced, 0x0cee, 0x2dd7,
+ 0x10f0, 0x10f1, 0x10f2, 0x10f3, 0x10f4, 0x10f5, 0x10f6, 0x31e7, 0x20f8, 0x20f9, 0x20fa, 0x20fb, 0x257c, 0x257d, 0x29be, 0x41f7,
+
+// previous 1s = 3:
+ 0x0000, 0x0001, 0x0002, 0x2003, 0x0004, 0x0005, 0x0006, 0x200b, 0x0008, 0x0009, 0x000a, 0x2013, 0x000c, 0x000d, 0x000e, 0x201b,
+ 0x0010, 0x0011, 0x0012, 0x2023, 0x0014, 0x0015, 0x0016, 0x202b, 0x0018, 0x0019, 0x001a, 0x2033, 0x001c, 0x001d, 0x001e, 0x203b,
+ 0x0020, 0x0021, 0x0022, 0x2043, 0x0024, 0x0025, 0x0026, 0x204b, 0x0028, 0x0029, 0x002a, 0x2053, 0x002c, 0x002d, 0x002e, 0x205b,
+ 0x0030, 0x0031, 0x0032, 0x2063, 0x0034, 0x0035, 0x0036, 0x206b, 0x0038, 0x0039, 0x003a, 0x2073, 0x003c, 0x003d, 0x203e, 0x207b,
+ 0x0040, 0x0041, 0x0042, 0x2083, 0x0044, 0x0045, 0x0046, 0x208b, 0x0048, 0x0049, 0x004a, 0x2093, 0x004c, 0x004d, 0x004e, 0x209b,
+ 0x0050, 0x0051, 0x0052, 0x20a3, 0x0054, 0x0055, 0x0056, 0x20ab, 0x0058, 0x0059, 0x005a, 0x20b3, 0x005c, 0x005d, 0x005e, 0x20bb,
+ 0x0060, 0x0061, 0x0062, 0x20c3, 0x0064, 0x0065, 0x0066, 0x20cb, 0x0068, 0x0069, 0x006a, 0x20d3, 0x006c, 0x006d, 0x006e, 0x20db,
+ 0x0070, 0x0071, 0x0072, 0x20e3, 0x0074, 0x0075, 0x0076, 0x20eb, 0x0078, 0x0079, 0x007a, 0x20f3, 0x207c, 0x207d, 0x20be, 0x40fb,
+ 0x0480, 0x0481, 0x0482, 0x2503, 0x0484, 0x0485, 0x0486, 0x250b, 0x0488, 0x0489, 0x048a, 0x2513, 0x048c, 0x048d, 0x048e, 0x251b,
+ 0x0490, 0x0491, 0x0492, 0x2523, 0x0494, 0x0495, 0x0496, 0x252b, 0x0498, 0x0499, 0x049a, 0x2533, 0x049c, 0x049d, 0x049e, 0x253b,
+ 0x04a0, 0x04a1, 0x04a2, 0x2543, 0x04a4, 0x04a5, 0x04a6, 0x254b, 0x04a8, 0x04a9, 0x04aa, 0x2553, 0x04ac, 0x04ad, 0x04ae, 0x255b,
+ 0x04b0, 0x04b1, 0x04b2, 0x2563, 0x04b4, 0x04b5, 0x04b6, 0x256b, 0x04b8, 0x04b9, 0x04ba, 0x2573, 0x04bc, 0x04bd, 0x253e, 0x257b,
+ 0x08c0, 0x08c1, 0x08c2, 0x2983, 0x08c4, 0x08c5, 0x08c6, 0x298b, 0x08c8, 0x08c9, 0x08ca, 0x2993, 0x08cc, 0x08cd, 0x08ce, 0x299b,
+ 0x08d0, 0x08d1, 0x08d2, 0x29a3, 0x08d4, 0x08d5, 0x08d6, 0x29ab, 0x08d8, 0x08d9, 0x08da, 0x29b3, 0x08dc, 0x08dd, 0x08de, 0x29bb,
+ 0x0ce0, 0x0ce1, 0x0ce2, 0x2dc3, 0x0ce4, 0x0ce5, 0x0ce6, 0x2dcb, 0x0ce8, 0x0ce9, 0x0cea, 0x2dd3, 0x0cec, 0x0ced, 0x0cee, 0x2ddb,
+ 0x10f0, 0x10f1, 0x10f2, 0x31e3, 0x10f4, 0x10f5, 0x10f6, 0x31eb, 0x20f8, 0x20f9, 0x20fa, 0x41f3, 0x257c, 0x257d, 0x29be, 0x46fb,
+
+// previous 1s = 4:
+ 0x0000, 0x2001, 0x0002, 0x2005, 0x0004, 0x2009, 0x0006, 0x200d, 0x0008, 0x2011, 0x000a, 0x2015, 0x000c, 0x2019, 0x000e, 0x201d,
+ 0x0010, 0x2021, 0x0012, 0x2025, 0x0014, 0x2029, 0x0016, 0x202d, 0x0018, 0x2031, 0x001a, 0x2035, 0x001c, 0x2039, 0x001e, 0x203d,
+ 0x0020, 0x2041, 0x0022, 0x2045, 0x0024, 0x2049, 0x0026, 0x204d, 0x0028, 0x2051, 0x002a, 0x2055, 0x002c, 0x2059, 0x002e, 0x205d,
+ 0x0030, 0x2061, 0x0032, 0x2065, 0x0034, 0x2069, 0x0036, 0x206d, 0x0038, 0x2071, 0x003a, 0x2075, 0x003c, 0x2079, 0x203e, 0x407d,
+ 0x0040, 0x2081, 0x0042, 0x2085, 0x0044, 0x2089, 0x0046, 0x208d, 0x0048, 0x2091, 0x004a, 0x2095, 0x004c, 0x2099, 0x004e, 0x209d,
+ 0x0050, 0x20a1, 0x0052, 0x20a5, 0x0054, 0x20a9, 0x0056, 0x20ad, 0x0058, 0x20b1, 0x005a, 0x20b5, 0x005c, 0x20b9, 0x005e, 0x20bd,
+ 0x0060, 0x20c1, 0x0062, 0x20c5, 0x0064, 0x20c9, 0x0066, 0x20cd, 0x0068, 0x20d1, 0x006a, 0x20d5, 0x006c, 0x20d9, 0x006e, 0x20dd,
+ 0x0070, 0x20e1, 0x0072, 0x20e5, 0x0074, 0x20e9, 0x0076, 0x20ed, 0x0078, 0x20f1, 0x007a, 0x20f5, 0x207c, 0x40f9, 0x20be, 0x417d,
+ 0x0480, 0x2501, 0x0482, 0x2505, 0x0484, 0x2509, 0x0486, 0x250d, 0x0488, 0x2511, 0x048a, 0x2515, 0x048c, 0x2519, 0x048e, 0x251d,
+ 0x0490, 0x2521, 0x0492, 0x2525, 0x0494, 0x2529, 0x0496, 0x252d, 0x0498, 0x2531, 0x049a, 0x2535, 0x049c, 0x2539, 0x049e, 0x253d,
+ 0x04a0, 0x2541, 0x04a2, 0x2545, 0x04a4, 0x2549, 0x04a6, 0x254d, 0x04a8, 0x2551, 0x04aa, 0x2555, 0x04ac, 0x2559, 0x04ae, 0x255d,
+ 0x04b0, 0x2561, 0x04b2, 0x2565, 0x04b4, 0x2569, 0x04b6, 0x256d, 0x04b8, 0x2571, 0x04ba, 0x2575, 0x04bc, 0x2579, 0x253e, 0x467d,
+ 0x08c0, 0x2981, 0x08c2, 0x2985, 0x08c4, 0x2989, 0x08c6, 0x298d, 0x08c8, 0x2991, 0x08ca, 0x2995, 0x08cc, 0x2999, 0x08ce, 0x299d,
+ 0x08d0, 0x29a1, 0x08d2, 0x29a5, 0x08d4, 0x29a9, 0x08d6, 0x29ad, 0x08d8, 0x29b1, 0x08da, 0x29b5, 0x08dc, 0x29b9, 0x08de, 0x29bd,
+ 0x0ce0, 0x2dc1, 0x0ce2, 0x2dc5, 0x0ce4, 0x2dc9, 0x0ce6, 0x2dcd, 0x0ce8, 0x2dd1, 0x0cea, 0x2dd5, 0x0cec, 0x2dd9, 0x0cee, 0x2ddd,
+ 0x10f0, 0x31e1, 0x10f2, 0x31e5, 0x10f4, 0x31e9, 0x10f6, 0x31ed, 0x20f8, 0x41f1, 0x20fa, 0x41f5, 0x257c, 0x46f9, 0x29be, 0x4b7d
+};
+
+/* hdlc_bitstuff_byte
+ * perform HDLC bitstuffing for one input byte (8 bits, LSB first)
+ * parameters:
+ * cin input byte
+ * ones number of trailing '1' bits in result before this step
+ * iwb pointer to output buffer structure (write semaphore must be held)
+ * return value:
+ * number of trailing '1' bits in result after this step
+ */
+
+static inline int hdlc_bitstuff_byte(struct isowbuf_t *iwb, unsigned char cin,
+ int ones)
+{
+ u16 stuff;
+ int shiftinc, newones;
+
+ /* get stuffing information for input byte
+ * value: bit 9.. 0 = result bits
+ * bit 12..10 = number of trailing '1' bits in result
+ * bit 14..13 = number of bits added by stuffing
+ */
+ stuff = stufftab[256 * ones + cin];
+ shiftinc = (stuff >> 13) & 3;
+ newones = (stuff >> 10) & 7;
+ stuff &= 0x3ff;
+
+ /* append stuffed byte to output stream */
+ isowbuf_putbits(iwb, stuff, 8 + shiftinc);
+ return newones;
+}
+
+/* hdlc_buildframe
+ * Perform HDLC framing with bitstuffing on a byte buffer
+ * The input buffer is regarded as a sequence of bits, starting with the least
+ * significant bit of the first byte and ending with the most significant bit
+ * of the last byte. A 16 bit FCS is appended as defined by RFC 1662.
+ * Whenever five consecutive '1' bits appear in the resulting bit sequence, a
+ * '0' bit is inserted after them.
+ * The resulting bit string and a closing flag pattern (PPP_FLAG, '01111110')
+ * are appended to the output buffer starting at the given bit position, which
+ * is assumed to already contain a leading flag.
+ * The output buffer must have sufficient length; count + count/5 + 6 bytes
+ * starting at *out are safe and are verified to be present.
+ * parameters:
+ * in input buffer
+ * count number of bytes in input buffer
+ * iwb pointer to output buffer structure (write semaphore must be held)
+ * return value:
+ * position of end of packet in output buffer on success,
+ * -EAGAIN if write semaphore busy or buffer full
+ */
+
+static inline int hdlc_buildframe(struct isowbuf_t *iwb,
+ unsigned char *in, int count)
+{
+ int ones;
+ u16 fcs;
+ int end;
+ unsigned char c;
+
+ if (isowbuf_freebytes(iwb) < count + count / 5 + 6 ||
+ !isowbuf_startwrite(iwb)) {
+ gig_dbg(DEBUG_ISO, "%s: %d bytes free -> -EAGAIN",
+ __func__, isowbuf_freebytes(iwb));
+ return -EAGAIN;
+ }
+
+ dump_bytes(DEBUG_STREAM, "snd data", in, count);
+
+ /* bitstuff and checksum input data */
+ fcs = PPP_INITFCS;
+ ones = 0;
+ while (count-- > 0) {
+ c = *in++;
+ ones = hdlc_bitstuff_byte(iwb, c, ones);
+ fcs = crc_ccitt_byte(fcs, c);
+ }
+
+ /* bitstuff and append FCS (complemented, least significant byte first) */
+ fcs ^= 0xffff;
+ ones = hdlc_bitstuff_byte(iwb, fcs & 0x00ff, ones);
+ ones = hdlc_bitstuff_byte(iwb, (fcs >> 8) & 0x00ff, ones);
+
+ /* put closing flag and repeat byte for flag idle */
+ isowbuf_putflag(iwb);
+ end = isowbuf_donewrite(iwb);
+ dump_bytes(DEBUG_STREAM_DUMP, "isowbuf", iwb->data, end + 1);
+ return end;
+}
+
+/* trans_buildframe
+ * Append a block of 'transparent' data to the output buffer,
+ * inverting the bytes.
+ * The output buffer must have sufficient length; count bytes
+ * starting at *out are safe and are verified to be present.
+ * parameters:
+ * in input buffer
+ * count number of bytes in input buffer
+ * iwb pointer to output buffer structure (write semaphore must be held)
+ * return value:
+ * position of end of packet in output buffer on success,
+ * -EAGAIN if write semaphore busy or buffer full
+ */
+
+static inline int trans_buildframe(struct isowbuf_t *iwb,
+ unsigned char *in, int count)
+{
+ int write;
+ unsigned char c;
+
+ if (unlikely(count <= 0))
+ return iwb->write;
+
+ if (isowbuf_freebytes(iwb) < count ||
+ !isowbuf_startwrite(iwb)) {
+ gig_dbg(DEBUG_ISO, "can't put %d bytes", count);
+ return -EAGAIN;
+ }
+
+ gig_dbg(DEBUG_STREAM, "put %d bytes", count);
+ write = iwb->write;
+ do {
+ c = bitrev8(*in++);
+ iwb->data[write++] = c;
+ write %= BAS_OUTBUFSIZE;
+ } while (--count > 0);
+ iwb->write = write;
+ iwb->idle = c;
+
+ return isowbuf_donewrite(iwb);
+}
+
+int gigaset_isoc_buildframe(struct bc_state *bcs, unsigned char *in, int len)
+{
+ int result;
+
+ switch (bcs->proto2) {
+ case ISDN_PROTO_L2_HDLC:
+ result = hdlc_buildframe(bcs->hw.bas->isooutbuf, in, len);
+ gig_dbg(DEBUG_ISO, "%s: %d bytes HDLC -> %d",
+ __func__, len, result);
+ break;
+ default: /* assume transparent */
+ result = trans_buildframe(bcs->hw.bas->isooutbuf, in, len);
+ gig_dbg(DEBUG_ISO, "%s: %d bytes trans -> %d",
+ __func__, len, result);
+ }
+ return result;
+}
+
+/* hdlc_putbyte
+ * append byte c to current skb of B channel structure *bcs, updating fcs
+ */
+static inline void hdlc_putbyte(unsigned char c, struct bc_state *bcs)
+{
+ bcs->fcs = crc_ccitt_byte(bcs->fcs, c);
+ if (unlikely(bcs->skb == NULL)) {
+ /* skipping */
+ return;
+ }
+ if (unlikely(bcs->skb->len == SBUFSIZE)) {
+ dev_warn(bcs->cs->dev, "received oversized packet discarded\n");
+ bcs->hw.bas->giants++;
+ dev_kfree_skb_any(bcs->skb);
+ bcs->skb = NULL;
+ return;
+ }
+ *__skb_put(bcs->skb, 1) = c;
+}
+
+/* hdlc_flush
+ * drop partial HDLC data packet
+ */
+static inline void hdlc_flush(struct bc_state *bcs)
+{
+ /* clear skb or allocate new if not skipping */
+ if (likely(bcs->skb != NULL))
+ skb_trim(bcs->skb, 0);
+ else if (!bcs->ignore) {
+ if ((bcs->skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL)
+ skb_reserve(bcs->skb, HW_HDR_LEN);
+ else
+ dev_err(bcs->cs->dev, "could not allocate skb\n");
+ }
+
+ /* reset packet state */
+ bcs->fcs = PPP_INITFCS;
+}
+
+/* hdlc_done
+ * process completed HDLC data packet
+ */
+static inline void hdlc_done(struct bc_state *bcs)
+{
+ struct sk_buff *procskb;
+
+ if (unlikely(bcs->ignore)) {
+ bcs->ignore--;
+ hdlc_flush(bcs);
+ return;
+ }
+
+ if ((procskb = bcs->skb) == NULL) {
+ /* previous error */
+ gig_dbg(DEBUG_ISO, "%s: skb=NULL", __func__);
+ gigaset_rcv_error(NULL, bcs->cs, bcs);
+ } else if (procskb->len < 2) {
+ dev_notice(bcs->cs->dev, "received short frame (%d octets)\n",
+ procskb->len);
+ bcs->hw.bas->runts++;
+ gigaset_rcv_error(procskb, bcs->cs, bcs);
+ } else if (bcs->fcs != PPP_GOODFCS) {
+ dev_notice(bcs->cs->dev, "frame check error (0x%04x)\n",
+ bcs->fcs);
+ bcs->hw.bas->fcserrs++;
+ gigaset_rcv_error(procskb, bcs->cs, bcs);
+ } else {
+ procskb->len -= 2; /* subtract FCS */
+ procskb->tail -= 2;
+ gig_dbg(DEBUG_ISO, "%s: good frame (%d octets)",
+ __func__, procskb->len);
+ dump_bytes(DEBUG_STREAM,
+ "rcv data", procskb->data, procskb->len);
+ bcs->hw.bas->goodbytes += procskb->len;
+ gigaset_rcv_skb(procskb, bcs->cs, bcs);
+ }
+
+ if ((bcs->skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL)
+ skb_reserve(bcs->skb, HW_HDR_LEN);
+ else
+ dev_err(bcs->cs->dev, "could not allocate skb\n");
+ bcs->fcs = PPP_INITFCS;
+}
+
+/* hdlc_frag
+ * drop HDLC data packet with non-integral last byte
+ */
+static inline void hdlc_frag(struct bc_state *bcs, unsigned inbits)
+{
+ if (unlikely(bcs->ignore)) {
+ bcs->ignore--;
+ hdlc_flush(bcs);
+ return;
+ }
+
+ dev_notice(bcs->cs->dev, "received partial byte (%d bits)\n", inbits);
+ bcs->hw.bas->alignerrs++;
+ gigaset_rcv_error(bcs->skb, bcs->cs, bcs);
+
+ if ((bcs->skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN)) != NULL)
+ skb_reserve(bcs->skb, HW_HDR_LEN);
+ else
+ dev_err(bcs->cs->dev, "could not allocate skb\n");
+ bcs->fcs = PPP_INITFCS;
+}
+
+/* bit counts lookup table for HDLC bit unstuffing
+ * index: input byte
+ * value: bit 0..3 = number of consecutive '1' bits starting from LSB
+ * bit 4..6 = number of consecutive '1' bits starting from MSB
+ * (replacing 8 by 7 to make it fit; the algorithm won't care)
+ * bit 7 set if there are 5 or more "interior" consecutive '1' bits
+ */
+static const unsigned char bitcounts[256] = {
+ 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x04,
+ 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x05,
+ 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x04,
+ 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x80, 0x06,
+ 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x04,
+ 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x05,
+ 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x04,
+ 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x80, 0x81, 0x80, 0x07,
+ 0x10, 0x11, 0x10, 0x12, 0x10, 0x11, 0x10, 0x13, 0x10, 0x11, 0x10, 0x12, 0x10, 0x11, 0x10, 0x14,
+ 0x10, 0x11, 0x10, 0x12, 0x10, 0x11, 0x10, 0x13, 0x10, 0x11, 0x10, 0x12, 0x10, 0x11, 0x10, 0x15,
+ 0x10, 0x11, 0x10, 0x12, 0x10, 0x11, 0x10, 0x13, 0x10, 0x11, 0x10, 0x12, 0x10, 0x11, 0x10, 0x14,
+ 0x10, 0x11, 0x10, 0x12, 0x10, 0x11, 0x10, 0x13, 0x10, 0x11, 0x10, 0x12, 0x10, 0x11, 0x90, 0x16,
+ 0x20, 0x21, 0x20, 0x22, 0x20, 0x21, 0x20, 0x23, 0x20, 0x21, 0x20, 0x22, 0x20, 0x21, 0x20, 0x24,
+ 0x20, 0x21, 0x20, 0x22, 0x20, 0x21, 0x20, 0x23, 0x20, 0x21, 0x20, 0x22, 0x20, 0x21, 0x20, 0x25,
+ 0x30, 0x31, 0x30, 0x32, 0x30, 0x31, 0x30, 0x33, 0x30, 0x31, 0x30, 0x32, 0x30, 0x31, 0x30, 0x34,
+ 0x40, 0x41, 0x40, 0x42, 0x40, 0x41, 0x40, 0x43, 0x50, 0x51, 0x50, 0x52, 0x60, 0x61, 0x70, 0x78
+};
+
+/* hdlc_unpack
+ * perform HDLC frame processing (bit unstuffing, flag detection, FCS calculation)
+ * on a sequence of received data bytes (8 bits each, LSB first)
+ * pass on successfully received, complete frames as SKBs via gigaset_rcv_skb
+ * notify of errors via gigaset_rcv_error
+ * tally frames, errors etc. in BC structure counters
+ * parameters:
+ * src received data
+ * count number of received bytes
+ * bcs receiving B channel structure
+ */
+static inline void hdlc_unpack(unsigned char *src, unsigned count,
+ struct bc_state *bcs)
+{
+ struct bas_bc_state *ubc = bcs->hw.bas;
+ int inputstate;
+ unsigned seqlen, inbyte, inbits;
+
+ /* load previous state:
+ * inputstate = set of flag bits:
+ * - INS_flag_hunt: no complete opening flag received since connection setup or last abort
+ * - INS_have_data: at least one complete data byte received since last flag
+ * seqlen = number of consecutive '1' bits in last 7 input stream bits (0..7)
+ * inbyte = accumulated partial data byte (if !INS_flag_hunt)
+ * inbits = number of valid bits in inbyte, starting at LSB (0..6)
+ */
+ inputstate = bcs->inputstate;
+ seqlen = ubc->seqlen;
+ inbyte = ubc->inbyte;
+ inbits = ubc->inbits;
+
+ /* bit unstuffing a byte a time
+ * Take your time to understand this; it's straightforward but tedious.
+ * The "bitcounts" lookup table is used to speed up the counting of
+ * leading and trailing '1' bits.
+ */
+ while (count--) {
+ unsigned char c = *src++;
+ unsigned char tabentry = bitcounts[c];
+ unsigned lead1 = tabentry & 0x0f;
+ unsigned trail1 = (tabentry >> 4) & 0x0f;
+
+ seqlen += lead1;
+
+ if (unlikely(inputstate & INS_flag_hunt)) {
+ if (c == PPP_FLAG) {
+ /* flag-in-one */
+ inputstate &= ~(INS_flag_hunt | INS_have_data);
+ inbyte = 0;
+ inbits = 0;
+ } else if (seqlen == 6 && trail1 != 7) {
+ /* flag completed & not followed by abort */
+ inputstate &= ~(INS_flag_hunt | INS_have_data);
+ inbyte = c >> (lead1 + 1);
+ inbits = 7 - lead1;
+ if (trail1 >= 8) {
+ /* interior stuffing: omitting the MSB handles most cases */
+ inbits--;
+ /* correct the incorrectly handled cases individually */
+ switch (c) {
+ case 0xbe:
+ inbyte = 0x3f;
+ break;
+ }
+ }
+ }
+ /* else: continue flag-hunting */
+ } else if (likely(seqlen < 5 && trail1 < 7)) {
+ /* streamlined case: 8 data bits, no stuffing */
+ inbyte |= c << inbits;
+ hdlc_putbyte(inbyte & 0xff, bcs);
+ inputstate |= INS_have_data;
+ inbyte >>= 8;
+ /* inbits unchanged */
+ } else if (likely(seqlen == 6 && inbits == 7 - lead1 &&
+ trail1 + 1 == inbits &&
+ !(inputstate & INS_have_data))) {
+ /* streamlined case: flag idle - state unchanged */
+ } else if (unlikely(seqlen > 6)) {
+ /* abort sequence */
+ ubc->aborts++;
+ hdlc_flush(bcs);
+ inputstate |= INS_flag_hunt;
+ } else if (seqlen == 6) {
+ /* closing flag, including (6 - lead1) '1's and one '0' from inbits */
+ if (inbits > 7 - lead1) {
+ hdlc_frag(bcs, inbits + lead1 - 7);
+ inputstate &= ~INS_have_data;
+ } else {
+ if (inbits < 7 - lead1)
+ ubc->stolen0s ++;
+ if (inputstate & INS_have_data) {
+ hdlc_done(bcs);
+ inputstate &= ~INS_have_data;
+ }
+ }
+
+ if (c == PPP_FLAG) {
+ /* complete flag, LSB overlaps preceding flag */
+ ubc->shared0s ++;
+ inbits = 0;
+ inbyte = 0;
+ } else if (trail1 != 7) {
+ /* remaining bits */
+ inbyte = c >> (lead1 + 1);
+ inbits = 7 - lead1;
+ if (trail1 >= 8) {
+ /* interior stuffing: omitting the MSB handles most cases */
+ inbits--;
+ /* correct the incorrectly handled cases individually */
+ switch (c) {
+ case 0xbe:
+ inbyte = 0x3f;
+ break;
+ }
+ }
+ } else {
+ /* abort sequence follows, skb already empty anyway */
+ ubc->aborts++;
+ inputstate |= INS_flag_hunt;
+ }
+ } else { /* (seqlen < 6) && (seqlen == 5 || trail1 >= 7) */
+
+ if (c == PPP_FLAG) {
+ /* complete flag */
+ if (seqlen == 5)
+ ubc->stolen0s++;
+ if (inbits) {
+ hdlc_frag(bcs, inbits);
+ inbits = 0;
+ inbyte = 0;
+ } else if (inputstate & INS_have_data)
+ hdlc_done(bcs);
+ inputstate &= ~INS_have_data;
+ } else if (trail1 == 7) {
+ /* abort sequence */
+ ubc->aborts++;
+ hdlc_flush(bcs);
+ inputstate |= INS_flag_hunt;
+ } else {
+ /* stuffed data */
+ if (trail1 < 7) { /* => seqlen == 5 */
+ /* stuff bit at position lead1, no interior stuffing */
+ unsigned char mask = (1 << lead1) - 1;
+ c = (c & mask) | ((c & ~mask) >> 1);
+ inbyte |= c << inbits;
+ inbits += 7;
+ } else if (seqlen < 5) { /* trail1 >= 8 */
+ /* interior stuffing: omitting the MSB handles most cases */
+ /* correct the incorrectly handled cases individually */
+ switch (c) {
+ case 0xbe:
+ c = 0x7e;
+ break;
+ }
+ inbyte |= c << inbits;
+ inbits += 7;
+ } else { /* seqlen == 5 && trail1 >= 8 */
+
+ /* stuff bit at lead1 *and* interior stuffing */
+ switch (c) { /* unstuff individually */
+ case 0x7d:
+ c = 0x3f;
+ break;
+ case 0xbe:
+ c = 0x3f;
+ break;
+ case 0x3e:
+ c = 0x1f;
+ break;
+ case 0x7c:
+ c = 0x3e;
+ break;
+ }
+ inbyte |= c << inbits;
+ inbits += 6;
+ }
+ if (inbits >= 8) {
+ inbits -= 8;
+ hdlc_putbyte(inbyte & 0xff, bcs);
+ inputstate |= INS_have_data;
+ inbyte >>= 8;
+ }
+ }
+ }
+ seqlen = trail1 & 7;
+ }
+
+ /* save new state */
+ bcs->inputstate = inputstate;
+ ubc->seqlen = seqlen;
+ ubc->inbyte = inbyte;
+ ubc->inbits = inbits;
+}
+
+/* trans_receive
+ * pass on received USB frame transparently as SKB via gigaset_rcv_skb
+ * invert bytes
+ * tally frames, errors etc. in BC structure counters
+ * parameters:
+ * src received data
+ * count number of received bytes
+ * bcs receiving B channel structure
+ */
+static inline void trans_receive(unsigned char *src, unsigned count,
+ struct bc_state *bcs)
+{
+ struct sk_buff *skb;
+ int dobytes;
+ unsigned char *dst;
+
+ if (unlikely(bcs->ignore)) {
+ bcs->ignore--;
+ hdlc_flush(bcs);
+ return;
+ }
+ if (unlikely((skb = bcs->skb) == NULL)) {
+ bcs->skb = skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN);
+ if (!skb) {
+ dev_err(bcs->cs->dev, "could not allocate skb\n");
+ return;
+ }
+ skb_reserve(skb, HW_HDR_LEN);
+ }
+ bcs->hw.bas->goodbytes += skb->len;
+ dobytes = TRANSBUFSIZE - skb->len;
+ while (count > 0) {
+ dst = skb_put(skb, count < dobytes ? count : dobytes);
+ while (count > 0 && dobytes > 0) {
+ *dst++ = bitrev8(*src++);
+ count--;
+ dobytes--;
+ }
+ if (dobytes == 0) {
+ gigaset_rcv_skb(skb, bcs->cs, bcs);
+ bcs->skb = skb = dev_alloc_skb(SBUFSIZE + HW_HDR_LEN);
+ if (!skb) {
+ dev_err(bcs->cs->dev,
+ "could not allocate skb\n");
+ return;
+ }
+ skb_reserve(bcs->skb, HW_HDR_LEN);
+ dobytes = TRANSBUFSIZE;
+ }
+ }
+}
+
+void gigaset_isoc_receive(unsigned char *src, unsigned count, struct bc_state *bcs)
+{
+ switch (bcs->proto2) {
+ case ISDN_PROTO_L2_HDLC:
+ hdlc_unpack(src, count, bcs);
+ break;
+ default: /* assume transparent */
+ trans_receive(src, count, bcs);
+ }
+}
+
+/* == data input =========================================================== */
+
+static void cmd_loop(unsigned char *src, int numbytes, struct inbuf_t *inbuf)
+{
+ struct cardstate *cs = inbuf->cs;
+ unsigned cbytes = cs->cbytes;
+
+ while (numbytes--) {
+ /* copy next character, check for end of line */
+ switch (cs->respdata[cbytes] = *src++) {
+ case '\r':
+ case '\n':
+ /* end of line */
+ gig_dbg(DEBUG_TRANSCMD, "%s: End of Command (%d Bytes)",
+ __func__, cbytes);
+ if (cbytes >= MAX_RESP_SIZE - 1)
+ dev_warn(cs->dev, "response too large\n");
+ cs->cbytes = cbytes;
+ gigaset_handle_modem_response(cs);
+ cbytes = 0;
+ break;
+ default:
+ /* advance in line buffer, checking for overflow */
+ if (cbytes < MAX_RESP_SIZE - 1)
+ cbytes++;
+ }
+ }
+
+ /* save state */
+ cs->cbytes = cbytes;
+}
+
+
+/* process a block of data received through the control channel
+ */
+void gigaset_isoc_input(struct inbuf_t *inbuf)
+{
+ struct cardstate *cs = inbuf->cs;
+ unsigned tail, head, numbytes;
+ unsigned char *src;
+
+ head = inbuf->head;
+ while (head != (tail = inbuf->tail)) {
+ gig_dbg(DEBUG_INTR, "buffer state: %u -> %u", head, tail);
+ if (head > tail)
+ tail = RBUFSIZE;
+ src = inbuf->data + head;
+ numbytes = tail - head;
+ gig_dbg(DEBUG_INTR, "processing %u bytes", numbytes);
+
+ if (cs->mstate == MS_LOCKED) {
+ gigaset_dbg_buffer(DEBUG_LOCKCMD, "received response",
+ numbytes, src);
+ gigaset_if_receive(inbuf->cs, src, numbytes);
+ } else {
+ gigaset_dbg_buffer(DEBUG_CMD, "received response",
+ numbytes, src);
+ cmd_loop(src, numbytes, inbuf);
+ }
+
+ head += numbytes;
+ if (head == RBUFSIZE)
+ head = 0;
+ gig_dbg(DEBUG_INTR, "setting head to %u", head);
+ inbuf->head = head;
+ }
+}
+
+
+/* == data output ========================================================== */
+
+/* gigaset_send_skb
+ * called by common.c to queue an skb for sending
+ * and start transmission if necessary
+ * parameters:
+ * B Channel control structure
+ * skb
+ * return value:
+ * number of bytes accepted for sending
+ * (skb->len if ok, 0 if out of buffer space)
+ * or error code (< 0, eg. -EINVAL)
+ */
+int gigaset_isoc_send_skb(struct bc_state *bcs, struct sk_buff *skb)
+{
+ int len = skb->len;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bcs->cs->lock, flags);
+ if (!bcs->cs->connected) {
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
+ return -ENODEV;
+ }
+
+ skb_queue_tail(&bcs->squeue, skb);
+ gig_dbg(DEBUG_ISO, "%s: skb queued, qlen=%d",
+ __func__, skb_queue_len(&bcs->squeue));
+
+ /* tasklet submits URB if necessary */
+ tasklet_schedule(&bcs->hw.bas->sent_tasklet);
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
+
+ return len; /* ok so far */
+}
diff --git a/drivers/isdn/gigaset/proc.c b/drivers/isdn/gigaset/proc.c
new file mode 100644
index 0000000..da6f3ac
--- /dev/null
+++ b/drivers/isdn/gigaset/proc.c
@@ -0,0 +1,83 @@
+/*
+ * Stuff used by all variants of the driver
+ *
+ * Copyright (c) 2001 by Stefan Eilers,
+ * Hansjoerg Lipp <hjlipp@web.de>,
+ * Tilman Schmidt <tilman@imap.cc>.
+ *
+ * =====================================================================
+ * 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 of
+ * the License, or (at your option) any later version.
+ * =====================================================================
+ */
+
+#include "gigaset.h"
+#include <linux/ctype.h>
+
+static ssize_t show_cidmode(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cardstate *cs = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", cs->cidmode);
+}
+
+static ssize_t set_cidmode(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cardstate *cs = dev_get_drvdata(dev);
+ long int value;
+ char *end;
+
+ value = simple_strtol(buf, &end, 0);
+ while (*end)
+ if (!isspace(*end++))
+ return -EINVAL;
+ if (value < 0 || value > 1)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&cs->mutex))
+ return -ERESTARTSYS; // FIXME -EINTR?
+
+ cs->waiting = 1;
+ if (!gigaset_add_event(cs, &cs->at_state, EV_PROC_CIDMODE,
+ NULL, value, NULL)) {
+ cs->waiting = 0;
+ mutex_unlock(&cs->mutex);
+ return -ENOMEM;
+ }
+
+ gig_dbg(DEBUG_CMD, "scheduling PROC_CIDMODE");
+ gigaset_schedule_event(cs);
+
+ wait_event(cs->waitqueue, !cs->waiting);
+
+ mutex_unlock(&cs->mutex);
+
+ return count;
+}
+
+static DEVICE_ATTR(cidmode, S_IRUGO|S_IWUSR, show_cidmode, set_cidmode);
+
+/* free sysfs for device */
+void gigaset_free_dev_sysfs(struct cardstate *cs)
+{
+ if (!cs->tty_dev)
+ return;
+
+ gig_dbg(DEBUG_INIT, "removing sysfs entries");
+ device_remove_file(cs->tty_dev, &dev_attr_cidmode);
+}
+
+/* initialize sysfs for device */
+void gigaset_init_dev_sysfs(struct cardstate *cs)
+{
+ if (!cs->tty_dev)
+ return;
+
+ gig_dbg(DEBUG_INIT, "setting up sysfs");
+ if (device_create_file(cs->tty_dev, &dev_attr_cidmode))
+ dev_err(cs->dev, "could not create sysfs attribute\n");
+}
diff --git a/drivers/isdn/gigaset/ser-gigaset.c b/drivers/isdn/gigaset/ser-gigaset.c
new file mode 100644
index 0000000..07052ed
--- /dev/null
+++ b/drivers/isdn/gigaset/ser-gigaset.c
@@ -0,0 +1,835 @@
+/* This is the serial hardware link layer (HLL) for the Gigaset 307x isdn
+ * DECT base (aka Sinus 45 isdn) using the RS232 DECT data module M101,
+ * written as a line discipline.
+ *
+ * =====================================================================
+ * 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 of
+ * the License, or (at your option) any later version.
+ * =====================================================================
+ */
+
+#include "gigaset.h"
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/tty.h>
+#include <linux/poll.h>
+#include <linux/completion.h>
+
+/* Version Information */
+#define DRIVER_AUTHOR "Tilman Schmidt"
+#define DRIVER_DESC "Serial Driver for Gigaset 307x using Siemens M101"
+
+#define GIGASET_MINORS 1
+#define GIGASET_MINOR 0
+#define GIGASET_MODULENAME "ser_gigaset"
+#define GIGASET_DEVNAME "ttyGS"
+
+/* length limit according to Siemens 3070usb-protokoll.doc ch. 2.1 */
+#define IF_WRITEBUF 264
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_LDISC(N_GIGASET_M101);
+
+static int startmode = SM_ISDN;
+module_param(startmode, int, S_IRUGO);
+MODULE_PARM_DESC(startmode, "initial operation mode");
+static int cidmode = 1;
+module_param(cidmode, int, S_IRUGO);
+MODULE_PARM_DESC(cidmode, "stay in CID mode when idle");
+
+static struct gigaset_driver *driver;
+
+struct ser_cardstate {
+ struct platform_device dev;
+ struct tty_struct *tty;
+ atomic_t refcnt;
+ struct completion dead_cmp;
+};
+
+static struct platform_driver device_driver = {
+ .driver = {
+ .name = GIGASET_MODULENAME,
+ },
+};
+
+static void flush_send_queue(struct cardstate *);
+
+/* transmit data from current open skb
+ * result: number of bytes sent or error code < 0
+ */
+static int write_modem(struct cardstate *cs)
+{
+ struct tty_struct *tty = cs->hw.ser->tty;
+ struct bc_state *bcs = &cs->bcs[0]; /* only one channel */
+ struct sk_buff *skb = bcs->tx_skb;
+ int sent = -EOPNOTSUPP;
+
+ if (!tty || !tty->driver || !skb)
+ return -EINVAL;
+
+ if (!skb->len) {
+ dev_kfree_skb_any(skb);
+ bcs->tx_skb = NULL;
+ return -EINVAL;
+ }
+
+ set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+ if (tty->ops->write)
+ sent = tty->ops->write(tty, skb->data, skb->len);
+ gig_dbg(DEBUG_OUTPUT, "write_modem: sent %d", sent);
+ if (sent < 0) {
+ /* error */
+ flush_send_queue(cs);
+ return sent;
+ }
+ skb_pull(skb, sent);
+ if (!skb->len) {
+ /* skb sent completely */
+ gigaset_skb_sent(bcs, skb);
+
+ gig_dbg(DEBUG_INTR, "kfree skb (Adr: %lx)!",
+ (unsigned long) skb);
+ dev_kfree_skb_any(skb);
+ bcs->tx_skb = NULL;
+ }
+ return sent;
+}
+
+/*
+ * transmit first queued command buffer
+ * result: number of bytes sent or error code < 0
+ */
+static int send_cb(struct cardstate *cs)
+{
+ struct tty_struct *tty = cs->hw.ser->tty;
+ struct cmdbuf_t *cb, *tcb;
+ unsigned long flags;
+ int sent = 0;
+
+ if (!tty || !tty->driver)
+ return -EFAULT;
+
+ cb = cs->cmdbuf;
+ if (!cb)
+ return 0; /* nothing to do */
+
+ if (cb->len) {
+ set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+ sent = tty->ops->write(tty, cb->buf + cb->offset, cb->len);
+ if (sent < 0) {
+ /* error */
+ gig_dbg(DEBUG_OUTPUT, "send_cb: write error %d", sent);
+ flush_send_queue(cs);
+ return sent;
+ }
+ cb->offset += sent;
+ cb->len -= sent;
+ gig_dbg(DEBUG_OUTPUT, "send_cb: sent %d, left %u, queued %u",
+ sent, cb->len, cs->cmdbytes);
+ }
+
+ while (cb && !cb->len) {
+ spin_lock_irqsave(&cs->cmdlock, flags);
+ cs->cmdbytes -= cs->curlen;
+ tcb = cb;
+ cs->cmdbuf = cb = cb->next;
+ if (cb) {
+ cb->prev = NULL;
+ cs->curlen = cb->len;
+ } else {
+ cs->lastcmdbuf = NULL;
+ cs->curlen = 0;
+ }
+ spin_unlock_irqrestore(&cs->cmdlock, flags);
+
+ if (tcb->wake_tasklet)
+ tasklet_schedule(tcb->wake_tasklet);
+ kfree(tcb);
+ }
+ return sent;
+}
+
+/*
+ * send queue tasklet
+ * If there is already a skb opened, put data to the transfer buffer
+ * by calling "write_modem".
+ * Otherwise take a new skb out of the queue.
+ */
+static void gigaset_modem_fill(unsigned long data)
+{
+ struct cardstate *cs = (struct cardstate *) data;
+ struct bc_state *bcs;
+ int sent = 0;
+
+ if (!cs || !(bcs = cs->bcs)) {
+ gig_dbg(DEBUG_OUTPUT, "%s: no cardstate", __func__);
+ return;
+ }
+ if (!bcs->tx_skb) {
+ /* no skb is being sent; send command if any */
+ sent = send_cb(cs);
+ gig_dbg(DEBUG_OUTPUT, "%s: send_cb -> %d", __func__, sent);
+ if (sent)
+ /* something sent or error */
+ return;
+
+ /* no command to send; get skb */
+ if (!(bcs->tx_skb = skb_dequeue(&bcs->squeue)))
+ /* no skb either, nothing to do */
+ return;
+
+ gig_dbg(DEBUG_INTR, "Dequeued skb (Adr: %lx)",
+ (unsigned long) bcs->tx_skb);
+ }
+
+ /* send skb */
+ gig_dbg(DEBUG_OUTPUT, "%s: tx_skb", __func__);
+ if (write_modem(cs) < 0)
+ gig_dbg(DEBUG_OUTPUT, "%s: write_modem failed", __func__);
+}
+
+/*
+ * throw away all data queued for sending
+ */
+static void flush_send_queue(struct cardstate *cs)
+{
+ struct sk_buff *skb;
+ struct cmdbuf_t *cb;
+ unsigned long flags;
+
+ /* command queue */
+ spin_lock_irqsave(&cs->cmdlock, flags);
+ while ((cb = cs->cmdbuf) != NULL) {
+ cs->cmdbuf = cb->next;
+ if (cb->wake_tasklet)
+ tasklet_schedule(cb->wake_tasklet);
+ kfree(cb);
+ }
+ cs->cmdbuf = cs->lastcmdbuf = NULL;
+ cs->cmdbytes = cs->curlen = 0;
+ spin_unlock_irqrestore(&cs->cmdlock, flags);
+
+ /* data queue */
+ if (cs->bcs->tx_skb)
+ dev_kfree_skb_any(cs->bcs->tx_skb);
+ while ((skb = skb_dequeue(&cs->bcs->squeue)) != NULL)
+ dev_kfree_skb_any(skb);
+}
+
+
+/* Gigaset Driver Interface */
+/* ======================== */
+
+/*
+ * queue an AT command string for transmission to the Gigaset device
+ * parameters:
+ * cs controller state structure
+ * buf buffer containing the string to send
+ * len number of characters to send
+ * wake_tasklet tasklet to run when transmission is complete, or NULL
+ * return value:
+ * number of bytes queued, or error code < 0
+ */
+static int gigaset_write_cmd(struct cardstate *cs, const unsigned char *buf,
+ int len, struct tasklet_struct *wake_tasklet)
+{
+ struct cmdbuf_t *cb;
+ unsigned long flags;
+
+ gigaset_dbg_buffer(cs->mstate != MS_LOCKED ?
+ DEBUG_TRANSCMD : DEBUG_LOCKCMD,
+ "CMD Transmit", len, buf);
+
+ if (len <= 0)
+ return 0;
+
+ if (!(cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC))) {
+ dev_err(cs->dev, "%s: out of memory!\n", __func__);
+ return -ENOMEM;
+ }
+
+ memcpy(cb->buf, buf, len);
+ cb->len = len;
+ cb->offset = 0;
+ cb->next = NULL;
+ cb->wake_tasklet = wake_tasklet;
+
+ spin_lock_irqsave(&cs->cmdlock, flags);
+ cb->prev = cs->lastcmdbuf;
+ if (cs->lastcmdbuf)
+ cs->lastcmdbuf->next = cb;
+ else {
+ cs->cmdbuf = cb;
+ cs->curlen = len;
+ }
+ cs->cmdbytes += len;
+ cs->lastcmdbuf = cb;
+ spin_unlock_irqrestore(&cs->cmdlock, flags);
+
+ spin_lock_irqsave(&cs->lock, flags);
+ if (cs->connected)
+ tasklet_schedule(&cs->write_tasklet);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return len;
+}
+
+/*
+ * tty_driver.write_room interface routine
+ * return number of characters the driver will accept to be written
+ * parameter:
+ * controller state structure
+ * return value:
+ * number of characters
+ */
+static int gigaset_write_room(struct cardstate *cs)
+{
+ unsigned bytes;
+
+ bytes = cs->cmdbytes;
+ return bytes < IF_WRITEBUF ? IF_WRITEBUF - bytes : 0;
+}
+
+/*
+ * tty_driver.chars_in_buffer interface routine
+ * return number of characters waiting to be sent
+ * parameter:
+ * controller state structure
+ * return value:
+ * number of characters
+ */
+static int gigaset_chars_in_buffer(struct cardstate *cs)
+{
+ return cs->cmdbytes;
+}
+
+/*
+ * implementation of ioctl(GIGASET_BRKCHARS)
+ * parameter:
+ * controller state structure
+ * return value:
+ * -EINVAL (unimplemented function)
+ */
+static int gigaset_brkchars(struct cardstate *cs, const unsigned char buf[6])
+{
+ /* not implemented */
+ return -EINVAL;
+}
+
+/*
+ * Open B channel
+ * Called by "do_action" in ev-layer.c
+ */
+static int gigaset_init_bchannel(struct bc_state *bcs)
+{
+ /* nothing to do for M10x */
+ gigaset_bchannel_up(bcs);
+ return 0;
+}
+
+/*
+ * Close B channel
+ * Called by "do_action" in ev-layer.c
+ */
+static int gigaset_close_bchannel(struct bc_state *bcs)
+{
+ /* nothing to do for M10x */
+ gigaset_bchannel_down(bcs);
+ return 0;
+}
+
+/*
+ * Set up B channel structure
+ * This is called by "gigaset_initcs" in common.c
+ */
+static int gigaset_initbcshw(struct bc_state *bcs)
+{
+ /* unused */
+ bcs->hw.ser = NULL;
+ return 1;
+}
+
+/*
+ * Free B channel structure
+ * Called by "gigaset_freebcs" in common.c
+ */
+static int gigaset_freebcshw(struct bc_state *bcs)
+{
+ /* unused */
+ return 1;
+}
+
+/*
+ * Reinitialize B channel structure
+ * This is called by "bcs_reinit" in common.c
+ */
+static void gigaset_reinitbcshw(struct bc_state *bcs)
+{
+ /* nothing to do for M10x */
+}
+
+/*
+ * Free hardware specific device data
+ * This will be called by "gigaset_freecs" in common.c
+ */
+static void gigaset_freecshw(struct cardstate *cs)
+{
+ tasklet_kill(&cs->write_tasklet);
+ if (!cs->hw.ser)
+ return;
+ dev_set_drvdata(&cs->hw.ser->dev.dev, NULL);
+ platform_device_unregister(&cs->hw.ser->dev);
+ kfree(cs->hw.ser);
+ cs->hw.ser = NULL;
+}
+
+static void gigaset_device_release(struct device *dev)
+{
+ struct platform_device *pdev =
+ container_of(dev, struct platform_device, dev);
+
+ /* adapted from platform_device_release() in drivers/base/platform.c */
+ //FIXME is this actually necessary?
+ kfree(dev->platform_data);
+ kfree(pdev->resource);
+}
+
+/*
+ * Set up hardware specific device data
+ * This is called by "gigaset_initcs" in common.c
+ */
+static int gigaset_initcshw(struct cardstate *cs)
+{
+ int rc;
+
+ if (!(cs->hw.ser = kzalloc(sizeof(struct ser_cardstate), GFP_KERNEL))) {
+ err("%s: out of memory!", __func__);
+ return 0;
+ }
+
+ cs->hw.ser->dev.name = GIGASET_MODULENAME;
+ cs->hw.ser->dev.id = cs->minor_index;
+ cs->hw.ser->dev.dev.release = gigaset_device_release;
+ if ((rc = platform_device_register(&cs->hw.ser->dev)) != 0) {
+ err("error %d registering platform device", rc);
+ kfree(cs->hw.ser);
+ cs->hw.ser = NULL;
+ return 0;
+ }
+ dev_set_drvdata(&cs->hw.ser->dev.dev, cs);
+
+ tasklet_init(&cs->write_tasklet,
+ &gigaset_modem_fill, (unsigned long) cs);
+ return 1;
+}
+
+/*
+ * set modem control lines
+ * Parameters:
+ * card state structure
+ * modem control line state ([TIOCM_DTR]|[TIOCM_RTS])
+ * Called by "gigaset_start" and "gigaset_enterconfigmode" in common.c
+ * and by "if_lock" and "if_termios" in interface.c
+ */
+static int gigaset_set_modem_ctrl(struct cardstate *cs, unsigned old_state, unsigned new_state)
+{
+ struct tty_struct *tty = cs->hw.ser->tty;
+ unsigned int set, clear;
+
+ if (!tty || !tty->driver || !tty->ops->tiocmset)
+ return -EINVAL;
+ set = new_state & ~old_state;
+ clear = old_state & ~new_state;
+ if (!set && !clear)
+ return 0;
+ gig_dbg(DEBUG_IF, "tiocmset set %x clear %x", set, clear);
+ return tty->ops->tiocmset(tty, NULL, set, clear);
+}
+
+static int gigaset_baud_rate(struct cardstate *cs, unsigned cflag)
+{
+ return -EINVAL;
+}
+
+static int gigaset_set_line_ctrl(struct cardstate *cs, unsigned cflag)
+{
+ return -EINVAL;
+}
+
+static const struct gigaset_ops ops = {
+ gigaset_write_cmd,
+ gigaset_write_room,
+ gigaset_chars_in_buffer,
+ gigaset_brkchars,
+ gigaset_init_bchannel,
+ gigaset_close_bchannel,
+ gigaset_initbcshw,
+ gigaset_freebcshw,
+ gigaset_reinitbcshw,
+ gigaset_initcshw,
+ gigaset_freecshw,
+ gigaset_set_modem_ctrl,
+ gigaset_baud_rate,
+ gigaset_set_line_ctrl,
+ gigaset_m10x_send_skb, /* asyncdata.c */
+ gigaset_m10x_input, /* asyncdata.c */
+};
+
+
+/* Line Discipline Interface */
+/* ========================= */
+
+/* helper functions for cardstate refcounting */
+static struct cardstate *cs_get(struct tty_struct *tty)
+{
+ struct cardstate *cs = tty->disc_data;
+
+ if (!cs || !cs->hw.ser) {
+ gig_dbg(DEBUG_ANY, "%s: no cardstate", __func__);
+ return NULL;
+ }
+ atomic_inc(&cs->hw.ser->refcnt);
+ return cs;
+}
+
+static void cs_put(struct cardstate *cs)
+{
+ if (atomic_dec_and_test(&cs->hw.ser->refcnt))
+ complete(&cs->hw.ser->dead_cmp);
+}
+
+/*
+ * Called by the tty driver when the line discipline is pushed onto the tty.
+ * Called in process context.
+ */
+static int
+gigaset_tty_open(struct tty_struct *tty)
+{
+ struct cardstate *cs;
+
+ gig_dbg(DEBUG_INIT, "Starting HLL for Gigaset M101");
+
+ info(DRIVER_AUTHOR);
+ info(DRIVER_DESC);
+
+ if (!driver) {
+ err("%s: no driver structure", __func__);
+ return -ENODEV;
+ }
+
+ /* allocate memory for our device state and intialize it */
+ if (!(cs = gigaset_initcs(driver, 1, 1, 0, cidmode,
+ GIGASET_MODULENAME)))
+ goto error;
+
+ cs->dev = &cs->hw.ser->dev.dev;
+ cs->hw.ser->tty = tty;
+ atomic_set(&cs->hw.ser->refcnt, 1);
+ init_completion(&cs->hw.ser->dead_cmp);
+
+ tty->disc_data = cs;
+
+ /* OK.. Initialization of the datastructures and the HW is done.. Now
+ * startup system and notify the LL that we are ready to run
+ */
+ if (startmode == SM_LOCKED)
+ cs->mstate = MS_LOCKED;
+ if (!gigaset_start(cs)) {
+ tasklet_kill(&cs->write_tasklet);
+ goto error;
+ }
+
+ gig_dbg(DEBUG_INIT, "Startup of HLL done");
+ return 0;
+
+error:
+ gig_dbg(DEBUG_INIT, "Startup of HLL failed");
+ tty->disc_data = NULL;
+ gigaset_freecs(cs);
+ return -ENODEV;
+}
+
+/*
+ * Called by the tty driver when the line discipline is removed.
+ * Called from process context.
+ */
+static void
+gigaset_tty_close(struct tty_struct *tty)
+{
+ struct cardstate *cs = tty->disc_data;
+
+ gig_dbg(DEBUG_INIT, "Stopping HLL for Gigaset M101");
+
+ if (!cs) {
+ gig_dbg(DEBUG_INIT, "%s: no cardstate", __func__);
+ return;
+ }
+
+ /* prevent other callers from entering ldisc methods */
+ /* FIXME: should use the tty state flags */
+ tty->disc_data = NULL;
+
+ if (!cs->hw.ser)
+ err("%s: no hw cardstate", __func__);
+ else {
+ /* wait for running methods to finish */
+ if (!atomic_dec_and_test(&cs->hw.ser->refcnt))
+ wait_for_completion(&cs->hw.ser->dead_cmp);
+ }
+
+ /* stop operations */
+ gigaset_stop(cs);
+ tasklet_kill(&cs->write_tasklet);
+ flush_send_queue(cs);
+ cs->dev = NULL;
+ gigaset_freecs(cs);
+
+ gig_dbg(DEBUG_INIT, "Shutdown of HLL done");
+}
+
+/*
+ * Called by the tty driver when the tty line is hung up.
+ * Wait for I/O to driver to complete and unregister ISDN device.
+ * This is already done by the close routine, so just call that.
+ * Called from process context.
+ */
+static int gigaset_tty_hangup(struct tty_struct *tty)
+{
+ gigaset_tty_close(tty);
+ return 0;
+}
+
+/*
+ * Read on the tty.
+ * Unused, received data goes only to the Gigaset driver.
+ */
+static ssize_t
+gigaset_tty_read(struct tty_struct *tty, struct file *file,
+ unsigned char __user *buf, size_t count)
+{
+ return -EAGAIN;
+}
+
+/*
+ * Write on the tty.
+ * Unused, transmit data comes only from the Gigaset driver.
+ */
+static ssize_t
+gigaset_tty_write(struct tty_struct *tty, struct file *file,
+ const unsigned char *buf, size_t count)
+{
+ return -EAGAIN;
+}
+
+/*
+ * Ioctl on the tty.
+ * Called in process context only.
+ * May be re-entered by multiple ioctl calling threads.
+ */
+static int
+gigaset_tty_ioctl(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct cardstate *cs = cs_get(tty);
+ int rc, val;
+ int __user *p = (int __user *)arg;
+
+ if (!cs)
+ return -ENXIO;
+
+ switch (cmd) {
+
+ case FIONREAD:
+ /* unused, always return zero */
+ val = 0;
+ rc = put_user(val, p);
+ break;
+
+ case TCFLSH:
+ /* flush our buffers and the serial port's buffer */
+ switch (arg) {
+ case TCIFLUSH:
+ /* no own input buffer to flush */
+ break;
+ case TCIOFLUSH:
+ case TCOFLUSH:
+ flush_send_queue(cs);
+ break;
+ }
+ /* Pass through */
+
+ default:
+ /* pass through to underlying serial device */
+ rc = n_tty_ioctl_helper(tty, file, cmd, arg);
+ break;
+ }
+ cs_put(cs);
+ return rc;
+}
+
+/*
+ * Poll on the tty.
+ * Unused, always return zero.
+ *
+ * FIXME: should probably return an exception - especially on hangup
+ */
+static unsigned int
+gigaset_tty_poll(struct tty_struct *tty, struct file *file, poll_table *wait)
+{
+ return 0;
+}
+
+/*
+ * Called by the tty driver when a block of data has been received.
+ * Will not be re-entered while running but other ldisc functions
+ * may be called in parallel.
+ * Can be called from hard interrupt level as well as soft interrupt
+ * level or mainline.
+ * Parameters:
+ * tty tty structure
+ * buf buffer containing received characters
+ * cflags buffer containing error flags for received characters (ignored)
+ * count number of received characters
+ */
+static void
+gigaset_tty_receive(struct tty_struct *tty, const unsigned char *buf,
+ char *cflags, int count)
+{
+ struct cardstate *cs = cs_get(tty);
+ unsigned tail, head, n;
+ struct inbuf_t *inbuf;
+
+ if (!cs)
+ return;
+ if (!(inbuf = cs->inbuf)) {
+ dev_err(cs->dev, "%s: no inbuf\n", __func__);
+ cs_put(cs);
+ return;
+ }
+
+ tail = inbuf->tail;
+ head = inbuf->head;
+ gig_dbg(DEBUG_INTR, "buffer state: %u -> %u, receive %u bytes",
+ head, tail, count);
+
+ if (head <= tail) {
+ /* possible buffer wraparound */
+ n = min_t(unsigned, count, RBUFSIZE - tail);
+ memcpy(inbuf->data + tail, buf, n);
+ tail = (tail + n) % RBUFSIZE;
+ buf += n;
+ count -= n;
+ }
+
+ if (count > 0) {
+ /* tail < head and some data left */
+ n = head - tail - 1;
+ if (count > n) {
+ dev_err(cs->dev,
+ "inbuf overflow, discarding %d bytes\n",
+ count - n);
+ count = n;
+ }
+ memcpy(inbuf->data + tail, buf, count);
+ tail += count;
+ }
+
+ gig_dbg(DEBUG_INTR, "setting tail to %u", tail);
+ inbuf->tail = tail;
+
+ /* Everything was received .. Push data into handler */
+ gig_dbg(DEBUG_INTR, "%s-->BH", __func__);
+ gigaset_schedule_event(cs);
+ cs_put(cs);
+}
+
+/*
+ * Called by the tty driver when there's room for more data to send.
+ */
+static void
+gigaset_tty_wakeup(struct tty_struct *tty)
+{
+ struct cardstate *cs = cs_get(tty);
+
+ clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+ if (!cs)
+ return;
+ tasklet_schedule(&cs->write_tasklet);
+ cs_put(cs);
+}
+
+static struct tty_ldisc_ops gigaset_ldisc = {
+ .owner = THIS_MODULE,
+ .magic = TTY_LDISC_MAGIC,
+ .name = "ser_gigaset",
+ .open = gigaset_tty_open,
+ .close = gigaset_tty_close,
+ .hangup = gigaset_tty_hangup,
+ .read = gigaset_tty_read,
+ .write = gigaset_tty_write,
+ .ioctl = gigaset_tty_ioctl,
+ .poll = gigaset_tty_poll,
+ .receive_buf = gigaset_tty_receive,
+ .write_wakeup = gigaset_tty_wakeup,
+};
+
+
+/* Initialization / Shutdown */
+/* ========================= */
+
+static int __init ser_gigaset_init(void)
+{
+ int rc;
+
+ gig_dbg(DEBUG_INIT, "%s", __func__);
+ if ((rc = platform_driver_register(&device_driver)) != 0) {
+ err("error %d registering platform driver", rc);
+ return rc;
+ }
+
+ /* allocate memory for our driver state and intialize it */
+ if (!(driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS,
+ GIGASET_MODULENAME, GIGASET_DEVNAME,
+ &ops, THIS_MODULE)))
+ goto error;
+
+ if ((rc = tty_register_ldisc(N_GIGASET_M101, &gigaset_ldisc)) != 0) {
+ err("error %d registering line discipline", rc);
+ goto error;
+ }
+
+ return 0;
+
+error:
+ if (driver) {
+ gigaset_freedriver(driver);
+ driver = NULL;
+ }
+ platform_driver_unregister(&device_driver);
+ return rc;
+}
+
+static void __exit ser_gigaset_exit(void)
+{
+ int rc;
+
+ gig_dbg(DEBUG_INIT, "%s", __func__);
+
+ if (driver) {
+ gigaset_freedriver(driver);
+ driver = NULL;
+ }
+
+ if ((rc = tty_unregister_ldisc(N_GIGASET_M101)) != 0)
+ err("error %d unregistering line discipline", rc);
+
+ platform_driver_unregister(&device_driver);
+}
+
+module_init(ser_gigaset_init);
+module_exit(ser_gigaset_exit);
diff --git a/drivers/isdn/gigaset/usb-gigaset.c b/drivers/isdn/gigaset/usb-gigaset.c
new file mode 100644
index 0000000..4661830
--- /dev/null
+++ b/drivers/isdn/gigaset/usb-gigaset.c
@@ -0,0 +1,990 @@
+/*
+ * USB driver for Gigaset 307x directly or using M105 Data.
+ *
+ * Copyright (c) 2001 by Stefan Eilers
+ * and Hansjoerg Lipp <hjlipp@web.de>.
+ *
+ * This driver was derived from the USB skeleton driver by
+ * Greg Kroah-Hartman <greg@kroah.com>
+ *
+ * =====================================================================
+ * 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 of
+ * the License, or (at your option) any later version.
+ * =====================================================================
+ */
+
+#include "gigaset.h"
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+/* Version Information */
+#define DRIVER_AUTHOR "Hansjoerg Lipp <hjlipp@web.de>, Stefan Eilers"
+#define DRIVER_DESC "USB Driver for Gigaset 307x using M105"
+
+/* Module parameters */
+
+static int startmode = SM_ISDN;
+static int cidmode = 1;
+
+module_param(startmode, int, S_IRUGO);
+module_param(cidmode, int, S_IRUGO);
+MODULE_PARM_DESC(startmode, "start in isdn4linux mode");
+MODULE_PARM_DESC(cidmode, "Call-ID mode");
+
+#define GIGASET_MINORS 1
+#define GIGASET_MINOR 8
+#define GIGASET_MODULENAME "usb_gigaset"
+#define GIGASET_DEVNAME "ttyGU"
+
+#define IF_WRITEBUF 2000 //FIXME // WAKEUP_CHARS: 256
+
+/* Values for the Gigaset M105 Data */
+#define USB_M105_VENDOR_ID 0x0681
+#define USB_M105_PRODUCT_ID 0x0009
+
+/* table of devices that work with this driver */
+static const struct usb_device_id gigaset_table [] = {
+ { USB_DEVICE(USB_M105_VENDOR_ID, USB_M105_PRODUCT_ID) },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, gigaset_table);
+
+/*
+ * Control requests (empty fields: 00)
+ *
+ * RT|RQ|VALUE|INDEX|LEN |DATA
+ * In:
+ * C1 08 01
+ * Get flags (1 byte). Bits: 0=dtr,1=rts,3-7:?
+ * C1 0F ll ll
+ * Get device information/status (llll: 0x200 and 0x40 seen).
+ * Real size: I only saw MIN(llll,0x64).
+ * Contents: seems to be always the same...
+ * offset 0x00: Length of this structure (0x64) (len: 1,2,3 bytes)
+ * offset 0x3c: String (16 bit chars): "MCCI USB Serial V2.0"
+ * rest: ?
+ * Out:
+ * 41 11
+ * Initialize/reset device ?
+ * 41 00 xx 00
+ * ? (xx=00 or 01; 01 on start, 00 on close)
+ * 41 07 vv mm
+ * Set/clear flags vv=value, mm=mask (see RQ 08)
+ * 41 12 xx
+ * Used before the following configuration requests are issued
+ * (with xx=0x0f). I've seen other values<0xf, though.
+ * 41 01 xx xx
+ * Set baud rate. xxxx=ceil(0x384000/rate)=trunc(0x383fff/rate)+1.
+ * 41 03 ps bb
+ * Set byte size and parity. p: 0x20=even,0x10=odd,0x00=no parity
+ * [ 0x30: m, 0x40: s ]
+ * [s: 0: 1 stop bit; 1: 1.5; 2: 2]
+ * bb: bits/byte (seen 7 and 8)
+ * 41 13 -- -- -- -- 10 00 ww 00 00 00 xx 00 00 00 yy 00 00 00 zz 00 00 00
+ * ??
+ * Initialization: 01, 40, 00, 00
+ * Open device: 00 40, 00, 00
+ * yy and zz seem to be equal, either 0x00 or 0x0a
+ * (ww,xx) pairs seen: (00,00), (00,40), (01,40), (09,80), (19,80)
+ * 41 19 -- -- -- -- 06 00 00 00 00 xx 11 13
+ * Used after every "configuration sequence" (RQ 12, RQs 01/03/13).
+ * xx is usually 0x00 but was 0x7e before starting data transfer
+ * in unimodem mode. So, this might be an array of characters that need
+ * special treatment ("commit all bufferd data"?), 11=^Q, 13=^S.
+ *
+ * Unimodem mode: use "modprobe ppp_async flag_time=0" as the device _needs_ two
+ * flags per packet.
+ */
+
+/* functions called if a device of this driver is connected/disconnected */
+static int gigaset_probe(struct usb_interface *interface,
+ const struct usb_device_id *id);
+static void gigaset_disconnect(struct usb_interface *interface);
+
+/* functions called before/after suspend */
+static int gigaset_suspend(struct usb_interface *intf, pm_message_t message);
+static int gigaset_resume(struct usb_interface *intf);
+static int gigaset_pre_reset(struct usb_interface *intf);
+
+static struct gigaset_driver *driver = NULL;
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver gigaset_usb_driver = {
+ .name = GIGASET_MODULENAME,
+ .probe = gigaset_probe,
+ .disconnect = gigaset_disconnect,
+ .id_table = gigaset_table,
+ .suspend = gigaset_suspend,
+ .resume = gigaset_resume,
+ .reset_resume = gigaset_resume,
+ .pre_reset = gigaset_pre_reset,
+ .post_reset = gigaset_resume,
+};
+
+struct usb_cardstate {
+ struct usb_device *udev; /* usb device pointer */
+ struct usb_interface *interface; /* interface for this device */
+ int busy; /* bulk output in progress */
+
+ /* Output buffer */
+ unsigned char *bulk_out_buffer;
+ int bulk_out_size;
+ __u8 bulk_out_endpointAddr;
+ struct urb *bulk_out_urb;
+
+ /* Input buffer */
+ int rcvbuf_size;
+ struct urb *read_urb;
+ __u8 int_in_endpointAddr;
+
+ char bchars[6]; /* for request 0x19 */
+};
+
+static inline unsigned tiocm_to_gigaset(unsigned state)
+{
+ return ((state & TIOCM_DTR) ? 1 : 0) | ((state & TIOCM_RTS) ? 2 : 0);
+}
+
+#ifdef CONFIG_GIGASET_UNDOCREQ
+/* WARNING: EXPERIMENTAL! */
+static int gigaset_set_modem_ctrl(struct cardstate *cs, unsigned old_state,
+ unsigned new_state)
+{
+ struct usb_device *udev = cs->hw.usb->udev;
+ unsigned mask, val;
+ int r;
+
+ mask = tiocm_to_gigaset(old_state ^ new_state);
+ val = tiocm_to_gigaset(new_state);
+
+ gig_dbg(DEBUG_USBREQ, "set flags 0x%02x with mask 0x%02x", val, mask);
+ // don't use this in an interrupt/BH
+ r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 7, 0x41,
+ (val & 0xff) | ((mask & 0xff) << 8), 0,
+ NULL, 0, 2000 /* timeout? */);
+ if (r < 0)
+ return r;
+ //..
+ return 0;
+}
+
+static int set_value(struct cardstate *cs, u8 req, u16 val)
+{
+ struct usb_device *udev = cs->hw.usb->udev;
+ int r, r2;
+
+ gig_dbg(DEBUG_USBREQ, "request %02x (%04x)",
+ (unsigned)req, (unsigned)val);
+ r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x12, 0x41,
+ 0xf /*?*/, 0, NULL, 0, 2000 /*?*/);
+ /* no idea what this does */
+ if (r < 0) {
+ dev_err(&udev->dev, "error %d on request 0x12\n", -r);
+ return r;
+ }
+
+ r = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), req, 0x41,
+ val, 0, NULL, 0, 2000 /*?*/);
+ if (r < 0)
+ dev_err(&udev->dev, "error %d on request 0x%02x\n",
+ -r, (unsigned)req);
+
+ r2 = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x19, 0x41,
+ 0, 0, cs->hw.usb->bchars, 6, 2000 /*?*/);
+ if (r2 < 0)
+ dev_err(&udev->dev, "error %d on request 0x19\n", -r2);
+
+ return r < 0 ? r : (r2 < 0 ? r2 : 0);
+}
+
+/* WARNING: HIGHLY EXPERIMENTAL! */
+// don't use this in an interrupt/BH
+static int gigaset_baud_rate(struct cardstate *cs, unsigned cflag)
+{
+ u16 val;
+ u32 rate;
+
+ cflag &= CBAUD;
+
+ switch (cflag) {
+ //FIXME more values?
+ case B300: rate = 300; break;
+ case B600: rate = 600; break;
+ case B1200: rate = 1200; break;
+ case B2400: rate = 2400; break;
+ case B4800: rate = 4800; break;
+ case B9600: rate = 9600; break;
+ case B19200: rate = 19200; break;
+ case B38400: rate = 38400; break;
+ case B57600: rate = 57600; break;
+ case B115200: rate = 115200; break;
+ default:
+ rate = 9600;
+ dev_err(cs->dev, "unsupported baudrate request 0x%x,"
+ " using default of B9600\n", cflag);
+ }
+
+ val = 0x383fff / rate + 1;
+
+ return set_value(cs, 1, val);
+}
+
+/* WARNING: HIGHLY EXPERIMENTAL! */
+// don't use this in an interrupt/BH
+static int gigaset_set_line_ctrl(struct cardstate *cs, unsigned cflag)
+{
+ u16 val = 0;
+
+ /* set the parity */
+ if (cflag & PARENB)
+ val |= (cflag & PARODD) ? 0x10 : 0x20;
+
+ /* set the number of data bits */
+ switch (cflag & CSIZE) {
+ case CS5:
+ val |= 5 << 8; break;
+ case CS6:
+ val |= 6 << 8; break;
+ case CS7:
+ val |= 7 << 8; break;
+ case CS8:
+ val |= 8 << 8; break;
+ default:
+ dev_err(cs->dev, "CSIZE was not CS5-CS8, using default of 8\n");
+ val |= 8 << 8;
+ break;
+ }
+
+ /* set the number of stop bits */
+ if (cflag & CSTOPB) {
+ if ((cflag & CSIZE) == CS5)
+ val |= 1; /* 1.5 stop bits */ //FIXME is this okay?
+ else
+ val |= 2; /* 2 stop bits */
+ }
+
+ return set_value(cs, 3, val);
+}
+
+#else
+static int gigaset_set_modem_ctrl(struct cardstate *cs, unsigned old_state,
+ unsigned new_state)
+{
+ return -EINVAL;
+}
+
+static int gigaset_set_line_ctrl(struct cardstate *cs, unsigned cflag)
+{
+ return -EINVAL;
+}
+
+static int gigaset_baud_rate(struct cardstate *cs, unsigned cflag)
+{
+ return -EINVAL;
+}
+#endif
+
+
+ /*================================================================================================================*/
+static int gigaset_init_bchannel(struct bc_state *bcs)
+{
+ /* nothing to do for M10x */
+ gigaset_bchannel_up(bcs);
+ return 0;
+}
+
+static int gigaset_close_bchannel(struct bc_state *bcs)
+{
+ /* nothing to do for M10x */
+ gigaset_bchannel_down(bcs);
+ return 0;
+}
+
+static int write_modem(struct cardstate *cs);
+static int send_cb(struct cardstate *cs, struct cmdbuf_t *cb);
+
+
+/* Write tasklet handler: Continue sending current skb, or send command, or
+ * start sending an skb from the send queue.
+ */
+static void gigaset_modem_fill(unsigned long data)
+{
+ struct cardstate *cs = (struct cardstate *) data;
+ struct bc_state *bcs = &cs->bcs[0]; /* only one channel */
+ struct cmdbuf_t *cb;
+ int again;
+
+ gig_dbg(DEBUG_OUTPUT, "modem_fill");
+
+ if (cs->hw.usb->busy) {
+ gig_dbg(DEBUG_OUTPUT, "modem_fill: busy");
+ return;
+ }
+
+ do {
+ again = 0;
+ if (!bcs->tx_skb) { /* no skb is being sent */
+ cb = cs->cmdbuf;
+ if (cb) { /* commands to send? */
+ gig_dbg(DEBUG_OUTPUT, "modem_fill: cb");
+ if (send_cb(cs, cb) < 0) {
+ gig_dbg(DEBUG_OUTPUT,
+ "modem_fill: send_cb failed");
+ again = 1; /* no callback will be
+ called! */
+ }
+ } else { /* skbs to send? */
+ bcs->tx_skb = skb_dequeue(&bcs->squeue);
+ if (bcs->tx_skb)
+ gig_dbg(DEBUG_INTR,
+ "Dequeued skb (Adr: %lx)!",
+ (unsigned long) bcs->tx_skb);
+ }
+ }
+
+ if (bcs->tx_skb) {
+ gig_dbg(DEBUG_OUTPUT, "modem_fill: tx_skb");
+ if (write_modem(cs) < 0) {
+ gig_dbg(DEBUG_OUTPUT,
+ "modem_fill: write_modem failed");
+ // FIXME should we tell the LL?
+ again = 1; /* no callback will be called! */
+ }
+ }
+ } while (again);
+}
+
+/**
+ * gigaset_read_int_callback
+ *
+ * It is called if the data was received from the device.
+ */
+static void gigaset_read_int_callback(struct urb *urb)
+{
+ struct inbuf_t *inbuf = urb->context;
+ struct cardstate *cs = inbuf->cs;
+ int status = urb->status;
+ int r;
+ unsigned numbytes;
+ unsigned char *src;
+ unsigned long flags;
+
+ if (!status) {
+ numbytes = urb->actual_length;
+
+ if (numbytes) {
+ src = inbuf->rcvbuf;
+ if (unlikely(*src))
+ dev_warn(cs->dev,
+ "%s: There was no leading 0, but 0x%02x!\n",
+ __func__, (unsigned) *src);
+ ++src; /* skip leading 0x00 */
+ --numbytes;
+ if (gigaset_fill_inbuf(inbuf, src, numbytes)) {
+ gig_dbg(DEBUG_INTR, "%s-->BH", __func__);
+ gigaset_schedule_event(inbuf->cs);
+ }
+ } else
+ gig_dbg(DEBUG_INTR, "Received zero block length");
+ } else {
+ /* The urb might have been killed. */
+ gig_dbg(DEBUG_ANY, "%s - nonzero status received: %d",
+ __func__, status);
+ if (status == -ENOENT || status == -ESHUTDOWN)
+ /* killed or endpoint shutdown: don't resubmit */
+ return;
+ }
+
+ /* resubmit URB */
+ spin_lock_irqsave(&cs->lock, flags);
+ if (!cs->connected) {
+ spin_unlock_irqrestore(&cs->lock, flags);
+ err("%s: disconnected", __func__);
+ return;
+ }
+ r = usb_submit_urb(urb, GFP_ATOMIC);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ if (r)
+ dev_err(cs->dev, "error %d resubmitting URB\n", -r);
+}
+
+
+/* This callback routine is called when data was transmitted to the device. */
+static void gigaset_write_bulk_callback(struct urb *urb)
+{
+ struct cardstate *cs = urb->context;
+ int status = urb->status;
+ unsigned long flags;
+
+ switch (status) {
+ case 0: /* normal completion */
+ break;
+ case -ENOENT: /* killed */
+ gig_dbg(DEBUG_ANY, "%s: killed", __func__);
+ cs->hw.usb->busy = 0;
+ return;
+ default:
+ dev_err(cs->dev, "bulk transfer failed (status %d)\n",
+ -status);
+ /* That's all we can do. Communication problems
+ are handled by timeouts or network protocols. */
+ }
+
+ spin_lock_irqsave(&cs->lock, flags);
+ if (!cs->connected) {
+ err("%s: not connected", __func__);
+ } else {
+ cs->hw.usb->busy = 0;
+ tasklet_schedule(&cs->write_tasklet);
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+}
+
+static int send_cb(struct cardstate *cs, struct cmdbuf_t *cb)
+{
+ struct cmdbuf_t *tcb;
+ unsigned long flags;
+ int count;
+ int status = -ENOENT; // FIXME
+ struct usb_cardstate *ucs = cs->hw.usb;
+
+ do {
+ if (!cb->len) {
+ tcb = cb;
+
+ spin_lock_irqsave(&cs->cmdlock, flags);
+ cs->cmdbytes -= cs->curlen;
+ gig_dbg(DEBUG_OUTPUT, "send_cb: sent %u bytes, %u left",
+ cs->curlen, cs->cmdbytes);
+ cs->cmdbuf = cb = cb->next;
+ if (cb) {
+ cb->prev = NULL;
+ cs->curlen = cb->len;
+ } else {
+ cs->lastcmdbuf = NULL;
+ cs->curlen = 0;
+ }
+ spin_unlock_irqrestore(&cs->cmdlock, flags);
+
+ if (tcb->wake_tasklet)
+ tasklet_schedule(tcb->wake_tasklet);
+ kfree(tcb);
+ }
+ if (cb) {
+ count = min(cb->len, ucs->bulk_out_size);
+ gig_dbg(DEBUG_OUTPUT, "send_cb: send %d bytes", count);
+
+ usb_fill_bulk_urb(ucs->bulk_out_urb, ucs->udev,
+ usb_sndbulkpipe(ucs->udev,
+ ucs->bulk_out_endpointAddr & 0x0f),
+ cb->buf + cb->offset, count,
+ gigaset_write_bulk_callback, cs);
+
+ cb->offset += count;
+ cb->len -= count;
+ ucs->busy = 1;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ status = cs->connected ? usb_submit_urb(ucs->bulk_out_urb, GFP_ATOMIC) : -ENODEV;
+ spin_unlock_irqrestore(&cs->lock, flags);
+
+ if (status) {
+ ucs->busy = 0;
+ dev_err(cs->dev,
+ "could not submit urb (error %d)\n",
+ -status);
+ cb->len = 0; /* skip urb => remove cb+wakeup
+ in next loop cycle */
+ }
+ }
+ } while (cb && status); /* next command on error */
+
+ return status;
+}
+
+/* Send command to device. */
+static int gigaset_write_cmd(struct cardstate *cs, const unsigned char *buf,
+ int len, struct tasklet_struct *wake_tasklet)
+{
+ struct cmdbuf_t *cb;
+ unsigned long flags;
+
+ gigaset_dbg_buffer(cs->mstate != MS_LOCKED ?
+ DEBUG_TRANSCMD : DEBUG_LOCKCMD,
+ "CMD Transmit", len, buf);
+
+ if (len <= 0)
+ return 0;
+
+ if (!(cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC))) {
+ dev_err(cs->dev, "%s: out of memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ memcpy(cb->buf, buf, len);
+ cb->len = len;
+ cb->offset = 0;
+ cb->next = NULL;
+ cb->wake_tasklet = wake_tasklet;
+
+ spin_lock_irqsave(&cs->cmdlock, flags);
+ cb->prev = cs->lastcmdbuf;
+ if (cs->lastcmdbuf)
+ cs->lastcmdbuf->next = cb;
+ else {
+ cs->cmdbuf = cb;
+ cs->curlen = len;
+ }
+ cs->cmdbytes += len;
+ cs->lastcmdbuf = cb;
+ spin_unlock_irqrestore(&cs->cmdlock, flags);
+
+ spin_lock_irqsave(&cs->lock, flags);
+ if (cs->connected)
+ tasklet_schedule(&cs->write_tasklet);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return len;
+}
+
+static int gigaset_write_room(struct cardstate *cs)
+{
+ unsigned bytes;
+
+ bytes = cs->cmdbytes;
+ return bytes < IF_WRITEBUF ? IF_WRITEBUF - bytes : 0;
+}
+
+static int gigaset_chars_in_buffer(struct cardstate *cs)
+{
+ return cs->cmdbytes;
+}
+
+static int gigaset_brkchars(struct cardstate *cs, const unsigned char buf[6])
+{
+#ifdef CONFIG_GIGASET_UNDOCREQ
+ struct usb_device *udev = cs->hw.usb->udev;
+
+ gigaset_dbg_buffer(DEBUG_USBREQ, "brkchars", 6, buf);
+ memcpy(cs->hw.usb->bchars, buf, 6);
+ return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x19, 0x41,
+ 0, 0, &buf, 6, 2000);
+#else
+ return -EINVAL;
+#endif
+}
+
+static int gigaset_freebcshw(struct bc_state *bcs)
+{
+ /* unused */
+ return 1;
+}
+
+/* Initialize the b-channel structure */
+static int gigaset_initbcshw(struct bc_state *bcs)
+{
+ /* unused */
+ bcs->hw.usb = NULL;
+ return 1;
+}
+
+static void gigaset_reinitbcshw(struct bc_state *bcs)
+{
+ /* nothing to do for M10x */
+}
+
+static void gigaset_freecshw(struct cardstate *cs)
+{
+ tasklet_kill(&cs->write_tasklet);
+ kfree(cs->hw.usb);
+}
+
+static int gigaset_initcshw(struct cardstate *cs)
+{
+ struct usb_cardstate *ucs;
+
+ cs->hw.usb = ucs =
+ kmalloc(sizeof(struct usb_cardstate), GFP_KERNEL);
+ if (!ucs)
+ return 0;
+
+ ucs->bchars[0] = 0;
+ ucs->bchars[1] = 0;
+ ucs->bchars[2] = 0;
+ ucs->bchars[3] = 0;
+ ucs->bchars[4] = 0x11;
+ ucs->bchars[5] = 0x13;
+ ucs->bulk_out_buffer = NULL;
+ ucs->bulk_out_urb = NULL;
+ //ucs->urb_cmd_out = NULL;
+ ucs->read_urb = NULL;
+ tasklet_init(&cs->write_tasklet,
+ &gigaset_modem_fill, (unsigned long) cs);
+
+ return 1;
+}
+
+/* Send data from current skb to the device. */
+static int write_modem(struct cardstate *cs)
+{
+ int ret = 0;
+ int count;
+ struct bc_state *bcs = &cs->bcs[0]; /* only one channel */
+ struct usb_cardstate *ucs = cs->hw.usb;
+ unsigned long flags;
+
+ gig_dbg(DEBUG_WRITE, "len: %d...", bcs->tx_skb->len);
+
+ if (!bcs->tx_skb->len) {
+ dev_kfree_skb_any(bcs->tx_skb);
+ bcs->tx_skb = NULL;
+ return -EINVAL;
+ }
+
+ /* Copy data to bulk out buffer and // FIXME copying not necessary
+ * transmit data
+ */
+ count = min(bcs->tx_skb->len, (unsigned) ucs->bulk_out_size);
+ skb_copy_from_linear_data(bcs->tx_skb, ucs->bulk_out_buffer, count);
+ skb_pull(bcs->tx_skb, count);
+ ucs->busy = 1;
+ gig_dbg(DEBUG_OUTPUT, "write_modem: send %d bytes", count);
+
+ spin_lock_irqsave(&cs->lock, flags);
+ if (cs->connected) {
+ usb_fill_bulk_urb(ucs->bulk_out_urb, ucs->udev,
+ usb_sndbulkpipe(ucs->udev,
+ ucs->bulk_out_endpointAddr & 0x0f),
+ ucs->bulk_out_buffer, count,
+ gigaset_write_bulk_callback, cs);
+ ret = usb_submit_urb(ucs->bulk_out_urb, GFP_ATOMIC);
+ } else {
+ ret = -ENODEV;
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+
+ if (ret) {
+ dev_err(cs->dev, "could not submit urb (error %d)\n", -ret);
+ ucs->busy = 0;
+ }
+
+ if (!bcs->tx_skb->len) {
+ /* skb sent completely */
+ gigaset_skb_sent(bcs, bcs->tx_skb); //FIXME also, when ret<0?
+
+ gig_dbg(DEBUG_INTR, "kfree skb (Adr: %lx)!",
+ (unsigned long) bcs->tx_skb);
+ dev_kfree_skb_any(bcs->tx_skb);
+ bcs->tx_skb = NULL;
+ }
+
+ return ret;
+}
+
+static int gigaset_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ int retval;
+ struct usb_device *udev = interface_to_usbdev(interface);
+ struct usb_host_interface *hostif = interface->cur_altsetting;
+ struct cardstate *cs = NULL;
+ struct usb_cardstate *ucs = NULL;
+ struct usb_endpoint_descriptor *endpoint;
+ int buffer_size;
+
+ gig_dbg(DEBUG_ANY, "%s: Check if device matches ...", __func__);
+
+ /* See if the device offered us matches what we can accept */
+ if ((le16_to_cpu(udev->descriptor.idVendor) != USB_M105_VENDOR_ID) ||
+ (le16_to_cpu(udev->descriptor.idProduct) != USB_M105_PRODUCT_ID)) {
+ gig_dbg(DEBUG_ANY, "device ID (0x%x, 0x%x) not for me - skip",
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct));
+ return -ENODEV;
+ }
+ if (hostif->desc.bInterfaceNumber != 0) {
+ gig_dbg(DEBUG_ANY, "interface %d not for me - skip",
+ hostif->desc.bInterfaceNumber);
+ return -ENODEV;
+ }
+ if (hostif->desc.bAlternateSetting != 0) {
+ dev_notice(&udev->dev, "unsupported altsetting %d - skip",
+ hostif->desc.bAlternateSetting);
+ return -ENODEV;
+ }
+ if (hostif->desc.bInterfaceClass != 255) {
+ dev_notice(&udev->dev, "unsupported interface class %d - skip",
+ hostif->desc.bInterfaceClass);
+ return -ENODEV;
+ }
+
+ dev_info(&udev->dev, "%s: Device matched ... !\n", __func__);
+
+ /* allocate memory for our device state and intialize it */
+ cs = gigaset_initcs(driver, 1, 1, 0, cidmode, GIGASET_MODULENAME);
+ if (!cs)
+ return -ENODEV;
+ ucs = cs->hw.usb;
+
+ /* save off device structure ptrs for later use */
+ usb_get_dev(udev);
+ ucs->udev = udev;
+ ucs->interface = interface;
+ cs->dev = &interface->dev;
+
+ /* save address of controller structure */
+ usb_set_intfdata(interface, cs); // dev_set_drvdata(&interface->dev, cs);
+
+ endpoint = &hostif->endpoint[0].desc;
+
+ buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
+ ucs->bulk_out_size = buffer_size;
+ ucs->bulk_out_endpointAddr = endpoint->bEndpointAddress;
+ ucs->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL);
+ if (!ucs->bulk_out_buffer) {
+ dev_err(cs->dev, "Couldn't allocate bulk_out_buffer\n");
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ ucs->bulk_out_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!ucs->bulk_out_urb) {
+ dev_err(cs->dev, "Couldn't allocate bulk_out_urb\n");
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ endpoint = &hostif->endpoint[1].desc;
+
+ ucs->busy = 0;
+
+ ucs->read_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!ucs->read_urb) {
+ dev_err(cs->dev, "No free urbs available\n");
+ retval = -ENOMEM;
+ goto error;
+ }
+ buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
+ ucs->rcvbuf_size = buffer_size;
+ ucs->int_in_endpointAddr = endpoint->bEndpointAddress;
+ cs->inbuf[0].rcvbuf = kmalloc(buffer_size, GFP_KERNEL);
+ if (!cs->inbuf[0].rcvbuf) {
+ dev_err(cs->dev, "Couldn't allocate rcvbuf\n");
+ retval = -ENOMEM;
+ goto error;
+ }
+ /* Fill the interrupt urb and send it to the core */
+ usb_fill_int_urb(ucs->read_urb, udev,
+ usb_rcvintpipe(udev,
+ endpoint->bEndpointAddress & 0x0f),
+ cs->inbuf[0].rcvbuf, buffer_size,
+ gigaset_read_int_callback,
+ cs->inbuf + 0, endpoint->bInterval);
+
+ retval = usb_submit_urb(ucs->read_urb, GFP_KERNEL);
+ if (retval) {
+ dev_err(cs->dev, "Could not submit URB (error %d)\n", -retval);
+ goto error;
+ }
+
+ /* tell common part that the device is ready */
+ if (startmode == SM_LOCKED)
+ cs->mstate = MS_LOCKED;
+
+ if (!gigaset_start(cs)) {
+ tasklet_kill(&cs->write_tasklet);
+ retval = -ENODEV; //FIXME
+ goto error;
+ }
+ return 0;
+
+error:
+ usb_kill_urb(ucs->read_urb);
+ kfree(ucs->bulk_out_buffer);
+ usb_free_urb(ucs->bulk_out_urb);
+ kfree(cs->inbuf[0].rcvbuf);
+ usb_free_urb(ucs->read_urb);
+ usb_set_intfdata(interface, NULL);
+ ucs->read_urb = ucs->bulk_out_urb = NULL;
+ cs->inbuf[0].rcvbuf = ucs->bulk_out_buffer = NULL;
+ usb_put_dev(ucs->udev);
+ ucs->udev = NULL;
+ ucs->interface = NULL;
+ gigaset_freecs(cs);
+ return retval;
+}
+
+static void gigaset_disconnect(struct usb_interface *interface)
+{
+ struct cardstate *cs;
+ struct usb_cardstate *ucs;
+
+ cs = usb_get_intfdata(interface);
+ ucs = cs->hw.usb;
+
+ dev_info(cs->dev, "disconnecting Gigaset USB adapter\n");
+
+ usb_kill_urb(ucs->read_urb);
+
+ gigaset_stop(cs);
+
+ usb_set_intfdata(interface, NULL);
+ tasklet_kill(&cs->write_tasklet);
+
+ usb_kill_urb(ucs->bulk_out_urb);
+
+ kfree(ucs->bulk_out_buffer);
+ usb_free_urb(ucs->bulk_out_urb);
+ kfree(cs->inbuf[0].rcvbuf);
+ usb_free_urb(ucs->read_urb);
+ ucs->read_urb = ucs->bulk_out_urb = NULL;
+ cs->inbuf[0].rcvbuf = ucs->bulk_out_buffer = NULL;
+
+ usb_put_dev(ucs->udev);
+ ucs->interface = NULL;
+ ucs->udev = NULL;
+ cs->dev = NULL;
+ gigaset_freecs(cs);
+}
+
+/* gigaset_suspend
+ * This function is called before the USB connection is suspended or reset.
+ */
+static int gigaset_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct cardstate *cs = usb_get_intfdata(intf);
+
+ /* stop activity */
+ cs->connected = 0; /* prevent rescheduling */
+ usb_kill_urb(cs->hw.usb->read_urb);
+ tasklet_kill(&cs->write_tasklet);
+ usb_kill_urb(cs->hw.usb->bulk_out_urb);
+
+ gig_dbg(DEBUG_SUSPEND, "suspend complete");
+ return 0;
+}
+
+/* gigaset_resume
+ * This function is called after the USB connection has been resumed or reset.
+ */
+static int gigaset_resume(struct usb_interface *intf)
+{
+ struct cardstate *cs = usb_get_intfdata(intf);
+ int rc;
+
+ /* resubmit interrupt URB */
+ cs->connected = 1;
+ rc = usb_submit_urb(cs->hw.usb->read_urb, GFP_KERNEL);
+ if (rc) {
+ dev_err(cs->dev, "Could not submit read URB (error %d)\n", -rc);
+ return rc;
+ }
+
+ gig_dbg(DEBUG_SUSPEND, "resume complete");
+ return 0;
+}
+
+/* gigaset_pre_reset
+ * This function is called before the USB connection is reset.
+ */
+static int gigaset_pre_reset(struct usb_interface *intf)
+{
+ /* same as suspend */
+ return gigaset_suspend(intf, PMSG_ON);
+}
+
+static const struct gigaset_ops ops = {
+ gigaset_write_cmd,
+ gigaset_write_room,
+ gigaset_chars_in_buffer,
+ gigaset_brkchars,
+ gigaset_init_bchannel,
+ gigaset_close_bchannel,
+ gigaset_initbcshw,
+ gigaset_freebcshw,
+ gigaset_reinitbcshw,
+ gigaset_initcshw,
+ gigaset_freecshw,
+ gigaset_set_modem_ctrl,
+ gigaset_baud_rate,
+ gigaset_set_line_ctrl,
+ gigaset_m10x_send_skb,
+ gigaset_m10x_input,
+};
+
+/**
+ * usb_gigaset_init
+ * This function is called while kernel-module is loaded
+ */
+static int __init usb_gigaset_init(void)
+{
+ int result;
+
+ /* allocate memory for our driver state and intialize it */
+ if ((driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS,
+ GIGASET_MODULENAME, GIGASET_DEVNAME,
+ &ops, THIS_MODULE)) == NULL)
+ goto error;
+
+ /* register this driver with the USB subsystem */
+ result = usb_register(&gigaset_usb_driver);
+ if (result < 0) {
+ err("usb_gigaset: usb_register failed (error %d)",
+ -result);
+ goto error;
+ }
+
+ info(DRIVER_AUTHOR);
+ info(DRIVER_DESC);
+ return 0;
+
+error:
+ if (driver)
+ gigaset_freedriver(driver);
+ driver = NULL;
+ return -1;
+}
+
+
+/**
+ * usb_gigaset_exit
+ * This function is called while unloading the kernel-module
+ */
+static void __exit usb_gigaset_exit(void)
+{
+ int i;
+
+ gigaset_blockdriver(driver); /* => probe will fail
+ * => no gigaset_start any more
+ */
+
+ /* stop all connected devices */
+ for (i = 0; i < driver->minors; i++)
+ gigaset_shutdown(driver->cs + i);
+
+ /* from now on, no isdn callback should be possible */
+
+ /* deregister this driver with the USB subsystem */
+ usb_deregister(&gigaset_usb_driver);
+ /* this will call the disconnect-callback */
+ /* from now on, no disconnect/probe callback should be running */
+
+ gigaset_freedriver(driver);
+ driver = NULL;
+}
+
+
+module_init(usb_gigaset_init);
+module_exit(usb_gigaset_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/isdn/hardware/Kconfig b/drivers/isdn/hardware/Kconfig
new file mode 100644
index 0000000..30d028d
--- /dev/null
+++ b/drivers/isdn/hardware/Kconfig
@@ -0,0 +1,9 @@
+#
+# ISDN hardware drivers
+#
+comment "CAPI hardware drivers"
+
+source "drivers/isdn/hardware/avm/Kconfig"
+
+source "drivers/isdn/hardware/eicon/Kconfig"
+
diff --git a/drivers/isdn/hardware/Makefile b/drivers/isdn/hardware/Makefile
new file mode 100644
index 0000000..a5d8fce
--- /dev/null
+++ b/drivers/isdn/hardware/Makefile
@@ -0,0 +1,7 @@
+# Makefile for the CAPI hardware drivers
+
+# Object files in subdirectories
+
+obj-$(CONFIG_CAPI_AVM) += avm/
+obj-$(CONFIG_CAPI_EICON) += eicon/
+obj-$(CONFIG_MISDN) += mISDN/
diff --git a/drivers/isdn/hardware/avm/Kconfig b/drivers/isdn/hardware/avm/Kconfig
new file mode 100644
index 0000000..5dbcbe3
--- /dev/null
+++ b/drivers/isdn/hardware/avm/Kconfig
@@ -0,0 +1,63 @@
+#
+# ISDN AVM drivers
+#
+
+menuconfig CAPI_AVM
+ bool "Active AVM cards"
+ help
+ Enable support for AVM active ISDN cards.
+
+if CAPI_AVM
+
+config ISDN_DRV_AVMB1_B1ISA
+ tristate "AVM B1 ISA support"
+ depends on ISA
+ help
+ Enable support for the ISA version of the AVM B1 card.
+
+config ISDN_DRV_AVMB1_B1PCI
+ tristate "AVM B1 PCI support"
+ depends on PCI
+ help
+ Enable support for the PCI version of the AVM B1 card.
+
+config ISDN_DRV_AVMB1_B1PCIV4
+ bool "AVM B1 PCI V4 support"
+ depends on ISDN_DRV_AVMB1_B1PCI
+ help
+ Enable support for the V4 version of AVM B1 PCI card.
+
+config ISDN_DRV_AVMB1_T1ISA
+ tristate "AVM T1/T1-B ISA support"
+ depends on ISA
+ help
+ Enable support for the AVM T1 T1B card.
+ Note: This is a PRI card and handle 30 B-channels.
+
+config ISDN_DRV_AVMB1_B1PCMCIA
+ tristate "AVM B1/M1/M2 PCMCIA support"
+ help
+ Enable support for the PCMCIA version of the AVM B1 card.
+
+config ISDN_DRV_AVMB1_AVM_CS
+ tristate "AVM B1/M1/M2 PCMCIA cs module"
+ depends on ISDN_DRV_AVMB1_B1PCMCIA && PCMCIA
+ help
+ Enable the PCMCIA client driver for the AVM B1/M1/M2
+ PCMCIA cards.
+
+config ISDN_DRV_AVMB1_T1PCI
+ tristate "AVM T1/T1-B PCI support"
+ depends on PCI
+ help
+ Enable support for the AVM T1 T1B card.
+ Note: This is a PRI card and handle 30 B-channels.
+
+config ISDN_DRV_AVMB1_C4
+ tristate "AVM C4/C2 support"
+ depends on PCI
+ help
+ Enable support for the AVM C4/C2 PCI cards.
+ These cards handle 4/2 BRI ISDN lines (8/4 channels).
+
+endif # CAPI_AVM
diff --git a/drivers/isdn/hardware/avm/Makefile b/drivers/isdn/hardware/avm/Makefile
new file mode 100644
index 0000000..b540e8f
--- /dev/null
+++ b/drivers/isdn/hardware/avm/Makefile
@@ -0,0 +1,11 @@
+# Makefile for the AVM ISDN device drivers
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_ISDN_DRV_AVMB1_B1ISA) += b1isa.o b1.o
+obj-$(CONFIG_ISDN_DRV_AVMB1_B1PCI) += b1pci.o b1.o b1dma.o
+obj-$(CONFIG_ISDN_DRV_AVMB1_B1PCMCIA) += b1pcmcia.o b1.o
+obj-$(CONFIG_ISDN_DRV_AVMB1_AVM_CS) += avm_cs.o
+obj-$(CONFIG_ISDN_DRV_AVMB1_T1ISA) += t1isa.o b1.o
+obj-$(CONFIG_ISDN_DRV_AVMB1_T1PCI) += t1pci.o b1.o b1dma.o
+obj-$(CONFIG_ISDN_DRV_AVMB1_C4) += c4.o b1.o
diff --git a/drivers/isdn/hardware/avm/avm_cs.c b/drivers/isdn/hardware/avm/avm_cs.c
new file mode 100644
index 0000000..c725655
--- /dev/null
+++ b/drivers/isdn/hardware/avm/avm_cs.c
@@ -0,0 +1,312 @@
+/* $Id: avm_cs.c,v 1.4.6.3 2001/09/23 22:24:33 kai Exp $
+ *
+ * A PCMCIA client driver for AVM B1/M1/M2
+ *
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/tty.h>
+#include <linux/serial.h>
+#include <linux/major.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ciscode.h>
+#include <pcmcia/ds.h>
+#include <pcmcia/cisreg.h>
+
+#include <linux/skbuff.h>
+#include <linux/capi.h>
+#include <linux/b1lli.h>
+#include <linux/b1pcmcia.h>
+
+/*====================================================================*/
+
+MODULE_DESCRIPTION("CAPI4Linux: PCMCIA client driver for AVM B1/M1/M2");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+
+/*====================================================================*/
+
+/*
+ The event() function is this driver's Card Services event handler.
+ It will be called by Card Services when an appropriate card status
+ event is received. The config() and release() entry points are
+ used to configure or release a socket, in response to card insertion
+ and ejection events. They are invoked from the skeleton event
+ handler.
+*/
+
+static int avmcs_config(struct pcmcia_device *link);
+static void avmcs_release(struct pcmcia_device *link);
+
+/*
+ The attach() and detach() entry points are used to create and destroy
+ "instances" of the driver, where each instance represents everything
+ needed to manage one actual PCMCIA card.
+*/
+
+static void avmcs_detach(struct pcmcia_device *p_dev);
+
+/*
+ A linked list of "instances" of the skeleton device. Each actual
+ PCMCIA card corresponds to one device instance, and is described
+ by one struct pcmcia_device structure (defined in ds.h).
+
+ You may not want to use a linked list for this -- for example, the
+ memory card driver uses an array of struct pcmcia_device pointers, where minor
+ device numbers are used to derive the corresponding array index.
+*/
+
+/*
+ A driver needs to provide a dev_node_t structure for each device
+ on a card. In some cases, there is only one device per card (for
+ example, ethernet cards, modems). In other cases, there may be
+ many actual or logical devices (SCSI adapters, memory cards with
+ multiple partitions). The dev_node_t structures need to be kept
+ in a linked list starting at the 'dev' field of a struct pcmcia_device
+ structure. We allocate them in the card's private data structure,
+ because they generally can't be allocated dynamically.
+*/
+
+typedef struct local_info_t {
+ dev_node_t node;
+} local_info_t;
+
+/*======================================================================
+
+ avmcs_attach() creates an "instance" of the driver, allocating
+ local data structures for one device. The device is registered
+ with Card Services.
+
+ The dev_link structure is initialized, but we don't actually
+ configure the card at this point -- we wait until we receive a
+ card insertion event.
+
+======================================================================*/
+
+static int avmcs_probe(struct pcmcia_device *p_dev)
+{
+ local_info_t *local;
+
+ /* The io structure describes IO port mapping */
+ p_dev->io.NumPorts1 = 16;
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ p_dev->io.NumPorts2 = 0;
+
+ /* Interrupt setup */
+ p_dev->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+ p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
+
+ p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID;
+
+ /* General socket configuration */
+ p_dev->conf.Attributes = CONF_ENABLE_IRQ;
+ p_dev->conf.IntType = INT_MEMORY_AND_IO;
+ p_dev->conf.ConfigIndex = 1;
+ p_dev->conf.Present = PRESENT_OPTION;
+
+ /* Allocate space for private device-specific data */
+ local = kzalloc(sizeof(local_info_t), GFP_KERNEL);
+ if (!local)
+ goto err;
+ p_dev->priv = local;
+
+ return avmcs_config(p_dev);
+
+ err:
+ return -ENOMEM;
+} /* avmcs_attach */
+
+/*======================================================================
+
+ This deletes a driver "instance". The device is de-registered
+ with Card Services. If it has been released, all local data
+ structures are freed. Otherwise, the structures will be freed
+ when the device is released.
+
+======================================================================*/
+
+static void avmcs_detach(struct pcmcia_device *link)
+{
+ avmcs_release(link);
+ kfree(link->priv);
+} /* avmcs_detach */
+
+/*======================================================================
+
+ avmcs_config() is scheduled to run after a CARD_INSERTION event
+ is received, to configure the PCMCIA socket, and to make the
+ ethernet device available to the system.
+
+======================================================================*/
+
+static int avmcs_configcheck(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cf,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ if (cf->io.nwin <= 0)
+ return -ENODEV;
+
+ p_dev->io.BasePort1 = cf->io.win[0].base;
+ p_dev->io.NumPorts1 = cf->io.win[0].len;
+ p_dev->io.NumPorts2 = 0;
+ printk(KERN_INFO "avm_cs: testing i/o %#x-%#x\n",
+ p_dev->io.BasePort1,
+ p_dev->io.BasePort1+p_dev->io.NumPorts1-1);
+ return pcmcia_request_io(p_dev, &p_dev->io);
+}
+
+static int avmcs_config(struct pcmcia_device *link)
+{
+ local_info_t *dev;
+ int i;
+ char devname[128];
+ int cardtype;
+ int (*addcard)(unsigned int port, unsigned irq);
+
+ dev = link->priv;
+
+ devname[0] = 0;
+ if (link->prod_id[1])
+ strlcpy(devname, link->prod_id[1], sizeof(devname));
+
+ /*
+ * find IO port
+ */
+ if (pcmcia_loop_config(link, avmcs_configcheck, NULL))
+ return -ENODEV;
+
+ do {
+ /*
+ * allocate an interrupt line
+ */
+ i = pcmcia_request_irq(link, &link->irq);
+ if (i != 0) {
+ cs_error(link, RequestIRQ, i);
+ /* undo */
+ pcmcia_disable_device(link);
+ break;
+ }
+
+ /*
+ * configure the PCMCIA socket
+ */
+ i = pcmcia_request_configuration(link, &link->conf);
+ if (i != 0) {
+ cs_error(link, RequestConfiguration, i);
+ pcmcia_disable_device(link);
+ break;
+ }
+
+ } while (0);
+
+ /* At this point, the dev_node_t structure(s) should be
+ initialized and arranged in a linked list at link->dev. */
+
+ if (devname[0]) {
+ char *s = strrchr(devname, ' ');
+ if (!s)
+ s = devname;
+ else s++;
+ strcpy(dev->node.dev_name, s);
+ if (strcmp("M1", s) == 0) {
+ cardtype = AVM_CARDTYPE_M1;
+ } else if (strcmp("M2", s) == 0) {
+ cardtype = AVM_CARDTYPE_M2;
+ } else {
+ cardtype = AVM_CARDTYPE_B1;
+ }
+ } else {
+ strcpy(dev->node.dev_name, "b1");
+ cardtype = AVM_CARDTYPE_B1;
+ }
+
+ dev->node.major = 64;
+ dev->node.minor = 0;
+ link->dev_node = &dev->node;
+
+ /* If any step failed, release any partially configured state */
+ if (i != 0) {
+ avmcs_release(link);
+ return -ENODEV;
+ }
+
+
+ switch (cardtype) {
+ case AVM_CARDTYPE_M1: addcard = b1pcmcia_addcard_m1; break;
+ case AVM_CARDTYPE_M2: addcard = b1pcmcia_addcard_m2; break;
+ default:
+ case AVM_CARDTYPE_B1: addcard = b1pcmcia_addcard_b1; break;
+ }
+ if ((i = (*addcard)(link->io.BasePort1, link->irq.AssignedIRQ)) < 0) {
+ printk(KERN_ERR "avm_cs: failed to add AVM-%s-Controller at i/o %#x, irq %d\n",
+ dev->node.dev_name, link->io.BasePort1, link->irq.AssignedIRQ);
+ avmcs_release(link);
+ return -ENODEV;
+ }
+ dev->node.minor = i;
+ return 0;
+
+} /* avmcs_config */
+
+/*======================================================================
+
+ After a card is removed, avmcs_release() will unregister the net
+ device, and release the PCMCIA configuration. If the device is
+ still open, this will be postponed until it is closed.
+
+======================================================================*/
+
+static void avmcs_release(struct pcmcia_device *link)
+{
+ b1pcmcia_delcard(link->io.BasePort1, link->irq.AssignedIRQ);
+ pcmcia_disable_device(link);
+} /* avmcs_release */
+
+
+static struct pcmcia_device_id avmcs_ids[] = {
+ PCMCIA_DEVICE_PROD_ID12("AVM", "ISDN-Controller B1", 0x95d42008, 0x845dc335),
+ PCMCIA_DEVICE_PROD_ID12("AVM", "Mobile ISDN-Controller M1", 0x95d42008, 0x81e10430),
+ PCMCIA_DEVICE_PROD_ID12("AVM", "Mobile ISDN-Controller M2", 0x95d42008, 0x18e8558a),
+ PCMCIA_DEVICE_NULL
+};
+MODULE_DEVICE_TABLE(pcmcia, avmcs_ids);
+
+static struct pcmcia_driver avmcs_driver = {
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = "avm_cs",
+ },
+ .probe = avmcs_probe,
+ .remove = avmcs_detach,
+ .id_table = avmcs_ids,
+};
+
+static int __init avmcs_init(void)
+{
+ return pcmcia_register_driver(&avmcs_driver);
+}
+
+static void __exit avmcs_exit(void)
+{
+ pcmcia_unregister_driver(&avmcs_driver);
+}
+
+module_init(avmcs_init);
+module_exit(avmcs_exit);
diff --git a/drivers/isdn/hardware/avm/avmcard.h b/drivers/isdn/hardware/avm/avmcard.h
new file mode 100644
index 0000000..d964f07
--- /dev/null
+++ b/drivers/isdn/hardware/avm/avmcard.h
@@ -0,0 +1,583 @@
+/* $Id: avmcard.h,v 1.1.4.1.2.1 2001/12/21 15:00:17 kai Exp $
+ *
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef _AVMCARD_H_
+#define _AVMCARD_H_
+
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+
+#define AVMB1_PORTLEN 0x1f
+#define AVM_MAXVERSION 8
+#define AVM_NCCI_PER_CHANNEL 4
+
+/*
+ * Versions
+ */
+
+#define VER_DRIVER 0
+#define VER_CARDTYPE 1
+#define VER_HWID 2
+#define VER_SERIAL 3
+#define VER_OPTION 4
+#define VER_PROTO 5
+#define VER_PROFILE 6
+#define VER_CAPI 7
+
+enum avmcardtype {
+ avm_b1isa,
+ avm_b1pci,
+ avm_b1pcmcia,
+ avm_m1,
+ avm_m2,
+ avm_t1isa,
+ avm_t1pci,
+ avm_c4,
+ avm_c2
+};
+
+typedef struct avmcard_dmabuf {
+ long size;
+ u8 *dmabuf;
+ dma_addr_t dmaaddr;
+} avmcard_dmabuf;
+
+typedef struct avmcard_dmainfo {
+ u32 recvlen;
+ avmcard_dmabuf recvbuf;
+
+ avmcard_dmabuf sendbuf;
+ struct sk_buff_head send_queue;
+
+ struct pci_dev *pcidev;
+} avmcard_dmainfo;
+
+typedef struct avmctrl_info {
+ char cardname[32];
+
+ int versionlen;
+ char versionbuf[1024];
+ char *version[AVM_MAXVERSION];
+
+ char infobuf[128]; /* for function procinfo */
+
+ struct avmcard *card;
+ struct capi_ctr capi_ctrl;
+
+ struct list_head ncci_head;
+} avmctrl_info;
+
+typedef struct avmcard {
+ char name[32];
+
+ spinlock_t lock;
+ unsigned int port;
+ unsigned irq;
+ unsigned long membase;
+ enum avmcardtype cardtype;
+ unsigned char revision;
+ unsigned char class;
+ int cardnr; /* for t1isa */
+
+ char msgbuf[128]; /* capimsg msg part */
+ char databuf[2048]; /* capimsg data part */
+
+ void __iomem *mbase;
+ volatile u32 csr;
+ avmcard_dmainfo *dma;
+
+ struct avmctrl_info *ctrlinfo;
+
+ u_int nr_controllers;
+ u_int nlogcontr;
+ struct list_head list;
+} avmcard;
+
+extern int b1_irq_table[16];
+
+/*
+ * LLI Messages to the ISDN-ControllerISDN Controller
+ */
+
+#define SEND_POLL 0x72 /*
+ * after load <- RECEIVE_POLL
+ */
+#define SEND_INIT 0x11 /*
+ * first message <- RECEIVE_INIT
+ * int32 NumApplications int32
+ * NumNCCIs int32 BoardNumber
+ */
+#define SEND_REGISTER 0x12 /*
+ * register an application int32
+ * ApplIDId int32 NumMessages
+ * int32 NumB3Connections int32
+ * NumB3Blocks int32 B3Size
+ *
+ * AnzB3Connection != 0 &&
+ * AnzB3Blocks >= 1 && B3Size >= 1
+ */
+#define SEND_RELEASE 0x14 /*
+ * deregister an application int32
+ * ApplID
+ */
+#define SEND_MESSAGE 0x15 /*
+ * send capi-message int32 length
+ * capi-data ...
+ */
+#define SEND_DATA_B3_REQ 0x13 /*
+ * send capi-data-message int32
+ * MsgLength capi-data ... int32
+ * B3Length data ....
+ */
+
+#define SEND_CONFIG 0x21 /*
+ */
+
+#define SEND_POLLACK 0x73 /* T1 Watchdog */
+
+/*
+ * LLI Messages from the ISDN-ControllerISDN Controller
+ */
+
+#define RECEIVE_POLL 0x32 /*
+ * <- after SEND_POLL
+ */
+#define RECEIVE_INIT 0x27 /*
+ * <- after SEND_INIT int32 length
+ * byte total length b1struct board
+ * driver revision b1struct card
+ * type b1struct reserved b1struct
+ * serial number b1struct driver
+ * capability b1struct d-channel
+ * protocol b1struct CAPI-2.0
+ * profile b1struct capi version
+ */
+#define RECEIVE_MESSAGE 0x21 /*
+ * <- after SEND_MESSAGE int32
+ * AppllID int32 Length capi-data
+ * ....
+ */
+#define RECEIVE_DATA_B3_IND 0x22 /*
+ * received data int32 AppllID
+ * int32 Length capi-data ...
+ * int32 B3Length data ...
+ */
+#define RECEIVE_START 0x23 /*
+ * Handshake
+ */
+#define RECEIVE_STOP 0x24 /*
+ * Handshake
+ */
+#define RECEIVE_NEW_NCCI 0x25 /*
+ * int32 AppllID int32 NCCI int32
+ * WindowSize
+ */
+#define RECEIVE_FREE_NCCI 0x26 /*
+ * int32 AppllID int32 NCCI
+ */
+#define RECEIVE_RELEASE 0x26 /*
+ * int32 AppllID int32 0xffffffff
+ */
+#define RECEIVE_TASK_READY 0x31 /*
+ * int32 tasknr
+ * int32 Length Taskname ...
+ */
+#define RECEIVE_DEBUGMSG 0x71 /*
+ * int32 Length message
+ *
+ */
+#define RECEIVE_POLLDWORD 0x75 /* t1pci in dword mode */
+
+#define WRITE_REGISTER 0x00
+#define READ_REGISTER 0x01
+
+/*
+ * port offsets
+ */
+
+#define B1_READ 0x00
+#define B1_WRITE 0x01
+#define B1_INSTAT 0x02
+#define B1_OUTSTAT 0x03
+#define B1_ANALYSE 0x04
+#define B1_REVISION 0x05
+#define B1_RESET 0x10
+
+
+#define B1_STAT0(cardtype) ((cardtype) == avm_m1 ? 0x81200000l : 0x80A00000l)
+#define B1_STAT1(cardtype) (0x80E00000l)
+
+/* ---------------------------------------------------------------- */
+
+static inline unsigned char b1outp(unsigned int base,
+ unsigned short offset,
+ unsigned char value)
+{
+ outb(value, base + offset);
+ return inb(base + B1_ANALYSE);
+}
+
+
+static inline int b1_rx_full(unsigned int base)
+{
+ return inb(base + B1_INSTAT) & 0x1;
+}
+
+static inline unsigned char b1_get_byte(unsigned int base)
+{
+ unsigned long stop = jiffies + 1 * HZ; /* maximum wait time 1 sec */
+ while (!b1_rx_full(base) && time_before(jiffies, stop));
+ if (b1_rx_full(base))
+ return inb(base + B1_READ);
+ printk(KERN_CRIT "b1lli(0x%x): rx not full after 1 second\n", base);
+ return 0;
+}
+
+static inline unsigned int b1_get_word(unsigned int base)
+{
+ unsigned int val = 0;
+ val |= b1_get_byte(base);
+ val |= (b1_get_byte(base) << 8);
+ val |= (b1_get_byte(base) << 16);
+ val |= (b1_get_byte(base) << 24);
+ return val;
+}
+
+static inline int b1_tx_empty(unsigned int base)
+{
+ return inb(base + B1_OUTSTAT) & 0x1;
+}
+
+static inline void b1_put_byte(unsigned int base, unsigned char val)
+{
+ while (!b1_tx_empty(base));
+ b1outp(base, B1_WRITE, val);
+}
+
+static inline int b1_save_put_byte(unsigned int base, unsigned char val)
+{
+ unsigned long stop = jiffies + 2 * HZ;
+ while (!b1_tx_empty(base) && time_before(jiffies,stop));
+ if (!b1_tx_empty(base)) return -1;
+ b1outp(base, B1_WRITE, val);
+ return 0;
+}
+
+static inline void b1_put_word(unsigned int base, unsigned int val)
+{
+ b1_put_byte(base, val & 0xff);
+ b1_put_byte(base, (val >> 8) & 0xff);
+ b1_put_byte(base, (val >> 16) & 0xff);
+ b1_put_byte(base, (val >> 24) & 0xff);
+}
+
+static inline unsigned int b1_get_slice(unsigned int base,
+ unsigned char *dp)
+{
+ unsigned int len, i;
+
+ len = i = b1_get_word(base);
+ while (i-- > 0) *dp++ = b1_get_byte(base);
+ return len;
+}
+
+static inline void b1_put_slice(unsigned int base,
+ unsigned char *dp, unsigned int len)
+{
+ unsigned i = len;
+ b1_put_word(base, i);
+ while (i-- > 0)
+ b1_put_byte(base, *dp++);
+}
+
+static void b1_wr_reg(unsigned int base,
+ unsigned int reg,
+ unsigned int value)
+{
+ b1_put_byte(base, WRITE_REGISTER);
+ b1_put_word(base, reg);
+ b1_put_word(base, value);
+}
+
+static inline unsigned int b1_rd_reg(unsigned int base,
+ unsigned int reg)
+{
+ b1_put_byte(base, READ_REGISTER);
+ b1_put_word(base, reg);
+ return b1_get_word(base);
+
+}
+
+static inline void b1_reset(unsigned int base)
+{
+ b1outp(base, B1_RESET, 0);
+ mdelay(55 * 2); /* 2 TIC's */
+
+ b1outp(base, B1_RESET, 1);
+ mdelay(55 * 2); /* 2 TIC's */
+
+ b1outp(base, B1_RESET, 0);
+ mdelay(55 * 2); /* 2 TIC's */
+}
+
+static inline unsigned char b1_disable_irq(unsigned int base)
+{
+ return b1outp(base, B1_INSTAT, 0x00);
+}
+
+/* ---------------------------------------------------------------- */
+
+static inline void b1_set_test_bit(unsigned int base,
+ enum avmcardtype cardtype,
+ int onoff)
+{
+ b1_wr_reg(base, B1_STAT0(cardtype), onoff ? 0x21 : 0x20);
+}
+
+static inline int b1_get_test_bit(unsigned int base,
+ enum avmcardtype cardtype)
+{
+ return (b1_rd_reg(base, B1_STAT0(cardtype)) & 0x01) != 0;
+}
+
+/* ---------------------------------------------------------------- */
+
+#define T1_FASTLINK 0x00
+#define T1_SLOWLINK 0x08
+
+#define T1_READ B1_READ
+#define T1_WRITE B1_WRITE
+#define T1_INSTAT B1_INSTAT
+#define T1_OUTSTAT B1_OUTSTAT
+#define T1_IRQENABLE 0x05
+#define T1_FIFOSTAT 0x06
+#define T1_RESETLINK 0x10
+#define T1_ANALYSE 0x11
+#define T1_IRQMASTER 0x12
+#define T1_IDENT 0x17
+#define T1_RESETBOARD 0x1f
+
+#define T1F_IREADY 0x01
+#define T1F_IHALF 0x02
+#define T1F_IFULL 0x04
+#define T1F_IEMPTY 0x08
+#define T1F_IFLAGS 0xF0
+
+#define T1F_OREADY 0x10
+#define T1F_OHALF 0x20
+#define T1F_OEMPTY 0x40
+#define T1F_OFULL 0x80
+#define T1F_OFLAGS 0xF0
+
+/* there are HEMA cards with 1k and 4k FIFO out */
+#define FIFO_OUTBSIZE 256
+#define FIFO_INPBSIZE 512
+
+#define HEMA_VERSION_ID 0
+#define HEMA_PAL_ID 0
+
+static inline void t1outp(unsigned int base,
+ unsigned short offset,
+ unsigned char value)
+{
+ outb(value, base + offset);
+}
+
+static inline unsigned char t1inp(unsigned int base,
+ unsigned short offset)
+{
+ return inb(base + offset);
+}
+
+static inline int t1_isfastlink(unsigned int base)
+{
+ return (inb(base + T1_IDENT) & ~0x82) == 1;
+}
+
+static inline unsigned char t1_fifostatus(unsigned int base)
+{
+ return inb(base + T1_FIFOSTAT);
+}
+
+static inline unsigned int t1_get_slice(unsigned int base,
+ unsigned char *dp)
+{
+ unsigned int len, i;
+#ifdef FASTLINK_DEBUG
+ unsigned wcnt = 0, bcnt = 0;
+#endif
+
+ len = i = b1_get_word(base);
+ if (t1_isfastlink(base)) {
+ int status;
+ while (i > 0) {
+ status = t1_fifostatus(base) & (T1F_IREADY|T1F_IHALF);
+ if (i >= FIFO_INPBSIZE) status |= T1F_IFULL;
+
+ switch (status) {
+ case T1F_IREADY|T1F_IHALF|T1F_IFULL:
+ insb(base+B1_READ, dp, FIFO_INPBSIZE);
+ dp += FIFO_INPBSIZE;
+ i -= FIFO_INPBSIZE;
+#ifdef FASTLINK_DEBUG
+ wcnt += FIFO_INPBSIZE;
+#endif
+ break;
+ case T1F_IREADY|T1F_IHALF:
+ insb(base+B1_READ,dp, i);
+#ifdef FASTLINK_DEBUG
+ wcnt += i;
+#endif
+ dp += i;
+ i = 0;
+ break;
+ default:
+ *dp++ = b1_get_byte(base);
+ i--;
+#ifdef FASTLINK_DEBUG
+ bcnt++;
+#endif
+ break;
+ }
+ }
+#ifdef FASTLINK_DEBUG
+ if (wcnt)
+ printk(KERN_DEBUG "b1lli(0x%x): get_slice l=%d w=%d b=%d\n",
+ base, len, wcnt, bcnt);
+#endif
+ } else {
+ while (i-- > 0)
+ *dp++ = b1_get_byte(base);
+ }
+ return len;
+}
+
+static inline void t1_put_slice(unsigned int base,
+ unsigned char *dp, unsigned int len)
+{
+ unsigned i = len;
+ b1_put_word(base, i);
+ if (t1_isfastlink(base)) {
+ int status;
+ while (i > 0) {
+ status = t1_fifostatus(base) & (T1F_OREADY|T1F_OHALF);
+ if (i >= FIFO_OUTBSIZE) status |= T1F_OEMPTY;
+ switch (status) {
+ case T1F_OREADY|T1F_OHALF|T1F_OEMPTY:
+ outsb(base+B1_WRITE, dp, FIFO_OUTBSIZE);
+ dp += FIFO_OUTBSIZE;
+ i -= FIFO_OUTBSIZE;
+ break;
+ case T1F_OREADY|T1F_OHALF:
+ outsb(base+B1_WRITE, dp, i);
+ dp += i;
+ i = 0;
+ break;
+ default:
+ b1_put_byte(base, *dp++);
+ i--;
+ break;
+ }
+ }
+ } else {
+ while (i-- > 0)
+ b1_put_byte(base, *dp++);
+ }
+}
+
+static inline void t1_disable_irq(unsigned int base)
+{
+ t1outp(base, T1_IRQMASTER, 0x00);
+}
+
+static inline void t1_reset(unsigned int base)
+{
+ /* reset T1 Controller */
+ b1_reset(base);
+ /* disable irq on HEMA */
+ t1outp(base, B1_INSTAT, 0x00);
+ t1outp(base, B1_OUTSTAT, 0x00);
+ t1outp(base, T1_IRQMASTER, 0x00);
+ /* reset HEMA board configuration */
+ t1outp(base, T1_RESETBOARD, 0xf);
+}
+
+static inline void b1_setinterrupt(unsigned int base, unsigned irq,
+ enum avmcardtype cardtype)
+{
+ switch (cardtype) {
+ case avm_t1isa:
+ t1outp(base, B1_INSTAT, 0x00);
+ t1outp(base, B1_INSTAT, 0x02);
+ t1outp(base, T1_IRQMASTER, 0x08);
+ break;
+ case avm_b1isa:
+ b1outp(base, B1_INSTAT, 0x00);
+ b1outp(base, B1_RESET, b1_irq_table[irq]);
+ b1outp(base, B1_INSTAT, 0x02);
+ break;
+ default:
+ case avm_m1:
+ case avm_m2:
+ case avm_b1pci:
+ b1outp(base, B1_INSTAT, 0x00);
+ b1outp(base, B1_RESET, 0xf0);
+ b1outp(base, B1_INSTAT, 0x02);
+ break;
+ case avm_c4:
+ case avm_t1pci:
+ b1outp(base, B1_RESET, 0xf0);
+ break;
+ }
+}
+
+/* b1.c */
+avmcard *b1_alloc_card(int nr_controllers);
+void b1_free_card(avmcard *card);
+int b1_detect(unsigned int base, enum avmcardtype cardtype);
+void b1_getrevision(avmcard *card);
+int b1_load_t4file(avmcard *card, capiloaddatapart * t4file);
+int b1_load_config(avmcard *card, capiloaddatapart * config);
+int b1_loaded(avmcard *card);
+
+int b1_load_firmware(struct capi_ctr *ctrl, capiloaddata *data);
+void b1_reset_ctr(struct capi_ctr *ctrl);
+void b1_register_appl(struct capi_ctr *ctrl, u16 appl,
+ capi_register_params *rp);
+void b1_release_appl(struct capi_ctr *ctrl, u16 appl);
+u16 b1_send_message(struct capi_ctr *ctrl, struct sk_buff *skb);
+void b1_parse_version(avmctrl_info *card);
+irqreturn_t b1_interrupt(int interrupt, void *devptr);
+
+int b1ctl_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, struct capi_ctr *ctrl);
+
+avmcard_dmainfo *avmcard_dma_alloc(char *name, struct pci_dev *,
+ long rsize, long ssize);
+void avmcard_dma_free(avmcard_dmainfo *);
+
+/* b1dma.c */
+int b1pciv4_detect(avmcard *card);
+int t1pci_detect(avmcard *card);
+void b1dma_reset(avmcard *card);
+irqreturn_t b1dma_interrupt(int interrupt, void *devptr);
+
+int b1dma_load_firmware(struct capi_ctr *ctrl, capiloaddata *data);
+void b1dma_reset_ctr(struct capi_ctr *ctrl);
+void b1dma_remove_ctr(struct capi_ctr *ctrl);
+void b1dma_register_appl(struct capi_ctr *ctrl,
+ u16 appl,
+ capi_register_params *rp);
+void b1dma_release_appl(struct capi_ctr *ctrl, u16 appl);
+u16 b1dma_send_message(struct capi_ctr *ctrl, struct sk_buff *skb);
+int b1dmactl_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, struct capi_ctr *ctrl);
+
+#endif /* _AVMCARD_H_ */
diff --git a/drivers/isdn/hardware/avm/b1.c b/drivers/isdn/hardware/avm/b1.c
new file mode 100644
index 0000000..abf05ec
--- /dev/null
+++ b/drivers/isdn/hardware/avm/b1.c
@@ -0,0 +1,808 @@
+/* $Id: b1.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $
+ *
+ * Common module for AVM B1 cards.
+ *
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/capi.h>
+#include <linux/kernelcapi.h>
+#include <asm/io.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <linux/netdevice.h>
+#include <linux/isdn/capilli.h>
+#include "avmcard.h"
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+
+static char *revision = "$Revision: 1.1.2.2 $";
+
+/* ------------------------------------------------------------- */
+
+MODULE_DESCRIPTION("CAPI4Linux: Common support for active AVM cards");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+
+/* ------------------------------------------------------------- */
+
+int b1_irq_table[16] =
+{0,
+ 0,
+ 0,
+ 192, /* irq 3 */
+ 32, /* irq 4 */
+ 160, /* irq 5 */
+ 96, /* irq 6 */
+ 224, /* irq 7 */
+ 0,
+ 64, /* irq 9 */
+ 80, /* irq 10 */
+ 208, /* irq 11 */
+ 48, /* irq 12 */
+ 0,
+ 0,
+ 112, /* irq 15 */
+};
+
+/* ------------------------------------------------------------- */
+
+avmcard *b1_alloc_card(int nr_controllers)
+{
+ avmcard *card;
+ avmctrl_info *cinfo;
+ int i;
+
+ card = kzalloc(sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return NULL;
+
+ cinfo = kzalloc(sizeof(*cinfo) * nr_controllers, GFP_KERNEL);
+ if (!cinfo) {
+ kfree(card);
+ return NULL;
+ }
+
+ card->ctrlinfo = cinfo;
+ for (i = 0; i < nr_controllers; i++) {
+ INIT_LIST_HEAD(&cinfo[i].ncci_head);
+ cinfo[i].card = card;
+ }
+ spin_lock_init(&card->lock);
+ card->nr_controllers = nr_controllers;
+
+ return card;
+}
+
+/* ------------------------------------------------------------- */
+
+void b1_free_card(avmcard *card)
+{
+ kfree(card->ctrlinfo);
+ kfree(card);
+}
+
+/* ------------------------------------------------------------- */
+
+int b1_detect(unsigned int base, enum avmcardtype cardtype)
+{
+ int onoff, i;
+
+ /*
+ * Statusregister 0000 00xx
+ */
+ if ((inb(base + B1_INSTAT) & 0xfc)
+ || (inb(base + B1_OUTSTAT) & 0xfc))
+ return 1;
+ /*
+ * Statusregister 0000 001x
+ */
+ b1outp(base, B1_INSTAT, 0x2); /* enable irq */
+ /* b1outp(base, B1_OUTSTAT, 0x2); */
+ if ((inb(base + B1_INSTAT) & 0xfe) != 0x2
+ /* || (inb(base + B1_OUTSTAT) & 0xfe) != 0x2 */)
+ return 2;
+ /*
+ * Statusregister 0000 000x
+ */
+ b1outp(base, B1_INSTAT, 0x0); /* disable irq */
+ b1outp(base, B1_OUTSTAT, 0x0);
+ if ((inb(base + B1_INSTAT) & 0xfe)
+ || (inb(base + B1_OUTSTAT) & 0xfe))
+ return 3;
+
+ for (onoff = !0, i= 0; i < 10 ; i++) {
+ b1_set_test_bit(base, cardtype, onoff);
+ if (b1_get_test_bit(base, cardtype) != onoff)
+ return 4;
+ onoff = !onoff;
+ }
+
+ if (cardtype == avm_m1)
+ return 0;
+
+ if ((b1_rd_reg(base, B1_STAT1(cardtype)) & 0x0f) != 0x01)
+ return 5;
+
+ return 0;
+}
+
+void b1_getrevision(avmcard *card)
+{
+ card->class = inb(card->port + B1_ANALYSE);
+ card->revision = inb(card->port + B1_REVISION);
+}
+
+#define FWBUF_SIZE 256
+int b1_load_t4file(avmcard *card, capiloaddatapart * t4file)
+{
+ unsigned char buf[FWBUF_SIZE];
+ unsigned char *dp;
+ int i, left;
+ unsigned int base = card->port;
+
+ dp = t4file->data;
+ left = t4file->len;
+ while (left > FWBUF_SIZE) {
+ if (t4file->user) {
+ if (copy_from_user(buf, dp, FWBUF_SIZE))
+ return -EFAULT;
+ } else {
+ memcpy(buf, dp, FWBUF_SIZE);
+ }
+ for (i = 0; i < FWBUF_SIZE; i++)
+ if (b1_save_put_byte(base, buf[i]) < 0) {
+ printk(KERN_ERR "%s: corrupted firmware file ?\n",
+ card->name);
+ return -EIO;
+ }
+ left -= FWBUF_SIZE;
+ dp += FWBUF_SIZE;
+ }
+ if (left) {
+ if (t4file->user) {
+ if (copy_from_user(buf, dp, left))
+ return -EFAULT;
+ } else {
+ memcpy(buf, dp, left);
+ }
+ for (i = 0; i < left; i++)
+ if (b1_save_put_byte(base, buf[i]) < 0) {
+ printk(KERN_ERR "%s: corrupted firmware file ?\n",
+ card->name);
+ return -EIO;
+ }
+ }
+ return 0;
+}
+
+int b1_load_config(avmcard *card, capiloaddatapart * config)
+{
+ unsigned char buf[FWBUF_SIZE];
+ unsigned char *dp;
+ unsigned int base = card->port;
+ int i, j, left;
+
+ dp = config->data;
+ left = config->len;
+ if (left) {
+ b1_put_byte(base, SEND_CONFIG);
+ b1_put_word(base, 1);
+ b1_put_byte(base, SEND_CONFIG);
+ b1_put_word(base, left);
+ }
+ while (left > FWBUF_SIZE) {
+ if (config->user) {
+ if (copy_from_user(buf, dp, FWBUF_SIZE))
+ return -EFAULT;
+ } else {
+ memcpy(buf, dp, FWBUF_SIZE);
+ }
+ for (i = 0; i < FWBUF_SIZE; ) {
+ b1_put_byte(base, SEND_CONFIG);
+ for (j=0; j < 4; j++) {
+ b1_put_byte(base, buf[i++]);
+ }
+ }
+ left -= FWBUF_SIZE;
+ dp += FWBUF_SIZE;
+ }
+ if (left) {
+ if (config->user) {
+ if (copy_from_user(buf, dp, left))
+ return -EFAULT;
+ } else {
+ memcpy(buf, dp, left);
+ }
+ for (i = 0; i < left; ) {
+ b1_put_byte(base, SEND_CONFIG);
+ for (j=0; j < 4; j++) {
+ if (i < left)
+ b1_put_byte(base, buf[i++]);
+ else
+ b1_put_byte(base, 0);
+ }
+ }
+ }
+ return 0;
+}
+
+int b1_loaded(avmcard *card)
+{
+ unsigned int base = card->port;
+ unsigned long stop;
+ unsigned char ans;
+ unsigned long tout = 2;
+
+ for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) {
+ if (b1_tx_empty(base))
+ break;
+ }
+ if (!b1_tx_empty(base)) {
+ printk(KERN_ERR "%s: b1_loaded: tx err, corrupted t4 file ?\n",
+ card->name);
+ return 0;
+ }
+ b1_put_byte(base, SEND_POLL);
+ for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) {
+ if (b1_rx_full(base)) {
+ if ((ans = b1_get_byte(base)) == RECEIVE_POLL) {
+ return 1;
+ }
+ printk(KERN_ERR "%s: b1_loaded: got 0x%x, firmware not running\n",
+ card->name, ans);
+ return 0;
+ }
+ }
+ printk(KERN_ERR "%s: b1_loaded: firmware not running\n", card->name);
+ return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+int b1_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
+{
+ avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+ avmcard *card = cinfo->card;
+ unsigned int port = card->port;
+ unsigned long flags;
+ int retval;
+
+ b1_reset(port);
+
+ if ((retval = b1_load_t4file(card, &data->firmware))) {
+ b1_reset(port);
+ printk(KERN_ERR "%s: failed to load t4file!!\n",
+ card->name);
+ return retval;
+ }
+
+ b1_disable_irq(port);
+
+ if (data->configuration.len > 0 && data->configuration.data) {
+ if ((retval = b1_load_config(card, &data->configuration))) {
+ b1_reset(port);
+ printk(KERN_ERR "%s: failed to load config!!\n",
+ card->name);
+ return retval;
+ }
+ }
+
+ if (!b1_loaded(card)) {
+ printk(KERN_ERR "%s: failed to load t4file.\n", card->name);
+ return -EIO;
+ }
+
+ spin_lock_irqsave(&card->lock, flags);
+ b1_setinterrupt(port, card->irq, card->cardtype);
+ b1_put_byte(port, SEND_INIT);
+ b1_put_word(port, CAPI_MAXAPPL);
+ b1_put_word(port, AVM_NCCI_PER_CHANNEL*2);
+ b1_put_word(port, ctrl->cnr - 1);
+ spin_unlock_irqrestore(&card->lock, flags);
+
+ return 0;
+}
+
+void b1_reset_ctr(struct capi_ctr *ctrl)
+{
+ avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+ avmcard *card = cinfo->card;
+ unsigned int port = card->port;
+ unsigned long flags;
+
+ b1_reset(port);
+ b1_reset(port);
+
+ memset(cinfo->version, 0, sizeof(cinfo->version));
+ spin_lock_irqsave(&card->lock, flags);
+ capilib_release(&cinfo->ncci_head);
+ spin_unlock_irqrestore(&card->lock, flags);
+ capi_ctr_reseted(ctrl);
+}
+
+void b1_register_appl(struct capi_ctr *ctrl,
+ u16 appl,
+ capi_register_params *rp)
+{
+ avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+ avmcard *card = cinfo->card;
+ unsigned int port = card->port;
+ unsigned long flags;
+ int nconn, want = rp->level3cnt;
+
+ if (want > 0) nconn = want;
+ else nconn = ctrl->profile.nbchannel * -want;
+ if (nconn == 0) nconn = ctrl->profile.nbchannel;
+
+ spin_lock_irqsave(&card->lock, flags);
+ b1_put_byte(port, SEND_REGISTER);
+ b1_put_word(port, appl);
+ b1_put_word(port, 1024 * (nconn+1));
+ b1_put_word(port, nconn);
+ b1_put_word(port, rp->datablkcnt);
+ b1_put_word(port, rp->datablklen);
+ spin_unlock_irqrestore(&card->lock, flags);
+}
+
+void b1_release_appl(struct capi_ctr *ctrl, u16 appl)
+{
+ avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+ avmcard *card = cinfo->card;
+ unsigned int port = card->port;
+ unsigned long flags;
+
+ spin_lock_irqsave(&card->lock, flags);
+ capilib_release_appl(&cinfo->ncci_head, appl);
+ b1_put_byte(port, SEND_RELEASE);
+ b1_put_word(port, appl);
+ spin_unlock_irqrestore(&card->lock, flags);
+}
+
+u16 b1_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
+{
+ avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+ avmcard *card = cinfo->card;
+ unsigned int port = card->port;
+ unsigned long flags;
+ u16 len = CAPIMSG_LEN(skb->data);
+ u8 cmd = CAPIMSG_COMMAND(skb->data);
+ u8 subcmd = CAPIMSG_SUBCOMMAND(skb->data);
+ u16 dlen, retval;
+
+ spin_lock_irqsave(&card->lock, flags);
+ if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) {
+ retval = capilib_data_b3_req(&cinfo->ncci_head,
+ CAPIMSG_APPID(skb->data),
+ CAPIMSG_NCCI(skb->data),
+ CAPIMSG_MSGID(skb->data));
+ if (retval != CAPI_NOERROR) {
+ spin_unlock_irqrestore(&card->lock, flags);
+ return retval;
+ }
+
+ dlen = CAPIMSG_DATALEN(skb->data);
+
+ b1_put_byte(port, SEND_DATA_B3_REQ);
+ b1_put_slice(port, skb->data, len);
+ b1_put_slice(port, skb->data + len, dlen);
+ } else {
+ b1_put_byte(port, SEND_MESSAGE);
+ b1_put_slice(port, skb->data, len);
+ }
+ spin_unlock_irqrestore(&card->lock, flags);
+
+ dev_kfree_skb_any(skb);
+ return CAPI_NOERROR;
+}
+
+/* ------------------------------------------------------------- */
+
+void b1_parse_version(avmctrl_info *cinfo)
+{
+ struct capi_ctr *ctrl = &cinfo->capi_ctrl;
+ avmcard *card = cinfo->card;
+ capi_profile *profp;
+ u8 *dversion;
+ u8 flag;
+ int i, j;
+
+ for (j = 0; j < AVM_MAXVERSION; j++)
+ cinfo->version[j] = "\0\0" + 1;
+ for (i = 0, j = 0;
+ j < AVM_MAXVERSION && i < cinfo->versionlen;
+ j++, i += cinfo->versionbuf[i] + 1)
+ cinfo->version[j] = &cinfo->versionbuf[i + 1];
+
+ strlcpy(ctrl->serial, cinfo->version[VER_SERIAL], sizeof(ctrl->serial));
+ memcpy(&ctrl->profile, cinfo->version[VER_PROFILE],sizeof(capi_profile));
+ strlcpy(ctrl->manu, "AVM GmbH", sizeof(ctrl->manu));
+ dversion = cinfo->version[VER_DRIVER];
+ ctrl->version.majorversion = 2;
+ ctrl->version.minorversion = 0;
+ ctrl->version.majormanuversion = (((dversion[0] - '0') & 0xf) << 4);
+ ctrl->version.majormanuversion |= ((dversion[2] - '0') & 0xf);
+ ctrl->version.minormanuversion = (dversion[3] - '0') << 4;
+ ctrl->version.minormanuversion |=
+ (dversion[5] - '0') * 10 + ((dversion[6] - '0') & 0xf);
+
+ profp = &ctrl->profile;
+
+ flag = ((u8 *)(profp->manu))[1];
+ switch (flag) {
+ case 0: if (cinfo->version[VER_CARDTYPE])
+ strcpy(cinfo->cardname, cinfo->version[VER_CARDTYPE]);
+ else strcpy(cinfo->cardname, "B1");
+ break;
+ case 3: strcpy(cinfo->cardname,"PCMCIA B"); break;
+ case 4: strcpy(cinfo->cardname,"PCMCIA M1"); break;
+ case 5: strcpy(cinfo->cardname,"PCMCIA M2"); break;
+ case 6: strcpy(cinfo->cardname,"B1 V3.0"); break;
+ case 7: strcpy(cinfo->cardname,"B1 PCI"); break;
+ default: sprintf(cinfo->cardname, "AVM?%u", (unsigned int)flag); break;
+ }
+ printk(KERN_NOTICE "%s: card %d \"%s\" ready.\n",
+ card->name, ctrl->cnr, cinfo->cardname);
+
+ flag = ((u8 *)(profp->manu))[3];
+ if (flag)
+ printk(KERN_NOTICE "%s: card %d Protocol:%s%s%s%s%s%s%s\n",
+ card->name,
+ ctrl->cnr,
+ (flag & 0x01) ? " DSS1" : "",
+ (flag & 0x02) ? " CT1" : "",
+ (flag & 0x04) ? " VN3" : "",
+ (flag & 0x08) ? " NI1" : "",
+ (flag & 0x10) ? " AUSTEL" : "",
+ (flag & 0x20) ? " ESS" : "",
+ (flag & 0x40) ? " 1TR6" : ""
+ );
+
+ flag = ((u8 *)(profp->manu))[5];
+ if (flag)
+ printk(KERN_NOTICE "%s: card %d Linetype:%s%s%s%s\n",
+ card->name,
+ ctrl->cnr,
+ (flag & 0x01) ? " point to point" : "",
+ (flag & 0x02) ? " point to multipoint" : "",
+ (flag & 0x08) ? " leased line without D-channel" : "",
+ (flag & 0x04) ? " leased line with D-channel" : ""
+ );
+}
+
+/* ------------------------------------------------------------- */
+
+irqreturn_t b1_interrupt(int interrupt, void *devptr)
+{
+ avmcard *card = devptr;
+ avmctrl_info *cinfo = &card->ctrlinfo[0];
+ struct capi_ctr *ctrl = &cinfo->capi_ctrl;
+ unsigned char b1cmd;
+ struct sk_buff *skb;
+
+ unsigned ApplId;
+ unsigned MsgLen;
+ unsigned DataB3Len;
+ unsigned NCCI;
+ unsigned WindowSize;
+ unsigned long flags;
+
+ spin_lock_irqsave(&card->lock, flags);
+
+ if (!b1_rx_full(card->port)) {
+ spin_unlock_irqrestore(&card->lock, flags);
+ return IRQ_NONE;
+ }
+
+ b1cmd = b1_get_byte(card->port);
+
+ switch (b1cmd) {
+
+ case RECEIVE_DATA_B3_IND:
+
+ ApplId = (unsigned) b1_get_word(card->port);
+ MsgLen = b1_get_slice(card->port, card->msgbuf);
+ DataB3Len = b1_get_slice(card->port, card->databuf);
+ spin_unlock_irqrestore(&card->lock, flags);
+
+ if (MsgLen < 30) { /* not CAPI 64Bit */
+ memset(card->msgbuf+MsgLen, 0, 30-MsgLen);
+ MsgLen = 30;
+ CAPIMSG_SETLEN(card->msgbuf, 30);
+ }
+ if (!(skb = alloc_skb(DataB3Len + MsgLen, GFP_ATOMIC))) {
+ printk(KERN_ERR "%s: incoming packet dropped\n",
+ card->name);
+ } else {
+ memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
+ memcpy(skb_put(skb, DataB3Len), card->databuf, DataB3Len);
+ capi_ctr_handle_message(ctrl, ApplId, skb);
+ }
+ break;
+
+ case RECEIVE_MESSAGE:
+
+ ApplId = (unsigned) b1_get_word(card->port);
+ MsgLen = b1_get_slice(card->port, card->msgbuf);
+ if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) {
+ printk(KERN_ERR "%s: incoming packet dropped\n",
+ card->name);
+ spin_unlock_irqrestore(&card->lock, flags);
+ } else {
+ memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
+ if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_CONF)
+ capilib_data_b3_conf(&cinfo->ncci_head, ApplId,
+ CAPIMSG_NCCI(skb->data),
+ CAPIMSG_MSGID(skb->data));
+ spin_unlock_irqrestore(&card->lock, flags);
+ capi_ctr_handle_message(ctrl, ApplId, skb);
+ }
+ break;
+
+ case RECEIVE_NEW_NCCI:
+
+ ApplId = b1_get_word(card->port);
+ NCCI = b1_get_word(card->port);
+ WindowSize = b1_get_word(card->port);
+ capilib_new_ncci(&cinfo->ncci_head, ApplId, NCCI, WindowSize);
+ spin_unlock_irqrestore(&card->lock, flags);
+ break;
+
+ case RECEIVE_FREE_NCCI:
+
+ ApplId = b1_get_word(card->port);
+ NCCI = b1_get_word(card->port);
+ if (NCCI != 0xffffffff)
+ capilib_free_ncci(&cinfo->ncci_head, ApplId, NCCI);
+ spin_unlock_irqrestore(&card->lock, flags);
+ break;
+
+ case RECEIVE_START:
+ /* b1_put_byte(card->port, SEND_POLLACK); */
+ spin_unlock_irqrestore(&card->lock, flags);
+ capi_ctr_resume_output(ctrl);
+ break;
+
+ case RECEIVE_STOP:
+ spin_unlock_irqrestore(&card->lock, flags);
+ capi_ctr_suspend_output(ctrl);
+ break;
+
+ case RECEIVE_INIT:
+
+ cinfo->versionlen = b1_get_slice(card->port, cinfo->versionbuf);
+ spin_unlock_irqrestore(&card->lock, flags);
+ b1_parse_version(cinfo);
+ printk(KERN_INFO "%s: %s-card (%s) now active\n",
+ card->name,
+ cinfo->version[VER_CARDTYPE],
+ cinfo->version[VER_DRIVER]);
+ capi_ctr_ready(ctrl);
+ break;
+
+ case RECEIVE_TASK_READY:
+ ApplId = (unsigned) b1_get_word(card->port);
+ MsgLen = b1_get_slice(card->port, card->msgbuf);
+ spin_unlock_irqrestore(&card->lock, flags);
+ card->msgbuf[MsgLen] = 0;
+ while ( MsgLen > 0
+ && ( card->msgbuf[MsgLen-1] == '\n'
+ || card->msgbuf[MsgLen-1] == '\r')) {
+ card->msgbuf[MsgLen-1] = 0;
+ MsgLen--;
+ }
+ printk(KERN_INFO "%s: task %d \"%s\" ready.\n",
+ card->name, ApplId, card->msgbuf);
+ break;
+
+ case RECEIVE_DEBUGMSG:
+ MsgLen = b1_get_slice(card->port, card->msgbuf);
+ spin_unlock_irqrestore(&card->lock, flags);
+ card->msgbuf[MsgLen] = 0;
+ while ( MsgLen > 0
+ && ( card->msgbuf[MsgLen-1] == '\n'
+ || card->msgbuf[MsgLen-1] == '\r')) {
+ card->msgbuf[MsgLen-1] = 0;
+ MsgLen--;
+ }
+ printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf);
+ break;
+
+ case 0xff:
+ spin_unlock_irqrestore(&card->lock, flags);
+ printk(KERN_ERR "%s: card removed ?\n", card->name);
+ return IRQ_NONE;
+ default:
+ spin_unlock_irqrestore(&card->lock, flags);
+ printk(KERN_ERR "%s: b1_interrupt: 0x%x ???\n",
+ card->name, b1cmd);
+ return IRQ_HANDLED;
+ }
+ return IRQ_HANDLED;
+}
+
+/* ------------------------------------------------------------- */
+int b1ctl_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, struct capi_ctr *ctrl)
+{
+ avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+ avmcard *card = cinfo->card;
+ u8 flag;
+ int len = 0;
+ char *s;
+
+ len += sprintf(page+len, "%-16s %s\n", "name", card->name);
+ len += sprintf(page+len, "%-16s 0x%x\n", "io", card->port);
+ len += sprintf(page+len, "%-16s %d\n", "irq", card->irq);
+ switch (card->cardtype) {
+ case avm_b1isa: s = "B1 ISA"; break;
+ case avm_b1pci: s = "B1 PCI"; break;
+ case avm_b1pcmcia: s = "B1 PCMCIA"; break;
+ case avm_m1: s = "M1"; break;
+ case avm_m2: s = "M2"; break;
+ case avm_t1isa: s = "T1 ISA (HEMA)"; break;
+ case avm_t1pci: s = "T1 PCI"; break;
+ case avm_c4: s = "C4"; break;
+ case avm_c2: s = "C2"; break;
+ default: s = "???"; break;
+ }
+ len += sprintf(page+len, "%-16s %s\n", "type", s);
+ if (card->cardtype == avm_t1isa)
+ len += sprintf(page+len, "%-16s %d\n", "cardnr", card->cardnr);
+ if ((s = cinfo->version[VER_DRIVER]) != NULL)
+ len += sprintf(page+len, "%-16s %s\n", "ver_driver", s);
+ if ((s = cinfo->version[VER_CARDTYPE]) != NULL)
+ len += sprintf(page+len, "%-16s %s\n", "ver_cardtype", s);
+ if ((s = cinfo->version[VER_SERIAL]) != NULL)
+ len += sprintf(page+len, "%-16s %s\n", "ver_serial", s);
+
+ if (card->cardtype != avm_m1) {
+ flag = ((u8 *)(ctrl->profile.manu))[3];
+ if (flag)
+ len += sprintf(page+len, "%-16s%s%s%s%s%s%s%s\n",
+ "protocol",
+ (flag & 0x01) ? " DSS1" : "",
+ (flag & 0x02) ? " CT1" : "",
+ (flag & 0x04) ? " VN3" : "",
+ (flag & 0x08) ? " NI1" : "",
+ (flag & 0x10) ? " AUSTEL" : "",
+ (flag & 0x20) ? " ESS" : "",
+ (flag & 0x40) ? " 1TR6" : ""
+ );
+ }
+ if (card->cardtype != avm_m1) {
+ flag = ((u8 *)(ctrl->profile.manu))[5];
+ if (flag)
+ len += sprintf(page+len, "%-16s%s%s%s%s\n",
+ "linetype",
+ (flag & 0x01) ? " point to point" : "",
+ (flag & 0x02) ? " point to multipoint" : "",
+ (flag & 0x08) ? " leased line without D-channel" : "",
+ (flag & 0x04) ? " leased line with D-channel" : ""
+ );
+ }
+ len += sprintf(page+len, "%-16s %s\n", "cardname", cinfo->cardname);
+
+ if (off+count >= len)
+ *eof = 1;
+ if (len < off)
+ return 0;
+ *start = page + off;
+ return ((count < len-off) ? count : len-off);
+}
+
+/* ------------------------------------------------------------- */
+
+#ifdef CONFIG_PCI
+
+avmcard_dmainfo *
+avmcard_dma_alloc(char *name, struct pci_dev *pdev, long rsize, long ssize)
+{
+ avmcard_dmainfo *p;
+ void *buf;
+
+ p = kzalloc(sizeof(avmcard_dmainfo), GFP_KERNEL);
+ if (!p) {
+ printk(KERN_WARNING "%s: no memory.\n", name);
+ goto err;
+ }
+
+ p->recvbuf.size = rsize;
+ buf = pci_alloc_consistent(pdev, rsize, &p->recvbuf.dmaaddr);
+ if (!buf) {
+ printk(KERN_WARNING "%s: allocation of receive dma buffer failed.\n", name);
+ goto err_kfree;
+ }
+ p->recvbuf.dmabuf = buf;
+
+ p->sendbuf.size = ssize;
+ buf = pci_alloc_consistent(pdev, ssize, &p->sendbuf.dmaaddr);
+ if (!buf) {
+ printk(KERN_WARNING "%s: allocation of send dma buffer failed.\n", name);
+ goto err_free_consistent;
+ }
+
+ p->sendbuf.dmabuf = buf;
+ skb_queue_head_init(&p->send_queue);
+
+ return p;
+
+ err_free_consistent:
+ pci_free_consistent(p->pcidev, p->recvbuf.size,
+ p->recvbuf.dmabuf, p->recvbuf.dmaaddr);
+ err_kfree:
+ kfree(p);
+ err:
+ return NULL;
+}
+
+void avmcard_dma_free(avmcard_dmainfo *p)
+{
+ pci_free_consistent(p->pcidev, p->recvbuf.size,
+ p->recvbuf.dmabuf, p->recvbuf.dmaaddr);
+ pci_free_consistent(p->pcidev, p->sendbuf.size,
+ p->sendbuf.dmabuf, p->sendbuf.dmaaddr);
+ skb_queue_purge(&p->send_queue);
+ kfree(p);
+}
+
+EXPORT_SYMBOL(avmcard_dma_alloc);
+EXPORT_SYMBOL(avmcard_dma_free);
+
+#endif
+
+EXPORT_SYMBOL(b1_irq_table);
+
+EXPORT_SYMBOL(b1_alloc_card);
+EXPORT_SYMBOL(b1_free_card);
+EXPORT_SYMBOL(b1_detect);
+EXPORT_SYMBOL(b1_getrevision);
+EXPORT_SYMBOL(b1_load_t4file);
+EXPORT_SYMBOL(b1_load_config);
+EXPORT_SYMBOL(b1_loaded);
+EXPORT_SYMBOL(b1_load_firmware);
+EXPORT_SYMBOL(b1_reset_ctr);
+EXPORT_SYMBOL(b1_register_appl);
+EXPORT_SYMBOL(b1_release_appl);
+EXPORT_SYMBOL(b1_send_message);
+
+EXPORT_SYMBOL(b1_parse_version);
+EXPORT_SYMBOL(b1_interrupt);
+
+EXPORT_SYMBOL(b1ctl_read_proc);
+
+static int __init b1_init(void)
+{
+ char *p;
+ char rev[32];
+
+ if ((p = strchr(revision, ':')) != NULL && p[1]) {
+ strlcpy(rev, p + 2, 32);
+ if ((p = strchr(rev, '$')) != NULL && p > rev)
+ *(p-1) = 0;
+ } else
+ strcpy(rev, "1.0");
+
+ printk(KERN_INFO "b1: revision %s\n", rev);
+
+ return 0;
+}
+
+static void __exit b1_exit(void)
+{
+}
+
+module_init(b1_init);
+module_exit(b1_exit);
diff --git a/drivers/isdn/hardware/avm/b1dma.c b/drivers/isdn/hardware/avm/b1dma.c
new file mode 100644
index 0000000..da34b98
--- /dev/null
+++ b/drivers/isdn/hardware/avm/b1dma.c
@@ -0,0 +1,990 @@
+/* $Id: b1dma.c,v 1.1.2.3 2004/02/10 01:07:12 keil Exp $
+ *
+ * Common module for AVM B1 cards that support dma with AMCC
+ *
+ * Copyright 2000 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/capi.h>
+#include <linux/kernelcapi.h>
+#include <asm/io.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <linux/netdevice.h>
+#include <linux/isdn/capilli.h>
+#include "avmcard.h"
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+
+static char *revision = "$Revision: 1.1.2.3 $";
+
+#undef AVM_B1DMA_DEBUG
+
+/* ------------------------------------------------------------- */
+
+MODULE_DESCRIPTION("CAPI4Linux: DMA support for active AVM cards");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+
+static int suppress_pollack = 0;
+module_param(suppress_pollack, bool, 0);
+
+/* ------------------------------------------------------------- */
+
+static void b1dma_dispatch_tx(avmcard *card);
+
+/* ------------------------------------------------------------- */
+
+/* S5933 */
+
+#define AMCC_RXPTR 0x24
+#define AMCC_RXLEN 0x28
+#define AMCC_TXPTR 0x2c
+#define AMCC_TXLEN 0x30
+
+#define AMCC_INTCSR 0x38
+# define EN_READ_TC_INT 0x00008000L
+# define EN_WRITE_TC_INT 0x00004000L
+# define EN_TX_TC_INT EN_READ_TC_INT
+# define EN_RX_TC_INT EN_WRITE_TC_INT
+# define AVM_FLAG 0x30000000L
+
+# define ANY_S5933_INT 0x00800000L
+# define READ_TC_INT 0x00080000L
+# define WRITE_TC_INT 0x00040000L
+# define TX_TC_INT READ_TC_INT
+# define RX_TC_INT WRITE_TC_INT
+# define MASTER_ABORT_INT 0x00100000L
+# define TARGET_ABORT_INT 0x00200000L
+# define BUS_MASTER_INT 0x00200000L
+# define ALL_INT 0x000C0000L
+
+#define AMCC_MCSR 0x3c
+# define A2P_HI_PRIORITY 0x00000100L
+# define EN_A2P_TRANSFERS 0x00000400L
+# define P2A_HI_PRIORITY 0x00001000L
+# define EN_P2A_TRANSFERS 0x00004000L
+# define RESET_A2P_FLAGS 0x04000000L
+# define RESET_P2A_FLAGS 0x02000000L
+
+/* ------------------------------------------------------------- */
+
+static inline void b1dma_writel(avmcard *card, u32 value, int off)
+{
+ writel(value, card->mbase + off);
+}
+
+static inline u32 b1dma_readl(avmcard *card, int off)
+{
+ return readl(card->mbase + off);
+}
+
+/* ------------------------------------------------------------- */
+
+static inline int b1dma_tx_empty(unsigned int port)
+{
+ return inb(port + 0x03) & 0x1;
+}
+
+static inline int b1dma_rx_full(unsigned int port)
+{
+ return inb(port + 0x02) & 0x1;
+}
+
+static int b1dma_tolink(avmcard *card, void *buf, unsigned int len)
+{
+ unsigned long stop = jiffies + 1 * HZ; /* maximum wait time 1 sec */
+ unsigned char *s = (unsigned char *)buf;
+ while (len--) {
+ while ( !b1dma_tx_empty(card->port)
+ && time_before(jiffies, stop));
+ if (!b1dma_tx_empty(card->port))
+ return -1;
+ t1outp(card->port, 0x01, *s++);
+ }
+ return 0;
+}
+
+static int b1dma_fromlink(avmcard *card, void *buf, unsigned int len)
+{
+ unsigned long stop = jiffies + 1 * HZ; /* maximum wait time 1 sec */
+ unsigned char *s = (unsigned char *)buf;
+ while (len--) {
+ while ( !b1dma_rx_full(card->port)
+ && time_before(jiffies, stop));
+ if (!b1dma_rx_full(card->port))
+ return -1;
+ *s++ = t1inp(card->port, 0x00);
+ }
+ return 0;
+}
+
+static int WriteReg(avmcard *card, u32 reg, u8 val)
+{
+ u8 cmd = 0x00;
+ if ( b1dma_tolink(card, &cmd, 1) == 0
+ && b1dma_tolink(card, &reg, 4) == 0) {
+ u32 tmp = val;
+ return b1dma_tolink(card, &tmp, 4);
+ }
+ return -1;
+}
+
+static u8 ReadReg(avmcard *card, u32 reg)
+{
+ u8 cmd = 0x01;
+ if ( b1dma_tolink(card, &cmd, 1) == 0
+ && b1dma_tolink(card, &reg, 4) == 0) {
+ u32 tmp;
+ if (b1dma_fromlink(card, &tmp, 4) == 0)
+ return (u8)tmp;
+ }
+ return 0xff;
+}
+
+/* ------------------------------------------------------------- */
+
+static inline void _put_byte(void **pp, u8 val)
+{
+ u8 *s = *pp;
+ *s++ = val;
+ *pp = s;
+}
+
+static inline void _put_word(void **pp, u32 val)
+{
+ u8 *s = *pp;
+ *s++ = val & 0xff;
+ *s++ = (val >> 8) & 0xff;
+ *s++ = (val >> 16) & 0xff;
+ *s++ = (val >> 24) & 0xff;
+ *pp = s;
+}
+
+static inline void _put_slice(void **pp, unsigned char *dp, unsigned int len)
+{
+ unsigned i = len;
+ _put_word(pp, i);
+ while (i-- > 0)
+ _put_byte(pp, *dp++);
+}
+
+static inline u8 _get_byte(void **pp)
+{
+ u8 *s = *pp;
+ u8 val;
+ val = *s++;
+ *pp = s;
+ return val;
+}
+
+static inline u32 _get_word(void **pp)
+{
+ u8 *s = *pp;
+ u32 val;
+ val = *s++;
+ val |= (*s++ << 8);
+ val |= (*s++ << 16);
+ val |= (*s++ << 24);
+ *pp = s;
+ return val;
+}
+
+static inline u32 _get_slice(void **pp, unsigned char *dp)
+{
+ unsigned int len, i;
+
+ len = i = _get_word(pp);
+ while (i-- > 0) *dp++ = _get_byte(pp);
+ return len;
+}
+
+/* ------------------------------------------------------------- */
+
+void b1dma_reset(avmcard *card)
+{
+ card->csr = 0x0;
+ b1dma_writel(card, card->csr, AMCC_INTCSR);
+ b1dma_writel(card, 0, AMCC_MCSR);
+ b1dma_writel(card, 0, AMCC_RXLEN);
+ b1dma_writel(card, 0, AMCC_TXLEN);
+
+ t1outp(card->port, 0x10, 0x00);
+ t1outp(card->port, 0x07, 0x00);
+
+ b1dma_writel(card, 0, AMCC_MCSR);
+ mdelay(10);
+ b1dma_writel(card, 0x0f000000, AMCC_MCSR); /* reset all */
+ mdelay(10);
+ b1dma_writel(card, 0, AMCC_MCSR);
+ if (card->cardtype == avm_t1pci)
+ mdelay(42);
+ else
+ mdelay(10);
+}
+
+/* ------------------------------------------------------------- */
+
+static int b1dma_detect(avmcard *card)
+{
+ b1dma_writel(card, 0, AMCC_MCSR);
+ mdelay(10);
+ b1dma_writel(card, 0x0f000000, AMCC_MCSR); /* reset all */
+ mdelay(10);
+ b1dma_writel(card, 0, AMCC_MCSR);
+ mdelay(42);
+
+ b1dma_writel(card, 0, AMCC_RXLEN);
+ b1dma_writel(card, 0, AMCC_TXLEN);
+ card->csr = 0x0;
+ b1dma_writel(card, card->csr, AMCC_INTCSR);
+
+ if (b1dma_readl(card, AMCC_MCSR) != 0x000000E6)
+ return 1;
+
+ b1dma_writel(card, 0xffffffff, AMCC_RXPTR);
+ b1dma_writel(card, 0xffffffff, AMCC_TXPTR);
+ if ( b1dma_readl(card, AMCC_RXPTR) != 0xfffffffc
+ || b1dma_readl(card, AMCC_TXPTR) != 0xfffffffc)
+ return 2;
+
+ b1dma_writel(card, 0x0, AMCC_RXPTR);
+ b1dma_writel(card, 0x0, AMCC_TXPTR);
+ if ( b1dma_readl(card, AMCC_RXPTR) != 0x0
+ || b1dma_readl(card, AMCC_TXPTR) != 0x0)
+ return 3;
+
+ t1outp(card->port, 0x10, 0x00);
+ t1outp(card->port, 0x07, 0x00);
+
+ t1outp(card->port, 0x02, 0x02);
+ t1outp(card->port, 0x03, 0x02);
+
+ if ( (t1inp(card->port, 0x02) & 0xFE) != 0x02
+ || t1inp(card->port, 0x3) != 0x03)
+ return 4;
+
+ t1outp(card->port, 0x02, 0x00);
+ t1outp(card->port, 0x03, 0x00);
+
+ if ( (t1inp(card->port, 0x02) & 0xFE) != 0x00
+ || t1inp(card->port, 0x3) != 0x01)
+ return 5;
+
+ return 0;
+}
+
+int t1pci_detect(avmcard *card)
+{
+ int ret;
+
+ if ((ret = b1dma_detect(card)) != 0)
+ return ret;
+
+ /* Transputer test */
+
+ if ( WriteReg(card, 0x80001000, 0x11) != 0
+ || WriteReg(card, 0x80101000, 0x22) != 0
+ || WriteReg(card, 0x80201000, 0x33) != 0
+ || WriteReg(card, 0x80301000, 0x44) != 0)
+ return 6;
+
+ if ( ReadReg(card, 0x80001000) != 0x11
+ || ReadReg(card, 0x80101000) != 0x22
+ || ReadReg(card, 0x80201000) != 0x33
+ || ReadReg(card, 0x80301000) != 0x44)
+ return 7;
+
+ if ( WriteReg(card, 0x80001000, 0x55) != 0
+ || WriteReg(card, 0x80101000, 0x66) != 0
+ || WriteReg(card, 0x80201000, 0x77) != 0
+ || WriteReg(card, 0x80301000, 0x88) != 0)
+ return 8;
+
+ if ( ReadReg(card, 0x80001000) != 0x55
+ || ReadReg(card, 0x80101000) != 0x66
+ || ReadReg(card, 0x80201000) != 0x77
+ || ReadReg(card, 0x80301000) != 0x88)
+ return 9;
+
+ return 0;
+}
+
+int b1pciv4_detect(avmcard *card)
+{
+ int ret, i;
+
+ if ((ret = b1dma_detect(card)) != 0)
+ return ret;
+
+ for (i=0; i < 5 ; i++) {
+ if (WriteReg(card, 0x80A00000, 0x21) != 0)
+ return 6;
+ if ((ReadReg(card, 0x80A00000) & 0x01) != 0x01)
+ return 7;
+ }
+ for (i=0; i < 5 ; i++) {
+ if (WriteReg(card, 0x80A00000, 0x20) != 0)
+ return 8;
+ if ((ReadReg(card, 0x80A00000) & 0x01) != 0x00)
+ return 9;
+ }
+
+ return 0;
+}
+
+static void b1dma_queue_tx(avmcard *card, struct sk_buff *skb)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&card->lock, flags);
+
+ skb_queue_tail(&card->dma->send_queue, skb);
+
+ if (!(card->csr & EN_TX_TC_INT)) {
+ b1dma_dispatch_tx(card);
+ b1dma_writel(card, card->csr, AMCC_INTCSR);
+ }
+
+ spin_unlock_irqrestore(&card->lock, flags);
+}
+
+/* ------------------------------------------------------------- */
+
+static void b1dma_dispatch_tx(avmcard *card)
+{
+ avmcard_dmainfo *dma = card->dma;
+ struct sk_buff *skb;
+ u8 cmd, subcmd;
+ u16 len;
+ u32 txlen;
+ void *p;
+
+ skb = skb_dequeue(&dma->send_queue);
+
+ len = CAPIMSG_LEN(skb->data);
+
+ if (len) {
+ cmd = CAPIMSG_COMMAND(skb->data);
+ subcmd = CAPIMSG_SUBCOMMAND(skb->data);
+
+ p = dma->sendbuf.dmabuf;
+
+ if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) {
+ u16 dlen = CAPIMSG_DATALEN(skb->data);
+ _put_byte(&p, SEND_DATA_B3_REQ);
+ _put_slice(&p, skb->data, len);
+ _put_slice(&p, skb->data + len, dlen);
+ } else {
+ _put_byte(&p, SEND_MESSAGE);
+ _put_slice(&p, skb->data, len);
+ }
+ txlen = (u8 *)p - (u8 *)dma->sendbuf.dmabuf;
+#ifdef AVM_B1DMA_DEBUG
+ printk(KERN_DEBUG "tx: put msg len=%d\n", txlen);
+#endif
+ } else {
+ txlen = skb->len-2;
+#ifdef AVM_B1DMA_POLLDEBUG
+ if (skb->data[2] == SEND_POLLACK)
+ printk(KERN_INFO "%s: send ack\n", card->name);
+#endif
+#ifdef AVM_B1DMA_DEBUG
+ printk(KERN_DEBUG "tx: put 0x%x len=%d\n",
+ skb->data[2], txlen);
+#endif
+ skb_copy_from_linear_data_offset(skb, 2, dma->sendbuf.dmabuf,
+ skb->len - 2);
+ }
+ txlen = (txlen + 3) & ~3;
+
+ b1dma_writel(card, dma->sendbuf.dmaaddr, AMCC_TXPTR);
+ b1dma_writel(card, txlen, AMCC_TXLEN);
+
+ card->csr |= EN_TX_TC_INT;
+
+ dev_kfree_skb_any(skb);
+}
+
+/* ------------------------------------------------------------- */
+
+static void queue_pollack(avmcard *card)
+{
+ struct sk_buff *skb;
+ void *p;
+
+ skb = alloc_skb(3, GFP_ATOMIC);
+ if (!skb) {
+ printk(KERN_CRIT "%s: no memory, lost poll ack\n",
+ card->name);
+ return;
+ }
+ p = skb->data;
+ _put_byte(&p, 0);
+ _put_byte(&p, 0);
+ _put_byte(&p, SEND_POLLACK);
+ skb_put(skb, (u8 *)p - (u8 *)skb->data);
+
+ b1dma_queue_tx(card, skb);
+}
+
+/* ------------------------------------------------------------- */
+
+static void b1dma_handle_rx(avmcard *card)
+{
+ avmctrl_info *cinfo = &card->ctrlinfo[0];
+ avmcard_dmainfo *dma = card->dma;
+ struct capi_ctr *ctrl = &cinfo->capi_ctrl;
+ struct sk_buff *skb;
+ void *p = dma->recvbuf.dmabuf+4;
+ u32 ApplId, MsgLen, DataB3Len, NCCI, WindowSize;
+ u8 b1cmd = _get_byte(&p);
+
+#ifdef AVM_B1DMA_DEBUG
+ printk(KERN_DEBUG "rx: 0x%x %lu\n", b1cmd, (unsigned long)dma->recvlen);
+#endif
+
+ switch (b1cmd) {
+ case RECEIVE_DATA_B3_IND:
+
+ ApplId = (unsigned) _get_word(&p);
+ MsgLen = _get_slice(&p, card->msgbuf);
+ DataB3Len = _get_slice(&p, card->databuf);
+
+ if (MsgLen < 30) { /* not CAPI 64Bit */
+ memset(card->msgbuf+MsgLen, 0, 30-MsgLen);
+ MsgLen = 30;
+ CAPIMSG_SETLEN(card->msgbuf, 30);
+ }
+ if (!(skb = alloc_skb(DataB3Len+MsgLen, GFP_ATOMIC))) {
+ printk(KERN_ERR "%s: incoming packet dropped\n",
+ card->name);
+ } else {
+ memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
+ memcpy(skb_put(skb, DataB3Len), card->databuf, DataB3Len);
+ capi_ctr_handle_message(ctrl, ApplId, skb);
+ }
+ break;
+
+ case RECEIVE_MESSAGE:
+
+ ApplId = (unsigned) _get_word(&p);
+ MsgLen = _get_slice(&p, card->msgbuf);
+ if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) {
+ printk(KERN_ERR "%s: incoming packet dropped\n",
+ card->name);
+ } else {
+ memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
+ if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_CONF) {
+ spin_lock(&card->lock);
+ capilib_data_b3_conf(&cinfo->ncci_head, ApplId,
+ CAPIMSG_NCCI(skb->data),
+ CAPIMSG_MSGID(skb->data));
+ spin_unlock(&card->lock);
+ }
+ capi_ctr_handle_message(ctrl, ApplId, skb);
+ }
+ break;
+
+ case RECEIVE_NEW_NCCI:
+
+ ApplId = _get_word(&p);
+ NCCI = _get_word(&p);
+ WindowSize = _get_word(&p);
+ spin_lock(&card->lock);
+ capilib_new_ncci(&cinfo->ncci_head, ApplId, NCCI, WindowSize);
+ spin_unlock(&card->lock);
+ break;
+
+ case RECEIVE_FREE_NCCI:
+
+ ApplId = _get_word(&p);
+ NCCI = _get_word(&p);
+
+ if (NCCI != 0xffffffff) {
+ spin_lock(&card->lock);
+ capilib_free_ncci(&cinfo->ncci_head, ApplId, NCCI);
+ spin_unlock(&card->lock);
+ }
+ break;
+
+ case RECEIVE_START:
+#ifdef AVM_B1DMA_POLLDEBUG
+ printk(KERN_INFO "%s: receive poll\n", card->name);
+#endif
+ if (!suppress_pollack)
+ queue_pollack(card);
+ capi_ctr_resume_output(ctrl);
+ break;
+
+ case RECEIVE_STOP:
+ capi_ctr_suspend_output(ctrl);
+ break;
+
+ case RECEIVE_INIT:
+
+ cinfo->versionlen = _get_slice(&p, cinfo->versionbuf);
+ b1_parse_version(cinfo);
+ printk(KERN_INFO "%s: %s-card (%s) now active\n",
+ card->name,
+ cinfo->version[VER_CARDTYPE],
+ cinfo->version[VER_DRIVER]);
+ capi_ctr_ready(ctrl);
+ break;
+
+ case RECEIVE_TASK_READY:
+ ApplId = (unsigned) _get_word(&p);
+ MsgLen = _get_slice(&p, card->msgbuf);
+ card->msgbuf[MsgLen] = 0;
+ while ( MsgLen > 0
+ && ( card->msgbuf[MsgLen-1] == '\n'
+ || card->msgbuf[MsgLen-1] == '\r')) {
+ card->msgbuf[MsgLen-1] = 0;
+ MsgLen--;
+ }
+ printk(KERN_INFO "%s: task %d \"%s\" ready.\n",
+ card->name, ApplId, card->msgbuf);
+ break;
+
+ case RECEIVE_DEBUGMSG:
+ MsgLen = _get_slice(&p, card->msgbuf);
+ card->msgbuf[MsgLen] = 0;
+ while ( MsgLen > 0
+ && ( card->msgbuf[MsgLen-1] == '\n'
+ || card->msgbuf[MsgLen-1] == '\r')) {
+ card->msgbuf[MsgLen-1] = 0;
+ MsgLen--;
+ }
+ printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf);
+ break;
+
+ default:
+ printk(KERN_ERR "%s: b1dma_interrupt: 0x%x ???\n",
+ card->name, b1cmd);
+ return;
+ }
+}
+
+/* ------------------------------------------------------------- */
+
+static void b1dma_handle_interrupt(avmcard *card)
+{
+ u32 status;
+ u32 newcsr;
+
+ spin_lock(&card->lock);
+
+ status = b1dma_readl(card, AMCC_INTCSR);
+ if ((status & ANY_S5933_INT) == 0) {
+ spin_unlock(&card->lock);
+ return;
+ }
+
+ newcsr = card->csr | (status & ALL_INT);
+ if (status & TX_TC_INT) newcsr &= ~EN_TX_TC_INT;
+ if (status & RX_TC_INT) newcsr &= ~EN_RX_TC_INT;
+ b1dma_writel(card, newcsr, AMCC_INTCSR);
+
+ if ((status & RX_TC_INT) != 0) {
+ struct avmcard_dmainfo *dma = card->dma;
+ u32 rxlen;
+ if (card->dma->recvlen == 0) {
+ rxlen = b1dma_readl(card, AMCC_RXLEN);
+ if (rxlen == 0) {
+ dma->recvlen = *((u32 *)dma->recvbuf.dmabuf);
+ rxlen = (dma->recvlen + 3) & ~3;
+ b1dma_writel(card, dma->recvbuf.dmaaddr+4, AMCC_RXPTR);
+ b1dma_writel(card, rxlen, AMCC_RXLEN);
+#ifdef AVM_B1DMA_DEBUG
+ } else {
+ printk(KERN_ERR "%s: rx not complete (%d).\n",
+ card->name, rxlen);
+#endif
+ }
+ } else {
+ spin_unlock(&card->lock);
+ b1dma_handle_rx(card);
+ dma->recvlen = 0;
+ spin_lock(&card->lock);
+ b1dma_writel(card, dma->recvbuf.dmaaddr, AMCC_RXPTR);
+ b1dma_writel(card, 4, AMCC_RXLEN);
+ }
+ }
+
+ if ((status & TX_TC_INT) != 0) {
+ if (skb_queue_empty(&card->dma->send_queue))
+ card->csr &= ~EN_TX_TC_INT;
+ else
+ b1dma_dispatch_tx(card);
+ }
+ b1dma_writel(card, card->csr, AMCC_INTCSR);
+
+ spin_unlock(&card->lock);
+}
+
+irqreturn_t b1dma_interrupt(int interrupt, void *devptr)
+{
+ avmcard *card = devptr;
+
+ b1dma_handle_interrupt(card);
+ return IRQ_HANDLED;
+}
+
+/* ------------------------------------------------------------- */
+
+static int b1dma_loaded(avmcard *card)
+{
+ unsigned long stop;
+ unsigned char ans;
+ unsigned long tout = 2;
+ unsigned int base = card->port;
+
+ for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) {
+ if (b1_tx_empty(base))
+ break;
+ }
+ if (!b1_tx_empty(base)) {
+ printk(KERN_ERR "%s: b1dma_loaded: tx err, corrupted t4 file ?\n",
+ card->name);
+ return 0;
+ }
+ b1_put_byte(base, SEND_POLLACK);
+ for (stop = jiffies + tout * HZ; time_before(jiffies, stop);) {
+ if (b1_rx_full(base)) {
+ if ((ans = b1_get_byte(base)) == RECEIVE_POLLDWORD) {
+ return 1;
+ }
+ printk(KERN_ERR "%s: b1dma_loaded: got 0x%x, firmware not running in dword mode\n", card->name, ans);
+ return 0;
+ }
+ }
+ printk(KERN_ERR "%s: b1dma_loaded: firmware not running\n", card->name);
+ return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+static void b1dma_send_init(avmcard *card)
+{
+ struct sk_buff *skb;
+ void *p;
+
+ skb = alloc_skb(15, GFP_ATOMIC);
+ if (!skb) {
+ printk(KERN_CRIT "%s: no memory, lost register appl.\n",
+ card->name);
+ return;
+ }
+ p = skb->data;
+ _put_byte(&p, 0);
+ _put_byte(&p, 0);
+ _put_byte(&p, SEND_INIT);
+ _put_word(&p, CAPI_MAXAPPL);
+ _put_word(&p, AVM_NCCI_PER_CHANNEL*30);
+ _put_word(&p, card->cardnr - 1);
+ skb_put(skb, (u8 *)p - (u8 *)skb->data);
+
+ b1dma_queue_tx(card, skb);
+}
+
+int b1dma_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
+{
+ avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+ avmcard *card = cinfo->card;
+ int retval;
+
+ b1dma_reset(card);
+
+ if ((retval = b1_load_t4file(card, &data->firmware))) {
+ b1dma_reset(card);
+ printk(KERN_ERR "%s: failed to load t4file!!\n",
+ card->name);
+ return retval;
+ }
+
+ if (data->configuration.len > 0 && data->configuration.data) {
+ if ((retval = b1_load_config(card, &data->configuration))) {
+ b1dma_reset(card);
+ printk(KERN_ERR "%s: failed to load config!!\n",
+ card->name);
+ return retval;
+ }
+ }
+
+ if (!b1dma_loaded(card)) {
+ b1dma_reset(card);
+ printk(KERN_ERR "%s: failed to load t4file.\n", card->name);
+ return -EIO;
+ }
+
+ card->csr = AVM_FLAG;
+ b1dma_writel(card, card->csr, AMCC_INTCSR);
+ b1dma_writel(card, EN_A2P_TRANSFERS|EN_P2A_TRANSFERS|A2P_HI_PRIORITY|
+ P2A_HI_PRIORITY|RESET_A2P_FLAGS|RESET_P2A_FLAGS,
+ AMCC_MCSR);
+ t1outp(card->port, 0x07, 0x30);
+ t1outp(card->port, 0x10, 0xF0);
+
+ card->dma->recvlen = 0;
+ b1dma_writel(card, card->dma->recvbuf.dmaaddr, AMCC_RXPTR);
+ b1dma_writel(card, 4, AMCC_RXLEN);
+ card->csr |= EN_RX_TC_INT;
+ b1dma_writel(card, card->csr, AMCC_INTCSR);
+
+ b1dma_send_init(card);
+
+ return 0;
+}
+
+void b1dma_reset_ctr(struct capi_ctr *ctrl)
+{
+ avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+ avmcard *card = cinfo->card;
+ unsigned long flags;
+
+ spin_lock_irqsave(&card->lock, flags);
+ b1dma_reset(card);
+
+ memset(cinfo->version, 0, sizeof(cinfo->version));
+ capilib_release(&cinfo->ncci_head);
+ spin_unlock_irqrestore(&card->lock, flags);
+ capi_ctr_reseted(ctrl);
+}
+
+/* ------------------------------------------------------------- */
+
+void b1dma_register_appl(struct capi_ctr *ctrl,
+ u16 appl,
+ capi_register_params *rp)
+{
+ avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+ avmcard *card = cinfo->card;
+ struct sk_buff *skb;
+ int want = rp->level3cnt;
+ int nconn;
+ void *p;
+
+ if (want > 0) nconn = want;
+ else nconn = ctrl->profile.nbchannel * -want;
+ if (nconn == 0) nconn = ctrl->profile.nbchannel;
+
+ skb = alloc_skb(23, GFP_ATOMIC);
+ if (!skb) {
+ printk(KERN_CRIT "%s: no memory, lost register appl.\n",
+ card->name);
+ return;
+ }
+ p = skb->data;
+ _put_byte(&p, 0);
+ _put_byte(&p, 0);
+ _put_byte(&p, SEND_REGISTER);
+ _put_word(&p, appl);
+ _put_word(&p, 1024 * (nconn+1));
+ _put_word(&p, nconn);
+ _put_word(&p, rp->datablkcnt);
+ _put_word(&p, rp->datablklen);
+ skb_put(skb, (u8 *)p - (u8 *)skb->data);
+
+ b1dma_queue_tx(card, skb);
+}
+
+/* ------------------------------------------------------------- */
+
+void b1dma_release_appl(struct capi_ctr *ctrl, u16 appl)
+{
+ avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+ avmcard *card = cinfo->card;
+ struct sk_buff *skb;
+ void *p;
+ unsigned long flags;
+
+ spin_lock_irqsave(&card->lock, flags);
+ capilib_release_appl(&cinfo->ncci_head, appl);
+ spin_unlock_irqrestore(&card->lock, flags);
+
+ skb = alloc_skb(7, GFP_ATOMIC);
+ if (!skb) {
+ printk(KERN_CRIT "%s: no memory, lost release appl.\n",
+ card->name);
+ return;
+ }
+ p = skb->data;
+ _put_byte(&p, 0);
+ _put_byte(&p, 0);
+ _put_byte(&p, SEND_RELEASE);
+ _put_word(&p, appl);
+
+ skb_put(skb, (u8 *)p - (u8 *)skb->data);
+
+ b1dma_queue_tx(card, skb);
+}
+
+/* ------------------------------------------------------------- */
+
+u16 b1dma_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
+{
+ avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+ avmcard *card = cinfo->card;
+ u16 retval = CAPI_NOERROR;
+
+ if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_REQ) {
+ unsigned long flags;
+ spin_lock_irqsave(&card->lock, flags);
+ retval = capilib_data_b3_req(&cinfo->ncci_head,
+ CAPIMSG_APPID(skb->data),
+ CAPIMSG_NCCI(skb->data),
+ CAPIMSG_MSGID(skb->data));
+ spin_unlock_irqrestore(&card->lock, flags);
+ }
+ if (retval == CAPI_NOERROR)
+ b1dma_queue_tx(card, skb);
+
+ return retval;
+}
+
+/* ------------------------------------------------------------- */
+
+int b1dmactl_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, struct capi_ctr *ctrl)
+{
+ avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+ avmcard *card = cinfo->card;
+ u8 flag;
+ int len = 0;
+ char *s;
+ u32 txoff, txlen, rxoff, rxlen, csr;
+ unsigned long flags;
+
+ len += sprintf(page+len, "%-16s %s\n", "name", card->name);
+ len += sprintf(page+len, "%-16s 0x%x\n", "io", card->port);
+ len += sprintf(page+len, "%-16s %d\n", "irq", card->irq);
+ len += sprintf(page+len, "%-16s 0x%lx\n", "membase", card->membase);
+ switch (card->cardtype) {
+ case avm_b1isa: s = "B1 ISA"; break;
+ case avm_b1pci: s = "B1 PCI"; break;
+ case avm_b1pcmcia: s = "B1 PCMCIA"; break;
+ case avm_m1: s = "M1"; break;
+ case avm_m2: s = "M2"; break;
+ case avm_t1isa: s = "T1 ISA (HEMA)"; break;
+ case avm_t1pci: s = "T1 PCI"; break;
+ case avm_c4: s = "C4"; break;
+ case avm_c2: s = "C2"; break;
+ default: s = "???"; break;
+ }
+ len += sprintf(page+len, "%-16s %s\n", "type", s);
+ if ((s = cinfo->version[VER_DRIVER]) != NULL)
+ len += sprintf(page+len, "%-16s %s\n", "ver_driver", s);
+ if ((s = cinfo->version[VER_CARDTYPE]) != NULL)
+ len += sprintf(page+len, "%-16s %s\n", "ver_cardtype", s);
+ if ((s = cinfo->version[VER_SERIAL]) != NULL)
+ len += sprintf(page+len, "%-16s %s\n", "ver_serial", s);
+
+ if (card->cardtype != avm_m1) {
+ flag = ((u8 *)(ctrl->profile.manu))[3];
+ if (flag)
+ len += sprintf(page+len, "%-16s%s%s%s%s%s%s%s\n",
+ "protocol",
+ (flag & 0x01) ? " DSS1" : "",
+ (flag & 0x02) ? " CT1" : "",
+ (flag & 0x04) ? " VN3" : "",
+ (flag & 0x08) ? " NI1" : "",
+ (flag & 0x10) ? " AUSTEL" : "",
+ (flag & 0x20) ? " ESS" : "",
+ (flag & 0x40) ? " 1TR6" : ""
+ );
+ }
+ if (card->cardtype != avm_m1) {
+ flag = ((u8 *)(ctrl->profile.manu))[5];
+ if (flag)
+ len += sprintf(page+len, "%-16s%s%s%s%s\n",
+ "linetype",
+ (flag & 0x01) ? " point to point" : "",
+ (flag & 0x02) ? " point to multipoint" : "",
+ (flag & 0x08) ? " leased line without D-channel" : "",
+ (flag & 0x04) ? " leased line with D-channel" : ""
+ );
+ }
+ len += sprintf(page+len, "%-16s %s\n", "cardname", cinfo->cardname);
+
+
+ spin_lock_irqsave(&card->lock, flags);
+
+ txoff = (dma_addr_t)b1dma_readl(card, AMCC_TXPTR)-card->dma->sendbuf.dmaaddr;
+ txlen = b1dma_readl(card, AMCC_TXLEN);
+
+ rxoff = (dma_addr_t)b1dma_readl(card, AMCC_RXPTR)-card->dma->recvbuf.dmaaddr;
+ rxlen = b1dma_readl(card, AMCC_RXLEN);
+
+ csr = b1dma_readl(card, AMCC_INTCSR);
+
+ spin_unlock_irqrestore(&card->lock, flags);
+
+ len += sprintf(page+len, "%-16s 0x%lx\n",
+ "csr (cached)", (unsigned long)card->csr);
+ len += sprintf(page+len, "%-16s 0x%lx\n",
+ "csr", (unsigned long)csr);
+ len += sprintf(page+len, "%-16s %lu\n",
+ "txoff", (unsigned long)txoff);
+ len += sprintf(page+len, "%-16s %lu\n",
+ "txlen", (unsigned long)txlen);
+ len += sprintf(page+len, "%-16s %lu\n",
+ "rxoff", (unsigned long)rxoff);
+ len += sprintf(page+len, "%-16s %lu\n",
+ "rxlen", (unsigned long)rxlen);
+
+ if (off+count >= len)
+ *eof = 1;
+ if (len < off)
+ return 0;
+ *start = page + off;
+ return ((count < len-off) ? count : len-off);
+}
+
+/* ------------------------------------------------------------- */
+
+EXPORT_SYMBOL(b1dma_reset);
+EXPORT_SYMBOL(t1pci_detect);
+EXPORT_SYMBOL(b1pciv4_detect);
+EXPORT_SYMBOL(b1dma_interrupt);
+
+EXPORT_SYMBOL(b1dma_load_firmware);
+EXPORT_SYMBOL(b1dma_reset_ctr);
+EXPORT_SYMBOL(b1dma_register_appl);
+EXPORT_SYMBOL(b1dma_release_appl);
+EXPORT_SYMBOL(b1dma_send_message);
+EXPORT_SYMBOL(b1dmactl_read_proc);
+
+static int __init b1dma_init(void)
+{
+ char *p;
+ char rev[32];
+
+ if ((p = strchr(revision, ':')) != NULL && p[1]) {
+ strlcpy(rev, p + 2, sizeof(rev));
+ if ((p = strchr(rev, '$')) != NULL && p > rev)
+ *(p-1) = 0;
+ } else
+ strcpy(rev, "1.0");
+
+ printk(KERN_INFO "b1dma: revision %s\n", rev);
+
+ return 0;
+}
+
+static void __exit b1dma_exit(void)
+{
+}
+
+module_init(b1dma_init);
+module_exit(b1dma_exit);
diff --git a/drivers/isdn/hardware/avm/b1isa.c b/drivers/isdn/hardware/avm/b1isa.c
new file mode 100644
index 0000000..6461a32
--- /dev/null
+++ b/drivers/isdn/hardware/avm/b1isa.c
@@ -0,0 +1,243 @@
+/* $Id: b1isa.c,v 1.1.2.3 2004/02/10 01:07:12 keil Exp $
+ *
+ * Module for AVM B1 ISA-card.
+ *
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/capi.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+#include <linux/isdn/capilli.h>
+#include "avmcard.h"
+
+/* ------------------------------------------------------------- */
+
+static char *revision = "$Revision: 1.1.2.3 $";
+
+/* ------------------------------------------------------------- */
+
+MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM B1 ISA card");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+
+/* ------------------------------------------------------------- */
+
+static void b1isa_remove(struct pci_dev *pdev)
+{
+ avmctrl_info *cinfo = pci_get_drvdata(pdev);
+ avmcard *card;
+
+ if (!cinfo)
+ return;
+
+ card = cinfo->card;
+
+ b1_reset(card->port);
+ b1_reset(card->port);
+
+ detach_capi_ctr(&cinfo->capi_ctrl);
+ free_irq(card->irq, card);
+ release_region(card->port, AVMB1_PORTLEN);
+ b1_free_card(card);
+}
+
+/* ------------------------------------------------------------- */
+
+static char *b1isa_procinfo(struct capi_ctr *ctrl);
+
+static int b1isa_probe(struct pci_dev *pdev)
+{
+ avmctrl_info *cinfo;
+ avmcard *card;
+ int retval;
+
+ card = b1_alloc_card(1);
+ if (!card) {
+ printk(KERN_WARNING "b1isa: no memory.\n");
+ retval = -ENOMEM;
+ goto err;
+ }
+
+ cinfo = card->ctrlinfo;
+
+ card->port = pci_resource_start(pdev, 0);
+ card->irq = pdev->irq;
+ card->cardtype = avm_b1isa;
+ sprintf(card->name, "b1isa-%x", card->port);
+
+ if ( card->port != 0x150 && card->port != 0x250
+ && card->port != 0x300 && card->port != 0x340) {
+ printk(KERN_WARNING "b1isa: invalid port 0x%x.\n", card->port);
+ retval = -EINVAL;
+ goto err_free;
+ }
+ if (b1_irq_table[card->irq & 0xf] == 0) {
+ printk(KERN_WARNING "b1isa: irq %d not valid.\n", card->irq);
+ retval = -EINVAL;
+ goto err_free;
+ }
+ if (!request_region(card->port, AVMB1_PORTLEN, card->name)) {
+ printk(KERN_WARNING "b1isa: ports 0x%03x-0x%03x in use.\n",
+ card->port, card->port + AVMB1_PORTLEN);
+ retval = -EBUSY;
+ goto err_free;
+ }
+ retval = request_irq(card->irq, b1_interrupt, 0, card->name, card);
+ if (retval) {
+ printk(KERN_ERR "b1isa: unable to get IRQ %d.\n", card->irq);
+ goto err_release_region;
+ }
+ b1_reset(card->port);
+ if ((retval = b1_detect(card->port, card->cardtype)) != 0) {
+ printk(KERN_NOTICE "b1isa: NO card at 0x%x (%d)\n",
+ card->port, retval);
+ retval = -ENODEV;
+ goto err_free_irq;
+ }
+ b1_reset(card->port);
+ b1_getrevision(card);
+
+ cinfo->capi_ctrl.owner = THIS_MODULE;
+ cinfo->capi_ctrl.driver_name = "b1isa";
+ cinfo->capi_ctrl.driverdata = cinfo;
+ cinfo->capi_ctrl.register_appl = b1_register_appl;
+ cinfo->capi_ctrl.release_appl = b1_release_appl;
+ cinfo->capi_ctrl.send_message = b1_send_message;
+ cinfo->capi_ctrl.load_firmware = b1_load_firmware;
+ cinfo->capi_ctrl.reset_ctr = b1_reset_ctr;
+ cinfo->capi_ctrl.procinfo = b1isa_procinfo;
+ cinfo->capi_ctrl.ctr_read_proc = b1ctl_read_proc;
+ strcpy(cinfo->capi_ctrl.name, card->name);
+
+ retval = attach_capi_ctr(&cinfo->capi_ctrl);
+ if (retval) {
+ printk(KERN_ERR "b1isa: attach controller failed.\n");
+ goto err_free_irq;
+ }
+
+ printk(KERN_INFO "b1isa: AVM B1 ISA at i/o %#x, irq %d, revision %d\n",
+ card->port, card->irq, card->revision);
+
+ pci_set_drvdata(pdev, cinfo);
+ return 0;
+
+ err_free_irq:
+ free_irq(card->irq, card);
+ err_release_region:
+ release_region(card->port, AVMB1_PORTLEN);
+ err_free:
+ b1_free_card(card);
+ err:
+ return retval;
+}
+
+static char *b1isa_procinfo(struct capi_ctr *ctrl)
+{
+ avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+
+ if (!cinfo)
+ return "";
+ sprintf(cinfo->infobuf, "%s %s 0x%x %d r%d",
+ cinfo->cardname[0] ? cinfo->cardname : "-",
+ cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
+ cinfo->card ? cinfo->card->port : 0x0,
+ cinfo->card ? cinfo->card->irq : 0,
+ cinfo->card ? cinfo->card->revision : 0
+ );
+ return cinfo->infobuf;
+}
+
+/* ------------------------------------------------------------- */
+
+#define MAX_CARDS 4
+static struct pci_dev isa_dev[MAX_CARDS];
+static int io[MAX_CARDS];
+static int irq[MAX_CARDS];
+
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+MODULE_PARM_DESC(io, "I/O base address(es)");
+MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
+
+static int b1isa_add_card(struct capi_driver *driver, capicardparams *data)
+{
+ int i;
+
+ for (i = 0; i < MAX_CARDS; i++) {
+ if (isa_dev[i].resource[0].start)
+ continue;
+
+ isa_dev[i].resource[0].start = data->port;
+ isa_dev[i].irq = data->irq;
+
+ if (b1isa_probe(&isa_dev[i]) == 0)
+ return 0;
+ }
+ return -ENODEV;
+}
+
+static struct capi_driver capi_driver_b1isa = {
+ .name = "b1isa",
+ .revision = "1.0",
+ .add_card = b1isa_add_card,
+};
+
+static int __init b1isa_init(void)
+{
+ char *p;
+ char rev[32];
+ int i;
+
+ if ((p = strchr(revision, ':')) != NULL && p[1]) {
+ strlcpy(rev, p + 2, 32);
+ if ((p = strchr(rev, '$')) != NULL && p > rev)
+ *(p-1) = 0;
+ } else
+ strcpy(rev, "1.0");
+
+ for (i = 0; i < MAX_CARDS; i++) {
+ if (!io[i])
+ break;
+
+ isa_dev[i].resource[0].start = io[i];
+ isa_dev[i].irq = irq[i];
+
+ if (b1isa_probe(&isa_dev[i]) != 0)
+ return -ENODEV;
+ }
+
+ strlcpy(capi_driver_b1isa.revision, rev, 32);
+ register_capi_driver(&capi_driver_b1isa);
+ printk(KERN_INFO "b1isa: revision %s\n", rev);
+
+ return 0;
+}
+
+static void __exit b1isa_exit(void)
+{
+ int i;
+
+ for (i = 0; i < MAX_CARDS; i++) {
+ if (isa_dev[i].resource[0].start)
+ b1isa_remove(&isa_dev[i]);
+ }
+ unregister_capi_driver(&capi_driver_b1isa);
+}
+
+module_init(b1isa_init);
+module_exit(b1isa_exit);
diff --git a/drivers/isdn/hardware/avm/b1pci.c b/drivers/isdn/hardware/avm/b1pci.c
new file mode 100644
index 0000000..5b314a2
--- /dev/null
+++ b/drivers/isdn/hardware/avm/b1pci.c
@@ -0,0 +1,416 @@
+/* $Id: b1pci.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $
+ *
+ * Module for AVM B1 PCI-card.
+ *
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/capi.h>
+#include <asm/io.h>
+#include <linux/init.h>
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+#include <linux/isdn/capilli.h>
+#include "avmcard.h"
+
+/* ------------------------------------------------------------- */
+
+static char *revision = "$Revision: 1.1.2.2 $";
+
+/* ------------------------------------------------------------- */
+
+static struct pci_device_id b1pci_pci_tbl[] = {
+ { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_B1, PCI_ANY_ID, PCI_ANY_ID },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(pci, b1pci_pci_tbl);
+MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM B1 PCI card");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+
+/* ------------------------------------------------------------- */
+
+static char *b1pci_procinfo(struct capi_ctr *ctrl)
+{
+ avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+
+ if (!cinfo)
+ return "";
+ sprintf(cinfo->infobuf, "%s %s 0x%x %d r%d",
+ cinfo->cardname[0] ? cinfo->cardname : "-",
+ cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
+ cinfo->card ? cinfo->card->port : 0x0,
+ cinfo->card ? cinfo->card->irq : 0,
+ cinfo->card ? cinfo->card->revision : 0
+ );
+ return cinfo->infobuf;
+}
+
+/* ------------------------------------------------------------- */
+
+static int b1pci_probe(struct capicardparams *p, struct pci_dev *pdev)
+{
+ avmcard *card;
+ avmctrl_info *cinfo;
+ int retval;
+
+ card = b1_alloc_card(1);
+ if (!card) {
+ printk(KERN_WARNING "b1pci: no memory.\n");
+ retval = -ENOMEM;
+ goto err;
+ }
+
+ cinfo = card->ctrlinfo;
+ sprintf(card->name, "b1pci-%x", p->port);
+ card->port = p->port;
+ card->irq = p->irq;
+ card->cardtype = avm_b1pci;
+
+ if (!request_region(card->port, AVMB1_PORTLEN, card->name)) {
+ printk(KERN_WARNING "b1pci: ports 0x%03x-0x%03x in use.\n",
+ card->port, card->port + AVMB1_PORTLEN);
+ retval = -EBUSY;
+ goto err_free;
+ }
+ b1_reset(card->port);
+ retval = b1_detect(card->port, card->cardtype);
+ if (retval) {
+ printk(KERN_NOTICE "b1pci: NO card at 0x%x (%d)\n",
+ card->port, retval);
+ retval = -ENODEV;
+ goto err_release_region;
+ }
+ b1_reset(card->port);
+ b1_getrevision(card);
+
+ retval = request_irq(card->irq, b1_interrupt, IRQF_SHARED, card->name, card);
+ if (retval) {
+ printk(KERN_ERR "b1pci: unable to get IRQ %d.\n", card->irq);
+ retval = -EBUSY;
+ goto err_release_region;
+ }
+
+ cinfo->capi_ctrl.driver_name = "b1pci";
+ cinfo->capi_ctrl.driverdata = cinfo;
+ cinfo->capi_ctrl.register_appl = b1_register_appl;
+ cinfo->capi_ctrl.release_appl = b1_release_appl;
+ cinfo->capi_ctrl.send_message = b1_send_message;
+ cinfo->capi_ctrl.load_firmware = b1_load_firmware;
+ cinfo->capi_ctrl.reset_ctr = b1_reset_ctr;
+ cinfo->capi_ctrl.procinfo = b1pci_procinfo;
+ cinfo->capi_ctrl.ctr_read_proc = b1ctl_read_proc;
+ strcpy(cinfo->capi_ctrl.name, card->name);
+ cinfo->capi_ctrl.owner = THIS_MODULE;
+
+ retval = attach_capi_ctr(&cinfo->capi_ctrl);
+ if (retval) {
+ printk(KERN_ERR "b1pci: attach controller failed.\n");
+ goto err_free_irq;
+ }
+
+ if (card->revision >= 4) {
+ printk(KERN_INFO "b1pci: AVM B1 PCI V4 at i/o %#x, irq %d, revision %d (no dma)\n",
+ card->port, card->irq, card->revision);
+ } else {
+ printk(KERN_INFO "b1pci: AVM B1 PCI at i/o %#x, irq %d, revision %d\n",
+ card->port, card->irq, card->revision);
+ }
+
+ pci_set_drvdata(pdev, card);
+ return 0;
+
+ err_free_irq:
+ free_irq(card->irq, card);
+ err_release_region:
+ release_region(card->port, AVMB1_PORTLEN);
+ err_free:
+ b1_free_card(card);
+ err:
+ return retval;
+}
+
+static void b1pci_remove(struct pci_dev *pdev)
+{
+ avmcard *card = pci_get_drvdata(pdev);
+ avmctrl_info *cinfo = card->ctrlinfo;
+ unsigned int port = card->port;
+
+ b1_reset(port);
+ b1_reset(port);
+
+ detach_capi_ctr(&cinfo->capi_ctrl);
+ free_irq(card->irq, card);
+ release_region(card->port, AVMB1_PORTLEN);
+ b1_free_card(card);
+}
+
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
+/* ------------------------------------------------------------- */
+
+static char *b1pciv4_procinfo(struct capi_ctr *ctrl)
+{
+ avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+
+ if (!cinfo)
+ return "";
+ sprintf(cinfo->infobuf, "%s %s 0x%x %d 0x%lx r%d",
+ cinfo->cardname[0] ? cinfo->cardname : "-",
+ cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
+ cinfo->card ? cinfo->card->port : 0x0,
+ cinfo->card ? cinfo->card->irq : 0,
+ cinfo->card ? cinfo->card->membase : 0,
+ cinfo->card ? cinfo->card->revision : 0
+ );
+ return cinfo->infobuf;
+}
+
+/* ------------------------------------------------------------- */
+
+static int b1pciv4_probe(struct capicardparams *p, struct pci_dev *pdev)
+{
+ avmcard *card;
+ avmctrl_info *cinfo;
+ int retval;
+
+ card = b1_alloc_card(1);
+ if (!card) {
+ printk(KERN_WARNING "b1pci: no memory.\n");
+ retval = -ENOMEM;
+ goto err;
+ }
+
+ card->dma = avmcard_dma_alloc("b1pci", pdev, 2048+128, 2048+128);
+ if (!card->dma) {
+ printk(KERN_WARNING "b1pci: dma alloc.\n");
+ retval = -ENOMEM;
+ goto err_free;
+ }
+
+ cinfo = card->ctrlinfo;
+ sprintf(card->name, "b1pciv4-%x", p->port);
+ card->port = p->port;
+ card->irq = p->irq;
+ card->membase = p->membase;
+ card->cardtype = avm_b1pci;
+
+ if (!request_region(card->port, AVMB1_PORTLEN, card->name)) {
+ printk(KERN_WARNING "b1pci: ports 0x%03x-0x%03x in use.\n",
+ card->port, card->port + AVMB1_PORTLEN);
+ retval = -EBUSY;
+ goto err_free_dma;
+ }
+
+ card->mbase = ioremap(card->membase, 64);
+ if (!card->mbase) {
+ printk(KERN_NOTICE "b1pci: can't remap memory at 0x%lx\n",
+ card->membase);
+ retval = -ENOMEM;
+ goto err_release_region;
+ }
+
+ b1dma_reset(card);
+
+ retval = b1pciv4_detect(card);
+ if (retval) {
+ printk(KERN_NOTICE "b1pci: NO card at 0x%x (%d)\n",
+ card->port, retval);
+ retval = -ENODEV;
+ goto err_unmap;
+ }
+ b1dma_reset(card);
+ b1_getrevision(card);
+
+ retval = request_irq(card->irq, b1dma_interrupt, IRQF_SHARED, card->name, card);
+ if (retval) {
+ printk(KERN_ERR "b1pci: unable to get IRQ %d.\n",
+ card->irq);
+ retval = -EBUSY;
+ goto err_unmap;
+ }
+
+ cinfo->capi_ctrl.owner = THIS_MODULE;
+ cinfo->capi_ctrl.driver_name = "b1pciv4";
+ cinfo->capi_ctrl.driverdata = cinfo;
+ cinfo->capi_ctrl.register_appl = b1dma_register_appl;
+ cinfo->capi_ctrl.release_appl = b1dma_release_appl;
+ cinfo->capi_ctrl.send_message = b1dma_send_message;
+ cinfo->capi_ctrl.load_firmware = b1dma_load_firmware;
+ cinfo->capi_ctrl.reset_ctr = b1dma_reset_ctr;
+ cinfo->capi_ctrl.procinfo = b1pciv4_procinfo;
+ cinfo->capi_ctrl.ctr_read_proc = b1dmactl_read_proc;
+ strcpy(cinfo->capi_ctrl.name, card->name);
+
+ retval = attach_capi_ctr(&cinfo->capi_ctrl);
+ if (retval) {
+ printk(KERN_ERR "b1pci: attach controller failed.\n");
+ goto err_free_irq;
+ }
+ card->cardnr = cinfo->capi_ctrl.cnr;
+
+ printk(KERN_INFO "b1pci: AVM B1 PCI V4 at i/o %#x, irq %d, mem %#lx, revision %d (dma)\n",
+ card->port, card->irq, card->membase, card->revision);
+
+ pci_set_drvdata(pdev, card);
+ return 0;
+
+ err_free_irq:
+ free_irq(card->irq, card);
+ err_unmap:
+ iounmap(card->mbase);
+ err_release_region:
+ release_region(card->port, AVMB1_PORTLEN);
+ err_free_dma:
+ avmcard_dma_free(card->dma);
+ err_free:
+ b1_free_card(card);
+ err:
+ return retval;
+
+}
+
+static void b1pciv4_remove(struct pci_dev *pdev)
+{
+ avmcard *card = pci_get_drvdata(pdev);
+ avmctrl_info *cinfo = card->ctrlinfo;
+
+ b1dma_reset(card);
+
+ detach_capi_ctr(&cinfo->capi_ctrl);
+ free_irq(card->irq, card);
+ iounmap(card->mbase);
+ release_region(card->port, AVMB1_PORTLEN);
+ avmcard_dma_free(card->dma);
+ b1_free_card(card);
+}
+
+#endif /* CONFIG_ISDN_DRV_AVMB1_B1PCIV4 */
+
+static int __devinit b1pci_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct capicardparams param;
+ int retval;
+
+ if (pci_enable_device(pdev) < 0) {
+ printk(KERN_ERR "b1pci: failed to enable AVM-B1\n");
+ return -ENODEV;
+ }
+ param.irq = pdev->irq;
+
+ if (pci_resource_start(pdev, 2)) { /* B1 PCI V4 */
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
+ pci_set_master(pdev);
+#endif
+ param.membase = pci_resource_start(pdev, 0);
+ param.port = pci_resource_start(pdev, 2);
+
+ printk(KERN_INFO "b1pci: PCI BIOS reports AVM-B1 V4 at i/o %#x, irq %d, mem %#x\n",
+ param.port, param.irq, param.membase);
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
+ retval = b1pciv4_probe(&param, pdev);
+#else
+ retval = b1pci_probe(&param, pdev);
+#endif
+ if (retval != 0) {
+ printk(KERN_ERR "b1pci: no AVM-B1 V4 at i/o %#x, irq %d, mem %#x detected\n",
+ param.port, param.irq, param.membase);
+ }
+ } else {
+ param.membase = 0;
+ param.port = pci_resource_start(pdev, 1);
+
+ printk(KERN_INFO "b1pci: PCI BIOS reports AVM-B1 at i/o %#x, irq %d\n",
+ param.port, param.irq);
+ retval = b1pci_probe(&param, pdev);
+ if (retval != 0) {
+ printk(KERN_ERR "b1pci: no AVM-B1 at i/o %#x, irq %d detected\n",
+ param.port, param.irq);
+ }
+ }
+ return retval;
+}
+
+static void __devexit b1pci_pci_remove(struct pci_dev *pdev)
+{
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
+ avmcard *card = pci_get_drvdata(pdev);
+
+ if (card->dma)
+ b1pciv4_remove(pdev);
+ else
+ b1pci_remove(pdev);
+#else
+ b1pci_remove(pdev);
+#endif
+}
+
+static struct pci_driver b1pci_pci_driver = {
+ .name = "b1pci",
+ .id_table = b1pci_pci_tbl,
+ .probe = b1pci_pci_probe,
+ .remove = __devexit_p(b1pci_pci_remove),
+};
+
+static struct capi_driver capi_driver_b1pci = {
+ .name = "b1pci",
+ .revision = "1.0",
+};
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
+static struct capi_driver capi_driver_b1pciv4 = {
+ .name = "b1pciv4",
+ .revision = "1.0",
+};
+#endif
+
+static int __init b1pci_init(void)
+{
+ char *p;
+ char rev[32];
+ int err;
+
+ if ((p = strchr(revision, ':')) != NULL && p[1]) {
+ strlcpy(rev, p + 2, 32);
+ if ((p = strchr(rev, '$')) != NULL && p > rev)
+ *(p-1) = 0;
+ } else
+ strcpy(rev, "1.0");
+
+
+ err = pci_register_driver(&b1pci_pci_driver);
+ if (!err) {
+ strlcpy(capi_driver_b1pci.revision, rev, 32);
+ register_capi_driver(&capi_driver_b1pci);
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
+ strlcpy(capi_driver_b1pciv4.revision, rev, 32);
+ register_capi_driver(&capi_driver_b1pciv4);
+#endif
+ printk(KERN_INFO "b1pci: revision %s\n", rev);
+ }
+ return err;
+}
+
+static void __exit b1pci_exit(void)
+{
+ unregister_capi_driver(&capi_driver_b1pci);
+#ifdef CONFIG_ISDN_DRV_AVMB1_B1PCIV4
+ unregister_capi_driver(&capi_driver_b1pciv4);
+#endif
+ pci_unregister_driver(&b1pci_pci_driver);
+}
+
+module_init(b1pci_init);
+module_exit(b1pci_exit);
diff --git a/drivers/isdn/hardware/avm/b1pcmcia.c b/drivers/isdn/hardware/avm/b1pcmcia.c
new file mode 100644
index 0000000..7740403
--- /dev/null
+++ b/drivers/isdn/hardware/avm/b1pcmcia.c
@@ -0,0 +1,224 @@
+/* $Id: b1pcmcia.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $
+ *
+ * Module for AVM B1/M1/M2 PCMCIA-card.
+ *
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <linux/capi.h>
+#include <linux/b1pcmcia.h>
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+#include <linux/isdn/capilli.h>
+#include "avmcard.h"
+
+/* ------------------------------------------------------------- */
+
+static char *revision = "$Revision: 1.1.2.2 $";
+
+/* ------------------------------------------------------------- */
+
+MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM PCMCIA cards");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+
+/* ------------------------------------------------------------- */
+
+static void b1pcmcia_remove_ctr(struct capi_ctr *ctrl)
+{
+ avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+ avmcard *card = cinfo->card;
+ unsigned int port = card->port;
+
+ b1_reset(port);
+ b1_reset(port);
+
+ detach_capi_ctr(ctrl);
+ free_irq(card->irq, card);
+ b1_free_card(card);
+}
+
+/* ------------------------------------------------------------- */
+
+static LIST_HEAD(cards);
+
+static char *b1pcmcia_procinfo(struct capi_ctr *ctrl);
+
+static int b1pcmcia_add_card(unsigned int port, unsigned irq,
+ enum avmcardtype cardtype)
+{
+ avmctrl_info *cinfo;
+ avmcard *card;
+ char *cardname;
+ int retval;
+
+ card = b1_alloc_card(1);
+ if (!card) {
+ printk(KERN_WARNING "b1pcmcia: no memory.\n");
+ retval = -ENOMEM;
+ goto err;
+ }
+ cinfo = card->ctrlinfo;
+
+ switch (cardtype) {
+ case avm_m1: sprintf(card->name, "m1-%x", port); break;
+ case avm_m2: sprintf(card->name, "m2-%x", port); break;
+ default: sprintf(card->name, "b1pcmcia-%x", port); break;
+ }
+ card->port = port;
+ card->irq = irq;
+ card->cardtype = cardtype;
+
+ retval = request_irq(card->irq, b1_interrupt, IRQF_SHARED, card->name, card);
+ if (retval) {
+ printk(KERN_ERR "b1pcmcia: unable to get IRQ %d.\n",
+ card->irq);
+ retval = -EBUSY;
+ goto err_free;
+ }
+ b1_reset(card->port);
+ if ((retval = b1_detect(card->port, card->cardtype)) != 0) {
+ printk(KERN_NOTICE "b1pcmcia: NO card at 0x%x (%d)\n",
+ card->port, retval);
+ retval = -ENODEV;
+ goto err_free_irq;
+ }
+ b1_reset(card->port);
+ b1_getrevision(card);
+
+ cinfo->capi_ctrl.owner = THIS_MODULE;
+ cinfo->capi_ctrl.driver_name = "b1pcmcia";
+ cinfo->capi_ctrl.driverdata = cinfo;
+ cinfo->capi_ctrl.register_appl = b1_register_appl;
+ cinfo->capi_ctrl.release_appl = b1_release_appl;
+ cinfo->capi_ctrl.send_message = b1_send_message;
+ cinfo->capi_ctrl.load_firmware = b1_load_firmware;
+ cinfo->capi_ctrl.reset_ctr = b1_reset_ctr;
+ cinfo->capi_ctrl.procinfo = b1pcmcia_procinfo;
+ cinfo->capi_ctrl.ctr_read_proc = b1ctl_read_proc;
+ strcpy(cinfo->capi_ctrl.name, card->name);
+
+ retval = attach_capi_ctr(&cinfo->capi_ctrl);
+ if (retval) {
+ printk(KERN_ERR "b1pcmcia: attach controller failed.\n");
+ goto err_free_irq;
+ }
+ switch (cardtype) {
+ case avm_m1: cardname = "M1"; break;
+ case avm_m2: cardname = "M2"; break;
+ default : cardname = "B1 PCMCIA"; break;
+ }
+
+ printk(KERN_INFO "b1pcmcia: AVM %s at i/o %#x, irq %d, revision %d\n",
+ cardname, card->port, card->irq, card->revision);
+
+ list_add(&card->list, &cards);
+ return cinfo->capi_ctrl.cnr;
+
+ err_free_irq:
+ free_irq(card->irq, card);
+ err_free:
+ b1_free_card(card);
+ err:
+ return retval;
+}
+
+/* ------------------------------------------------------------- */
+
+static char *b1pcmcia_procinfo(struct capi_ctr *ctrl)
+{
+ avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+
+ if (!cinfo)
+ return "";
+ sprintf(cinfo->infobuf, "%s %s 0x%x %d r%d",
+ cinfo->cardname[0] ? cinfo->cardname : "-",
+ cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
+ cinfo->card ? cinfo->card->port : 0x0,
+ cinfo->card ? cinfo->card->irq : 0,
+ cinfo->card ? cinfo->card->revision : 0
+ );
+ return cinfo->infobuf;
+}
+
+/* ------------------------------------------------------------- */
+
+int b1pcmcia_addcard_b1(unsigned int port, unsigned irq)
+{
+ return b1pcmcia_add_card(port, irq, avm_b1pcmcia);
+}
+
+int b1pcmcia_addcard_m1(unsigned int port, unsigned irq)
+{
+ return b1pcmcia_add_card(port, irq, avm_m1);
+}
+
+int b1pcmcia_addcard_m2(unsigned int port, unsigned irq)
+{
+ return b1pcmcia_add_card(port, irq, avm_m2);
+}
+
+int b1pcmcia_delcard(unsigned int port, unsigned irq)
+{
+ struct list_head *l;
+ avmcard *card;
+
+ list_for_each(l, &cards) {
+ card = list_entry(l, avmcard, list);
+ if (card->port == port && card->irq == irq) {
+ b1pcmcia_remove_ctr(&card->ctrlinfo[0].capi_ctrl);
+ return 0;
+ }
+ }
+ return -ESRCH;
+}
+
+EXPORT_SYMBOL(b1pcmcia_addcard_b1);
+EXPORT_SYMBOL(b1pcmcia_addcard_m1);
+EXPORT_SYMBOL(b1pcmcia_addcard_m2);
+EXPORT_SYMBOL(b1pcmcia_delcard);
+
+static struct capi_driver capi_driver_b1pcmcia = {
+ .name = "b1pcmcia",
+ .revision = "1.0",
+};
+
+static int __init b1pcmcia_init(void)
+{
+ char *p;
+ char rev[32];
+
+ if ((p = strchr(revision, ':')) != NULL && p[1]) {
+ strlcpy(rev, p + 2, 32);
+ if ((p = strchr(rev, '$')) != NULL && p > rev)
+ *(p-1) = 0;
+ } else
+ strcpy(rev, "1.0");
+
+ strlcpy(capi_driver_b1pcmcia.revision, rev, 32);
+ register_capi_driver(&capi_driver_b1pcmcia);
+ printk(KERN_INFO "b1pci: revision %s\n", rev);
+
+ return 0;
+}
+
+static void __exit b1pcmcia_exit(void)
+{
+ unregister_capi_driver(&capi_driver_b1pcmcia);
+}
+
+module_init(b1pcmcia_init);
+module_exit(b1pcmcia_exit);
diff --git a/drivers/isdn/hardware/avm/c4.c b/drivers/isdn/hardware/avm/c4.c
new file mode 100644
index 0000000..9df1d3f
--- /dev/null
+++ b/drivers/isdn/hardware/avm/c4.c
@@ -0,0 +1,1320 @@
+/* $Id: c4.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $
+ *
+ * Module for AVM C4 & C2 card.
+ *
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/capi.h>
+#include <linux/kernelcapi.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/netdevice.h>
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+#include <linux/isdn/capilli.h>
+#include "avmcard.h"
+
+#undef AVM_C4_DEBUG
+#undef AVM_C4_POLLDEBUG
+
+/* ------------------------------------------------------------- */
+
+static char *revision = "$Revision: 1.1.2.2 $";
+
+/* ------------------------------------------------------------- */
+
+static int suppress_pollack;
+
+static struct pci_device_id c4_pci_tbl[] = {
+ { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21285, PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_C4, 0, 0, (unsigned long)4 },
+ { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21285, PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_C2, 0, 0, (unsigned long)2 },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(pci, c4_pci_tbl);
+MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM C2/C4 cards");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+module_param(suppress_pollack, bool, 0);
+
+/* ------------------------------------------------------------- */
+
+static void c4_dispatch_tx(avmcard *card);
+
+/* ------------------------------------------------------------- */
+
+#define DC21285_DRAM_A0MR 0x40000000
+#define DC21285_DRAM_A1MR 0x40004000
+#define DC21285_DRAM_A2MR 0x40008000
+#define DC21285_DRAM_A3MR 0x4000C000
+
+#define CAS_OFFSET 0x88
+
+#define DC21285_ARMCSR_BASE 0x42000000
+
+#define PCI_OUT_INT_STATUS 0x30
+#define PCI_OUT_INT_MASK 0x34
+#define MAILBOX_0 0x50
+#define MAILBOX_1 0x54
+#define MAILBOX_2 0x58
+#define MAILBOX_3 0x5C
+#define DOORBELL 0x60
+#define DOORBELL_SETUP 0x64
+
+#define CHAN_1_CONTROL 0x90
+#define CHAN_2_CONTROL 0xB0
+#define DRAM_TIMING 0x10C
+#define DRAM_ADDR_SIZE_0 0x110
+#define DRAM_ADDR_SIZE_1 0x114
+#define DRAM_ADDR_SIZE_2 0x118
+#define DRAM_ADDR_SIZE_3 0x11C
+#define SA_CONTROL 0x13C
+#define XBUS_CYCLE 0x148
+#define XBUS_STROBE 0x14C
+#define DBELL_PCI_MASK 0x150
+#define DBELL_SA_MASK 0x154
+
+#define SDRAM_SIZE 0x1000000
+
+/* ------------------------------------------------------------- */
+
+#define MBOX_PEEK_POKE MAILBOX_0
+
+#define DBELL_ADDR 0x01
+#define DBELL_DATA 0x02
+#define DBELL_RNWR 0x40
+#define DBELL_INIT 0x80
+
+/* ------------------------------------------------------------- */
+
+#define MBOX_UP_ADDR MAILBOX_0
+#define MBOX_UP_LEN MAILBOX_1
+#define MBOX_DOWN_ADDR MAILBOX_2
+#define MBOX_DOWN_LEN MAILBOX_3
+
+#define DBELL_UP_HOST 0x00000100
+#define DBELL_UP_ARM 0x00000200
+#define DBELL_DOWN_HOST 0x00000400
+#define DBELL_DOWN_ARM 0x00000800
+#define DBELL_RESET_HOST 0x40000000
+#define DBELL_RESET_ARM 0x80000000
+
+/* ------------------------------------------------------------- */
+
+#define DRAM_TIMING_DEF 0x001A01A5
+#define DRAM_AD_SZ_DEF0 0x00000045
+#define DRAM_AD_SZ_NULL 0x00000000
+
+#define SA_CTL_ALLRIGHT 0x64AA0271
+
+#define INIT_XBUS_CYCLE 0x100016DB
+#define INIT_XBUS_STROBE 0xF1F1F1F1
+
+/* ------------------------------------------------------------- */
+
+#define RESET_TIMEOUT (15*HZ) /* 15 sec */
+#define PEEK_POKE_TIMEOUT (HZ/10) /* 0.1 sec */
+
+/* ------------------------------------------------------------- */
+
+#define c4outmeml(addr, value) writel(value, addr)
+#define c4inmeml(addr) readl(addr)
+#define c4outmemw(addr, value) writew(value, addr)
+#define c4inmemw(addr) readw(addr)
+#define c4outmemb(addr, value) writeb(value, addr)
+#define c4inmemb(addr) readb(addr)
+
+/* ------------------------------------------------------------- */
+
+static inline int wait_for_doorbell(avmcard *card, unsigned long t)
+{
+ unsigned long stop;
+
+ stop = jiffies + t;
+ while (c4inmeml(card->mbase+DOORBELL) != 0xffffffff) {
+ if (!time_before(jiffies, stop))
+ return -1;
+ mb();
+ }
+ return 0;
+}
+
+static int c4_poke(avmcard *card, unsigned long off, unsigned long value)
+{
+
+ if (wait_for_doorbell(card, HZ/10) < 0)
+ return -1;
+
+ c4outmeml(card->mbase+MBOX_PEEK_POKE, off);
+ c4outmeml(card->mbase+DOORBELL, DBELL_ADDR);
+
+ if (wait_for_doorbell(card, HZ/10) < 0)
+ return -1;
+
+ c4outmeml(card->mbase+MBOX_PEEK_POKE, value);
+ c4outmeml(card->mbase+DOORBELL, DBELL_DATA | DBELL_ADDR);
+
+ return 0;
+}
+
+static int c4_peek(avmcard *card, unsigned long off, unsigned long *valuep)
+{
+ if (wait_for_doorbell(card, HZ/10) < 0)
+ return -1;
+
+ c4outmeml(card->mbase+MBOX_PEEK_POKE, off);
+ c4outmeml(card->mbase+DOORBELL, DBELL_RNWR | DBELL_ADDR);
+
+ if (wait_for_doorbell(card, HZ/10) < 0)
+ return -1;
+
+ *valuep = c4inmeml(card->mbase+MBOX_PEEK_POKE);
+
+ return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+static int c4_load_t4file(avmcard *card, capiloaddatapart * t4file)
+{
+ u32 val;
+ unsigned char *dp;
+ u_int left;
+ u32 loadoff = 0;
+
+ dp = t4file->data;
+ left = t4file->len;
+ while (left >= sizeof(u32)) {
+ if (t4file->user) {
+ if (copy_from_user(&val, dp, sizeof(val)))
+ return -EFAULT;
+ } else {
+ memcpy(&val, dp, sizeof(val));
+ }
+ if (c4_poke(card, loadoff, val)) {
+ printk(KERN_ERR "%s: corrupted firmware file ?\n",
+ card->name);
+ return -EIO;
+ }
+ left -= sizeof(u32);
+ dp += sizeof(u32);
+ loadoff += sizeof(u32);
+ }
+ if (left) {
+ val = 0;
+ if (t4file->user) {
+ if (copy_from_user(&val, dp, left))
+ return -EFAULT;
+ } else {
+ memcpy(&val, dp, left);
+ }
+ if (c4_poke(card, loadoff, val)) {
+ printk(KERN_ERR "%s: corrupted firmware file ?\n",
+ card->name);
+ return -EIO;
+ }
+ }
+ return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+static inline void _put_byte(void **pp, u8 val)
+{
+ u8 *s = *pp;
+ *s++ = val;
+ *pp = s;
+}
+
+static inline void _put_word(void **pp, u32 val)
+{
+ u8 *s = *pp;
+ *s++ = val & 0xff;
+ *s++ = (val >> 8) & 0xff;
+ *s++ = (val >> 16) & 0xff;
+ *s++ = (val >> 24) & 0xff;
+ *pp = s;
+}
+
+static inline void _put_slice(void **pp, unsigned char *dp, unsigned int len)
+{
+ unsigned i = len;
+ _put_word(pp, i);
+ while (i-- > 0)
+ _put_byte(pp, *dp++);
+}
+
+static inline u8 _get_byte(void **pp)
+{
+ u8 *s = *pp;
+ u8 val;
+ val = *s++;
+ *pp = s;
+ return val;
+}
+
+static inline u32 _get_word(void **pp)
+{
+ u8 *s = *pp;
+ u32 val;
+ val = *s++;
+ val |= (*s++ << 8);
+ val |= (*s++ << 16);
+ val |= (*s++ << 24);
+ *pp = s;
+ return val;
+}
+
+static inline u32 _get_slice(void **pp, unsigned char *dp)
+{
+ unsigned int len, i;
+
+ len = i = _get_word(pp);
+ while (i-- > 0) *dp++ = _get_byte(pp);
+ return len;
+}
+
+/* ------------------------------------------------------------- */
+
+static void c4_reset(avmcard *card)
+{
+ unsigned long stop;
+
+ c4outmeml(card->mbase+DOORBELL, DBELL_RESET_ARM);
+
+ stop = jiffies + HZ*10;
+ while (c4inmeml(card->mbase+DOORBELL) != 0xffffffff) {
+ if (!time_before(jiffies, stop))
+ return;
+ c4outmeml(card->mbase+DOORBELL, DBELL_ADDR);
+ mb();
+ }
+
+ c4_poke(card, DC21285_ARMCSR_BASE + CHAN_1_CONTROL, 0);
+ c4_poke(card, DC21285_ARMCSR_BASE + CHAN_2_CONTROL, 0);
+}
+
+/* ------------------------------------------------------------- */
+
+static int c4_detect(avmcard *card)
+{
+ unsigned long stop, dummy;
+
+ c4outmeml(card->mbase+PCI_OUT_INT_MASK, 0x0c);
+ if (c4inmeml(card->mbase+PCI_OUT_INT_MASK) != 0x0c)
+ return 1;
+
+ c4outmeml(card->mbase+DOORBELL, DBELL_RESET_ARM);
+
+ stop = jiffies + HZ*10;
+ while (c4inmeml(card->mbase+DOORBELL) != 0xffffffff) {
+ if (!time_before(jiffies, stop))
+ return 2;
+ c4outmeml(card->mbase+DOORBELL, DBELL_ADDR);
+ mb();
+ }
+
+ c4_poke(card, DC21285_ARMCSR_BASE + CHAN_1_CONTROL, 0);
+ c4_poke(card, DC21285_ARMCSR_BASE + CHAN_2_CONTROL, 0);
+
+ c4outmeml(card->mbase+MAILBOX_0, 0x55aa55aa);
+ if (c4inmeml(card->mbase+MAILBOX_0) != 0x55aa55aa) return 3;
+
+ c4outmeml(card->mbase+MAILBOX_0, 0xaa55aa55);
+ if (c4inmeml(card->mbase+MAILBOX_0) != 0xaa55aa55) return 4;
+
+ if (c4_poke(card, DC21285_ARMCSR_BASE+DBELL_SA_MASK, 0)) return 5;
+ if (c4_poke(card, DC21285_ARMCSR_BASE+DBELL_PCI_MASK, 0)) return 6;
+ if (c4_poke(card, DC21285_ARMCSR_BASE+SA_CONTROL, SA_CTL_ALLRIGHT))
+ return 7;
+ if (c4_poke(card, DC21285_ARMCSR_BASE+XBUS_CYCLE, INIT_XBUS_CYCLE))
+ return 8;
+ if (c4_poke(card, DC21285_ARMCSR_BASE+XBUS_STROBE, INIT_XBUS_STROBE))
+ return 8;
+ if (c4_poke(card, DC21285_ARMCSR_BASE+DRAM_TIMING, 0)) return 9;
+
+ mdelay(1);
+
+ if (c4_peek(card, DC21285_DRAM_A0MR, &dummy)) return 10;
+ if (c4_peek(card, DC21285_DRAM_A1MR, &dummy)) return 11;
+ if (c4_peek(card, DC21285_DRAM_A2MR, &dummy)) return 12;
+ if (c4_peek(card, DC21285_DRAM_A3MR, &dummy)) return 13;
+
+ if (c4_poke(card, DC21285_DRAM_A0MR+CAS_OFFSET, 0)) return 14;
+ if (c4_poke(card, DC21285_DRAM_A1MR+CAS_OFFSET, 0)) return 15;
+ if (c4_poke(card, DC21285_DRAM_A2MR+CAS_OFFSET, 0)) return 16;
+ if (c4_poke(card, DC21285_DRAM_A3MR+CAS_OFFSET, 0)) return 17;
+
+ mdelay(1);
+
+ if (c4_poke(card, DC21285_ARMCSR_BASE+DRAM_TIMING, DRAM_TIMING_DEF))
+ return 18;
+
+ if (c4_poke(card, DC21285_ARMCSR_BASE+DRAM_ADDR_SIZE_0,DRAM_AD_SZ_DEF0))
+ return 19;
+ if (c4_poke(card, DC21285_ARMCSR_BASE+DRAM_ADDR_SIZE_1,DRAM_AD_SZ_NULL))
+ return 20;
+ if (c4_poke(card, DC21285_ARMCSR_BASE+DRAM_ADDR_SIZE_2,DRAM_AD_SZ_NULL))
+ return 21;
+ if (c4_poke(card, DC21285_ARMCSR_BASE+DRAM_ADDR_SIZE_3,DRAM_AD_SZ_NULL))
+ return 22;
+
+ /* Transputer test */
+
+ if ( c4_poke(card, 0x000000, 0x11111111)
+ || c4_poke(card, 0x400000, 0x22222222)
+ || c4_poke(card, 0x800000, 0x33333333)
+ || c4_poke(card, 0xC00000, 0x44444444))
+ return 23;
+
+ if ( c4_peek(card, 0x000000, &dummy) || dummy != 0x11111111
+ || c4_peek(card, 0x400000, &dummy) || dummy != 0x22222222
+ || c4_peek(card, 0x800000, &dummy) || dummy != 0x33333333
+ || c4_peek(card, 0xC00000, &dummy) || dummy != 0x44444444)
+ return 24;
+
+ if ( c4_poke(card, 0x000000, 0x55555555)
+ || c4_poke(card, 0x400000, 0x66666666)
+ || c4_poke(card, 0x800000, 0x77777777)
+ || c4_poke(card, 0xC00000, 0x88888888))
+ return 25;
+
+ if ( c4_peek(card, 0x000000, &dummy) || dummy != 0x55555555
+ || c4_peek(card, 0x400000, &dummy) || dummy != 0x66666666
+ || c4_peek(card, 0x800000, &dummy) || dummy != 0x77777777
+ || c4_peek(card, 0xC00000, &dummy) || dummy != 0x88888888)
+ return 26;
+
+ return 0;
+}
+
+/* ------------------------------------------------------------- */
+
+static void c4_dispatch_tx(avmcard *card)
+{
+ avmcard_dmainfo *dma = card->dma;
+ struct sk_buff *skb;
+ u8 cmd, subcmd;
+ u16 len;
+ u32 txlen;
+ void *p;
+
+
+ if (card->csr & DBELL_DOWN_ARM) { /* tx busy */
+ return;
+ }
+
+ skb = skb_dequeue(&dma->send_queue);
+ if (!skb) {
+#ifdef AVM_C4_DEBUG
+ printk(KERN_DEBUG "%s: tx underrun\n", card->name);
+#endif
+ return;
+ }
+
+ len = CAPIMSG_LEN(skb->data);
+
+ if (len) {
+ cmd = CAPIMSG_COMMAND(skb->data);
+ subcmd = CAPIMSG_SUBCOMMAND(skb->data);
+
+ p = dma->sendbuf.dmabuf;
+
+ if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) {
+ u16 dlen = CAPIMSG_DATALEN(skb->data);
+ _put_byte(&p, SEND_DATA_B3_REQ);
+ _put_slice(&p, skb->data, len);
+ _put_slice(&p, skb->data + len, dlen);
+ } else {
+ _put_byte(&p, SEND_MESSAGE);
+ _put_slice(&p, skb->data, len);
+ }
+ txlen = (u8 *)p - (u8 *)dma->sendbuf.dmabuf;
+#ifdef AVM_C4_DEBUG
+ printk(KERN_DEBUG "%s: tx put msg len=%d\n", card->name, txlen);
+#endif
+ } else {
+ txlen = skb->len-2;
+#ifdef AVM_C4_POLLDEBUG
+ if (skb->data[2] == SEND_POLLACK)
+ printk(KERN_INFO "%s: ack to c4\n", card->name);
+#endif
+#ifdef AVM_C4_DEBUG
+ printk(KERN_DEBUG "%s: tx put 0x%x len=%d\n",
+ card->name, skb->data[2], txlen);
+#endif
+ skb_copy_from_linear_data_offset(skb, 2, dma->sendbuf.dmabuf,
+ skb->len - 2);
+ }
+ txlen = (txlen + 3) & ~3;
+
+ c4outmeml(card->mbase+MBOX_DOWN_ADDR, dma->sendbuf.dmaaddr);
+ c4outmeml(card->mbase+MBOX_DOWN_LEN, txlen);
+
+ card->csr |= DBELL_DOWN_ARM;
+
+ c4outmeml(card->mbase+DOORBELL, DBELL_DOWN_ARM);
+
+ dev_kfree_skb_any(skb);
+}
+
+/* ------------------------------------------------------------- */
+
+static void queue_pollack(avmcard *card)
+{
+ struct sk_buff *skb;
+ void *p;
+
+ skb = alloc_skb(3, GFP_ATOMIC);
+ if (!skb) {
+ printk(KERN_CRIT "%s: no memory, lost poll ack\n",
+ card->name);
+ return;
+ }
+ p = skb->data;
+ _put_byte(&p, 0);
+ _put_byte(&p, 0);
+ _put_byte(&p, SEND_POLLACK);
+ skb_put(skb, (u8 *)p - (u8 *)skb->data);
+
+ skb_queue_tail(&card->dma->send_queue, skb);
+ c4_dispatch_tx(card);
+}
+
+/* ------------------------------------------------------------- */
+
+static void c4_handle_rx(avmcard *card)
+{
+ avmcard_dmainfo *dma = card->dma;
+ struct capi_ctr *ctrl;
+ avmctrl_info *cinfo;
+ struct sk_buff *skb;
+ void *p = dma->recvbuf.dmabuf;
+ u32 ApplId, MsgLen, DataB3Len, NCCI, WindowSize;
+ u8 b1cmd = _get_byte(&p);
+ u32 cidx;
+
+
+#ifdef AVM_C4_DEBUG
+ printk(KERN_DEBUG "%s: rx 0x%x len=%lu\n", card->name,
+ b1cmd, (unsigned long)dma->recvlen);
+#endif
+
+ switch (b1cmd) {
+ case RECEIVE_DATA_B3_IND:
+
+ ApplId = (unsigned) _get_word(&p);
+ MsgLen = _get_slice(&p, card->msgbuf);
+ DataB3Len = _get_slice(&p, card->databuf);
+ cidx = CAPIMSG_CONTROLLER(card->msgbuf)-card->cardnr;
+ if (cidx >= card->nlogcontr) cidx = 0;
+ ctrl = &card->ctrlinfo[cidx].capi_ctrl;
+
+ if (MsgLen < 30) { /* not CAPI 64Bit */
+ memset(card->msgbuf+MsgLen, 0, 30-MsgLen);
+ MsgLen = 30;
+ CAPIMSG_SETLEN(card->msgbuf, 30);
+ }
+ if (!(skb = alloc_skb(DataB3Len+MsgLen, GFP_ATOMIC))) {
+ printk(KERN_ERR "%s: incoming packet dropped\n",
+ card->name);
+ } else {
+ memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
+ memcpy(skb_put(skb, DataB3Len), card->databuf, DataB3Len);
+ capi_ctr_handle_message(ctrl, ApplId, skb);
+ }
+ break;
+
+ case RECEIVE_MESSAGE:
+
+ ApplId = (unsigned) _get_word(&p);
+ MsgLen = _get_slice(&p, card->msgbuf);
+ cidx = CAPIMSG_CONTROLLER(card->msgbuf)-card->cardnr;
+ if (cidx >= card->nlogcontr) cidx = 0;
+ cinfo = &card->ctrlinfo[cidx];
+ ctrl = &card->ctrlinfo[cidx].capi_ctrl;
+
+ if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) {
+ printk(KERN_ERR "%s: incoming packet dropped\n",
+ card->name);
+ } else {
+ memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
+ if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_CONF)
+ capilib_data_b3_conf(&cinfo->ncci_head, ApplId,
+ CAPIMSG_NCCI(skb->data),
+ CAPIMSG_MSGID(skb->data));
+
+ capi_ctr_handle_message(ctrl, ApplId, skb);
+ }
+ break;
+
+ case RECEIVE_NEW_NCCI:
+
+ ApplId = _get_word(&p);
+ NCCI = _get_word(&p);
+ WindowSize = _get_word(&p);
+ cidx = (NCCI&0x7f) - card->cardnr;
+ if (cidx >= card->nlogcontr) cidx = 0;
+
+ capilib_new_ncci(&card->ctrlinfo[cidx].ncci_head, ApplId, NCCI, WindowSize);
+
+ break;
+
+ case RECEIVE_FREE_NCCI:
+
+ ApplId = _get_word(&p);
+ NCCI = _get_word(&p);
+
+ if (NCCI != 0xffffffff) {
+ cidx = (NCCI&0x7f) - card->cardnr;
+ if (cidx >= card->nlogcontr) cidx = 0;
+ capilib_free_ncci(&card->ctrlinfo[cidx].ncci_head, ApplId, NCCI);
+ }
+ break;
+
+ case RECEIVE_START:
+#ifdef AVM_C4_POLLDEBUG
+ printk(KERN_INFO "%s: poll from c4\n", card->name);
+#endif
+ if (!suppress_pollack)
+ queue_pollack(card);
+ for (cidx=0; cidx < card->nr_controllers; cidx++) {
+ ctrl = &card->ctrlinfo[cidx].capi_ctrl;
+ capi_ctr_resume_output(ctrl);
+ }
+ break;
+
+ case RECEIVE_STOP:
+ for (cidx=0; cidx < card->nr_controllers; cidx++) {
+ ctrl = &card->ctrlinfo[cidx].capi_ctrl;
+ capi_ctr_suspend_output(ctrl);
+ }
+ break;
+
+ case RECEIVE_INIT:
+
+ cidx = card->nlogcontr;
+ if (cidx >= card->nr_controllers) {
+ printk(KERN_ERR "%s: card with %d controllers ??\n",
+ card->name, cidx+1);
+ break;
+ }
+ card->nlogcontr++;
+ cinfo = &card->ctrlinfo[cidx];
+ ctrl = &cinfo->capi_ctrl;
+ cinfo->versionlen = _get_slice(&p, cinfo->versionbuf);
+ b1_parse_version(cinfo);
+ printk(KERN_INFO "%s: %s-card (%s) now active\n",
+ card->name,
+ cinfo->version[VER_CARDTYPE],
+ cinfo->version[VER_DRIVER]);
+ capi_ctr_ready(&cinfo->capi_ctrl);
+ break;
+
+ case RECEIVE_TASK_READY:
+ ApplId = (unsigned) _get_word(&p);
+ MsgLen = _get_slice(&p, card->msgbuf);
+ card->msgbuf[MsgLen] = 0;
+ while ( MsgLen > 0
+ && ( card->msgbuf[MsgLen-1] == '\n'
+ || card->msgbuf[MsgLen-1] == '\r')) {
+ card->msgbuf[MsgLen-1] = 0;
+ MsgLen--;
+ }
+ printk(KERN_INFO "%s: task %d \"%s\" ready.\n",
+ card->name, ApplId, card->msgbuf);
+ break;
+
+ case RECEIVE_DEBUGMSG:
+ MsgLen = _get_slice(&p, card->msgbuf);
+ card->msgbuf[MsgLen] = 0;
+ while ( MsgLen > 0
+ && ( card->msgbuf[MsgLen-1] == '\n'
+ || card->msgbuf[MsgLen-1] == '\r')) {
+ card->msgbuf[MsgLen-1] = 0;
+ MsgLen--;
+ }
+ printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf);
+ break;
+
+ default:
+ printk(KERN_ERR "%s: c4_interrupt: 0x%x ???\n",
+ card->name, b1cmd);
+ return;
+ }
+}
+
+/* ------------------------------------------------------------- */
+
+static irqreturn_t c4_handle_interrupt(avmcard *card)
+{
+ unsigned long flags;
+ u32 status;
+
+ spin_lock_irqsave(&card->lock, flags);
+ status = c4inmeml(card->mbase+DOORBELL);
+
+ if (status & DBELL_RESET_HOST) {
+ u_int i;
+ c4outmeml(card->mbase+PCI_OUT_INT_MASK, 0x0c);
+ spin_unlock_irqrestore(&card->lock, flags);
+ if (card->nlogcontr == 0)
+ return IRQ_HANDLED;
+ printk(KERN_ERR "%s: unexpected reset\n", card->name);
+ for (i=0; i < card->nr_controllers; i++) {
+ avmctrl_info *cinfo = &card->ctrlinfo[i];
+ memset(cinfo->version, 0, sizeof(cinfo->version));
+ spin_lock_irqsave(&card->lock, flags);
+ capilib_release(&cinfo->ncci_head);
+ spin_unlock_irqrestore(&card->lock, flags);
+ capi_ctr_reseted(&cinfo->capi_ctrl);
+ }
+ card->nlogcontr = 0;
+ return IRQ_HANDLED;
+ }
+
+ status &= (DBELL_UP_HOST | DBELL_DOWN_HOST);
+ if (!status) {
+ spin_unlock_irqrestore(&card->lock, flags);
+ return IRQ_HANDLED;
+ }
+ c4outmeml(card->mbase+DOORBELL, status);
+
+ if ((status & DBELL_UP_HOST) != 0) {
+ card->dma->recvlen = c4inmeml(card->mbase+MBOX_UP_LEN);
+ c4outmeml(card->mbase+MBOX_UP_LEN, 0);
+ c4_handle_rx(card);
+ card->dma->recvlen = 0;
+ c4outmeml(card->mbase+MBOX_UP_LEN, card->dma->recvbuf.size);
+ c4outmeml(card->mbase+DOORBELL, DBELL_UP_ARM);
+ }
+
+ if ((status & DBELL_DOWN_HOST) != 0) {
+ card->csr &= ~DBELL_DOWN_ARM;
+ c4_dispatch_tx(card);
+ } else if (card->csr & DBELL_DOWN_HOST) {
+ if (c4inmeml(card->mbase+MBOX_DOWN_LEN) == 0) {
+ card->csr &= ~DBELL_DOWN_ARM;
+ c4_dispatch_tx(card);
+ }
+ }
+ spin_unlock_irqrestore(&card->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t c4_interrupt(int interrupt, void *devptr)
+{
+ avmcard *card = devptr;
+
+ return c4_handle_interrupt(card);
+}
+
+/* ------------------------------------------------------------- */
+
+static void c4_send_init(avmcard *card)
+{
+ struct sk_buff *skb;
+ void *p;
+ unsigned long flags;
+
+ skb = alloc_skb(15, GFP_ATOMIC);
+ if (!skb) {
+ printk(KERN_CRIT "%s: no memory, lost register appl.\n",
+ card->name);
+ return;
+ }
+ p = skb->data;
+ _put_byte(&p, 0);
+ _put_byte(&p, 0);
+ _put_byte(&p, SEND_INIT);
+ _put_word(&p, CAPI_MAXAPPL);
+ _put_word(&p, AVM_NCCI_PER_CHANNEL*30);
+ _put_word(&p, card->cardnr - 1);
+ skb_put(skb, (u8 *)p - (u8 *)skb->data);
+
+ skb_queue_tail(&card->dma->send_queue, skb);
+ spin_lock_irqsave(&card->lock, flags);
+ c4_dispatch_tx(card);
+ spin_unlock_irqrestore(&card->lock, flags);
+}
+
+static int queue_sendconfigword(avmcard *card, u32 val)
+{
+ struct sk_buff *skb;
+ unsigned long flags;
+ void *p;
+
+ skb = alloc_skb(3+4, GFP_ATOMIC);
+ if (!skb) {
+ printk(KERN_CRIT "%s: no memory, send config\n",
+ card->name);
+ return -ENOMEM;
+ }
+ p = skb->data;
+ _put_byte(&p, 0);
+ _put_byte(&p, 0);
+ _put_byte(&p, SEND_CONFIG);
+ _put_word(&p, val);
+ skb_put(skb, (u8 *)p - (u8 *)skb->data);
+
+ skb_queue_tail(&card->dma->send_queue, skb);
+ spin_lock_irqsave(&card->lock, flags);
+ c4_dispatch_tx(card);
+ spin_unlock_irqrestore(&card->lock, flags);
+ return 0;
+}
+
+static int queue_sendconfig(avmcard *card, char cval[4])
+{
+ struct sk_buff *skb;
+ unsigned long flags;
+ void *p;
+
+ skb = alloc_skb(3+4, GFP_ATOMIC);
+ if (!skb) {
+ printk(KERN_CRIT "%s: no memory, send config\n",
+ card->name);
+ return -ENOMEM;
+ }
+ p = skb->data;
+ _put_byte(&p, 0);
+ _put_byte(&p, 0);
+ _put_byte(&p, SEND_CONFIG);
+ _put_byte(&p, cval[0]);
+ _put_byte(&p, cval[1]);
+ _put_byte(&p, cval[2]);
+ _put_byte(&p, cval[3]);
+ skb_put(skb, (u8 *)p - (u8 *)skb->data);
+
+ skb_queue_tail(&card->dma->send_queue, skb);
+
+ spin_lock_irqsave(&card->lock, flags);
+ c4_dispatch_tx(card);
+ spin_unlock_irqrestore(&card->lock, flags);
+ return 0;
+}
+
+static int c4_send_config(avmcard *card, capiloaddatapart * config)
+{
+ u8 val[4];
+ unsigned char *dp;
+ u_int left;
+ int retval;
+
+ if ((retval = queue_sendconfigword(card, 1)) != 0)
+ return retval;
+ if ((retval = queue_sendconfigword(card, config->len)) != 0)
+ return retval;
+
+ dp = config->data;
+ left = config->len;
+ while (left >= sizeof(u32)) {
+ if (config->user) {
+ if (copy_from_user(val, dp, sizeof(val)))
+ return -EFAULT;
+ } else {
+ memcpy(val, dp, sizeof(val));
+ }
+ if ((retval = queue_sendconfig(card, val)) != 0)
+ return retval;
+ left -= sizeof(val);
+ dp += sizeof(val);
+ }
+ if (left) {
+ memset(val, 0, sizeof(val));
+ if (config->user) {
+ if (copy_from_user(&val, dp, left))
+ return -EFAULT;
+ } else {
+ memcpy(&val, dp, left);
+ }
+ if ((retval = queue_sendconfig(card, val)) != 0)
+ return retval;
+ }
+
+ return 0;
+}
+
+static int c4_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
+{
+ avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+ avmcard *card = cinfo->card;
+ int retval;
+
+ if ((retval = c4_load_t4file(card, &data->firmware))) {
+ printk(KERN_ERR "%s: failed to load t4file!!\n",
+ card->name);
+ c4_reset(card);
+ return retval;
+ }
+
+ card->csr = 0;
+ c4outmeml(card->mbase+MBOX_UP_LEN, 0);
+ c4outmeml(card->mbase+MBOX_DOWN_LEN, 0);
+ c4outmeml(card->mbase+DOORBELL, DBELL_INIT);
+ mdelay(1);
+ c4outmeml(card->mbase+DOORBELL,
+ DBELL_UP_HOST | DBELL_DOWN_HOST | DBELL_RESET_HOST);
+
+ c4outmeml(card->mbase+PCI_OUT_INT_MASK, 0x08);
+
+ card->dma->recvlen = 0;
+ c4outmeml(card->mbase+MBOX_UP_ADDR, card->dma->recvbuf.dmaaddr);
+ c4outmeml(card->mbase+MBOX_UP_LEN, card->dma->recvbuf.size);
+ c4outmeml(card->mbase+DOORBELL, DBELL_UP_ARM);
+
+ if (data->configuration.len > 0 && data->configuration.data) {
+ retval = c4_send_config(card, &data->configuration);
+ if (retval) {
+ printk(KERN_ERR "%s: failed to set config!!\n",
+ card->name);
+ c4_reset(card);
+ return retval;
+ }
+ }
+
+ c4_send_init(card);
+
+ return 0;
+}
+
+
+static void c4_reset_ctr(struct capi_ctr *ctrl)
+{
+ avmcard *card = ((avmctrl_info *)(ctrl->driverdata))->card;
+ avmctrl_info *cinfo;
+ u_int i;
+ unsigned long flags;
+
+ spin_lock_irqsave(&card->lock, flags);
+
+ c4_reset(card);
+
+ spin_unlock_irqrestore(&card->lock, flags);
+
+ for (i=0; i < card->nr_controllers; i++) {
+ cinfo = &card->ctrlinfo[i];
+ memset(cinfo->version, 0, sizeof(cinfo->version));
+ capi_ctr_reseted(&cinfo->capi_ctrl);
+ }
+ card->nlogcontr = 0;
+}
+
+static void c4_remove(struct pci_dev *pdev)
+{
+ avmcard *card = pci_get_drvdata(pdev);
+ avmctrl_info *cinfo;
+ u_int i;
+
+ if (!card)
+ return;
+
+ c4_reset(card);
+
+ for (i=0; i < card->nr_controllers; i++) {
+ cinfo = &card->ctrlinfo[i];
+ detach_capi_ctr(&cinfo->capi_ctrl);
+ }
+
+ free_irq(card->irq, card);
+ iounmap(card->mbase);
+ release_region(card->port, AVMB1_PORTLEN);
+ avmcard_dma_free(card->dma);
+ pci_set_drvdata(pdev, NULL);
+ b1_free_card(card);
+}
+
+/* ------------------------------------------------------------- */
+
+
+static void c4_register_appl(struct capi_ctr *ctrl,
+ u16 appl,
+ capi_register_params *rp)
+{
+ avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+ avmcard *card = cinfo->card;
+ struct sk_buff *skb;
+ int want = rp->level3cnt;
+ unsigned long flags;
+ int nconn;
+ void *p;
+
+ if (ctrl->cnr == card->cardnr) {
+
+ if (want > 0) nconn = want;
+ else nconn = ctrl->profile.nbchannel * 4 * -want;
+ if (nconn == 0) nconn = ctrl->profile.nbchannel * 4;
+
+ skb = alloc_skb(23, GFP_ATOMIC);
+ if (!skb) {
+ printk(KERN_CRIT "%s: no memory, lost register appl.\n",
+ card->name);
+ return;
+ }
+ p = skb->data;
+ _put_byte(&p, 0);
+ _put_byte(&p, 0);
+ _put_byte(&p, SEND_REGISTER);
+ _put_word(&p, appl);
+ _put_word(&p, 1024 * (nconn+1));
+ _put_word(&p, nconn);
+ _put_word(&p, rp->datablkcnt);
+ _put_word(&p, rp->datablklen);
+ skb_put(skb, (u8 *)p - (u8 *)skb->data);
+
+ skb_queue_tail(&card->dma->send_queue, skb);
+
+ spin_lock_irqsave(&card->lock, flags);
+ c4_dispatch_tx(card);
+ spin_unlock_irqrestore(&card->lock, flags);
+ }
+}
+
+/* ------------------------------------------------------------- */
+
+static void c4_release_appl(struct capi_ctr *ctrl, u16 appl)
+{
+ avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+ avmcard *card = cinfo->card;
+ unsigned long flags;
+ struct sk_buff *skb;
+ void *p;
+
+ spin_lock_irqsave(&card->lock, flags);
+ capilib_release_appl(&cinfo->ncci_head, appl);
+ spin_unlock_irqrestore(&card->lock, flags);
+
+ if (ctrl->cnr == card->cardnr) {
+ skb = alloc_skb(7, GFP_ATOMIC);
+ if (!skb) {
+ printk(KERN_CRIT "%s: no memory, lost release appl.\n",
+ card->name);
+ return;
+ }
+ p = skb->data;
+ _put_byte(&p, 0);
+ _put_byte(&p, 0);
+ _put_byte(&p, SEND_RELEASE);
+ _put_word(&p, appl);
+
+ skb_put(skb, (u8 *)p - (u8 *)skb->data);
+ skb_queue_tail(&card->dma->send_queue, skb);
+ spin_lock_irqsave(&card->lock, flags);
+ c4_dispatch_tx(card);
+ spin_unlock_irqrestore(&card->lock, flags);
+ }
+}
+
+/* ------------------------------------------------------------- */
+
+
+static u16 c4_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
+{
+ avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+ avmcard *card = cinfo->card;
+ u16 retval = CAPI_NOERROR;
+ unsigned long flags;
+
+ spin_lock_irqsave(&card->lock, flags);
+ if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3_REQ) {
+ retval = capilib_data_b3_req(&cinfo->ncci_head,
+ CAPIMSG_APPID(skb->data),
+ CAPIMSG_NCCI(skb->data),
+ CAPIMSG_MSGID(skb->data));
+ }
+ if (retval == CAPI_NOERROR) {
+ skb_queue_tail(&card->dma->send_queue, skb);
+ c4_dispatch_tx(card);
+ }
+ spin_unlock_irqrestore(&card->lock, flags);
+ return retval;
+}
+
+/* ------------------------------------------------------------- */
+
+static char *c4_procinfo(struct capi_ctr *ctrl)
+{
+ avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+
+ if (!cinfo)
+ return "";
+ sprintf(cinfo->infobuf, "%s %s 0x%x %d 0x%lx",
+ cinfo->cardname[0] ? cinfo->cardname : "-",
+ cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
+ cinfo->card ? cinfo->card->port : 0x0,
+ cinfo->card ? cinfo->card->irq : 0,
+ cinfo->card ? cinfo->card->membase : 0
+ );
+ return cinfo->infobuf;
+}
+
+static int c4_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, struct capi_ctr *ctrl)
+{
+ avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+ avmcard *card = cinfo->card;
+ u8 flag;
+ int len = 0;
+ char *s;
+
+ len += sprintf(page+len, "%-16s %s\n", "name", card->name);
+ len += sprintf(page+len, "%-16s 0x%x\n", "io", card->port);
+ len += sprintf(page+len, "%-16s %d\n", "irq", card->irq);
+ len += sprintf(page+len, "%-16s 0x%lx\n", "membase", card->membase);
+ switch (card->cardtype) {
+ case avm_b1isa: s = "B1 ISA"; break;
+ case avm_b1pci: s = "B1 PCI"; break;
+ case avm_b1pcmcia: s = "B1 PCMCIA"; break;
+ case avm_m1: s = "M1"; break;
+ case avm_m2: s = "M2"; break;
+ case avm_t1isa: s = "T1 ISA (HEMA)"; break;
+ case avm_t1pci: s = "T1 PCI"; break;
+ case avm_c4: s = "C4"; break;
+ case avm_c2: s = "C2"; break;
+ default: s = "???"; break;
+ }
+ len += sprintf(page+len, "%-16s %s\n", "type", s);
+ if ((s = cinfo->version[VER_DRIVER]) != NULL)
+ len += sprintf(page+len, "%-16s %s\n", "ver_driver", s);
+ if ((s = cinfo->version[VER_CARDTYPE]) != NULL)
+ len += sprintf(page+len, "%-16s %s\n", "ver_cardtype", s);
+ if ((s = cinfo->version[VER_SERIAL]) != NULL)
+ len += sprintf(page+len, "%-16s %s\n", "ver_serial", s);
+
+ if (card->cardtype != avm_m1) {
+ flag = ((u8 *)(ctrl->profile.manu))[3];
+ if (flag)
+ len += sprintf(page+len, "%-16s%s%s%s%s%s%s%s\n",
+ "protocol",
+ (flag & 0x01) ? " DSS1" : "",
+ (flag & 0x02) ? " CT1" : "",
+ (flag & 0x04) ? " VN3" : "",
+ (flag & 0x08) ? " NI1" : "",
+ (flag & 0x10) ? " AUSTEL" : "",
+ (flag & 0x20) ? " ESS" : "",
+ (flag & 0x40) ? " 1TR6" : ""
+ );
+ }
+ if (card->cardtype != avm_m1) {
+ flag = ((u8 *)(ctrl->profile.manu))[5];
+ if (flag)
+ len += sprintf(page+len, "%-16s%s%s%s%s\n",
+ "linetype",
+ (flag & 0x01) ? " point to point" : "",
+ (flag & 0x02) ? " point to multipoint" : "",
+ (flag & 0x08) ? " leased line without D-channel" : "",
+ (flag & 0x04) ? " leased line with D-channel" : ""
+ );
+ }
+ len += sprintf(page+len, "%-16s %s\n", "cardname", cinfo->cardname);
+
+ if (off+count >= len)
+ *eof = 1;
+ if (len < off)
+ return 0;
+ *start = page + off;
+ return ((count < len-off) ? count : len-off);
+}
+
+/* ------------------------------------------------------------- */
+
+static int c4_add_card(struct capicardparams *p, struct pci_dev *dev,
+ int nr_controllers)
+{
+ avmcard *card;
+ avmctrl_info *cinfo;
+ int retval;
+ int i;
+
+ card = b1_alloc_card(nr_controllers);
+ if (!card) {
+ printk(KERN_WARNING "c4: no memory.\n");
+ retval = -ENOMEM;
+ goto err;
+ }
+ card->dma = avmcard_dma_alloc("c4", dev, 2048+128, 2048+128);
+ if (!card->dma) {
+ printk(KERN_WARNING "c4: no memory.\n");
+ retval = -ENOMEM;
+ goto err_free;
+ }
+
+ sprintf(card->name, "c%d-%x", nr_controllers, p->port);
+ card->port = p->port;
+ card->irq = p->irq;
+ card->membase = p->membase;
+ card->cardtype = (nr_controllers == 4) ? avm_c4 : avm_c2;
+
+ if (!request_region(card->port, AVMB1_PORTLEN, card->name)) {
+ printk(KERN_WARNING "c4: ports 0x%03x-0x%03x in use.\n",
+ card->port, card->port + AVMB1_PORTLEN);
+ retval = -EBUSY;
+ goto err_free_dma;
+ }
+
+ card->mbase = ioremap(card->membase, 128);
+ if (card->mbase == NULL) {
+ printk(KERN_NOTICE "c4: can't remap memory at 0x%lx\n",
+ card->membase);
+ retval = -EIO;
+ goto err_release_region;
+ }
+
+ retval = c4_detect(card);
+ if (retval != 0) {
+ printk(KERN_NOTICE "c4: NO card at 0x%x error(%d)\n",
+ card->port, retval);
+ retval = -EIO;
+ goto err_unmap;
+ }
+ c4_reset(card);
+
+ retval = request_irq(card->irq, c4_interrupt, IRQF_SHARED, card->name, card);
+ if (retval) {
+ printk(KERN_ERR "c4: unable to get IRQ %d.\n",card->irq);
+ retval = -EBUSY;
+ goto err_unmap;
+ }
+
+ for (i=0; i < nr_controllers ; i++) {
+ cinfo = &card->ctrlinfo[i];
+ cinfo->capi_ctrl.owner = THIS_MODULE;
+ cinfo->capi_ctrl.driver_name = "c4";
+ cinfo->capi_ctrl.driverdata = cinfo;
+ cinfo->capi_ctrl.register_appl = c4_register_appl;
+ cinfo->capi_ctrl.release_appl = c4_release_appl;
+ cinfo->capi_ctrl.send_message = c4_send_message;
+ cinfo->capi_ctrl.load_firmware = c4_load_firmware;
+ cinfo->capi_ctrl.reset_ctr = c4_reset_ctr;
+ cinfo->capi_ctrl.procinfo = c4_procinfo;
+ cinfo->capi_ctrl.ctr_read_proc = c4_read_proc;
+ strcpy(cinfo->capi_ctrl.name, card->name);
+
+ retval = attach_capi_ctr(&cinfo->capi_ctrl);
+ if (retval) {
+ printk(KERN_ERR "c4: attach controller failed (%d).\n", i);
+ for (i--; i >= 0; i--) {
+ cinfo = &card->ctrlinfo[i];
+ detach_capi_ctr(&cinfo->capi_ctrl);
+ }
+ goto err_free_irq;
+ }
+ if (i == 0)
+ card->cardnr = cinfo->capi_ctrl.cnr;
+ }
+
+ printk(KERN_INFO "c4: AVM C%d at i/o %#x, irq %d, mem %#lx\n",
+ nr_controllers, card->port, card->irq,
+ card->membase);
+ pci_set_drvdata(dev, card);
+ return 0;
+
+ err_free_irq:
+ free_irq(card->irq, card);
+ err_unmap:
+ iounmap(card->mbase);
+ err_release_region:
+ release_region(card->port, AVMB1_PORTLEN);
+ err_free_dma:
+ avmcard_dma_free(card->dma);
+ err_free:
+ b1_free_card(card);
+ err:
+ return retval;
+}
+
+/* ------------------------------------------------------------- */
+
+static int __devinit c4_probe(struct pci_dev *dev,
+ const struct pci_device_id *ent)
+{
+ int nr = ent->driver_data;
+ int retval = 0;
+ struct capicardparams param;
+
+ if (pci_enable_device(dev) < 0) {
+ printk(KERN_ERR "c4: failed to enable AVM-C%d\n", nr);
+ return -ENODEV;
+ }
+ pci_set_master(dev);
+
+ param.port = pci_resource_start(dev, 1);
+ param.irq = dev->irq;
+ param.membase = pci_resource_start(dev, 0);
+
+ printk(KERN_INFO "c4: PCI BIOS reports AVM-C%d at i/o %#x, irq %d, mem %#x\n",
+ nr, param.port, param.irq, param.membase);
+
+ retval = c4_add_card(&param, dev, nr);
+ if (retval != 0) {
+ printk(KERN_ERR "c4: no AVM-C%d at i/o %#x, irq %d detected, mem %#x\n",
+ nr, param.port, param.irq, param.membase);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static struct pci_driver c4_pci_driver = {
+ .name = "c4",
+ .id_table = c4_pci_tbl,
+ .probe = c4_probe,
+ .remove = c4_remove,
+};
+
+static struct capi_driver capi_driver_c2 = {
+ .name = "c2",
+ .revision = "1.0",
+};
+
+static struct capi_driver capi_driver_c4 = {
+ .name = "c4",
+ .revision = "1.0",
+};
+
+static int __init c4_init(void)
+{
+ char *p;
+ char rev[32];
+ int err;
+
+ if ((p = strchr(revision, ':')) != NULL && p[1]) {
+ strlcpy(rev, p + 2, 32);
+ if ((p = strchr(rev, '$')) != NULL && p > rev)
+ *(p-1) = 0;
+ } else
+ strcpy(rev, "1.0");
+
+ err = pci_register_driver(&c4_pci_driver);
+ if (!err) {
+ strlcpy(capi_driver_c2.revision, rev, 32);
+ register_capi_driver(&capi_driver_c2);
+ strlcpy(capi_driver_c4.revision, rev, 32);
+ register_capi_driver(&capi_driver_c4);
+ printk(KERN_INFO "c4: revision %s\n", rev);
+ }
+ return err;
+}
+
+static void __exit c4_exit(void)
+{
+ unregister_capi_driver(&capi_driver_c2);
+ unregister_capi_driver(&capi_driver_c4);
+ pci_unregister_driver(&c4_pci_driver);
+}
+
+module_init(c4_init);
+module_exit(c4_exit);
diff --git a/drivers/isdn/hardware/avm/t1isa.c b/drivers/isdn/hardware/avm/t1isa.c
new file mode 100644
index 0000000..e772449
--- /dev/null
+++ b/drivers/isdn/hardware/avm/t1isa.c
@@ -0,0 +1,593 @@
+/* $Id: t1isa.c,v 1.1.2.3 2004/02/10 01:07:12 keil Exp $
+ *
+ * Module for AVM T1 HEMA-card.
+ *
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/capi.h>
+#include <linux/netdevice.h>
+#include <linux/kernelcapi.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+#include <linux/isdn/capilli.h>
+#include "avmcard.h"
+
+/* ------------------------------------------------------------- */
+
+static char *revision = "$Revision: 1.1.2.3 $";
+
+/* ------------------------------------------------------------- */
+
+MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM T1 HEMA ISA card");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+
+/* ------------------------------------------------------------- */
+
+static int hema_irq_table[16] =
+{0,
+ 0,
+ 0,
+ 0x80, /* irq 3 */
+ 0,
+ 0x90, /* irq 5 */
+ 0,
+ 0xA0, /* irq 7 */
+ 0,
+ 0xB0, /* irq 9 */
+ 0xC0, /* irq 10 */
+ 0xD0, /* irq 11 */
+ 0xE0, /* irq 12 */
+ 0,
+ 0,
+ 0xF0, /* irq 15 */
+};
+
+static int t1_detectandinit(unsigned int base, unsigned irq, int cardnr)
+{
+ unsigned char cregs[8];
+ unsigned char reverse_cardnr;
+ unsigned char dummy;
+ int i;
+
+ reverse_cardnr = ((cardnr & 0x01) << 3) | ((cardnr & 0x02) << 1)
+ | ((cardnr & 0x04) >> 1) | ((cardnr & 0x08) >> 3);
+ cregs[0] = (HEMA_VERSION_ID << 4) | (reverse_cardnr & 0xf);
+ cregs[1] = 0x00; /* fast & slow link connected to CON1 */
+ cregs[2] = 0x05; /* fast link 20MBit, slow link 20 MBit */
+ cregs[3] = 0;
+ cregs[4] = 0x11; /* zero wait state */
+ cregs[5] = hema_irq_table[irq & 0xf];
+ cregs[6] = 0;
+ cregs[7] = 0;
+
+ /*
+ * no one else should use the ISA bus in this moment,
+ * but no function there to prevent this :-(
+ * save_flags(flags); cli();
+ */
+
+ /* board reset */
+ t1outp(base, T1_RESETBOARD, 0xf);
+ mdelay(100);
+ dummy = t1inp(base, T1_FASTLINK+T1_OUTSTAT); /* first read */
+
+ /* write config */
+ dummy = (base >> 4) & 0xff;
+ for (i=1;i<=0xf;i++) t1outp(base, i, dummy);
+ t1outp(base, HEMA_PAL_ID & 0xf, dummy);
+ t1outp(base, HEMA_PAL_ID >> 4, cregs[0]);
+ for(i=1;i<7;i++) t1outp(base, 0, cregs[i]);
+ t1outp(base, ((base >> 4)) & 0x3, cregs[7]);
+ /* restore_flags(flags); */
+
+ mdelay(100);
+ t1outp(base, T1_FASTLINK+T1_RESETLINK, 0);
+ t1outp(base, T1_SLOWLINK+T1_RESETLINK, 0);
+ mdelay(10);
+ t1outp(base, T1_FASTLINK+T1_RESETLINK, 1);
+ t1outp(base, T1_SLOWLINK+T1_RESETLINK, 1);
+ mdelay(100);
+ t1outp(base, T1_FASTLINK+T1_RESETLINK, 0);
+ t1outp(base, T1_SLOWLINK+T1_RESETLINK, 0);
+ mdelay(10);
+ t1outp(base, T1_FASTLINK+T1_ANALYSE, 0);
+ mdelay(5);
+ t1outp(base, T1_SLOWLINK+T1_ANALYSE, 0);
+
+ if (t1inp(base, T1_FASTLINK+T1_OUTSTAT) != 0x1) /* tx empty */
+ return 1;
+ if (t1inp(base, T1_FASTLINK+T1_INSTAT) != 0x0) /* rx empty */
+ return 2;
+ if (t1inp(base, T1_FASTLINK+T1_IRQENABLE) != 0x0)
+ return 3;
+ if ((t1inp(base, T1_FASTLINK+T1_FIFOSTAT) & 0xf0) != 0x70)
+ return 4;
+ if ((t1inp(base, T1_FASTLINK+T1_IRQMASTER) & 0x0e) != 0)
+ return 5;
+ if ((t1inp(base, T1_FASTLINK+T1_IDENT) & 0x7d) != 1)
+ return 6;
+ if (t1inp(base, T1_SLOWLINK+T1_OUTSTAT) != 0x1) /* tx empty */
+ return 7;
+ if ((t1inp(base, T1_SLOWLINK+T1_IRQMASTER) & 0x0e) != 0)
+ return 8;
+ if ((t1inp(base, T1_SLOWLINK+T1_IDENT) & 0x7d) != 0)
+ return 9;
+ return 0;
+}
+
+static irqreturn_t t1isa_interrupt(int interrupt, void *devptr)
+{
+ avmcard *card = devptr;
+ avmctrl_info *cinfo = &card->ctrlinfo[0];
+ struct capi_ctr *ctrl = &cinfo->capi_ctrl;
+ unsigned char b1cmd;
+ struct sk_buff *skb;
+
+ unsigned ApplId;
+ unsigned MsgLen;
+ unsigned DataB3Len;
+ unsigned NCCI;
+ unsigned WindowSize;
+ unsigned long flags;
+
+ spin_lock_irqsave(&card->lock, flags);
+
+ while (b1_rx_full(card->port)) {
+
+ b1cmd = b1_get_byte(card->port);
+
+ switch (b1cmd) {
+
+ case RECEIVE_DATA_B3_IND:
+
+ ApplId = (unsigned) b1_get_word(card->port);
+ MsgLen = t1_get_slice(card->port, card->msgbuf);
+ DataB3Len = t1_get_slice(card->port, card->databuf);
+ spin_unlock_irqrestore(&card->lock, flags);
+
+ if (MsgLen < 30) { /* not CAPI 64Bit */
+ memset(card->msgbuf+MsgLen, 0, 30-MsgLen);
+ MsgLen = 30;
+ CAPIMSG_SETLEN(card->msgbuf, 30);
+ }
+ if (!(skb = alloc_skb(DataB3Len+MsgLen, GFP_ATOMIC))) {
+ printk(KERN_ERR "%s: incoming packet dropped\n",
+ card->name);
+ } else {
+ memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
+ memcpy(skb_put(skb, DataB3Len), card->databuf, DataB3Len);
+ capi_ctr_handle_message(ctrl, ApplId, skb);
+ }
+ break;
+
+ case RECEIVE_MESSAGE:
+
+ ApplId = (unsigned) b1_get_word(card->port);
+ MsgLen = t1_get_slice(card->port, card->msgbuf);
+ if (!(skb = alloc_skb(MsgLen, GFP_ATOMIC))) {
+ spin_unlock_irqrestore(&card->lock, flags);
+ printk(KERN_ERR "%s: incoming packet dropped\n",
+ card->name);
+ } else {
+ memcpy(skb_put(skb, MsgLen), card->msgbuf, MsgLen);
+ if (CAPIMSG_CMD(skb->data) == CAPI_DATA_B3)
+ capilib_data_b3_conf(&cinfo->ncci_head, ApplId,
+ CAPIMSG_NCCI(skb->data),
+ CAPIMSG_MSGID(skb->data));
+ spin_unlock_irqrestore(&card->lock, flags);
+ capi_ctr_handle_message(ctrl, ApplId, skb);
+ }
+ break;
+
+ case RECEIVE_NEW_NCCI:
+
+ ApplId = b1_get_word(card->port);
+ NCCI = b1_get_word(card->port);
+ WindowSize = b1_get_word(card->port);
+ capilib_new_ncci(&cinfo->ncci_head, ApplId, NCCI, WindowSize);
+ spin_unlock_irqrestore(&card->lock, flags);
+ break;
+
+ case RECEIVE_FREE_NCCI:
+
+ ApplId = b1_get_word(card->port);
+ NCCI = b1_get_word(card->port);
+ if (NCCI != 0xffffffff)
+ capilib_free_ncci(&cinfo->ncci_head, ApplId, NCCI);
+ spin_unlock_irqrestore(&card->lock, flags);
+ break;
+
+ case RECEIVE_START:
+ b1_put_byte(card->port, SEND_POLLACK);
+ spin_unlock_irqrestore(&card->lock, flags);
+ capi_ctr_resume_output(ctrl);
+ break;
+
+ case RECEIVE_STOP:
+ spin_unlock_irqrestore(&card->lock, flags);
+ capi_ctr_suspend_output(ctrl);
+ break;
+
+ case RECEIVE_INIT:
+
+ cinfo->versionlen = t1_get_slice(card->port, cinfo->versionbuf);
+ spin_unlock_irqrestore(&card->lock, flags);
+ b1_parse_version(cinfo);
+ printk(KERN_INFO "%s: %s-card (%s) now active\n",
+ card->name,
+ cinfo->version[VER_CARDTYPE],
+ cinfo->version[VER_DRIVER]);
+ capi_ctr_ready(ctrl);
+ break;
+
+ case RECEIVE_TASK_READY:
+ ApplId = (unsigned) b1_get_word(card->port);
+ MsgLen = t1_get_slice(card->port, card->msgbuf);
+ spin_unlock_irqrestore(&card->lock, flags);
+ card->msgbuf[MsgLen] = 0;
+ while ( MsgLen > 0
+ && ( card->msgbuf[MsgLen-1] == '\n'
+ || card->msgbuf[MsgLen-1] == '\r')) {
+ card->msgbuf[MsgLen-1] = 0;
+ MsgLen--;
+ }
+ printk(KERN_INFO "%s: task %d \"%s\" ready.\n",
+ card->name, ApplId, card->msgbuf);
+ break;
+
+ case RECEIVE_DEBUGMSG:
+ MsgLen = t1_get_slice(card->port, card->msgbuf);
+ spin_unlock_irqrestore(&card->lock, flags);
+ card->msgbuf[MsgLen] = 0;
+ while ( MsgLen > 0
+ && ( card->msgbuf[MsgLen-1] == '\n'
+ || card->msgbuf[MsgLen-1] == '\r')) {
+ card->msgbuf[MsgLen-1] = 0;
+ MsgLen--;
+ }
+ printk(KERN_INFO "%s: DEBUG: %s\n", card->name, card->msgbuf);
+ break;
+
+
+ case 0xff:
+ spin_unlock_irqrestore(&card->lock, flags);
+ printk(KERN_ERR "%s: card reseted ?\n", card->name);
+ return IRQ_HANDLED;
+ default:
+ spin_unlock_irqrestore(&card->lock, flags);
+ printk(KERN_ERR "%s: b1_interrupt: 0x%x ???\n",
+ card->name, b1cmd);
+ return IRQ_NONE;
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+/* ------------------------------------------------------------- */
+
+static int t1isa_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
+{
+ avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+ avmcard *card = cinfo->card;
+ unsigned int port = card->port;
+ unsigned long flags;
+ int retval;
+
+ t1_disable_irq(port);
+ b1_reset(port);
+
+ if ((retval = b1_load_t4file(card, &data->firmware))) {
+ b1_reset(port);
+ printk(KERN_ERR "%s: failed to load t4file!!\n",
+ card->name);
+ return retval;
+ }
+
+ if (data->configuration.len > 0 && data->configuration.data) {
+ if ((retval = b1_load_config(card, &data->configuration))) {
+ b1_reset(port);
+ printk(KERN_ERR "%s: failed to load config!!\n",
+ card->name);
+ return retval;
+ }
+ }
+
+ if (!b1_loaded(card)) {
+ printk(KERN_ERR "%s: failed to load t4file.\n", card->name);
+ return -EIO;
+ }
+
+ spin_lock_irqsave(&card->lock, flags);
+ b1_setinterrupt(port, card->irq, card->cardtype);
+ b1_put_byte(port, SEND_INIT);
+ b1_put_word(port, CAPI_MAXAPPL);
+ b1_put_word(port, AVM_NCCI_PER_CHANNEL*30);
+ b1_put_word(port, ctrl->cnr - 1);
+ spin_unlock_irqrestore(&card->lock, flags);
+
+ return 0;
+}
+
+static void t1isa_reset_ctr(struct capi_ctr *ctrl)
+{
+ avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+ avmcard *card = cinfo->card;
+ unsigned int port = card->port;
+ unsigned long flags;
+
+ t1_disable_irq(port);
+ b1_reset(port);
+ b1_reset(port);
+
+ memset(cinfo->version, 0, sizeof(cinfo->version));
+ spin_lock_irqsave(&card->lock, flags);
+ capilib_release(&cinfo->ncci_head);
+ spin_unlock_irqrestore(&card->lock, flags);
+ capi_ctr_reseted(ctrl);
+}
+
+static void t1isa_remove(struct pci_dev *pdev)
+{
+ avmctrl_info *cinfo = pci_get_drvdata(pdev);
+ avmcard *card;
+
+ if (!cinfo)
+ return;
+
+ card = cinfo->card;
+
+ t1_disable_irq(card->port);
+ b1_reset(card->port);
+ b1_reset(card->port);
+ t1_reset(card->port);
+
+ detach_capi_ctr(&cinfo->capi_ctrl);
+ free_irq(card->irq, card);
+ release_region(card->port, AVMB1_PORTLEN);
+ b1_free_card(card);
+}
+
+/* ------------------------------------------------------------- */
+
+static u16 t1isa_send_message(struct capi_ctr *ctrl, struct sk_buff *skb);
+static char *t1isa_procinfo(struct capi_ctr *ctrl);
+
+static int t1isa_probe(struct pci_dev *pdev, int cardnr)
+{
+ avmctrl_info *cinfo;
+ avmcard *card;
+ int retval;
+
+ card = b1_alloc_card(1);
+ if (!card) {
+ printk(KERN_WARNING "t1isa: no memory.\n");
+ retval = -ENOMEM;
+ goto err;
+ }
+
+ cinfo = card->ctrlinfo;
+ card->port = pci_resource_start(pdev, 0);
+ card->irq = pdev->irq;
+ card->cardtype = avm_t1isa;
+ card->cardnr = cardnr;
+ sprintf(card->name, "t1isa-%x", card->port);
+
+ if (!(((card->port & 0x7) == 0) && ((card->port & 0x30) != 0x30))) {
+ printk(KERN_WARNING "t1isa: invalid port 0x%x.\n", card->port);
+ retval = -EINVAL;
+ goto err_free;
+ }
+ if (hema_irq_table[card->irq & 0xf] == 0) {
+ printk(KERN_WARNING "t1isa: irq %d not valid.\n", card->irq);
+ retval = -EINVAL;
+ goto err_free;
+ }
+ if (!request_region(card->port, AVMB1_PORTLEN, card->name)) {
+ printk(KERN_INFO "t1isa: ports 0x%03x-0x%03x in use.\n",
+ card->port, card->port + AVMB1_PORTLEN);
+ retval = -EBUSY;
+ goto err_free;
+ }
+ retval = request_irq(card->irq, t1isa_interrupt, 0, card->name, card);
+ if (retval) {
+ printk(KERN_INFO "t1isa: unable to get IRQ %d.\n", card->irq);
+ retval = -EBUSY;
+ goto err_release_region;
+ }
+
+ if ((retval = t1_detectandinit(card->port, card->irq, card->cardnr)) != 0) {
+ printk(KERN_INFO "t1isa: NO card at 0x%x (%d)\n",
+ card->port, retval);
+ retval = -ENODEV;
+ goto err_free_irq;
+ }
+ t1_disable_irq(card->port);
+ b1_reset(card->port);
+
+ cinfo->capi_ctrl.owner = THIS_MODULE;
+ cinfo->capi_ctrl.driver_name = "t1isa";
+ cinfo->capi_ctrl.driverdata = cinfo;
+ cinfo->capi_ctrl.register_appl = b1_register_appl;
+ cinfo->capi_ctrl.release_appl = b1_release_appl;
+ cinfo->capi_ctrl.send_message = t1isa_send_message;
+ cinfo->capi_ctrl.load_firmware = t1isa_load_firmware;
+ cinfo->capi_ctrl.reset_ctr = t1isa_reset_ctr;
+ cinfo->capi_ctrl.procinfo = t1isa_procinfo;
+ cinfo->capi_ctrl.ctr_read_proc = b1ctl_read_proc;
+ strcpy(cinfo->capi_ctrl.name, card->name);
+
+ retval = attach_capi_ctr(&cinfo->capi_ctrl);
+ if (retval) {
+ printk(KERN_INFO "t1isa: attach controller failed.\n");
+ goto err_free_irq;
+ }
+
+ printk(KERN_INFO "t1isa: AVM T1 ISA at i/o %#x, irq %d, card %d\n",
+ card->port, card->irq, card->cardnr);
+
+ pci_set_drvdata(pdev, cinfo);
+ return 0;
+
+ err_free_irq:
+ free_irq(card->irq, card);
+ err_release_region:
+ release_region(card->port, AVMB1_PORTLEN);
+ err_free:
+ b1_free_card(card);
+ err:
+ return retval;
+}
+
+static u16 t1isa_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
+{
+ avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+ avmcard *card = cinfo->card;
+ unsigned int port = card->port;
+ unsigned long flags;
+ u16 len = CAPIMSG_LEN(skb->data);
+ u8 cmd = CAPIMSG_COMMAND(skb->data);
+ u8 subcmd = CAPIMSG_SUBCOMMAND(skb->data);
+ u16 dlen, retval;
+
+ spin_lock_irqsave(&card->lock, flags);
+ if (CAPICMD(cmd, subcmd) == CAPI_DATA_B3_REQ) {
+ retval = capilib_data_b3_req(&cinfo->ncci_head,
+ CAPIMSG_APPID(skb->data),
+ CAPIMSG_NCCI(skb->data),
+ CAPIMSG_MSGID(skb->data));
+ if (retval != CAPI_NOERROR) {
+ spin_unlock_irqrestore(&card->lock, flags);
+ return retval;
+ }
+ dlen = CAPIMSG_DATALEN(skb->data);
+
+ b1_put_byte(port, SEND_DATA_B3_REQ);
+ t1_put_slice(port, skb->data, len);
+ t1_put_slice(port, skb->data + len, dlen);
+ } else {
+ b1_put_byte(port, SEND_MESSAGE);
+ t1_put_slice(port, skb->data, len);
+ }
+ spin_unlock_irqrestore(&card->lock, flags);
+ dev_kfree_skb_any(skb);
+ return CAPI_NOERROR;
+}
+/* ------------------------------------------------------------- */
+
+static char *t1isa_procinfo(struct capi_ctr *ctrl)
+{
+ avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+
+ if (!cinfo)
+ return "";
+ sprintf(cinfo->infobuf, "%s %s 0x%x %d %d",
+ cinfo->cardname[0] ? cinfo->cardname : "-",
+ cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
+ cinfo->card ? cinfo->card->port : 0x0,
+ cinfo->card ? cinfo->card->irq : 0,
+ cinfo->card ? cinfo->card->cardnr : 0
+ );
+ return cinfo->infobuf;
+}
+
+
+/* ------------------------------------------------------------- */
+
+#define MAX_CARDS 4
+static struct pci_dev isa_dev[MAX_CARDS];
+static int io[MAX_CARDS];
+static int irq[MAX_CARDS];
+static int cardnr[MAX_CARDS];
+
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param_array(cardnr, int, NULL, 0);
+MODULE_PARM_DESC(io, "I/O base address(es)");
+MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
+MODULE_PARM_DESC(cardnr, "Card number(s) (as jumpered)");
+
+static int t1isa_add_card(struct capi_driver *driver, capicardparams *data)
+{
+ int i;
+
+ for (i = 0; i < MAX_CARDS; i++) {
+ if (isa_dev[i].resource[0].start)
+ continue;
+
+ isa_dev[i].resource[0].start = data->port;
+ isa_dev[i].irq = data->irq;
+
+ if (t1isa_probe(&isa_dev[i], data->cardnr) == 0)
+ return 0;
+ }
+ return -ENODEV;
+}
+
+static struct capi_driver capi_driver_t1isa = {
+ .name = "t1isa",
+ .revision = "1.0",
+ .add_card = t1isa_add_card,
+};
+
+static int __init t1isa_init(void)
+{
+ char rev[32];
+ char *p;
+ int i;
+
+ if ((p = strchr(revision, ':')) != NULL && p[1]) {
+ strlcpy(rev, p + 2, 32);
+ if ((p = strchr(rev, '$')) != NULL && p > rev)
+ *(p-1) = 0;
+ } else
+ strcpy(rev, "1.0");
+
+ for (i = 0; i < MAX_CARDS; i++) {
+ if (!io[i])
+ break;
+
+ isa_dev[i].resource[0].start = io[i];
+ isa_dev[i].irq = irq[i];
+
+ if (t1isa_probe(&isa_dev[i], cardnr[i]) != 0)
+ return -ENODEV;
+ }
+
+ strlcpy(capi_driver_t1isa.revision, rev, 32);
+ register_capi_driver(&capi_driver_t1isa);
+ printk(KERN_INFO "t1isa: revision %s\n", rev);
+
+ return 0;
+}
+
+static void __exit t1isa_exit(void)
+{
+ int i;
+
+ unregister_capi_driver(&capi_driver_t1isa);
+ for (i = 0; i < MAX_CARDS; i++) {
+ if (!io[i])
+ break;
+
+ t1isa_remove(&isa_dev[i]);
+ }
+}
+
+module_init(t1isa_init);
+module_exit(t1isa_exit);
diff --git a/drivers/isdn/hardware/avm/t1pci.c b/drivers/isdn/hardware/avm/t1pci.c
new file mode 100644
index 0000000..e6d298d
--- /dev/null
+++ b/drivers/isdn/hardware/avm/t1pci.c
@@ -0,0 +1,259 @@
+/* $Id: t1pci.c,v 1.1.2.2 2004/01/16 21:09:27 keil Exp $
+ *
+ * Module for AVM T1 PCI-card.
+ *
+ * Copyright 1999 by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/capi.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+#include <linux/isdn/capilli.h>
+#include "avmcard.h"
+
+#undef CONFIG_T1PCI_DEBUG
+#undef CONFIG_T1PCI_POLLDEBUG
+
+/* ------------------------------------------------------------- */
+static char *revision = "$Revision: 1.1.2.2 $";
+/* ------------------------------------------------------------- */
+
+static struct pci_device_id t1pci_pci_tbl[] = {
+ { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_T1, PCI_ANY_ID, PCI_ANY_ID },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(pci, t1pci_pci_tbl);
+MODULE_DESCRIPTION("CAPI4Linux: Driver for AVM T1 PCI card");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+
+/* ------------------------------------------------------------- */
+
+static char *t1pci_procinfo(struct capi_ctr *ctrl);
+
+static int t1pci_add_card(struct capicardparams *p, struct pci_dev *pdev)
+{
+ avmcard *card;
+ avmctrl_info *cinfo;
+ int retval;
+
+ card = b1_alloc_card(1);
+ if (!card) {
+ printk(KERN_WARNING "t1pci: no memory.\n");
+ retval = -ENOMEM;
+ goto err;
+ }
+
+ card->dma = avmcard_dma_alloc("t1pci", pdev, 2048+128, 2048+128);
+ if (!card->dma) {
+ printk(KERN_WARNING "t1pci: no memory.\n");
+ retval = -ENOMEM;
+ goto err_free;
+ }
+
+ cinfo = card->ctrlinfo;
+ sprintf(card->name, "t1pci-%x", p->port);
+ card->port = p->port;
+ card->irq = p->irq;
+ card->membase = p->membase;
+ card->cardtype = avm_t1pci;
+
+ if (!request_region(card->port, AVMB1_PORTLEN, card->name)) {
+ printk(KERN_WARNING "t1pci: ports 0x%03x-0x%03x in use.\n",
+ card->port, card->port + AVMB1_PORTLEN);
+ retval = -EBUSY;
+ goto err_free_dma;
+ }
+
+ card->mbase = ioremap(card->membase, 64);
+ if (!card->mbase) {
+ printk(KERN_NOTICE "t1pci: can't remap memory at 0x%lx\n",
+ card->membase);
+ retval = -EIO;
+ goto err_release_region;
+ }
+
+ b1dma_reset(card);
+
+ retval = t1pci_detect(card);
+ if (retval != 0) {
+ if (retval < 6)
+ printk(KERN_NOTICE "t1pci: NO card at 0x%x (%d)\n",
+ card->port, retval);
+ else
+ printk(KERN_NOTICE "t1pci: card at 0x%x, but cable not connected or T1 has no power (%d)\n",
+ card->port, retval);
+ retval = -EIO;
+ goto err_unmap;
+ }
+ b1dma_reset(card);
+
+ retval = request_irq(card->irq, b1dma_interrupt, IRQF_SHARED, card->name, card);
+ if (retval) {
+ printk(KERN_ERR "t1pci: unable to get IRQ %d.\n", card->irq);
+ retval = -EBUSY;
+ goto err_unmap;
+ }
+
+ cinfo->capi_ctrl.owner = THIS_MODULE;
+ cinfo->capi_ctrl.driver_name = "t1pci";
+ cinfo->capi_ctrl.driverdata = cinfo;
+ cinfo->capi_ctrl.register_appl = b1dma_register_appl;
+ cinfo->capi_ctrl.release_appl = b1dma_release_appl;
+ cinfo->capi_ctrl.send_message = b1dma_send_message;
+ cinfo->capi_ctrl.load_firmware = b1dma_load_firmware;
+ cinfo->capi_ctrl.reset_ctr = b1dma_reset_ctr;
+ cinfo->capi_ctrl.procinfo = t1pci_procinfo;
+ cinfo->capi_ctrl.ctr_read_proc = b1dmactl_read_proc;
+ strcpy(cinfo->capi_ctrl.name, card->name);
+
+ retval = attach_capi_ctr(&cinfo->capi_ctrl);
+ if (retval) {
+ printk(KERN_ERR "t1pci: attach controller failed.\n");
+ retval = -EBUSY;
+ goto err_free_irq;
+ }
+ card->cardnr = cinfo->capi_ctrl.cnr;
+
+ printk(KERN_INFO "t1pci: AVM T1 PCI at i/o %#x, irq %d, mem %#lx\n",
+ card->port, card->irq, card->membase);
+
+ pci_set_drvdata(pdev, card);
+ return 0;
+
+ err_free_irq:
+ free_irq(card->irq, card);
+ err_unmap:
+ iounmap(card->mbase);
+ err_release_region:
+ release_region(card->port, AVMB1_PORTLEN);
+ err_free_dma:
+ avmcard_dma_free(card->dma);
+ err_free:
+ b1_free_card(card);
+ err:
+ return retval;
+}
+
+/* ------------------------------------------------------------- */
+
+static void t1pci_remove(struct pci_dev *pdev)
+{
+ avmcard *card = pci_get_drvdata(pdev);
+ avmctrl_info *cinfo = card->ctrlinfo;
+
+ b1dma_reset(card);
+
+ detach_capi_ctr(&cinfo->capi_ctrl);
+ free_irq(card->irq, card);
+ iounmap(card->mbase);
+ release_region(card->port, AVMB1_PORTLEN);
+ avmcard_dma_free(card->dma);
+ b1_free_card(card);
+}
+
+/* ------------------------------------------------------------- */
+
+static char *t1pci_procinfo(struct capi_ctr *ctrl)
+{
+ avmctrl_info *cinfo = (avmctrl_info *)(ctrl->driverdata);
+
+ if (!cinfo)
+ return "";
+ sprintf(cinfo->infobuf, "%s %s 0x%x %d 0x%lx",
+ cinfo->cardname[0] ? cinfo->cardname : "-",
+ cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
+ cinfo->card ? cinfo->card->port : 0x0,
+ cinfo->card ? cinfo->card->irq : 0,
+ cinfo->card ? cinfo->card->membase : 0
+ );
+ return cinfo->infobuf;
+}
+
+/* ------------------------------------------------------------- */
+
+static int __devinit t1pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *ent)
+{
+ struct capicardparams param;
+ int retval;
+
+ if (pci_enable_device(dev) < 0) {
+ printk(KERN_ERR "t1pci: failed to enable AVM-T1-PCI\n");
+ return -ENODEV;
+ }
+ pci_set_master(dev);
+
+ param.port = pci_resource_start(dev, 1);
+ param.irq = dev->irq;
+ param.membase = pci_resource_start(dev, 0);
+
+ printk(KERN_INFO "t1pci: PCI BIOS reports AVM-T1-PCI at i/o %#x, irq %d, mem %#x\n",
+ param.port, param.irq, param.membase);
+
+ retval = t1pci_add_card(&param, dev);
+ if (retval != 0) {
+ printk(KERN_ERR "t1pci: no AVM-T1-PCI at i/o %#x, irq %d detected, mem %#x\n",
+ param.port, param.irq, param.membase);
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static struct pci_driver t1pci_pci_driver = {
+ .name = "t1pci",
+ .id_table = t1pci_pci_tbl,
+ .probe = t1pci_probe,
+ .remove = t1pci_remove,
+};
+
+static struct capi_driver capi_driver_t1pci = {
+ .name = "t1pci",
+ .revision = "1.0",
+};
+
+static int __init t1pci_init(void)
+{
+ char *p;
+ char rev[32];
+ int err;
+
+ if ((p = strchr(revision, ':')) != NULL && p[1]) {
+ strlcpy(rev, p + 2, 32);
+ if ((p = strchr(rev, '$')) != NULL && p > rev)
+ *(p-1) = 0;
+ } else
+ strcpy(rev, "1.0");
+
+ err = pci_register_driver(&t1pci_pci_driver);
+ if (!err) {
+ strlcpy(capi_driver_t1pci.revision, rev, 32);
+ register_capi_driver(&capi_driver_t1pci);
+ printk(KERN_INFO "t1pci: revision %s\n", rev);
+ }
+ return err;
+}
+
+static void __exit t1pci_exit(void)
+{
+ unregister_capi_driver(&capi_driver_t1pci);
+ pci_unregister_driver(&t1pci_pci_driver);
+}
+
+module_init(t1pci_init);
+module_exit(t1pci_exit);
diff --git a/drivers/isdn/hardware/eicon/Kconfig b/drivers/isdn/hardware/eicon/Kconfig
new file mode 100644
index 0000000..6082b6a
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/Kconfig
@@ -0,0 +1,51 @@
+#
+# ISDN DIVAS Eicon driver
+#
+
+menuconfig CAPI_EICON
+ bool "Active Eicon DIVA Server cards"
+ help
+ Enable support for Eicon Networks active ISDN cards.
+
+if CAPI_EICON
+
+config ISDN_DIVAS
+ tristate "Support Eicon DIVA Server cards"
+ depends on PROC_FS && PCI
+ help
+ Say Y here if you have an Eicon Networks DIVA Server PCI ISDN card.
+ In order to use this card, additional firmware is necessary, which
+ has to be downloaded into the card using the divactrl utility.
+
+if ISDN_DIVAS
+
+config ISDN_DIVAS_BRIPCI
+ bool "DIVA Server BRI/PCI support"
+ help
+ Enable support for DIVA Server BRI-PCI.
+
+config ISDN_DIVAS_PRIPCI
+ bool "DIVA Server PRI/PCI support"
+ help
+ Enable support for DIVA Server PRI-PCI.
+
+config ISDN_DIVAS_DIVACAPI
+ tristate "DIVA CAPI2.0 interface support"
+ help
+ You need this to provide the CAPI interface
+ for DIVA Server cards.
+
+config ISDN_DIVAS_USERIDI
+ tristate "DIVA User-IDI interface support"
+ help
+ Enable support for user-mode IDI interface.
+
+config ISDN_DIVAS_MAINT
+ tristate "DIVA Maint driver support"
+ depends on m
+ help
+ Enable Divas Maintenance driver.
+
+endif # ISDN_DIVAS
+
+endif # CAPI_EICON
diff --git a/drivers/isdn/hardware/eicon/Makefile b/drivers/isdn/hardware/eicon/Makefile
new file mode 100644
index 0000000..4fa7fdb
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/Makefile
@@ -0,0 +1,23 @@
+# Makefile for the Eicon DIVA ISDN drivers.
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_ISDN_DIVAS) += divadidd.o divas.o
+obj-$(CONFIG_ISDN_DIVAS_MAINT) += diva_mnt.o
+obj-$(CONFIG_ISDN_DIVAS_USERIDI) += diva_idi.o
+obj-$(CONFIG_ISDN_DIVAS_DIVACAPI) += divacapi.o
+
+# Multipart objects.
+
+divas-y := divasmain.o divasfunc.o di.o io.o istream.o \
+ diva.o divasproc.o diva_dma.o
+divas-$(CONFIG_ISDN_DIVAS_BRIPCI) += os_bri.o s_bri.o os_4bri.o s_4bri.o
+divas-$(CONFIG_ISDN_DIVAS_PRIPCI) += os_pri.o s_pri.o
+
+divacapi-y := capimain.o capifunc.o message.o capidtmf.o
+
+divadidd-y := diva_didd.o diddfunc.o dadapter.o
+
+diva_mnt-y := divamnt.o mntfunc.o debug.o maintidi.o
+
+diva_idi-y := divasi.o idifunc.o um_idi.o dqueue.o
diff --git a/drivers/isdn/hardware/eicon/adapter.h b/drivers/isdn/hardware/eicon/adapter.h
new file mode 100644
index 0000000..71a7c2f
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/adapter.h
@@ -0,0 +1,17 @@
+/* $Id: adapter.h,v 1.4 2004/03/21 17:26:01 armin Exp $ */
+
+#ifndef __DIVA_USER_MODE_IDI_ADAPTER_H__
+#define __DIVA_USER_MODE_IDI_ADAPTER_H__
+
+#define DIVA_UM_IDI_ADAPTER_REMOVED 0x00000001
+
+typedef struct _diva_um_idi_adapter {
+ struct list_head link;
+ DESCRIPTOR d;
+ int adapter_nr;
+ struct list_head entity_q; /* entities linked to this adapter */
+ dword status;
+} diva_um_idi_adapter_t;
+
+
+#endif
diff --git a/drivers/isdn/hardware/eicon/capi20.h b/drivers/isdn/hardware/eicon/capi20.h
new file mode 100644
index 0000000..7ebcccd
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/capi20.h
@@ -0,0 +1,699 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+#ifndef _INC_CAPI20
+#define _INC_CAPI20
+ /* operations on message queues */
+ /* the common device type for CAPI20 drivers */
+#define FILE_DEVICE_CAPI20 0x8001
+ /* DEVICE_CONTROL codes for user and kernel mode applications */
+#define CAPI20_CTL_REGISTER 0x0801
+#define CAPI20_CTL_RELEASE 0x0802
+#define CAPI20_CTL_GET_MANUFACTURER 0x0805
+#define CAPI20_CTL_GET_VERSION 0x0806
+#define CAPI20_CTL_GET_SERIAL 0x0807
+#define CAPI20_CTL_GET_PROFILE 0x0808
+ /* INTERNAL_DEVICE_CONTROL codes for kernel mode applicatios only */
+#define CAPI20_CTL_PUT_MESSAGE 0x0803
+#define CAPI20_CTL_GET_MESSAGE 0x0804
+ /* the wrapped codes as required by the system */
+#define CAPI_CTL_CODE(f,m) CTL_CODE(FILE_DEVICE_CAPI20,f,m,FILE_ANY_ACCESS)
+#define IOCTL_CAPI_REGISTER CAPI_CTL_CODE(CAPI20_CTL_REGISTER,METHOD_BUFFERED)
+#define IOCTL_CAPI_RELEASE CAPI_CTL_CODE(CAPI20_CTL_RELEASE,METHOD_BUFFERED)
+#define IOCTL_CAPI_GET_MANUFACTURER CAPI_CTL_CODE(CAPI20_CTL_GET_MANUFACTURER,METHOD_BUFFERED)
+#define IOCTL_CAPI_GET_VERSION CAPI_CTL_CODE(CAPI20_CTL_GET_VERSION,METHOD_BUFFERED)
+#define IOCTL_CAPI_GET_SERIAL CAPI_CTL_CODE(CAPI20_CTL_GET_SERIAL,METHOD_BUFFERED)
+#define IOCTL_CAPI_GET_PROFILE CAPI_CTL_CODE(CAPI20_CTL_GET_PROFILE,METHOD_BUFFERED)
+#define IOCTL_CAPI_PUT_MESSAGE CAPI_CTL_CODE(CAPI20_CTL_PUT_MESSAGE,METHOD_BUFFERED)
+#define IOCTL_CAPI_GET_MESSAGE CAPI_CTL_CODE(CAPI20_CTL_GET_MESSAGE,METHOD_BUFFERED)
+struct divas_capi_register_params {
+ word MessageBufferSize;
+ word maxLogicalConnection;
+ word maxBDataBlocks;
+ word maxBDataLen;
+};
+struct divas_capi_version {
+ word CapiMajor;
+ word CapiMinor;
+ word ManuMajor;
+ word ManuMinor;
+};
+typedef struct api_profile_s {
+ word Number;
+ word Channels;
+ dword Global_Options;
+ dword B1_Protocols;
+ dword B2_Protocols;
+ dword B3_Protocols;
+} API_PROFILE;
+ /* ISDN Common API message types */
+#define _ALERT_R 0x8001
+#define _CONNECT_R 0x8002
+#define _CONNECT_I 0x8202
+#define _CONNECT_ACTIVE_I 0x8203
+#define _DISCONNECT_R 0x8004
+#define _DISCONNECT_I 0x8204
+#define _LISTEN_R 0x8005
+#define _INFO_R 0x8008
+#define _INFO_I 0x8208
+#define _SELECT_B_REQ 0x8041
+#define _FACILITY_R 0x8080
+#define _FACILITY_I 0x8280
+#define _CONNECT_B3_R 0x8082
+#define _CONNECT_B3_I 0x8282
+#define _CONNECT_B3_ACTIVE_I 0x8283
+#define _DISCONNECT_B3_R 0x8084
+#define _DISCONNECT_B3_I 0x8284
+#define _DATA_B3_R 0x8086
+#define _DATA_B3_I 0x8286
+#define _RESET_B3_R 0x8087
+#define _RESET_B3_I 0x8287
+#define _CONNECT_B3_T90_ACTIVE_I 0x8288
+#define _MANUFACTURER_R 0x80ff
+#define _MANUFACTURER_I 0x82ff
+ /* OR this to convert a REQUEST to a CONFIRM */
+#define CONFIRM 0x0100
+ /* OR this to convert a INDICATION to a RESPONSE */
+#define RESPONSE 0x0100
+/*------------------------------------------------------------------*/
+/* diehl isdn private MANUFACTURER codes */
+/*------------------------------------------------------------------*/
+#define _DI_MANU_ID 0x44444944
+#define _DI_ASSIGN_PLCI 0x0001
+#define _DI_ADV_CODEC 0x0002
+#define _DI_DSP_CTRL 0x0003
+#define _DI_SIG_CTRL 0x0004
+#define _DI_RXT_CTRL 0x0005
+#define _DI_IDI_CTRL 0x0006
+#define _DI_CFG_CTRL 0x0007
+#define _DI_REMOVE_CODEC 0x0008
+#define _DI_OPTIONS_REQUEST 0x0009
+#define _DI_SSEXT_CTRL 0x000a
+#define _DI_NEGOTIATE_B3 0x000b
+/*------------------------------------------------------------------*/
+/* parameter structures */
+/*------------------------------------------------------------------*/
+ /* ALERT-REQUEST */
+typedef struct {
+ byte structs[1]; /* Additional Info */
+} _ALT_REQP;
+ /* ALERT-CONFIRM */
+typedef struct {
+ word Info;
+} _ALT_CONP;
+ /* CONNECT-REQUEST */
+typedef struct {
+ word CIP_Value;
+ byte structs[1]; /* Called party number,
+ Called party subaddress,
+ Calling party number,
+ Calling party subaddress,
+ B_protocol,
+ BC,
+ LLC,
+ HLC,
+ Additional Info */
+} _CON_REQP;
+ /* CONNECT-CONFIRM */
+typedef struct {
+ word Info;
+} _CON_CONP;
+ /* CONNECT-INDICATION */
+typedef struct {
+ word CIP_Value;
+ byte structs[1]; /* Called party number,
+ Called party subaddress,
+ Calling party number,
+ Calling party subaddress,
+ BC,
+ LLC,
+ HLC,
+ Additional Info */
+} _CON_INDP;
+ /* CONNECT-RESPONSE */
+typedef struct {
+ word Accept;
+ byte structs[1]; /* B_protocol,
+ Connected party number,
+ Connected party subaddress,
+ LLC */
+} _CON_RESP;
+ /* CONNECT-ACTIVE-INDICATION */
+typedef struct {
+ byte structs[1]; /* Connected party number,
+ Connected party subaddress,
+ LLC */
+} _CON_A_INDP;
+ /* CONNECT-ACTIVE-RESPONSE */
+typedef struct {
+ byte structs[1]; /* empty */
+} _CON_A_RESP;
+ /* DISCONNECT-REQUEST */
+typedef struct {
+ byte structs[1]; /* Additional Info */
+} _DIS_REQP;
+ /* DISCONNECT-CONFIRM */
+typedef struct {
+ word Info;
+} _DIS_CONP;
+ /* DISCONNECT-INDICATION */
+typedef struct {
+ word Info;
+} _DIS_INDP;
+ /* DISCONNECT-RESPONSE */
+typedef struct {
+ byte structs[1]; /* empty */
+} _DIS_RESP;
+ /* LISTEN-REQUEST */
+typedef struct {
+ dword Info_Mask;
+ dword CIP_Mask;
+ byte structs[1]; /* Calling party number,
+ Calling party subaddress */
+} _LIS_REQP;
+ /* LISTEN-CONFIRM */
+typedef struct {
+ word Info;
+} _LIS_CONP;
+ /* INFO-REQUEST */
+typedef struct {
+ byte structs[1]; /* Called party number,
+ Additional Info */
+} _INF_REQP;
+ /* INFO-CONFIRM */
+typedef struct {
+ word Info;
+} _INF_CONP;
+ /* INFO-INDICATION */
+typedef struct {
+ word Number;
+ byte structs[1]; /* Info element */
+} _INF_INDP;
+ /* INFO-RESPONSE */
+typedef struct {
+ byte structs[1]; /* empty */
+} _INF_RESP;
+ /* SELECT-B-REQUEST */
+typedef struct {
+ byte structs[1]; /* B-protocol */
+} _SEL_B_REQP;
+ /* SELECT-B-CONFIRM */
+typedef struct {
+ word Info;
+} _SEL_B_CONP;
+ /* FACILITY-REQUEST */
+typedef struct {
+ word Selector;
+ byte structs[1]; /* Facility parameters */
+} _FAC_REQP;
+ /* FACILITY-CONFIRM STRUCT FOR SUPPLEMENT. SERVICES */
+typedef struct {
+ byte struct_length;
+ word function;
+ byte length;
+ word SupplementaryServiceInfo;
+ dword SupportedServices;
+} _FAC_CON_STRUCTS;
+ /* FACILITY-CONFIRM */
+typedef struct {
+ word Info;
+ word Selector;
+ byte structs[1]; /* Facility parameters */
+} _FAC_CONP;
+ /* FACILITY-INDICATION */
+typedef struct {
+ word Selector;
+ byte structs[1]; /* Facility parameters */
+} _FAC_INDP;
+ /* FACILITY-RESPONSE */
+typedef struct {
+ word Selector;
+ byte structs[1]; /* Facility parameters */
+} _FAC_RESP;
+ /* CONNECT-B3-REQUEST */
+typedef struct {
+ byte structs[1]; /* NCPI */
+} _CON_B3_REQP;
+ /* CONNECT-B3-CONFIRM */
+typedef struct {
+ word Info;
+} _CON_B3_CONP;
+ /* CONNECT-B3-INDICATION */
+typedef struct {
+ byte structs[1]; /* NCPI */
+} _CON_B3_INDP;
+ /* CONNECT-B3-RESPONSE */
+typedef struct {
+ word Accept;
+ byte structs[1]; /* NCPI */
+} _CON_B3_RESP;
+ /* CONNECT-B3-ACTIVE-INDICATION */
+typedef struct {
+ byte structs[1]; /* NCPI */
+} _CON_B3_A_INDP;
+ /* CONNECT-B3-ACTIVE-RESPONSE */
+typedef struct {
+ byte structs[1]; /* empty */
+} _CON_B3_A_RESP;
+ /* DISCONNECT-B3-REQUEST */
+typedef struct {
+ byte structs[1]; /* NCPI */
+} _DIS_B3_REQP;
+ /* DISCONNECT-B3-CONFIRM */
+typedef struct {
+ word Info;
+} _DIS_B3_CONP;
+ /* DISCONNECT-B3-INDICATION */
+typedef struct {
+ word Info;
+ byte structs[1]; /* NCPI */
+} _DIS_B3_INDP;
+ /* DISCONNECT-B3-RESPONSE */
+typedef struct {
+ byte structs[1]; /* empty */
+} _DIS_B3_RESP;
+ /* DATA-B3-REQUEST */
+typedef struct {
+ dword Data;
+ word Data_Length;
+ word Number;
+ word Flags;
+} _DAT_B3_REQP;
+ /* DATA-B3-REQUEST 64 BIT Systems */
+typedef struct {
+ dword Data;
+ word Data_Length;
+ word Number;
+ word Flags;
+ void *pData;
+} _DAT_B3_REQ64P;
+ /* DATA-B3-CONFIRM */
+typedef struct {
+ word Number;
+ word Info;
+} _DAT_B3_CONP;
+ /* DATA-B3-INDICATION */
+typedef struct {
+ dword Data;
+ word Data_Length;
+ word Number;
+ word Flags;
+} _DAT_B3_INDP;
+ /* DATA-B3-INDICATION 64 BIT Systems */
+typedef struct {
+ dword Data;
+ word Data_Length;
+ word Number;
+ word Flags;
+ void *pData;
+} _DAT_B3_IND64P;
+ /* DATA-B3-RESPONSE */
+typedef struct {
+ word Number;
+} _DAT_B3_RESP;
+ /* RESET-B3-REQUEST */
+typedef struct {
+ byte structs[1]; /* NCPI */
+} _RES_B3_REQP;
+ /* RESET-B3-CONFIRM */
+typedef struct {
+ word Info;
+} _RES_B3_CONP;
+ /* RESET-B3-INDICATION */
+typedef struct {
+ byte structs[1]; /* NCPI */
+} _RES_B3_INDP;
+ /* RESET-B3-RESPONSE */
+typedef struct {
+ byte structs[1]; /* empty */
+} _RES_B3_RESP;
+ /* CONNECT-B3-T90-ACTIVE-INDICATION */
+typedef struct {
+ byte structs[1]; /* NCPI */
+} _CON_B3_T90_A_INDP;
+ /* CONNECT-B3-T90-ACTIVE-RESPONSE */
+typedef struct {
+ word Reject;
+ byte structs[1]; /* NCPI */
+} _CON_B3_T90_A_RESP;
+/*------------------------------------------------------------------*/
+/* message structure */
+/*------------------------------------------------------------------*/
+typedef struct _API_MSG CAPI_MSG;
+typedef struct _MSG_HEADER CAPI_MSG_HEADER;
+struct _API_MSG {
+ struct _MSG_HEADER {
+ word length;
+ word appl_id;
+ word command;
+ word number;
+ byte controller;
+ byte plci;
+ word ncci;
+ } header;
+ union {
+ _ALT_REQP alert_req;
+ _ALT_CONP alert_con;
+ _CON_REQP connect_req;
+ _CON_CONP connect_con;
+ _CON_INDP connect_ind;
+ _CON_RESP connect_res;
+ _CON_A_INDP connect_a_ind;
+ _CON_A_RESP connect_a_res;
+ _DIS_REQP disconnect_req;
+ _DIS_CONP disconnect_con;
+ _DIS_INDP disconnect_ind;
+ _DIS_RESP disconnect_res;
+ _LIS_REQP listen_req;
+ _LIS_CONP listen_con;
+ _INF_REQP info_req;
+ _INF_CONP info_con;
+ _INF_INDP info_ind;
+ _INF_RESP info_res;
+ _SEL_B_REQP select_b_req;
+ _SEL_B_CONP select_b_con;
+ _FAC_REQP facility_req;
+ _FAC_CONP facility_con;
+ _FAC_INDP facility_ind;
+ _FAC_RESP facility_res;
+ _CON_B3_REQP connect_b3_req;
+ _CON_B3_CONP connect_b3_con;
+ _CON_B3_INDP connect_b3_ind;
+ _CON_B3_RESP connect_b3_res;
+ _CON_B3_A_INDP connect_b3_a_ind;
+ _CON_B3_A_RESP connect_b3_a_res;
+ _DIS_B3_REQP disconnect_b3_req;
+ _DIS_B3_CONP disconnect_b3_con;
+ _DIS_B3_INDP disconnect_b3_ind;
+ _DIS_B3_RESP disconnect_b3_res;
+ _DAT_B3_REQP data_b3_req;
+ _DAT_B3_REQ64P data_b3_req64;
+ _DAT_B3_CONP data_b3_con;
+ _DAT_B3_INDP data_b3_ind;
+ _DAT_B3_IND64P data_b3_ind64;
+ _DAT_B3_RESP data_b3_res;
+ _RES_B3_REQP reset_b3_req;
+ _RES_B3_CONP reset_b3_con;
+ _RES_B3_INDP reset_b3_ind;
+ _RES_B3_RESP reset_b3_res;
+ _CON_B3_T90_A_INDP connect_b3_t90_a_ind;
+ _CON_B3_T90_A_RESP connect_b3_t90_a_res;
+ byte b[200];
+ } info;
+};
+/*------------------------------------------------------------------*/
+/* non-fatal errors */
+/*------------------------------------------------------------------*/
+#define _NCPI_IGNORED 0x0001
+#define _FLAGS_IGNORED 0x0002
+#define _ALERT_IGNORED 0x0003
+/*------------------------------------------------------------------*/
+/* API function error codes */
+/*------------------------------------------------------------------*/
+#define GOOD 0x0000
+#define _TOO_MANY_APPLICATIONS 0x1001
+#define _BLOCK_TOO_SMALL 0x1002
+#define _BUFFER_TOO_BIG 0x1003
+#define _MSG_BUFFER_TOO_SMALL 0x1004
+#define _TOO_MANY_CONNECTIONS 0x1005
+#define _REG_CAPI_BUSY 0x1007
+#define _REG_RESOURCE_ERROR 0x1008
+#define _REG_CAPI_NOT_INSTALLED 0x1009
+#define _WRONG_APPL_ID 0x1101
+#define _BAD_MSG 0x1102
+#define _QUEUE_FULL 0x1103
+#define _GET_NO_MSG 0x1104
+#define _MSG_LOST 0x1105
+#define _WRONG_NOTIFY 0x1106
+#define _CAPI_BUSY 0x1107
+#define _RESOURCE_ERROR 0x1108
+#define _CAPI_NOT_INSTALLED 0x1109
+#define _NO_EXTERNAL_EQUIPMENT 0x110a
+#define _ONLY_EXTERNAL_EQUIPMENT 0x110b
+/*------------------------------------------------------------------*/
+/* addressing/coding error codes */
+/*------------------------------------------------------------------*/
+#define _WRONG_STATE 0x2001
+#define _WRONG_IDENTIFIER 0x2002
+#define _OUT_OF_PLCI 0x2003
+#define _OUT_OF_NCCI 0x2004
+#define _OUT_OF_LISTEN 0x2005
+#define _OUT_OF_FAX 0x2006
+#define _WRONG_MESSAGE_FORMAT 0x2007
+#define _OUT_OF_INTERCONNECT_RESOURCES 0x2008
+/*------------------------------------------------------------------*/
+/* configuration error codes */
+/*------------------------------------------------------------------*/
+#define _B1_NOT_SUPPORTED 0x3001
+#define _B2_NOT_SUPPORTED 0x3002
+#define _B3_NOT_SUPPORTED 0x3003
+#define _B1_PARM_NOT_SUPPORTED 0x3004
+#define _B2_PARM_NOT_SUPPORTED 0x3005
+#define _B3_PARM_NOT_SUPPORTED 0x3006
+#define _B_STACK_NOT_SUPPORTED 0x3007
+#define _NCPI_NOT_SUPPORTED 0x3008
+#define _CIP_NOT_SUPPORTED 0x3009
+#define _FLAGS_NOT_SUPPORTED 0x300a
+#define _FACILITY_NOT_SUPPORTED 0x300b
+#define _DATA_LEN_NOT_SUPPORTED 0x300c
+#define _RESET_NOT_SUPPORTED 0x300d
+#define _SUPPLEMENTARY_SERVICE_NOT_SUPPORTED 0x300e
+#define _REQUEST_NOT_ALLOWED_IN_THIS_STATE 0x3010
+#define _FACILITY_SPECIFIC_FUNCTION_NOT_SUPP 0x3011
+/*------------------------------------------------------------------*/
+/* reason codes */
+/*------------------------------------------------------------------*/
+#define _L1_ERROR 0x3301
+#define _L2_ERROR 0x3302
+#define _L3_ERROR 0x3303
+#define _OTHER_APPL_CONNECTED 0x3304
+#define _CAPI_GUARD_ERROR 0x3305
+#define _L3_CAUSE 0x3400
+/*------------------------------------------------------------------*/
+/* b3 reason codes */
+/*------------------------------------------------------------------*/
+#define _B_CHANNEL_LOST 0x3301
+#define _B2_ERROR 0x3302
+#define _B3_ERROR 0x3303
+/*------------------------------------------------------------------*/
+/* fax error codes */
+/*------------------------------------------------------------------*/
+#define _FAX_NO_CONNECTION 0x3311
+#define _FAX_TRAINING_ERROR 0x3312
+#define _FAX_REMOTE_REJECT 0x3313
+#define _FAX_REMOTE_ABORT 0x3314
+#define _FAX_PROTOCOL_ERROR 0x3315
+#define _FAX_TX_UNDERRUN 0x3316
+#define _FAX_RX_OVERFLOW 0x3317
+#define _FAX_LOCAL_ABORT 0x3318
+#define _FAX_PARAMETER_ERROR 0x3319
+/*------------------------------------------------------------------*/
+/* line interconnect error codes */
+/*------------------------------------------------------------------*/
+#define _LI_USER_INITIATED 0x0000
+#define _LI_LINE_NO_LONGER_AVAILABLE 0x3805
+#define _LI_INTERCONNECT_NOT_ESTABLISHED 0x3806
+#define _LI_LINES_NOT_COMPATIBLE 0x3807
+#define _LI2_USER_INITIATED 0x0000
+#define _LI2_PLCI_HAS_NO_BCHANNEL 0x3800
+#define _LI2_LINES_NOT_COMPATIBLE 0x3801
+#define _LI2_NOT_IN_SAME_INTERCONNECTION 0x3802
+/*------------------------------------------------------------------*/
+/* global options */
+/*------------------------------------------------------------------*/
+#define GL_INTERNAL_CONTROLLER_SUPPORTED 0x00000001L
+#define GL_EXTERNAL_EQUIPMENT_SUPPORTED 0x00000002L
+#define GL_HANDSET_SUPPORTED 0x00000004L
+#define GL_DTMF_SUPPORTED 0x00000008L
+#define GL_SUPPLEMENTARY_SERVICES_SUPPORTED 0x00000010L
+#define GL_CHANNEL_ALLOCATION_SUPPORTED 0x00000020L
+#define GL_BCHANNEL_OPERATION_SUPPORTED 0x00000040L
+#define GL_LINE_INTERCONNECT_SUPPORTED 0x00000080L
+#define GL_ECHO_CANCELLER_SUPPORTED 0x00000100L
+/*------------------------------------------------------------------*/
+/* protocol selection */
+/*------------------------------------------------------------------*/
+#define B1_HDLC 0
+#define B1_TRANSPARENT 1
+#define B1_V110_ASYNC 2
+#define B1_V110_SYNC 3
+#define B1_T30 4
+#define B1_HDLC_INVERTED 5
+#define B1_TRANSPARENT_R 6
+#define B1_MODEM_ALL_NEGOTIATE 7
+#define B1_MODEM_ASYNC 8
+#define B1_MODEM_SYNC_HDLC 9
+#define B2_X75 0
+#define B2_TRANSPARENT 1
+#define B2_SDLC 2
+#define B2_LAPD 3
+#define B2_T30 4
+#define B2_PPP 5
+#define B2_TRANSPARENT_NO_CRC 6
+#define B2_MODEM_EC_COMPRESSION 7
+#define B2_X75_V42BIS 8
+#define B2_V120_ASYNC 9
+#define B2_V120_ASYNC_V42BIS 10
+#define B2_V120_BIT_TRANSPARENT 11
+#define B2_LAPD_FREE_SAPI_SEL 12
+#define B3_TRANSPARENT 0
+#define B3_T90NL 1
+#define B3_ISO8208 2
+#define B3_X25_DCE 3
+#define B3_T30 4
+#define B3_T30_WITH_EXTENSIONS 5
+#define B3_RESERVED 6
+#define B3_MODEM 7
+/*------------------------------------------------------------------*/
+/* facility definitions */
+/*------------------------------------------------------------------*/
+#define SELECTOR_HANDSET 0
+#define SELECTOR_DTMF 1
+#define SELECTOR_V42BIS 2
+#define SELECTOR_SU_SERV 3
+#define SELECTOR_POWER_MANAGEMENT 4
+#define SELECTOR_LINE_INTERCONNECT 5
+#define SELECTOR_ECHO_CANCELLER 6
+/*------------------------------------------------------------------*/
+/* supplementary services definitions */
+/*------------------------------------------------------------------*/
+#define S_GET_SUPPORTED_SERVICES 0x0000
+#define S_LISTEN 0x0001
+#define S_HOLD 0x0002
+#define S_RETRIEVE 0x0003
+#define S_SUSPEND 0x0004
+#define S_RESUME 0x0005
+#define S_ECT 0x0006
+#define S_3PTY_BEGIN 0x0007
+#define S_3PTY_END 0x0008
+#define S_CALL_DEFLECTION 0x000d
+#define S_CALL_FORWARDING_START 0x0009
+#define S_CALL_FORWARDING_STOP 0x000a
+#define S_INTERROGATE_DIVERSION 0x000b /* or interrogate parameters */
+#define S_INTERROGATE_NUMBERS 0x000c
+#define S_CCBS_REQUEST 0x000f
+#define S_CCBS_DEACTIVATE 0x0010
+#define S_CCBS_INTERROGATE 0x0011
+#define S_CCBS_CALL 0x0012
+#define S_MWI_ACTIVATE 0x0013
+#define S_MWI_DEACTIVATE 0x0014
+#define S_CONF_BEGIN 0x0017
+#define S_CONF_ADD 0x0018
+#define S_CONF_SPLIT 0x0019
+#define S_CONF_DROP 0x001a
+#define S_CONF_ISOLATE 0x001b
+#define S_CONF_REATTACH 0x001c
+#define S_CCBS_ERASECALLLINKAGEID 0x800d
+#define S_CCBS_STOP_ALERTING 0x8012
+#define S_CCBS_INFO_RETAIN 0x8013
+#define S_MWI_INDICATE 0x8014
+#define S_CONF_PARTYDISC 0x8016
+#define S_CONF_NOTIFICATION 0x8017
+/* Service Masks */
+#define MASK_HOLD_RETRIEVE 0x00000001
+#define MASK_TERMINAL_PORTABILITY 0x00000002
+#define MASK_ECT 0x00000004
+#define MASK_3PTY 0x00000008
+#define MASK_CALL_FORWARDING 0x00000010
+#define MASK_CALL_DEFLECTION 0x00000020
+#define MASK_MWI 0x00000100
+#define MASK_CCNR 0x00000200
+#define MASK_CONF 0x00000400
+/*------------------------------------------------------------------*/
+/* dtmf definitions */
+/*------------------------------------------------------------------*/
+#define DTMF_LISTEN_START 1
+#define DTMF_LISTEN_STOP 2
+#define DTMF_DIGITS_SEND 3
+#define DTMF_SUCCESS 0
+#define DTMF_INCORRECT_DIGIT 1
+#define DTMF_UNKNOWN_REQUEST 2
+/*------------------------------------------------------------------*/
+/* line interconnect definitions */
+/*------------------------------------------------------------------*/
+#define LI_GET_SUPPORTED_SERVICES 0
+#define LI_REQ_CONNECT 1
+#define LI_REQ_DISCONNECT 2
+#define LI_IND_CONNECT_ACTIVE 1
+#define LI_IND_DISCONNECT 2
+#define LI_FLAG_CONFERENCE_A_B ((dword) 0x00000001L)
+#define LI_FLAG_CONFERENCE_B_A ((dword) 0x00000002L)
+#define LI_FLAG_MONITOR_A ((dword) 0x00000004L)
+#define LI_FLAG_MONITOR_B ((dword) 0x00000008L)
+#define LI_FLAG_ANNOUNCEMENT_A ((dword) 0x00000010L)
+#define LI_FLAG_ANNOUNCEMENT_B ((dword) 0x00000020L)
+#define LI_FLAG_MIX_A ((dword) 0x00000040L)
+#define LI_FLAG_MIX_B ((dword) 0x00000080L)
+#define LI_CONFERENCING_SUPPORTED ((dword) 0x00000001L)
+#define LI_MONITORING_SUPPORTED ((dword) 0x00000002L)
+#define LI_ANNOUNCEMENTS_SUPPORTED ((dword) 0x00000004L)
+#define LI_MIXING_SUPPORTED ((dword) 0x00000008L)
+#define LI_CROSS_CONTROLLER_SUPPORTED ((dword) 0x00000010L)
+#define LI2_GET_SUPPORTED_SERVICES 0
+#define LI2_REQ_CONNECT 1
+#define LI2_REQ_DISCONNECT 2
+#define LI2_IND_CONNECT_ACTIVE 1
+#define LI2_IND_DISCONNECT 2
+#define LI2_FLAG_INTERCONNECT_A_B ((dword) 0x00000001L)
+#define LI2_FLAG_INTERCONNECT_B_A ((dword) 0x00000002L)
+#define LI2_FLAG_MONITOR_B ((dword) 0x00000004L)
+#define LI2_FLAG_MIX_B ((dword) 0x00000008L)
+#define LI2_FLAG_MONITOR_X ((dword) 0x00000010L)
+#define LI2_FLAG_MIX_X ((dword) 0x00000020L)
+#define LI2_FLAG_LOOP_B ((dword) 0x00000040L)
+#define LI2_FLAG_LOOP_PC ((dword) 0x00000080L)
+#define LI2_FLAG_LOOP_X ((dword) 0x00000100L)
+#define LI2_CROSS_CONTROLLER_SUPPORTED ((dword) 0x00000001L)
+#define LI2_ASYMMETRIC_SUPPORTED ((dword) 0x00000002L)
+#define LI2_MONITORING_SUPPORTED ((dword) 0x00000004L)
+#define LI2_MIXING_SUPPORTED ((dword) 0x00000008L)
+#define LI2_REMOTE_MONITORING_SUPPORTED ((dword) 0x00000010L)
+#define LI2_REMOTE_MIXING_SUPPORTED ((dword) 0x00000020L)
+#define LI2_B_LOOPING_SUPPORTED ((dword) 0x00000040L)
+#define LI2_PC_LOOPING_SUPPORTED ((dword) 0x00000080L)
+#define LI2_X_LOOPING_SUPPORTED ((dword) 0x00000100L)
+/*------------------------------------------------------------------*/
+/* echo canceller definitions */
+/*------------------------------------------------------------------*/
+#define EC_GET_SUPPORTED_SERVICES 0
+#define EC_ENABLE_OPERATION 1
+#define EC_DISABLE_OPERATION 2
+#define EC_ENABLE_NON_LINEAR_PROCESSING 0x0001
+#define EC_DO_NOT_REQUIRE_REVERSALS 0x0002
+#define EC_DETECT_DISABLE_TONE 0x0004
+#define EC_ENABLE_ADAPTIVE_PREDELAY 0x0008
+#define EC_NON_LINEAR_PROCESSING_SUPPORTED 0x0001
+#define EC_BYPASS_ON_ANY_2100HZ_SUPPORTED 0x0002
+#define EC_BYPASS_ON_REV_2100HZ_SUPPORTED 0x0004
+#define EC_ADAPTIVE_PREDELAY_SUPPORTED 0x0008
+#define EC_BYPASS_INDICATION 1
+#define EC_BYPASS_DUE_TO_CONTINUOUS_2100HZ 1
+#define EC_BYPASS_DUE_TO_REVERSED_2100HZ 2
+#define EC_BYPASS_RELEASED 3
+/*------------------------------------------------------------------*/
+/* function prototypes */
+/*------------------------------------------------------------------*/
+/*------------------------------------------------------------------*/
+#endif /* _INC_CAPI20 */
diff --git a/drivers/isdn/hardware/eicon/capidtmf.c b/drivers/isdn/hardware/eicon/capidtmf.c
new file mode 100644
index 0000000..f130724
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/capidtmf.c
@@ -0,0 +1,685 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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 "platform.h"
+
+
+
+
+
+
+
+
+
+#include "capidtmf.h"
+
+/* #define TRACE_ */
+
+#define FILE_ "CAPIDTMF.C"
+
+/*---------------------------------------------------------------------------*/
+
+
+#define trace(a)
+
+
+
+/*---------------------------------------------------------------------------*/
+
+static short capidtmf_expand_table_alaw[0x0100] =
+{
+ -5504, 5504, -344, 344, -22016, 22016, -1376, 1376,
+ -2752, 2752, -88, 88, -11008, 11008, -688, 688,
+ -7552, 7552, -472, 472, -30208, 30208, -1888, 1888,
+ -3776, 3776, -216, 216, -15104, 15104, -944, 944,
+ -4480, 4480, -280, 280, -17920, 17920, -1120, 1120,
+ -2240, 2240, -24, 24, -8960, 8960, -560, 560,
+ -6528, 6528, -408, 408, -26112, 26112, -1632, 1632,
+ -3264, 3264, -152, 152, -13056, 13056, -816, 816,
+ -6016, 6016, -376, 376, -24064, 24064, -1504, 1504,
+ -3008, 3008, -120, 120, -12032, 12032, -752, 752,
+ -8064, 8064, -504, 504, -32256, 32256, -2016, 2016,
+ -4032, 4032, -248, 248, -16128, 16128, -1008, 1008,
+ -4992, 4992, -312, 312, -19968, 19968, -1248, 1248,
+ -2496, 2496, -56, 56, -9984, 9984, -624, 624,
+ -7040, 7040, -440, 440, -28160, 28160, -1760, 1760,
+ -3520, 3520, -184, 184, -14080, 14080, -880, 880,
+ -5248, 5248, -328, 328, -20992, 20992, -1312, 1312,
+ -2624, 2624, -72, 72, -10496, 10496, -656, 656,
+ -7296, 7296, -456, 456, -29184, 29184, -1824, 1824,
+ -3648, 3648, -200, 200, -14592, 14592, -912, 912,
+ -4224, 4224, -264, 264, -16896, 16896, -1056, 1056,
+ -2112, 2112, -8, 8, -8448, 8448, -528, 528,
+ -6272, 6272, -392, 392, -25088, 25088, -1568, 1568,
+ -3136, 3136, -136, 136, -12544, 12544, -784, 784,
+ -5760, 5760, -360, 360, -23040, 23040, -1440, 1440,
+ -2880, 2880, -104, 104, -11520, 11520, -720, 720,
+ -7808, 7808, -488, 488, -31232, 31232, -1952, 1952,
+ -3904, 3904, -232, 232, -15616, 15616, -976, 976,
+ -4736, 4736, -296, 296, -18944, 18944, -1184, 1184,
+ -2368, 2368, -40, 40, -9472, 9472, -592, 592,
+ -6784, 6784, -424, 424, -27136, 27136, -1696, 1696,
+ -3392, 3392, -168, 168, -13568, 13568, -848, 848
+};
+
+static short capidtmf_expand_table_ulaw[0x0100] =
+{
+ -32124, 32124, -1884, 1884, -7932, 7932, -372, 372,
+ -15996, 15996, -876, 876, -3900, 3900, -120, 120,
+ -23932, 23932, -1372, 1372, -5884, 5884, -244, 244,
+ -11900, 11900, -620, 620, -2876, 2876, -56, 56,
+ -28028, 28028, -1628, 1628, -6908, 6908, -308, 308,
+ -13948, 13948, -748, 748, -3388, 3388, -88, 88,
+ -19836, 19836, -1116, 1116, -4860, 4860, -180, 180,
+ -9852, 9852, -492, 492, -2364, 2364, -24, 24,
+ -30076, 30076, -1756, 1756, -7420, 7420, -340, 340,
+ -14972, 14972, -812, 812, -3644, 3644, -104, 104,
+ -21884, 21884, -1244, 1244, -5372, 5372, -212, 212,
+ -10876, 10876, -556, 556, -2620, 2620, -40, 40,
+ -25980, 25980, -1500, 1500, -6396, 6396, -276, 276,
+ -12924, 12924, -684, 684, -3132, 3132, -72, 72,
+ -17788, 17788, -988, 988, -4348, 4348, -148, 148,
+ -8828, 8828, -428, 428, -2108, 2108, -8, 8,
+ -31100, 31100, -1820, 1820, -7676, 7676, -356, 356,
+ -15484, 15484, -844, 844, -3772, 3772, -112, 112,
+ -22908, 22908, -1308, 1308, -5628, 5628, -228, 228,
+ -11388, 11388, -588, 588, -2748, 2748, -48, 48,
+ -27004, 27004, -1564, 1564, -6652, 6652, -292, 292,
+ -13436, 13436, -716, 716, -3260, 3260, -80, 80,
+ -18812, 18812, -1052, 1052, -4604, 4604, -164, 164,
+ -9340, 9340, -460, 460, -2236, 2236, -16, 16,
+ -29052, 29052, -1692, 1692, -7164, 7164, -324, 324,
+ -14460, 14460, -780, 780, -3516, 3516, -96, 96,
+ -20860, 20860, -1180, 1180, -5116, 5116, -196, 196,
+ -10364, 10364, -524, 524, -2492, 2492, -32, 32,
+ -24956, 24956, -1436, 1436, -6140, 6140, -260, 260,
+ -12412, 12412, -652, 652, -3004, 3004, -64, 64,
+ -16764, 16764, -924, 924, -4092, 4092, -132, 132,
+ -8316, 8316, -396, 396, -1980, 1980, 0, 0
+};
+
+
+/*---------------------------------------------------------------------------*/
+
+static short capidtmf_recv_window_function[CAPIDTMF_RECV_ACCUMULATE_CYCLES] =
+{
+ -500L, -999L, -1499L, -1998L, -2496L, -2994L, -3491L, -3988L,
+ -4483L, -4978L, -5471L, -5963L, -6454L, -6943L, -7431L, -7917L,
+ -8401L, -8883L, -9363L, -9840L, -10316L, -10789L, -11259L, -11727L,
+ -12193L, -12655L, -13115L, -13571L, -14024L, -14474L, -14921L, -15364L,
+ -15804L, -16240L, -16672L, -17100L, -17524L, -17944L, -18360L, -18772L,
+ -19180L, -19583L, -19981L, -20375L, -20764L, -21148L, -21527L, -21901L,
+ -22270L, -22634L, -22993L, -23346L, -23694L, -24037L, -24374L, -24705L,
+ -25030L, -25350L, -25664L, -25971L, -26273L, -26568L, -26858L, -27141L,
+ -27418L, -27688L, -27952L, -28210L, -28461L, -28705L, -28943L, -29174L,
+ -29398L, -29615L, -29826L, -30029L, -30226L, -30415L, -30598L, -30773L,
+ -30941L, -31102L, -31256L, -31402L, -31541L, -31673L, -31797L, -31914L,
+ -32024L, -32126L, -32221L, -32308L, -32388L, -32460L, -32524L, -32581L,
+ -32631L, -32673L, -32707L, -32734L, -32753L, -32764L, -32768L, -32764L,
+ -32753L, -32734L, -32707L, -32673L, -32631L, -32581L, -32524L, -32460L,
+ -32388L, -32308L, -32221L, -32126L, -32024L, -31914L, -31797L, -31673L,
+ -31541L, -31402L, -31256L, -31102L, -30941L, -30773L, -30598L, -30415L,
+ -30226L, -30029L, -29826L, -29615L, -29398L, -29174L, -28943L, -28705L,
+ -28461L, -28210L, -27952L, -27688L, -27418L, -27141L, -26858L, -26568L,
+ -26273L, -25971L, -25664L, -25350L, -25030L, -24705L, -24374L, -24037L,
+ -23694L, -23346L, -22993L, -22634L, -22270L, -21901L, -21527L, -21148L,
+ -20764L, -20375L, -19981L, -19583L, -19180L, -18772L, -18360L, -17944L,
+ -17524L, -17100L, -16672L, -16240L, -15804L, -15364L, -14921L, -14474L,
+ -14024L, -13571L, -13115L, -12655L, -12193L, -11727L, -11259L, -10789L,
+ -10316L, -9840L, -9363L, -8883L, -8401L, -7917L, -7431L, -6943L,
+ -6454L, -5963L, -5471L, -4978L, -4483L, -3988L, -3491L, -2994L,
+ -2496L, -1998L, -1499L, -999L, -500L,
+};
+
+static byte capidtmf_leading_zeroes_table[0x100] =
+{
+ 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 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, 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, 1, 1, 1, 1,
+ 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, 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, 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, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+#define capidtmf_byte_leading_zeroes(b) (capidtmf_leading_zeroes_table[(BYTE)(b)])
+#define capidtmf_word_leading_zeroes(w) (((w) & 0xff00) ? capidtmf_leading_zeroes_table[(w) >> 8] : 8 + capidtmf_leading_zeroes_table[(w)])
+#define capidtmf_dword_leading_zeroes(d) (((d) & 0xffff0000L) ? (((d) & 0xff000000L) ? capidtmf_leading_zeroes_table[(d) >> 24] : 8 + capidtmf_leading_zeroes_table[(d) >> 16]) : (((d) & 0xff00) ? 16 + capidtmf_leading_zeroes_table[(d) >> 8] : 24 + capidtmf_leading_zeroes_table[(d)]))
+
+
+/*---------------------------------------------------------------------------*/
+
+
+static void capidtmf_goertzel_loop (long *buffer, long *coeffs, short *sample, long count)
+{
+ int i, j;
+ long c, d, q0, q1, q2;
+
+ for (i = 0; i < CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT - 1; i++)
+ {
+ q1 = buffer[i];
+ q2 = buffer[i + CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT];
+ d = coeffs[i] >> 1;
+ c = d << 1;
+ if (c >= 0)
+ {
+ for (j = 0; j < count; j++)
+ {
+ q0 = sample[j] - q2 + (c * (q1 >> 16)) + (((dword)(((dword) d) * ((dword)(q1 & 0xffff)))) >> 15);
+ q2 = q1;
+ q1 = q0;
+ }
+ }
+ else
+ {
+ c = -c;
+ d = -d;
+ for (j = 0; j < count; j++)
+ {
+ q0 = sample[j] - q2 - ((c * (q1 >> 16)) + (((dword)(((dword) d) * ((dword)(q1 & 0xffff)))) >> 15));
+ q2 = q1;
+ q1 = q0;
+ }
+ }
+ buffer[i] = q1;
+ buffer[i + CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT] = q2;
+ }
+ q1 = buffer[i];
+ q2 = buffer[i + CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT];
+ c = (coeffs[i] >> 1) << 1;
+ if (c >= 0)
+ {
+ for (j = 0; j < count; j++)
+ {
+ q0 = sample[j] - q2 + (c * (q1 >> 16)) + (((dword)(((dword)(c >> 1)) * ((dword)(q1 & 0xffff)))) >> 15);
+ q2 = q1;
+ q1 = q0;
+ c -= CAPIDTMF_RECV_FUNDAMENTAL_DECREMENT;
+ }
+ }
+ else
+ {
+ c = -c;
+ for (j = 0; j < count; j++)
+ {
+ q0 = sample[j] - q2 - ((c * (q1 >> 16)) + (((dword)(((dword)(c >> 1)) * ((dword)(q1 & 0xffff)))) >> 15));
+ q2 = q1;
+ q1 = q0;
+ c += CAPIDTMF_RECV_FUNDAMENTAL_DECREMENT;
+ }
+ }
+ coeffs[i] = c;
+ buffer[i] = q1;
+ buffer[i + CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT] = q2;
+}
+
+
+static void capidtmf_goertzel_result (long *buffer, long *coeffs)
+{
+ int i;
+ long d, e, q1, q2, lo, mid, hi;
+ dword k;
+
+ for (i = 0; i < CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT; i++)
+ {
+ q1 = buffer[i];
+ q2 = buffer[i + CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT];
+ d = coeffs[i] >> 1;
+ if (d >= 0)
+ d = ((d << 1) * (-q1 >> 16)) + (((dword)(((dword) d) * ((dword)(-q1 & 0xffff)))) >> 15);
+ else
+ d = ((-d << 1) * (-q1 >> 16)) + (((dword)(((dword) -d) * ((dword)(-q1 & 0xffff)))) >> 15);
+ e = (q2 >= 0) ? q2 : -q2;
+ if (d >= 0)
+ {
+ k = ((dword)(d & 0xffff)) * ((dword)(e & 0xffff));
+ lo = k & 0xffff;
+ mid = k >> 16;
+ k = ((dword)(d >> 16)) * ((dword)(e & 0xffff));
+ mid += k & 0xffff;
+ hi = k >> 16;
+ k = ((dword)(d & 0xffff)) * ((dword)(e >> 16));
+ mid += k & 0xffff;
+ hi += k >> 16;
+ hi += ((dword)(d >> 16)) * ((dword)(e >> 16));
+ }
+ else
+ {
+ d = -d;
+ k = ((dword)(d & 0xffff)) * ((dword)(e & 0xffff));
+ lo = -((long)(k & 0xffff));
+ mid = -((long)(k >> 16));
+ k = ((dword)(d >> 16)) * ((dword)(e & 0xffff));
+ mid -= k & 0xffff;
+ hi = -((long)(k >> 16));
+ k = ((dword)(d & 0xffff)) * ((dword)(e >> 16));
+ mid -= k & 0xffff;
+ hi -= k >> 16;
+ hi -= ((dword)(d >> 16)) * ((dword)(e >> 16));
+ }
+ if (q2 < 0)
+ {
+ lo = -lo;
+ mid = -mid;
+ hi = -hi;
+ }
+ d = (q1 >= 0) ? q1 : -q1;
+ k = ((dword)(d & 0xffff)) * ((dword)(d & 0xffff));
+ lo += k & 0xffff;
+ mid += k >> 16;
+ k = ((dword)(d >> 16)) * ((dword)(d & 0xffff));
+ mid += (k & 0xffff) << 1;
+ hi += (k >> 16) << 1;
+ hi += ((dword)(d >> 16)) * ((dword)(d >> 16));
+ d = (q2 >= 0) ? q2 : -q2;
+ k = ((dword)(d & 0xffff)) * ((dword)(d & 0xffff));
+ lo += k & 0xffff;
+ mid += k >> 16;
+ k = ((dword)(d >> 16)) * ((dword)(d & 0xffff));
+ mid += (k & 0xffff) << 1;
+ hi += (k >> 16) << 1;
+ hi += ((dword)(d >> 16)) * ((dword)(d >> 16));
+ mid += lo >> 16;
+ hi += mid >> 16;
+ buffer[i] = (lo & 0xffff) | (mid << 16);
+ buffer[i + CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT] = hi;
+ }
+}
+
+
+/*---------------------------------------------------------------------------*/
+
+#define CAPIDTMF_RECV_GUARD_SNR_INDEX_697 0
+#define CAPIDTMF_RECV_GUARD_SNR_INDEX_770 1
+#define CAPIDTMF_RECV_GUARD_SNR_INDEX_852 2
+#define CAPIDTMF_RECV_GUARD_SNR_INDEX_941 3
+#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1209 4
+#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1336 5
+#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1477 6
+#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1633 7
+#define CAPIDTMF_RECV_GUARD_SNR_INDEX_635 8
+#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1010 9
+#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1140 10
+#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1272 11
+#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1405 12
+#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1555 13
+#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1715 14
+#define CAPIDTMF_RECV_GUARD_SNR_INDEX_1875 15
+
+#define CAPIDTMF_RECV_GUARD_SNR_DONTCARE 0xc000
+#define CAPIDTMF_RECV_NO_DIGIT 0xff
+#define CAPIDTMF_RECV_TIME_GRANULARITY (CAPIDTMF_RECV_ACCUMULATE_CYCLES + 1)
+
+#define CAPIDTMF_RECV_INDICATION_DIGIT 0x0001
+
+static long capidtmf_recv_goertzel_coef_table[CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT] =
+{
+ 0xda97L * 2, /* 697 Hz (Low group 697 Hz) */
+ 0xd299L * 2, /* 770 Hz (Low group 770 Hz) */
+ 0xc8cbL * 2, /* 852 Hz (Low group 852 Hz) */
+ 0xbd36L * 2, /* 941 Hz (Low group 941 Hz) */
+ 0x9501L * 2, /* 1209 Hz (High group 1209 Hz) */
+ 0x7f89L * 2, /* 1336 Hz (High group 1336 Hz) */
+ 0x6639L * 2, /* 1477 Hz (High group 1477 Hz) */
+ 0x48c6L * 2, /* 1633 Hz (High group 1633 Hz) */
+ 0xe14cL * 2, /* 630 Hz (Lower guard of low group 631 Hz) */
+ 0xb2e0L * 2, /* 1015 Hz (Upper guard of low group 1039 Hz) */
+ 0xa1a0L * 2, /* 1130 Hz (Lower guard of high group 1140 Hz) */
+ 0x8a87L * 2, /* 1272 Hz (Guard between 1209 Hz and 1336 Hz: 1271 Hz) */
+ 0x7353L * 2, /* 1405 Hz (2nd harmonics of 697 Hz and guard between 1336 Hz and 1477 Hz: 1405 Hz) */
+ 0x583bL * 2, /* 1552 Hz (2nd harmonics of 770 Hz and guard between 1477 Hz and 1633 Hz: 1553 Hz) */
+ 0x37d8L * 2, /* 1720 Hz (2nd harmonics of 852 Hz and upper guard of high group: 1715 Hz) */
+ 0x0000L * 2 /* 100-630 Hz (fundamentals) */
+};
+
+
+static word capidtmf_recv_guard_snr_low_table[CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT] =
+{
+ 14, /* Low group peak versus 697 Hz */
+ 14, /* Low group peak versus 770 Hz */
+ 16, /* Low group peak versus 852 Hz */
+ 16, /* Low group peak versus 941 Hz */
+ CAPIDTMF_RECV_GUARD_SNR_DONTCARE, /* Low group peak versus 1209 Hz */
+ CAPIDTMF_RECV_GUARD_SNR_DONTCARE, /* Low group peak versus 1336 Hz */
+ CAPIDTMF_RECV_GUARD_SNR_DONTCARE, /* Low group peak versus 1477 Hz */
+ CAPIDTMF_RECV_GUARD_SNR_DONTCARE, /* Low group peak versus 1633 Hz */
+ 14, /* Low group peak versus 635 Hz */
+ 16, /* Low group peak versus 1010 Hz */
+ CAPIDTMF_RECV_GUARD_SNR_DONTCARE, /* Low group peak versus 1140 Hz */
+ CAPIDTMF_RECV_GUARD_SNR_DONTCARE, /* Low group peak versus 1272 Hz */
+ DSPDTMF_RX_HARMONICS_SEL_DEFAULT - 8, /* Low group peak versus 1405 Hz */
+ DSPDTMF_RX_HARMONICS_SEL_DEFAULT - 4, /* Low group peak versus 1555 Hz */
+ DSPDTMF_RX_HARMONICS_SEL_DEFAULT - 4, /* Low group peak versus 1715 Hz */
+ 12 /* Low group peak versus 100-630 Hz */
+};
+
+
+static word capidtmf_recv_guard_snr_high_table[CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT] =
+{
+ CAPIDTMF_RECV_GUARD_SNR_DONTCARE, /* High group peak versus 697 Hz */
+ CAPIDTMF_RECV_GUARD_SNR_DONTCARE, /* High group peak versus 770 Hz */
+ CAPIDTMF_RECV_GUARD_SNR_DONTCARE, /* High group peak versus 852 Hz */
+ CAPIDTMF_RECV_GUARD_SNR_DONTCARE, /* High group peak versus 941 Hz */
+ 20, /* High group peak versus 1209 Hz */
+ 20, /* High group peak versus 1336 Hz */
+ 20, /* High group peak versus 1477 Hz */
+ 20, /* High group peak versus 1633 Hz */
+ CAPIDTMF_RECV_GUARD_SNR_DONTCARE, /* High group peak versus 635 Hz */
+ CAPIDTMF_RECV_GUARD_SNR_DONTCARE, /* High group peak versus 1010 Hz */
+ 16, /* High group peak versus 1140 Hz */
+ 4, /* High group peak versus 1272 Hz */
+ 6, /* High group peak versus 1405 Hz */
+ 8, /* High group peak versus 1555 Hz */
+ 16, /* High group peak versus 1715 Hz */
+ 12 /* High group peak versus 100-630 Hz */
+};
+
+
+/*---------------------------------------------------------------------------*/
+
+static void capidtmf_recv_init (t_capidtmf_state *p_state)
+{
+ p_state->recv.min_gap_duration = 1;
+ p_state->recv.min_digit_duration = 1;
+
+ p_state->recv.cycle_counter = 0;
+ p_state->recv.current_digit_on_time = 0;
+ p_state->recv.current_digit_off_time = 0;
+ p_state->recv.current_digit_value = CAPIDTMF_RECV_NO_DIGIT;
+
+ p_state->recv.digit_write_pos = 0;
+ p_state->recv.digit_read_pos = 0;
+ p_state->recv.indication_state = 0;
+ p_state->recv.indication_state_ack = 0;
+ p_state->recv.state = CAPIDTMF_RECV_STATE_IDLE;
+}
+
+
+void capidtmf_recv_enable (t_capidtmf_state *p_state, word min_digit_duration, word min_gap_duration)
+{
+ p_state->recv.indication_state_ack &= CAPIDTMF_RECV_INDICATION_DIGIT;
+ p_state->recv.min_digit_duration = (word)(((((dword) min_digit_duration) * 8) +
+ ((dword)(CAPIDTMF_RECV_TIME_GRANULARITY / 2))) / ((dword) CAPIDTMF_RECV_TIME_GRANULARITY));
+ if (p_state->recv.min_digit_duration <= 1)
+ p_state->recv.min_digit_duration = 1;
+ else
+ (p_state->recv.min_digit_duration)--;
+ p_state->recv.min_gap_duration =
+ (word)((((dword) min_gap_duration) * 8) / ((dword) CAPIDTMF_RECV_TIME_GRANULARITY));
+ if (p_state->recv.min_gap_duration <= 1)
+ p_state->recv.min_gap_duration = 1;
+ else
+ (p_state->recv.min_gap_duration)--;
+ p_state->recv.state |= CAPIDTMF_RECV_STATE_DTMF_ACTIVE;
+}
+
+
+void capidtmf_recv_disable (t_capidtmf_state *p_state)
+{
+ p_state->recv.state &= ~CAPIDTMF_RECV_STATE_DTMF_ACTIVE;
+ if (p_state->recv.state == CAPIDTMF_RECV_STATE_IDLE)
+ capidtmf_recv_init (p_state);
+ else
+ {
+ p_state->recv.cycle_counter = 0;
+ p_state->recv.current_digit_on_time = 0;
+ p_state->recv.current_digit_off_time = 0;
+ p_state->recv.current_digit_value = CAPIDTMF_RECV_NO_DIGIT;
+ }
+}
+
+
+word capidtmf_recv_indication (t_capidtmf_state *p_state, byte *buffer)
+{
+ word i, j, k, flags;
+
+ flags = p_state->recv.indication_state ^ p_state->recv.indication_state_ack;
+ p_state->recv.indication_state_ack ^= flags & CAPIDTMF_RECV_INDICATION_DIGIT;
+ if (p_state->recv.digit_write_pos != p_state->recv.digit_read_pos)
+ {
+ i = 0;
+ k = p_state->recv.digit_write_pos;
+ j = p_state->recv.digit_read_pos;
+ do
+ {
+ buffer[i++] = p_state->recv.digit_buffer[j];
+ j = (j == CAPIDTMF_RECV_DIGIT_BUFFER_SIZE - 1) ? 0 : j + 1;
+ } while (j != k);
+ p_state->recv.digit_read_pos = k;
+ return (i);
+ }
+ p_state->recv.indication_state_ack ^= flags;
+ return (0);
+}
+
+
+#define CAPIDTMF_RECV_WINDOWED_SAMPLES 32
+
+void capidtmf_recv_block (t_capidtmf_state *p_state, byte *buffer, word length)
+{
+ byte result_digit;
+ word sample_number, cycle_counter, n, i;
+ word low_peak, high_peak;
+ dword lo, hi;
+ byte *p;
+ short *q;
+ byte goertzel_result_buffer[CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT];
+ short windowed_sample_buffer[CAPIDTMF_RECV_WINDOWED_SAMPLES];
+
+
+ if (p_state->recv.state & CAPIDTMF_RECV_STATE_DTMF_ACTIVE)
+ {
+ cycle_counter = p_state->recv.cycle_counter;
+ sample_number = 0;
+ while (sample_number < length)
+ {
+ if (cycle_counter < CAPIDTMF_RECV_ACCUMULATE_CYCLES)
+ {
+ if (cycle_counter == 0)
+ {
+ for (i = 0; i < CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT; i++)
+ {
+ p_state->recv.goertzel_buffer[0][i] = 0;
+ p_state->recv.goertzel_buffer[1][i] = 0;
+ }
+ }
+ n = CAPIDTMF_RECV_ACCUMULATE_CYCLES - cycle_counter;
+ if (n > length - sample_number)
+ n = length - sample_number;
+ if (n > CAPIDTMF_RECV_WINDOWED_SAMPLES)
+ n = CAPIDTMF_RECV_WINDOWED_SAMPLES;
+ p = buffer + sample_number;
+ q = capidtmf_recv_window_function + cycle_counter;
+ if (p_state->ulaw)
+ {
+ for (i = 0; i < n; i++)
+ {
+ windowed_sample_buffer[i] =
+ (short)((capidtmf_expand_table_ulaw[p[i]] * ((long)(q[i]))) >> 15);
+ }
+ }
+ else
+ {
+ for (i = 0; i < n; i++)
+ {
+ windowed_sample_buffer[i] =
+ (short)((capidtmf_expand_table_alaw[p[i]] * ((long)(q[i]))) >> 15);
+ }
+ }
+ capidtmf_recv_goertzel_coef_table[CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT - 1] = CAPIDTMF_RECV_FUNDAMENTAL_OFFSET;
+ capidtmf_goertzel_loop (p_state->recv.goertzel_buffer[0],
+ capidtmf_recv_goertzel_coef_table, windowed_sample_buffer, n);
+ cycle_counter += n;
+ sample_number += n;
+ }
+ else
+ {
+ capidtmf_goertzel_result (p_state->recv.goertzel_buffer[0],
+ capidtmf_recv_goertzel_coef_table);
+ for (i = 0; i < CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT; i++)
+ {
+ lo = (dword)(p_state->recv.goertzel_buffer[0][i]);
+ hi = (dword)(p_state->recv.goertzel_buffer[1][i]);
+ if (hi != 0)
+ {
+ n = capidtmf_dword_leading_zeroes (hi);
+ hi = (hi << n) | (lo >> (32 - n));
+ }
+ else
+ {
+ n = capidtmf_dword_leading_zeroes (lo);
+ hi = lo << n;
+ n += 32;
+ }
+ n = 195 - 3 * n;
+ if (hi >= 0xcb300000L)
+ n += 2;
+ else if (hi >= 0xa1450000L)
+ n++;
+ goertzel_result_buffer[i] = (byte) n;
+ }
+ low_peak = DSPDTMF_RX_SENSITIVITY_LOW_DEFAULT;
+ result_digit = CAPIDTMF_RECV_NO_DIGIT;
+ for (i = 0; i < CAPIDTMF_LOW_GROUP_FREQUENCIES; i++)
+ {
+ if (goertzel_result_buffer[i] > low_peak)
+ {
+ low_peak = goertzel_result_buffer[i];
+ result_digit = (byte) i;
+ }
+ }
+ high_peak = DSPDTMF_RX_SENSITIVITY_HIGH_DEFAULT;
+ n = CAPIDTMF_RECV_NO_DIGIT;
+ for (i = CAPIDTMF_LOW_GROUP_FREQUENCIES; i < CAPIDTMF_RECV_BASE_FREQUENCY_COUNT; i++)
+ {
+ if (goertzel_result_buffer[i] > high_peak)
+ {
+ high_peak = goertzel_result_buffer[i];
+ n = (i - CAPIDTMF_LOW_GROUP_FREQUENCIES) << 2;
+ }
+ }
+ result_digit |= (byte) n;
+ if (low_peak + DSPDTMF_RX_HIGH_EXCEEDING_LOW_DEFAULT < high_peak)
+ result_digit = CAPIDTMF_RECV_NO_DIGIT;
+ if (high_peak + DSPDTMF_RX_LOW_EXCEEDING_HIGH_DEFAULT < low_peak)
+ result_digit = CAPIDTMF_RECV_NO_DIGIT;
+ n = 0;
+ for (i = 0; i < CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT; i++)
+ {
+ if ((((short)(low_peak - goertzel_result_buffer[i] - capidtmf_recv_guard_snr_low_table[i])) < 0)
+ || (((short)(high_peak - goertzel_result_buffer[i] - capidtmf_recv_guard_snr_high_table[i])) < 0))
+ {
+ n++;
+ }
+ }
+ if (n != 2)
+ result_digit = CAPIDTMF_RECV_NO_DIGIT;
+
+ if (result_digit == CAPIDTMF_RECV_NO_DIGIT)
+ {
+ if (p_state->recv.current_digit_on_time != 0)
+ {
+ if (++(p_state->recv.current_digit_off_time) >= p_state->recv.min_gap_duration)
+ {
+ p_state->recv.current_digit_on_time = 0;
+ p_state->recv.current_digit_off_time = 0;
+ }
+ }
+ else
+ {
+ if (p_state->recv.current_digit_off_time != 0)
+ (p_state->recv.current_digit_off_time)--;
+ }
+ }
+ else
+ {
+ if ((p_state->recv.current_digit_on_time == 0)
+ && (p_state->recv.current_digit_off_time != 0))
+ {
+ (p_state->recv.current_digit_off_time)--;
+ }
+ else
+ {
+ n = p_state->recv.current_digit_off_time;
+ if ((p_state->recv.current_digit_on_time != 0)
+ && (result_digit != p_state->recv.current_digit_value))
+ {
+ p_state->recv.current_digit_on_time = 0;
+ n = 0;
+ }
+ p_state->recv.current_digit_value = result_digit;
+ p_state->recv.current_digit_off_time = 0;
+ if (p_state->recv.current_digit_on_time != 0xffff)
+ {
+ p_state->recv.current_digit_on_time += n + 1;
+ if (p_state->recv.current_digit_on_time >= p_state->recv.min_digit_duration)
+ {
+ p_state->recv.current_digit_on_time = 0xffff;
+ i = (p_state->recv.digit_write_pos == CAPIDTMF_RECV_DIGIT_BUFFER_SIZE - 1) ?
+ 0 : p_state->recv.digit_write_pos + 1;
+ if (i == p_state->recv.digit_read_pos)
+ {
+ trace (dprintf ("%s,%d: Receive digit overrun",
+ (char *)(FILE_), __LINE__));
+ }
+ else
+ {
+ p_state->recv.digit_buffer[p_state->recv.digit_write_pos] = result_digit;
+ p_state->recv.digit_write_pos = i;
+ p_state->recv.indication_state =
+ (p_state->recv.indication_state & ~CAPIDTMF_RECV_INDICATION_DIGIT) |
+ (~p_state->recv.indication_state_ack & CAPIDTMF_RECV_INDICATION_DIGIT);
+ }
+ }
+ }
+ }
+ }
+ cycle_counter = 0;
+ sample_number++;
+ }
+ }
+ p_state->recv.cycle_counter = cycle_counter;
+ }
+}
+
+
+void capidtmf_init (t_capidtmf_state *p_state, byte ulaw)
+{
+ p_state->ulaw = ulaw;
+ capidtmf_recv_init (p_state);
+}
+
+
+/*---------------------------------------------------------------------------*/
diff --git a/drivers/isdn/hardware/eicon/capidtmf.h b/drivers/isdn/hardware/eicon/capidtmf.h
new file mode 100644
index 0000000..242048f
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/capidtmf.h
@@ -0,0 +1,79 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+#ifndef CAPIDTMF_H_
+#define CAPIDTMF_H_
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+#define CAPIDTMF_TONE_GROUP_COUNT 2
+#define CAPIDTMF_LOW_GROUP_FREQUENCIES 4
+#define CAPIDTMF_HIGH_GROUP_FREQUENCIES 4
+#define DSPDTMF_RX_SENSITIVITY_LOW_DEFAULT 50 /* -52 dBm */
+#define DSPDTMF_RX_SENSITIVITY_HIGH_DEFAULT 50 /* -52 dBm */
+#define DSPDTMF_RX_HIGH_EXCEEDING_LOW_DEFAULT 10 /* dB */
+#define DSPDTMF_RX_LOW_EXCEEDING_HIGH_DEFAULT 10 /* dB */
+#define DSPDTMF_RX_HARMONICS_SEL_DEFAULT 12 /* dB */
+#define CAPIDTMF_RECV_BASE_FREQUENCY_COUNT (CAPIDTMF_LOW_GROUP_FREQUENCIES + CAPIDTMF_HIGH_GROUP_FREQUENCIES)
+#define CAPIDTMF_RECV_GUARD_FREQUENCY_COUNT 8
+#define CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT (CAPIDTMF_RECV_BASE_FREQUENCY_COUNT + CAPIDTMF_RECV_GUARD_FREQUENCY_COUNT)
+#define CAPIDTMF_RECV_POSITIVE_COEFF_COUNT 16
+#define CAPIDTMF_RECV_NEGATIVE_COEFF_COUNT (CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT - CAPIDTMF_RECV_POSITIVE_COEFF_COUNT)
+#define CAPIDTMF_RECV_ACCUMULATE_CYCLES 205
+#define CAPIDTMF_RECV_FUNDAMENTAL_OFFSET (0xff35L * 2)
+#define CAPIDTMF_RECV_FUNDAMENTAL_DECREMENT (0x0028L * 2)
+#define CAPIDTMF_RECV_DIGIT_BUFFER_SIZE 32
+#define CAPIDTMF_RECV_STATE_IDLE 0x00
+#define CAPIDTMF_RECV_STATE_DTMF_ACTIVE 0x01
+typedef struct tag_capidtmf_recv_state
+{
+ byte digit_buffer[CAPIDTMF_RECV_DIGIT_BUFFER_SIZE];
+ word digit_write_pos;
+ word digit_read_pos;
+ word indication_state;
+ word indication_state_ack;
+ long goertzel_buffer[2][CAPIDTMF_RECV_TOTAL_FREQUENCY_COUNT];
+ word min_gap_duration;
+ word min_digit_duration;
+ word cycle_counter;
+ word current_digit_on_time;
+ word current_digit_off_time;
+ byte current_digit_value;
+ byte state;
+} t_capidtmf_recv_state;
+typedef struct tag_capidtmf_state
+{
+ byte ulaw;
+ t_capidtmf_recv_state recv;
+} t_capidtmf_state;
+word capidtmf_recv_indication (t_capidtmf_state *p_state, byte *buffer);
+void capidtmf_recv_block (t_capidtmf_state *p_state, byte *buffer, word length);
+void capidtmf_init (t_capidtmf_state *p_state, byte ulaw);
+void capidtmf_recv_enable (t_capidtmf_state *p_state, word min_digit_duration, word min_gap_duration);
+void capidtmf_recv_disable (t_capidtmf_state *p_state);
+#define capidtmf_indication(p_state,buffer) (((p_state)->recv.indication_state != (p_state)->recv.indication_state_ack) ? capidtmf_recv_indication (p_state, buffer) : 0)
+#define capidtmf_recv_process_block(p_state,buffer,length) { if ((p_state)->recv.state != CAPIDTMF_RECV_STATE_IDLE) capidtmf_recv_block (p_state, buffer, length); }
+/*---------------------------------------------------------------------------*/
+/*---------------------------------------------------------------------------*/
+#endif
diff --git a/drivers/isdn/hardware/eicon/capifunc.c b/drivers/isdn/hardware/eicon/capifunc.c
new file mode 100644
index 0000000..4d425c6
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/capifunc.c
@@ -0,0 +1,1219 @@
+/* $Id: capifunc.c,v 1.61.4.7 2005/02/11 19:40:25 armin Exp $
+ *
+ * ISDN interface module for Eicon active cards DIVA.
+ * CAPI Interface common functions
+ *
+ * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
+ * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include "platform.h"
+#include "os_capi.h"
+#include "di_defs.h"
+#include "capi20.h"
+#include "divacapi.h"
+#include "divasync.h"
+#include "capifunc.h"
+
+#define DBG_MINIMUM (DL_LOG + DL_FTL + DL_ERR)
+#define DBG_DEFAULT (DBG_MINIMUM + DL_XLOG + DL_REG)
+
+DIVA_CAPI_ADAPTER *adapter = (DIVA_CAPI_ADAPTER *) NULL;
+APPL *application = (APPL *) NULL;
+byte max_appl = MAX_APPL;
+byte max_adapter = 0;
+static CAPI_MSG *mapped_msg = (CAPI_MSG *) NULL;
+
+byte UnMapController(byte);
+char DRIVERRELEASE_CAPI[32];
+
+extern void AutomaticLaw(DIVA_CAPI_ADAPTER *);
+extern void callback(ENTITY *);
+extern word api_remove_start(void);
+extern word CapiRelease(word);
+extern word CapiRegister(word);
+extern word api_put(APPL *, CAPI_MSG *);
+
+static diva_os_spin_lock_t api_lock;
+
+static LIST_HEAD(cards);
+
+static dword notify_handle;
+static void DIRequest(ENTITY * e);
+static DESCRIPTOR MAdapter;
+static DESCRIPTOR DAdapter;
+static byte ControllerMap[MAX_DESCRIPTORS + 1];
+
+
+static void diva_register_appl(struct capi_ctr *, __u16,
+ capi_register_params *);
+static void diva_release_appl(struct capi_ctr *, __u16);
+static char *diva_procinfo(struct capi_ctr *);
+static u16 diva_send_message(struct capi_ctr *,
+ diva_os_message_buffer_s *);
+extern void diva_os_set_controller_struct(struct capi_ctr *);
+
+extern void DIVA_DIDD_Read(DESCRIPTOR *, int);
+
+/*
+ * debug
+ */
+static void no_printf(unsigned char *, ...);
+#include "debuglib.c"
+static void xlog(char *x, ...)
+{
+#ifndef DIVA_NO_DEBUGLIB
+ va_list ap;
+ if (myDriverDebugHandle.dbgMask & DL_XLOG) {
+ va_start(ap, x);
+ if (myDriverDebugHandle.dbg_irq) {
+ myDriverDebugHandle.dbg_irq(myDriverDebugHandle.id,
+ DLI_XLOG, x, ap);
+ } else if (myDriverDebugHandle.dbg_old) {
+ myDriverDebugHandle.dbg_old(myDriverDebugHandle.id,
+ x, ap);
+ }
+ va_end(ap);
+ }
+#endif
+}
+
+/*
+ * info for proc
+ */
+static char *diva_procinfo(struct capi_ctr *ctrl)
+{
+ return (ctrl->serial);
+}
+
+/*
+ * stop debugging
+ */
+static void stop_dbg(void)
+{
+ DbgDeregister();
+ memset(&MAdapter, 0, sizeof(MAdapter));
+ dprintf = no_printf;
+}
+
+/*
+ * dummy debug function
+ */
+static void no_printf(unsigned char *x, ...)
+{
+}
+
+/*
+ * Controller mapping
+ */
+byte MapController(byte Controller)
+{
+ byte i;
+ byte MappedController = 0;
+ byte ctrl = Controller & 0x7f; /* mask external controller bit off */
+
+ for (i = 1; i < max_adapter + 1; i++) {
+ if (ctrl == ControllerMap[i]) {
+ MappedController = (byte) i;
+ break;
+ }
+ }
+ if (i > max_adapter) {
+ ControllerMap[0] = ctrl;
+ MappedController = 0;
+ }
+ return (MappedController | (Controller & 0x80)); /* put back external controller bit */
+}
+
+/*
+ * Controller unmapping
+ */
+byte UnMapController(byte MappedController)
+{
+ byte Controller;
+ byte ctrl = MappedController & 0x7f; /* mask external controller bit off */
+
+ if (ctrl <= max_adapter) {
+ Controller = ControllerMap[ctrl];
+ } else {
+ Controller = 0;
+ }
+
+ return (Controller | (MappedController & 0x80)); /* put back external controller bit */
+}
+
+/*
+ * find a new free id
+ */
+static int find_free_id(void)
+{
+ int num = 0;
+ DIVA_CAPI_ADAPTER *a;
+
+ while (num < MAX_DESCRIPTORS) {
+ a = &adapter[num];
+ if (!a->Id)
+ break;
+ num++;
+ }
+ return(num + 1);
+}
+
+/*
+ * find a card structure by controller number
+ */
+static diva_card *find_card_by_ctrl(word controller)
+{
+ struct list_head *tmp;
+ diva_card *card;
+
+ list_for_each(tmp, &cards) {
+ card = list_entry(tmp, diva_card, list);
+ if (ControllerMap[card->Id] == controller) {
+ if (card->remove_in_progress)
+ card = NULL;
+ return(card);
+ }
+ }
+ return (diva_card *) 0;
+}
+
+/*
+ * Buffer RX/TX
+ */
+void *TransmitBufferSet(APPL * appl, dword ref)
+{
+ appl->xbuffer_used[ref] = true;
+ DBG_PRV1(("%d:xbuf_used(%d)", appl->Id, ref + 1))
+ return (void *)(long)ref;
+}
+
+void *TransmitBufferGet(APPL * appl, void *p)
+{
+ if (appl->xbuffer_internal[(dword)(long)p])
+ return appl->xbuffer_internal[(dword)(long)p];
+
+ return appl->xbuffer_ptr[(dword)(long)p];
+}
+
+void TransmitBufferFree(APPL * appl, void *p)
+{
+ appl->xbuffer_used[(dword)(long)p] = false;
+ DBG_PRV1(("%d:xbuf_free(%d)", appl->Id, ((dword)(long)p) + 1))
+}
+
+void *ReceiveBufferGet(APPL * appl, int Num)
+{
+ return &appl->ReceiveBuffer[Num * appl->MaxDataLength];
+}
+
+/*
+ * api_remove_start/complete for cleanup
+ */
+void api_remove_complete(void)
+{
+ DBG_PRV1(("api_remove_complete"))
+}
+
+/*
+ * main function called by message.c
+ */
+void sendf(APPL * appl, word command, dword Id, word Number, byte * format, ...)
+{
+ word i, j;
+ word length = 12, dlength = 0;
+ byte *write;
+ CAPI_MSG msg;
+ byte *string = NULL;
+ va_list ap;
+ diva_os_message_buffer_s *dmb;
+ diva_card *card = NULL;
+ dword tmp;
+
+ if (!appl)
+ return;
+
+ DBG_PRV1(("sendf(a=%d,cmd=%x,format=%s)",
+ appl->Id, command, (byte *) format))
+
+ PUT_WORD(&msg.header.appl_id, appl->Id);
+ PUT_WORD(&msg.header.command, command);
+ if ((byte) (command >> 8) == 0x82)
+ Number = appl->Number++;
+ PUT_WORD(&msg.header.number, Number);
+
+ PUT_DWORD(&msg.header.controller, Id);
+ write = (byte *) & msg;
+ write += 12;
+
+ va_start(ap, format);
+ for (i = 0; format[i]; i++) {
+ switch (format[i]) {
+ case 'b':
+ tmp = va_arg(ap, dword);
+ *(byte *) write = (byte) (tmp & 0xff);
+ write += 1;
+ length += 1;
+ break;
+ case 'w':
+ tmp = va_arg(ap, dword);
+ PUT_WORD(write, (tmp & 0xffff));
+ write += 2;
+ length += 2;
+ break;
+ case 'd':
+ tmp = va_arg(ap, dword);
+ PUT_DWORD(write, tmp);
+ write += 4;
+ length += 4;
+ break;
+ case 's':
+ case 'S':
+ string = va_arg(ap, byte *);
+ length += string[0] + 1;
+ for (j = 0; j <= string[0]; j++)
+ *write++ = string[j];
+ break;
+ }
+ }
+ va_end(ap);
+
+ PUT_WORD(&msg.header.length, length);
+ msg.header.controller = UnMapController(msg.header.controller);
+
+ if (command == _DATA_B3_I)
+ dlength = GET_WORD(
+ ((byte *) & msg.info.data_b3_ind.Data_Length));
+
+ if (!(dmb = diva_os_alloc_message_buffer(length + dlength,
+ (void **) &write))) {
+ DBG_ERR(("sendf: alloc_message_buffer failed, incoming msg dropped."))
+ return;
+ }
+
+ /* copy msg header to sk_buff */
+ memcpy(write, (byte *) & msg, length);
+
+ /* if DATA_B3_IND, copy data too */
+ if (command == _DATA_B3_I) {
+ dword data = GET_DWORD(&msg.info.data_b3_ind.Data);
+ memcpy(write + length, (void *)(long)data, dlength);
+ }
+
+#ifndef DIVA_NO_DEBUGLIB
+ if (myDriverDebugHandle.dbgMask & DL_XLOG) {
+ switch (command) {
+ default:
+ xlog("\x00\x02", &msg, 0x81, length);
+ break;
+ case _DATA_B3_R | CONFIRM:
+ if (myDriverDebugHandle.dbgMask & DL_BLK)
+ xlog("\x00\x02", &msg, 0x81, length);
+ break;
+ case _DATA_B3_I:
+ if (myDriverDebugHandle.dbgMask & DL_BLK) {
+ xlog("\x00\x02", &msg, 0x81, length);
+ for (i = 0; i < dlength; i += 256) {
+ DBG_BLK((((char *)(long)GET_DWORD(&msg.info.data_b3_ind.Data)) + i,
+ ((dlength - i) < 256) ? (dlength - i) : 256))
+ if (!(myDriverDebugHandle.dbgMask & DL_PRV0))
+ break; /* not more if not explicitly requested */
+ }
+ }
+ break;
+ }
+ }
+#endif
+
+ /* find the card structure for this controller */
+ if (!(card = find_card_by_ctrl(write[8] & 0x7f))) {
+ DBG_ERR(("sendf - controller %d not found, incoming msg dropped",
+ write[8] & 0x7f))
+ diva_os_free_message_buffer(dmb);
+ return;
+ }
+ /* send capi msg to capi layer */
+ capi_ctr_handle_message(&card->capi_ctrl, appl->Id, dmb);
+}
+
+/*
+ * cleanup adapter
+ */
+static void clean_adapter(int id, struct list_head *free_mem_q)
+{
+ DIVA_CAPI_ADAPTER *a;
+ int i, k;
+
+ a = &adapter[id];
+ k = li_total_channels - a->li_channels;
+ if (k == 0) {
+ if (li_config_table) {
+ list_add((struct list_head *)li_config_table, free_mem_q);
+ li_config_table = NULL;
+ }
+ } else {
+ if (a->li_base < k) {
+ memmove(&li_config_table[a->li_base],
+ &li_config_table[a->li_base + a->li_channels],
+ (k - a->li_base) * sizeof(LI_CONFIG));
+ for (i = 0; i < k; i++) {
+ memmove(&li_config_table[i].flag_table[a->li_base],
+ &li_config_table[i].flag_table[a->li_base + a->li_channels],
+ k - a->li_base);
+ memmove(&li_config_table[i].
+ coef_table[a->li_base],
+ &li_config_table[i].coef_table[a->li_base + a->li_channels],
+ k - a->li_base);
+ }
+ }
+ }
+ li_total_channels = k;
+ for (i = id; i < max_adapter; i++) {
+ if (adapter[i].request)
+ adapter[i].li_base -= a->li_channels;
+ }
+ if (a->plci)
+ list_add((struct list_head *)a->plci, free_mem_q);
+
+ memset(a, 0x00, sizeof(DIVA_CAPI_ADAPTER));
+ while ((max_adapter != 0) && !adapter[max_adapter - 1].request)
+ max_adapter--;
+}
+
+/*
+ * remove a card, but ensures consistent state of LI tables
+ * in the time adapter is removed
+ */
+static void divacapi_remove_card(DESCRIPTOR * d)
+{
+ diva_card *card = NULL;
+ diva_os_spin_lock_magic_t old_irql;
+ LIST_HEAD(free_mem_q);
+ struct list_head *link;
+ struct list_head *tmp;
+
+ /*
+ * Set "remove in progress flag".
+ * Ensures that there is no call from sendf to CAPI in
+ * the time CAPI controller is about to be removed.
+ */
+ diva_os_enter_spin_lock(&api_lock, &old_irql, "remove card");
+ list_for_each(tmp, &cards) {
+ card = list_entry(tmp, diva_card, list);
+ if (card->d.request == d->request) {
+ card->remove_in_progress = 1;
+ list_del(tmp);
+ break;
+ }
+ }
+ diva_os_leave_spin_lock(&api_lock, &old_irql, "remove card");
+
+ if (card) {
+ /*
+ * Detach CAPI. Sendf cannot call to CAPI any more.
+ * After detach no call to send_message() is done too.
+ */
+ detach_capi_ctr(&card->capi_ctrl);
+
+ /*
+ * Now get API lock (to ensure stable state of LI tables)
+ * and update the adapter map/LI table.
+ */
+ diva_os_enter_spin_lock(&api_lock, &old_irql, "remove card");
+
+ clean_adapter(card->Id - 1, &free_mem_q);
+ DBG_TRC(("DelAdapterMap (%d) -> (%d)",
+ ControllerMap[card->Id], card->Id))
+ ControllerMap[card->Id] = 0;
+ DBG_TRC(("adapter remove, max_adapter=%d",
+ max_adapter));
+ diva_os_leave_spin_lock(&api_lock, &old_irql, "remove card");
+
+ /* After releasing the lock, we can free the memory */
+ diva_os_free (0, card);
+ }
+
+ /* free queued memory areas */
+ list_for_each_safe(link, tmp, &free_mem_q) {
+ list_del(link);
+ diva_os_free(0, link);
+ }
+}
+
+/*
+ * remove cards
+ */
+static void divacapi_remove_cards(void)
+{
+ DESCRIPTOR d;
+ struct list_head *tmp;
+ diva_card *card;
+ diva_os_spin_lock_magic_t old_irql;
+
+rescan:
+ diva_os_enter_spin_lock(&api_lock, &old_irql, "remove cards");
+ list_for_each(tmp, &cards) {
+ card = list_entry(tmp, diva_card, list);
+ diva_os_leave_spin_lock(&api_lock, &old_irql, "remove cards");
+ d.request = card->d.request;
+ divacapi_remove_card(&d);
+ goto rescan;
+ }
+ diva_os_leave_spin_lock(&api_lock, &old_irql, "remove cards");
+}
+
+/*
+ * sync_callback
+ */
+static void sync_callback(ENTITY * e)
+{
+ diva_os_spin_lock_magic_t old_irql;
+
+ DBG_TRC(("cb:Id=%x,Rc=%x,Ind=%x", e->Id, e->Rc, e->Ind))
+
+ diva_os_enter_spin_lock(&api_lock, &old_irql, "sync_callback");
+ callback(e);
+ diva_os_leave_spin_lock(&api_lock, &old_irql, "sync_callback");
+}
+
+/*
+ * add a new card
+ */
+static int diva_add_card(DESCRIPTOR * d)
+{
+ int k = 0, i = 0;
+ diva_os_spin_lock_magic_t old_irql;
+ diva_card *card = NULL;
+ struct capi_ctr *ctrl = NULL;
+ DIVA_CAPI_ADAPTER *a = NULL;
+ IDI_SYNC_REQ sync_req;
+ char serial[16];
+ void* mem_to_free;
+ LI_CONFIG *new_li_config_table;
+ int j;
+
+ if (!(card = (diva_card *) diva_os_malloc(0, sizeof(diva_card)))) {
+ DBG_ERR(("diva_add_card: failed to allocate card struct."))
+ return (0);
+ }
+ memset((char *) card, 0x00, sizeof(diva_card));
+ memcpy(&card->d, d, sizeof(DESCRIPTOR));
+ sync_req.GetName.Req = 0;
+ sync_req.GetName.Rc = IDI_SYNC_REQ_GET_NAME;
+ card->d.request((ENTITY *) & sync_req);
+ strlcpy(card->name, sync_req.GetName.name, sizeof(card->name));
+ ctrl = &card->capi_ctrl;
+ strcpy(ctrl->name, card->name);
+ ctrl->register_appl = diva_register_appl;
+ ctrl->release_appl = diva_release_appl;
+ ctrl->send_message = diva_send_message;
+ ctrl->procinfo = diva_procinfo;
+ ctrl->driverdata = card;
+ diva_os_set_controller_struct(ctrl);
+
+ if (attach_capi_ctr(ctrl)) {
+ DBG_ERR(("diva_add_card: failed to attach controller."))
+ diva_os_free(0, card);
+ return (0);
+ }
+
+ diva_os_enter_spin_lock(&api_lock, &old_irql, "find id");
+ card->Id = find_free_id();
+ diva_os_leave_spin_lock(&api_lock, &old_irql, "find id");
+
+ strlcpy(ctrl->manu, M_COMPANY, sizeof(ctrl->manu));
+ ctrl->version.majorversion = 2;
+ ctrl->version.minorversion = 0;
+ ctrl->version.majormanuversion = DRRELMAJOR;
+ ctrl->version.minormanuversion = DRRELMINOR;
+ sync_req.GetSerial.Req = 0;
+ sync_req.GetSerial.Rc = IDI_SYNC_REQ_GET_SERIAL;
+ sync_req.GetSerial.serial = 0;
+ card->d.request((ENTITY *) & sync_req);
+ if ((i = ((sync_req.GetSerial.serial & 0xff000000) >> 24))) {
+ sprintf(serial, "%ld-%d",
+ sync_req.GetSerial.serial & 0x00ffffff, i + 1);
+ } else {
+ sprintf(serial, "%ld", sync_req.GetSerial.serial);
+ }
+ serial[CAPI_SERIAL_LEN - 1] = 0;
+ strlcpy(ctrl->serial, serial, sizeof(ctrl->serial));
+
+ a = &adapter[card->Id - 1];
+ card->adapter = a;
+ a->os_card = card;
+ ControllerMap[card->Id] = (byte) (ctrl->cnr);
+
+ DBG_TRC(("AddAdapterMap (%d) -> (%d)", ctrl->cnr, card->Id))
+
+ sync_req.xdi_capi_prms.Req = 0;
+ sync_req.xdi_capi_prms.Rc = IDI_SYNC_REQ_XDI_GET_CAPI_PARAMS;
+ sync_req.xdi_capi_prms.info.structure_length =
+ sizeof(diva_xdi_get_capi_parameters_t);
+ card->d.request((ENTITY *) & sync_req);
+ a->flag_dynamic_l1_down =
+ sync_req.xdi_capi_prms.info.flag_dynamic_l1_down;
+ a->group_optimization_enabled =
+ sync_req.xdi_capi_prms.info.group_optimization_enabled;
+ a->request = DIRequest; /* card->d.request; */
+ a->max_plci = card->d.channels + 30;
+ a->max_listen = (card->d.channels > 2) ? 8 : 2;
+ if (!
+ (a->plci =
+ (PLCI *) diva_os_malloc(0, sizeof(PLCI) * a->max_plci))) {
+ DBG_ERR(("diva_add_card: failed alloc plci struct."))
+ memset(a, 0, sizeof(DIVA_CAPI_ADAPTER));
+ return (0);
+ }
+ memset(a->plci, 0, sizeof(PLCI) * a->max_plci);
+
+ for (k = 0; k < a->max_plci; k++) {
+ a->Id = (byte) card->Id;
+ a->plci[k].Sig.callback = sync_callback;
+ a->plci[k].Sig.XNum = 1;
+ a->plci[k].Sig.X = a->plci[k].XData;
+ a->plci[k].Sig.user[0] = (word) (card->Id - 1);
+ a->plci[k].Sig.user[1] = (word) k;
+ a->plci[k].NL.callback = sync_callback;
+ a->plci[k].NL.XNum = 1;
+ a->plci[k].NL.X = a->plci[k].XData;
+ a->plci[k].NL.user[0] = (word) ((card->Id - 1) | 0x8000);
+ a->plci[k].NL.user[1] = (word) k;
+ a->plci[k].adapter = a;
+ }
+
+ a->profile.Number = card->Id;
+ a->profile.Channels = card->d.channels;
+ if (card->d.features & DI_FAX3) {
+ a->profile.Global_Options = 0x71;
+ if (card->d.features & DI_CODEC)
+ a->profile.Global_Options |= 0x6;
+#if IMPLEMENT_DTMF
+ a->profile.Global_Options |= 0x8;
+#endif /* IMPLEMENT_DTMF */
+ a->profile.Global_Options |= 0x80; /* Line Interconnect */
+#if IMPLEMENT_ECHO_CANCELLER
+ a->profile.Global_Options |= 0x100;
+#endif /* IMPLEMENT_ECHO_CANCELLER */
+ a->profile.B1_Protocols = 0xdf;
+ a->profile.B2_Protocols = 0x1fdb;
+ a->profile.B3_Protocols = 0xb7;
+ a->manufacturer_features = MANUFACTURER_FEATURE_HARDDTMF;
+ } else {
+ a->profile.Global_Options = 0x71;
+ if (card->d.features & DI_CODEC)
+ a->profile.Global_Options |= 0x2;
+ a->profile.B1_Protocols = 0x43;
+ a->profile.B2_Protocols = 0x1f0f;
+ a->profile.B3_Protocols = 0x07;
+ a->manufacturer_features = 0;
+ }
+
+ a->li_pri = (a->profile.Channels > 2);
+ a->li_channels = a->li_pri ? MIXER_CHANNELS_PRI : MIXER_CHANNELS_BRI;
+ a->li_base = 0;
+ for (i = 0; &adapter[i] != a; i++) {
+ if (adapter[i].request)
+ a->li_base = adapter[i].li_base + adapter[i].li_channels;
+ }
+ k = li_total_channels + a->li_channels;
+ new_li_config_table =
+ (LI_CONFIG *) diva_os_malloc(0, ((k * sizeof(LI_CONFIG) + 3) & ~3) + (2 * k) * ((k + 3) & ~3));
+ if (new_li_config_table == NULL) {
+ DBG_ERR(("diva_add_card: failed alloc li_config table."))
+ memset(a, 0, sizeof(DIVA_CAPI_ADAPTER));
+ return (0);
+ }
+
+ /* Prevent access to line interconnect table in process update */
+ diva_os_enter_spin_lock(&api_lock, &old_irql, "add card");
+
+ j = 0;
+ for (i = 0; i < k; i++) {
+ if ((i >= a->li_base) && (i < a->li_base + a->li_channels))
+ memset(&new_li_config_table[i], 0, sizeof(LI_CONFIG));
+ else
+ memcpy(&new_li_config_table[i], &li_config_table[j], sizeof(LI_CONFIG));
+ new_li_config_table[i].flag_table =
+ ((byte *) new_li_config_table) + (((k * sizeof(LI_CONFIG) + 3) & ~3) + (2 * i) * ((k + 3) & ~3));
+ new_li_config_table[i].coef_table =
+ ((byte *) new_li_config_table) + (((k * sizeof(LI_CONFIG) + 3) & ~3) + (2 * i + 1) * ((k + 3) & ~3));
+ if ((i >= a->li_base) && (i < a->li_base + a->li_channels)) {
+ new_li_config_table[i].adapter = a;
+ memset(&new_li_config_table[i].flag_table[0], 0, k);
+ memset(&new_li_config_table[i].coef_table[0], 0, k);
+ } else {
+ if (a->li_base != 0) {
+ memcpy(&new_li_config_table[i].flag_table[0],
+ &li_config_table[j].flag_table[0],
+ a->li_base);
+ memcpy(&new_li_config_table[i].coef_table[0],
+ &li_config_table[j].coef_table[0],
+ a->li_base);
+ }
+ memset(&new_li_config_table[i].flag_table[a->li_base], 0, a->li_channels);
+ memset(&new_li_config_table[i].coef_table[a->li_base], 0, a->li_channels);
+ if (a->li_base + a->li_channels < k) {
+ memcpy(&new_li_config_table[i].flag_table[a->li_base +
+ a->li_channels],
+ &li_config_table[j].flag_table[a->li_base],
+ k - (a->li_base + a->li_channels));
+ memcpy(&new_li_config_table[i].coef_table[a->li_base +
+ a->li_channels],
+ &li_config_table[j].coef_table[a->li_base],
+ k - (a->li_base + a->li_channels));
+ }
+ j++;
+ }
+ }
+ li_total_channels = k;
+
+ mem_to_free = li_config_table;
+
+ li_config_table = new_li_config_table;
+ for (i = card->Id; i < max_adapter; i++) {
+ if (adapter[i].request)
+ adapter[i].li_base += a->li_channels;
+ }
+
+ if (a == &adapter[max_adapter])
+ max_adapter++;
+
+ list_add(&(card->list), &cards);
+ AutomaticLaw(a);
+
+ diva_os_leave_spin_lock(&api_lock, &old_irql, "add card");
+
+ if (mem_to_free) {
+ diva_os_free (0, mem_to_free);
+ }
+
+ i = 0;
+ while (i++ < 30) {
+ if (a->automatic_law > 3)
+ break;
+ diva_os_sleep(10);
+ }
+
+ /* profile information */
+ PUT_WORD(&ctrl->profile.nbchannel, card->d.channels);
+ ctrl->profile.goptions = a->profile.Global_Options;
+ ctrl->profile.support1 = a->profile.B1_Protocols;
+ ctrl->profile.support2 = a->profile.B2_Protocols;
+ ctrl->profile.support3 = a->profile.B3_Protocols;
+ /* manufacturer profile information */
+ ctrl->profile.manu[0] = a->man_profile.private_options;
+ ctrl->profile.manu[1] = a->man_profile.rtp_primary_payloads;
+ ctrl->profile.manu[2] = a->man_profile.rtp_additional_payloads;
+ ctrl->profile.manu[3] = 0;
+ ctrl->profile.manu[4] = 0;
+
+ capi_ctr_ready(ctrl);
+
+ DBG_TRC(("adapter added, max_adapter=%d", max_adapter));
+ return (1);
+}
+
+/*
+ * register appl
+ */
+static void diva_register_appl(struct capi_ctr *ctrl, __u16 appl,
+ capi_register_params * rp)
+{
+ APPL *this;
+ word bnum, xnum;
+ int i = 0;
+ unsigned char *p;
+ void *DataNCCI, *DataFlags, *ReceiveBuffer, *xbuffer_used;
+ void **xbuffer_ptr, **xbuffer_internal;
+ diva_os_spin_lock_magic_t old_irql;
+ unsigned int mem_len;
+ int nconn = rp->level3cnt;
+
+
+ if (diva_os_in_irq()) {
+ DBG_ERR(("CAPI_REGISTER - in irq context !"))
+ return;
+ }
+
+ DBG_TRC(("application register Id=%d", appl))
+
+ if (appl > MAX_APPL) {
+ DBG_ERR(("CAPI_REGISTER - appl.Id exceeds MAX_APPL"))
+ return;
+ }
+
+ if (nconn <= 0)
+ nconn = ctrl->profile.nbchannel * -nconn;
+
+ if (nconn == 0)
+ nconn = ctrl->profile.nbchannel;
+
+ DBG_LOG(("CAPI_REGISTER - Id = %d", appl))
+ DBG_LOG((" MaxLogicalConnections = %d(%d)", nconn, rp->level3cnt))
+ DBG_LOG((" MaxBDataBuffers = %d", rp->datablkcnt))
+ DBG_LOG((" MaxBDataLength = %d", rp->datablklen))
+
+ if (nconn < 1 ||
+ nconn > 255 ||
+ rp->datablklen < 80 ||
+ rp->datablklen > 2150 || rp->datablkcnt > 255) {
+ DBG_ERR(("CAPI_REGISTER - invalid parameters"))
+ return;
+ }
+
+ if (application[appl - 1].Id == appl) {
+ DBG_LOG(("CAPI_REGISTER - appl already registered"))
+ return; /* appl already registered */
+ }
+
+ /* alloc memory */
+
+ bnum = nconn * rp->datablkcnt;
+ xnum = nconn * MAX_DATA_B3;
+
+ mem_len = bnum * sizeof(word); /* DataNCCI */
+ mem_len += bnum * sizeof(word); /* DataFlags */
+ mem_len += bnum * rp->datablklen; /* ReceiveBuffer */
+ mem_len += xnum; /* xbuffer_used */
+ mem_len += xnum * sizeof(void *); /* xbuffer_ptr */
+ mem_len += xnum * sizeof(void *); /* xbuffer_internal */
+ mem_len += xnum * rp->datablklen; /* xbuffer_ptr[xnum] */
+
+ DBG_LOG((" Allocated Memory = %d", mem_len))
+ if (!(p = diva_os_malloc(0, mem_len))) {
+ DBG_ERR(("CAPI_REGISTER - memory allocation failed"))
+ return;
+ }
+ memset(p, 0, mem_len);
+
+ DataNCCI = (void *)p;
+ p += bnum * sizeof(word);
+ DataFlags = (void *)p;
+ p += bnum * sizeof(word);
+ ReceiveBuffer = (void *)p;
+ p += bnum * rp->datablklen;
+ xbuffer_used = (void *)p;
+ p += xnum;
+ xbuffer_ptr = (void **)p;
+ p += xnum * sizeof(void *);
+ xbuffer_internal = (void **)p;
+ p += xnum * sizeof(void *);
+ for (i = 0; i < xnum; i++) {
+ xbuffer_ptr[i] = (void *)p;
+ p += rp->datablklen;
+ }
+
+ /* initialize application data */
+ diva_os_enter_spin_lock(&api_lock, &old_irql, "register_appl");
+
+ this = &application[appl - 1];
+ memset(this, 0, sizeof(APPL));
+
+ this->Id = appl;
+
+ for (i = 0; i < max_adapter; i++) {
+ adapter[i].CIP_Mask[appl - 1] = 0;
+ }
+
+ this->queue_size = 1000;
+
+ this->MaxNCCI = (byte) nconn;
+ this->MaxNCCIData = (byte) rp->datablkcnt;
+ this->MaxBuffer = bnum;
+ this->MaxDataLength = rp->datablklen;
+
+ this->DataNCCI = DataNCCI;
+ this->DataFlags = DataFlags;
+ this->ReceiveBuffer = ReceiveBuffer;
+ this->xbuffer_used = xbuffer_used;
+ this->xbuffer_ptr = xbuffer_ptr;
+ this->xbuffer_internal = xbuffer_internal;
+ for (i = 0; i < xnum; i++) {
+ this->xbuffer_ptr[i] = xbuffer_ptr[i];
+ }
+
+ CapiRegister(this->Id);
+ diva_os_leave_spin_lock(&api_lock, &old_irql, "register_appl");
+
+}
+
+/*
+ * release appl
+ */
+static void diva_release_appl(struct capi_ctr *ctrl, __u16 appl)
+{
+ diva_os_spin_lock_magic_t old_irql;
+ APPL *this = &application[appl - 1];
+ void *mem_to_free = NULL;
+
+ DBG_TRC(("application %d(%d) cleanup", this->Id, appl))
+
+ if (diva_os_in_irq()) {
+ DBG_ERR(("CAPI_RELEASE - in irq context !"))
+ return;
+ }
+
+ diva_os_enter_spin_lock(&api_lock, &old_irql, "release_appl");
+ if (this->Id) {
+ CapiRelease(this->Id);
+ mem_to_free = this->DataNCCI;
+ this->DataNCCI = NULL;
+ this->Id = 0;
+ }
+ diva_os_leave_spin_lock(&api_lock, &old_irql, "release_appl");
+
+ if (mem_to_free)
+ diva_os_free(0, mem_to_free);
+
+}
+
+/*
+ * send message
+ */
+static u16 diva_send_message(struct capi_ctr *ctrl,
+ diva_os_message_buffer_s * dmb)
+{
+ int i = 0;
+ word ret = 0;
+ diva_os_spin_lock_magic_t old_irql;
+ CAPI_MSG *msg = (CAPI_MSG *) DIVA_MESSAGE_BUFFER_DATA(dmb);
+ APPL *this = &application[GET_WORD(&msg->header.appl_id) - 1];
+ diva_card *card = ctrl->driverdata;
+ __u32 length = DIVA_MESSAGE_BUFFER_LEN(dmb);
+ word clength = GET_WORD(&msg->header.length);
+ word command = GET_WORD(&msg->header.command);
+ u16 retval = CAPI_NOERROR;
+
+ if (diva_os_in_irq()) {
+ DBG_ERR(("CAPI_SEND_MSG - in irq context !"))
+ return CAPI_REGOSRESOURCEERR;
+ }
+ DBG_PRV1(("Write - appl = %d, cmd = 0x%x", this->Id, command))
+
+ if (card->remove_in_progress) {
+ DBG_ERR(("CAPI_SEND_MSG - remove in progress!"))
+ return CAPI_REGOSRESOURCEERR;
+ }
+
+ diva_os_enter_spin_lock(&api_lock, &old_irql, "send message");
+
+ if (!this->Id) {
+ diva_os_leave_spin_lock(&api_lock, &old_irql, "send message");
+ return CAPI_ILLAPPNR;
+ }
+
+ /* patch controller number */
+ msg->header.controller = ControllerMap[card->Id]
+ | (msg->header.controller & 0x80); /* preserve external controller bit */
+
+ switch (command) {
+ default:
+ xlog("\x00\x02", msg, 0x80, clength);
+ break;
+
+ case _DATA_B3_I | RESPONSE:
+#ifndef DIVA_NO_DEBUGLIB
+ if (myDriverDebugHandle.dbgMask & DL_BLK)
+ xlog("\x00\x02", msg, 0x80, clength);
+#endif
+ break;
+
+ case _DATA_B3_R:
+#ifndef DIVA_NO_DEBUGLIB
+ if (myDriverDebugHandle.dbgMask & DL_BLK)
+ xlog("\x00\x02", msg, 0x80, clength);
+#endif
+
+ if (clength == 24)
+ clength = 22; /* workaround for PPcom bug */
+ /* header is always 22 */
+ if (GET_WORD(&msg->info.data_b3_req.Data_Length) >
+ this->MaxDataLength
+ || GET_WORD(&msg->info.data_b3_req.Data_Length) >
+ (length - clength)) {
+ DBG_ERR(("Write - invalid message size"))
+ retval = CAPI_ILLCMDORSUBCMDORMSGTOSMALL;
+ goto write_end;
+ }
+
+ for (i = 0; i < (MAX_DATA_B3 * this->MaxNCCI)
+ && this->xbuffer_used[i]; i++);
+ if (i == (MAX_DATA_B3 * this->MaxNCCI)) {
+ DBG_ERR(("Write - too many data pending"))
+ retval = CAPI_SENDQUEUEFULL;
+ goto write_end;
+ }
+ msg->info.data_b3_req.Data = i;
+
+ this->xbuffer_internal[i] = NULL;
+ memcpy(this->xbuffer_ptr[i], &((__u8 *) msg)[clength],
+ GET_WORD(&msg->info.data_b3_req.Data_Length));
+
+#ifndef DIVA_NO_DEBUGLIB
+ if ((myDriverDebugHandle.dbgMask & DL_BLK)
+ && (myDriverDebugHandle.dbgMask & DL_XLOG)) {
+ int j;
+ for (j = 0; j <
+ GET_WORD(&msg->info.data_b3_req.Data_Length);
+ j += 256) {
+ DBG_BLK((((char *) this->xbuffer_ptr[i]) + j,
+ ((GET_WORD(&msg->info.data_b3_req.Data_Length) - j) <
+ 256) ? (GET_WORD(&msg->info.data_b3_req.Data_Length) - j) : 256))
+ if (!(myDriverDebugHandle.dbgMask & DL_PRV0))
+ break; /* not more if not explicitly requested */
+ }
+ }
+#endif
+ break;
+ }
+
+ memcpy(mapped_msg, msg, (__u32) clength);
+ mapped_msg->header.controller = MapController(mapped_msg->header.controller);
+ mapped_msg->header.length = clength;
+ mapped_msg->header.command = command;
+ mapped_msg->header.number = GET_WORD(&msg->header.number);
+
+ ret = api_put(this, mapped_msg);
+ switch (ret) {
+ case 0:
+ break;
+ case _BAD_MSG:
+ DBG_ERR(("Write - bad message"))
+ retval = CAPI_ILLCMDORSUBCMDORMSGTOSMALL;
+ break;
+ case _QUEUE_FULL:
+ DBG_ERR(("Write - queue full"))
+ retval = CAPI_SENDQUEUEFULL;
+ break;
+ default:
+ DBG_ERR(("Write - api_put returned unknown error"))
+ retval = CAPI_UNKNOWNNOTPAR;
+ break;
+ }
+
+ write_end:
+ diva_os_leave_spin_lock(&api_lock, &old_irql, "send message");
+ if (retval == CAPI_NOERROR)
+ diva_os_free_message_buffer(dmb);
+ return retval;
+}
+
+
+/*
+ * cards request function
+ */
+static void DIRequest(ENTITY * e)
+{
+ DIVA_CAPI_ADAPTER *a = &(adapter[(byte) e->user[0]]);
+ diva_card *os_card = (diva_card *) a->os_card;
+
+ if (e->Req && (a->FlowControlIdTable[e->ReqCh] == e->Id)) {
+ a->FlowControlSkipTable[e->ReqCh] = 1;
+ }
+
+ (*(os_card->d.request)) (e);
+}
+
+/*
+ * callback function from didd
+ */
+static void didd_callback(void *context, DESCRIPTOR * adapter, int removal)
+{
+ if (adapter->type == IDI_DADAPTER) {
+ DBG_ERR(("Notification about IDI_DADAPTER change ! Oops."));
+ return;
+ } else if (adapter->type == IDI_DIMAINT) {
+ if (removal) {
+ stop_dbg();
+ } else {
+ memcpy(&MAdapter, adapter, sizeof(MAdapter));
+ dprintf = (DIVA_DI_PRINTF) MAdapter.request;
+ DbgRegister("CAPI20", DRIVERRELEASE_CAPI, DBG_DEFAULT);
+ }
+ } else if ((adapter->type > 0) && (adapter->type < 16)) { /* IDI Adapter */
+ if (removal) {
+ divacapi_remove_card(adapter);
+ } else {
+ diva_add_card(adapter);
+ }
+ }
+ return;
+}
+
+/*
+ * connect to didd
+ */
+static int divacapi_connect_didd(void)
+{
+ int x = 0;
+ int dadapter = 0;
+ IDI_SYNC_REQ req;
+ DESCRIPTOR DIDD_Table[MAX_DESCRIPTORS];
+
+ DIVA_DIDD_Read(DIDD_Table, sizeof(DIDD_Table));
+
+ for (x = 0; x < MAX_DESCRIPTORS; x++) {
+ if (DIDD_Table[x].type == IDI_DIMAINT) { /* MAINT found */
+ memcpy(&MAdapter, &DIDD_Table[x], sizeof(DAdapter));
+ dprintf = (DIVA_DI_PRINTF) MAdapter.request;
+ DbgRegister("CAPI20", DRIVERRELEASE_CAPI, DBG_DEFAULT);
+ break;
+ }
+ }
+ for (x = 0; x < MAX_DESCRIPTORS; x++) {
+ if (DIDD_Table[x].type == IDI_DADAPTER) { /* DADAPTER found */
+ dadapter = 1;
+ memcpy(&DAdapter, &DIDD_Table[x], sizeof(DAdapter));
+ req.didd_notify.e.Req = 0;
+ req.didd_notify.e.Rc =
+ IDI_SYNC_REQ_DIDD_REGISTER_ADAPTER_NOTIFY;
+ req.didd_notify.info.callback = (void *)didd_callback;
+ req.didd_notify.info.context = NULL;
+ DAdapter.request((ENTITY *) & req);
+ if (req.didd_notify.e.Rc != 0xff) {
+ stop_dbg();
+ return (0);
+ }
+ notify_handle = req.didd_notify.info.handle;
+ }
+ else if ((DIDD_Table[x].type > 0) && (DIDD_Table[x].type < 16)) { /* IDI Adapter found */
+ diva_add_card(&DIDD_Table[x]);
+ }
+ }
+
+ if (!dadapter) {
+ stop_dbg();
+ }
+
+ return (dadapter);
+}
+
+/*
+ * diconnect from didd
+ */
+static void divacapi_disconnect_didd(void)
+{
+ IDI_SYNC_REQ req;
+
+ stop_dbg();
+
+ req.didd_notify.e.Req = 0;
+ req.didd_notify.e.Rc = IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER_NOTIFY;
+ req.didd_notify.info.handle = notify_handle;
+ DAdapter.request((ENTITY *) & req);
+}
+
+/*
+ * we do not provide date/time here,
+ * the application should do this.
+ */
+int fax_head_line_time(char *buffer)
+{
+ return (0);
+}
+
+/*
+ * init (alloc) main structures
+ */
+static int DIVA_INIT_FUNCTION init_main_structs(void)
+{
+ if (!(mapped_msg = (CAPI_MSG *) diva_os_malloc(0, MAX_MSG_SIZE))) {
+ DBG_ERR(("init: failed alloc mapped_msg."))
+ return 0;
+ }
+
+ if (!(adapter = diva_os_malloc(0, sizeof(DIVA_CAPI_ADAPTER) * MAX_DESCRIPTORS))) {
+ DBG_ERR(("init: failed alloc adapter struct."))
+ diva_os_free(0, mapped_msg);
+ return 0;
+ }
+ memset(adapter, 0, sizeof(DIVA_CAPI_ADAPTER) * MAX_DESCRIPTORS);
+
+ if (!(application = diva_os_malloc(0, sizeof(APPL) * MAX_APPL))) {
+ DBG_ERR(("init: failed alloc application struct."))
+ diva_os_free(0, mapped_msg);
+ diva_os_free(0, adapter);
+ return 0;
+ }
+ memset(application, 0, sizeof(APPL) * MAX_APPL);
+
+ return (1);
+}
+
+/*
+ * remove (free) main structures
+ */
+static void remove_main_structs(void)
+{
+ if (application)
+ diva_os_free(0, application);
+ if (adapter)
+ diva_os_free(0, adapter);
+ if (mapped_msg)
+ diva_os_free(0, mapped_msg);
+}
+
+/*
+ * api_remove_start
+ */
+static void do_api_remove_start(void)
+{
+ diva_os_spin_lock_magic_t old_irql;
+ int ret = 1, count = 100;
+
+ do {
+ diva_os_enter_spin_lock(&api_lock, &old_irql, "api remove start");
+ ret = api_remove_start();
+ diva_os_leave_spin_lock(&api_lock, &old_irql, "api remove start");
+
+ diva_os_sleep(10);
+ } while (ret && count--);
+
+ if (ret)
+ DBG_ERR(("could not remove signaling ID's"))
+}
+
+/*
+ * init
+ */
+int DIVA_INIT_FUNCTION init_capifunc(void)
+{
+ diva_os_initialize_spin_lock(&api_lock, "capifunc");
+ memset(ControllerMap, 0, MAX_DESCRIPTORS + 1);
+ max_adapter = 0;
+
+
+ if (!init_main_structs()) {
+ DBG_ERR(("init: failed to init main structs."))
+ diva_os_destroy_spin_lock(&api_lock, "capifunc");
+ return (0);
+ }
+
+ if (!divacapi_connect_didd()) {
+ DBG_ERR(("init: failed to connect to DIDD."))
+ do_api_remove_start();
+ divacapi_remove_cards();
+ remove_main_structs();
+ diva_os_destroy_spin_lock(&api_lock, "capifunc");
+ return (0);
+ }
+
+ return (1);
+}
+
+/*
+ * finit
+ */
+void DIVA_EXIT_FUNCTION finit_capifunc(void)
+{
+ do_api_remove_start();
+ divacapi_disconnect_didd();
+ divacapi_remove_cards();
+ remove_main_structs();
+ diva_os_destroy_spin_lock(&api_lock, "capifunc");
+}
diff --git a/drivers/isdn/hardware/eicon/capifunc.h b/drivers/isdn/hardware/eicon/capifunc.h
new file mode 100644
index 0000000..bd256f2
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/capifunc.h
@@ -0,0 +1,40 @@
+/* $Id: capifunc.h,v 1.11.4.1 2004/08/28 20:03:53 armin Exp $
+ *
+ * ISDN interface module for Eicon active cards DIVA.
+ * CAPI Interface common functions
+ *
+ * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
+ * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#ifndef __CAPIFUNC_H__
+#define __CAPIFUNC_H__
+
+#define DRRELMAJOR 2
+#define DRRELMINOR 0
+#define DRRELEXTRA ""
+
+#define M_COMPANY "Eicon Networks"
+
+extern char DRIVERRELEASE_CAPI[];
+
+typedef struct _diva_card {
+ struct list_head list;
+ int remove_in_progress;
+ int Id;
+ struct capi_ctr capi_ctrl;
+ DIVA_CAPI_ADAPTER *adapter;
+ DESCRIPTOR d;
+ char name[32];
+} diva_card;
+
+/*
+ * prototypes
+ */
+int init_capifunc(void);
+void finit_capifunc(void);
+
+#endif /* __CAPIFUNC_H__ */
diff --git a/drivers/isdn/hardware/eicon/capimain.c b/drivers/isdn/hardware/eicon/capimain.c
new file mode 100644
index 0000000..98fcdfc
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/capimain.c
@@ -0,0 +1,145 @@
+/* $Id: capimain.c,v 1.24 2003/09/09 06:51:05 schindler Exp $
+ *
+ * ISDN interface module for Eicon active cards DIVA.
+ * CAPI Interface
+ *
+ * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
+ * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <linux/skbuff.h>
+
+#include "os_capi.h"
+
+#include "platform.h"
+#include "di_defs.h"
+#include "capi20.h"
+#include "divacapi.h"
+#include "cp_vers.h"
+#include "capifunc.h"
+
+static char *main_revision = "$Revision: 1.24 $";
+static char *DRIVERNAME =
+ "Eicon DIVA - CAPI Interface driver (http://www.melware.net)";
+static char *DRIVERLNAME = "divacapi";
+
+MODULE_DESCRIPTION("CAPI driver for Eicon DIVA cards");
+MODULE_AUTHOR("Cytronics & Melware, Eicon Networks");
+MODULE_SUPPORTED_DEVICE("CAPI and DIVA card drivers");
+MODULE_LICENSE("GPL");
+
+/*
+ * get revision number from revision string
+ */
+static char *getrev(const char *revision)
+{
+ char *rev;
+ char *p;
+ if ((p = strchr(revision, ':'))) {
+ rev = p + 2;
+ p = strchr(rev, '$');
+ *--p = 0;
+ } else
+ rev = "1.0";
+ return rev;
+
+}
+
+/*
+ * alloc a message buffer
+ */
+diva_os_message_buffer_s *diva_os_alloc_message_buffer(unsigned long size,
+ void **data_buf)
+{
+ diva_os_message_buffer_s *dmb = alloc_skb(size, GFP_ATOMIC);
+ if (dmb) {
+ *data_buf = skb_put(dmb, size);
+ }
+ return (dmb);
+}
+
+/*
+ * free a message buffer
+ */
+void diva_os_free_message_buffer(diva_os_message_buffer_s * dmb)
+{
+ kfree_skb(dmb);
+}
+
+/*
+ * proc function for controller info
+ */
+static int diva_ctl_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, struct capi_ctr *ctrl)
+{
+ diva_card *card = (diva_card *) ctrl->driverdata;
+ int len = 0;
+
+ len += sprintf(page + len, "%s\n", ctrl->name);
+ len += sprintf(page + len, "Serial No. : %s\n", ctrl->serial);
+ len += sprintf(page + len, "Id : %d\n", card->Id);
+ len += sprintf(page + len, "Channels : %d\n", card->d.channels);
+
+ if (off + count >= len)
+ *eof = 1;
+ if (len < off)
+ return 0;
+ *start = page + off;
+ return ((count < len - off) ? count : len - off);
+}
+
+/*
+ * set additional os settings in capi_ctr struct
+ */
+void diva_os_set_controller_struct(struct capi_ctr *ctrl)
+{
+ ctrl->driver_name = DRIVERLNAME;
+ ctrl->load_firmware = NULL;
+ ctrl->reset_ctr = NULL;
+ ctrl->ctr_read_proc = diva_ctl_read_proc;
+ ctrl->owner = THIS_MODULE;
+}
+
+/*
+ * module init
+ */
+static int DIVA_INIT_FUNCTION divacapi_init(void)
+{
+ char tmprev[32];
+ int ret = 0;
+
+ sprintf(DRIVERRELEASE_CAPI, "%d.%d%s", DRRELMAJOR, DRRELMINOR,
+ DRRELEXTRA);
+
+ printk(KERN_INFO "%s\n", DRIVERNAME);
+ printk(KERN_INFO "%s: Rel:%s Rev:", DRIVERLNAME, DRIVERRELEASE_CAPI);
+ strcpy(tmprev, main_revision);
+ printk("%s Build: %s(%s)\n", getrev(tmprev),
+ diva_capi_common_code_build, DIVA_BUILD);
+
+ if (!(init_capifunc())) {
+ printk(KERN_ERR "%s: failed init capi_driver.\n",
+ DRIVERLNAME);
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+/*
+ * module exit
+ */
+static void DIVA_EXIT_FUNCTION divacapi_exit(void)
+{
+ finit_capifunc();
+ printk(KERN_INFO "%s: module unloaded.\n", DRIVERLNAME);
+}
+
+module_init(divacapi_init);
+module_exit(divacapi_exit);
diff --git a/drivers/isdn/hardware/eicon/cardtype.h b/drivers/isdn/hardware/eicon/cardtype.h
new file mode 100644
index 0000000..18a5c42
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/cardtype.h
@@ -0,0 +1,1098 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+#ifndef _CARDTYPE_H_
+#define _CARDTYPE_H_
+#ifndef CARDTYPE_H_WANT_DATA
+#define CARDTYPE_H_WANT_DATA 0
+#endif
+#ifndef CARDTYPE_H_WANT_IDI_DATA
+#define CARDTYPE_H_WANT_IDI_DATA 0
+#endif
+#ifndef CARDTYPE_H_WANT_RESOURCE_DATA
+#define CARDTYPE_H_WANT_RESOURCE_DATA 1
+#endif
+#ifndef CARDTYPE_H_WANT_FILE_DATA
+#define CARDTYPE_H_WANT_FILE_DATA 1
+#endif
+/*
+ * D-channel protocol identifiers
+ *
+ * Attention: Unfortunately the identifiers defined here differ from
+ * the identifiers used in Protocol/1/Common/prot/q931.h .
+ * The only reason for this is that q931.h has not a global
+ * scope and we did not know about the definitions there.
+ * But the definitions here cannot be changed easily because
+ * they are used in setup scripts and programs.
+ * Thus the definitions here have to be mapped if they are
+ * used in the protocol code context !
+ *
+ * Now the identifiers are defined in the q931lib/constant.h file.
+ * Unfortunately this file has also not a global scope.
+ * But beginning with PROTTYPE_US any new identifier will get the same
+ * value as the corresponding PROT_* definition in q931lib/constant.h !
+ */
+#define PROTTYPE_MINVAL 0
+#define PROTTYPE_ETSI 0
+#define PROTTYPE_1TR6 1
+#define PROTTYPE_BELG 2
+#define PROTTYPE_FRANC 3
+#define PROTTYPE_ATEL 4
+#define PROTTYPE_NI 5 /* DMS 100, Nortel, National ISDN */
+#define PROTTYPE_5ESS 6 /* 5ESS , AT&T, 5ESS Custom */
+#define PROTTYPE_JAPAN 7
+#define PROTTYPE_SWED 8
+#define PROTTYPE_US 9 /* US autodetect */
+#define PROTTYPE_ITALY 10
+#define PROTTYPE_TWAN 11
+#define PROTTYPE_AUSTRAL 12
+#define PROTTYPE_4ESDN 13
+#define PROTTYPE_4ESDS 14
+#define PROTTYPE_4ELDS 15
+#define PROTTYPE_4EMGC 16
+#define PROTTYPE_4EMGI 17
+#define PROTTYPE_HONGKONG 18
+#define PROTTYPE_RBSCAS 19
+#define PROTTYPE_CORNETN 20
+#define PROTTYPE_QSIG 21
+#define PROTTYPE_NI_EWSD 22 /* EWSD, Siemens, National ISDN */
+#define PROTTYPE_5ESS_NI 23 /* 5ESS, Lucent, National ISDN */
+#define PROTTYPE_T1CORNETN 24
+#define PROTTYPE_CORNETNQ 25
+#define PROTTYPE_T1CORNETNQ 26
+#define PROTTYPE_T1QSIG 27
+#define PROTTYPE_E1UNCH 28
+#define PROTTYPE_T1UNCH 29
+#define PROTTYPE_E1CHAN 30
+#define PROTTYPE_T1CHAN 31
+#define PROTTYPE_R2CAS 32
+#define PROTTYPE_MAXVAL 32
+/*
+ * Card type identifiers
+ */
+#define CARD_UNKNOWN 0
+#define CARD_NONE 0
+ /* DIVA cards */
+#define CARDTYPE_DIVA_MCA 0
+#define CARDTYPE_DIVA_ISA 1
+#define CARDTYPE_DIVA_PCM 2
+#define CARDTYPE_DIVAPRO_ISA 3
+#define CARDTYPE_DIVAPRO_PCM 4
+#define CARDTYPE_DIVAPICO_ISA 5
+#define CARDTYPE_DIVAPICO_PCM 6
+ /* DIVA 2.0 cards */
+#define CARDTYPE_DIVAPRO20_PCI 7
+#define CARDTYPE_DIVA20_PCI 8
+ /* S cards */
+#define CARDTYPE_QUADRO_ISA 9
+#define CARDTYPE_S_ISA 10
+#define CARDTYPE_S_MCA 11
+#define CARDTYPE_SX_ISA 12
+#define CARDTYPE_SX_MCA 13
+#define CARDTYPE_SXN_ISA 14
+#define CARDTYPE_SXN_MCA 15
+#define CARDTYPE_SCOM_ISA 16
+#define CARDTYPE_SCOM_MCA 17
+#define CARDTYPE_PR_ISA 18
+#define CARDTYPE_PR_MCA 19
+ /* Diva Server cards (formerly called Maestra, later Amadeo) */
+#define CARDTYPE_MAESTRA_ISA 20
+#define CARDTYPE_MAESTRA_PCI 21
+ /* Diva Server cards to be developed (Quadro, Primary rate) */
+#define CARDTYPE_DIVASRV_Q_8M_PCI 22
+#define CARDTYPE_DIVASRV_P_30M_PCI 23
+#define CARDTYPE_DIVASRV_P_2M_PCI 24
+#define CARDTYPE_DIVASRV_P_9M_PCI 25
+ /* DIVA 2.0 cards */
+#define CARDTYPE_DIVA20_ISA 26
+#define CARDTYPE_DIVA20U_ISA 27
+#define CARDTYPE_DIVA20U_PCI 28
+#define CARDTYPE_DIVAPRO20_ISA 29
+#define CARDTYPE_DIVAPRO20U_ISA 30
+#define CARDTYPE_DIVAPRO20U_PCI 31
+ /* DIVA combi cards (piccola ISDN + rockwell V.34 modem) */
+#define CARDTYPE_DIVAMOBILE_PCM 32
+#define CARDTYPE_TDKGLOBALPRO_PCM 33
+ /* DIVA Pro PC OEM card for 'New Media Corporation' */
+#define CARDTYPE_NMC_DIVAPRO_PCM 34
+ /* DIVA Pro 2.0 OEM cards for 'British Telecom' */
+#define CARDTYPE_BT_EXLANE_PCI 35
+#define CARDTYPE_BT_EXLANE_ISA 36
+ /* DIVA low cost cards, 1st name DIVA 3.0, 2nd DIVA 2.01, 3rd ??? */
+#define CARDTYPE_DIVALOW_ISA 37
+#define CARDTYPE_DIVALOWU_ISA 38
+#define CARDTYPE_DIVALOW_PCI 39
+#define CARDTYPE_DIVALOWU_PCI 40
+ /* DIVA combi cards (piccola ISDN + rockwell V.90 modem) */
+#define CARDTYPE_DIVAMOBILE_V90_PCM 41
+#define CARDTYPE_TDKGLOBPRO_V90_PCM 42
+#define CARDTYPE_DIVASRV_P_23M_PCI 43
+#define CARDTYPE_DIVALOW_USB 44
+ /* DIVA Audio (CT) family */
+#define CARDTYPE_DIVA_CT_ST 45
+#define CARDTYPE_DIVA_CT_U 46
+#define CARDTYPE_DIVA_CTLITE_ST 47
+#define CARDTYPE_DIVA_CTLITE_U 48
+ /* DIVA ISDN plus V.90 series */
+#define CARDTYPE_DIVAISDN_V90_PCM 49
+#define CARDTYPE_DIVAISDN_V90_PCI 50
+#define CARDTYPE_DIVAISDN_TA 51
+ /* DIVA Server Voice cards */
+#define CARDTYPE_DIVASRV_VOICE_Q_8M_PCI 52
+ /* DIVA Server V2 cards */
+#define CARDTYPE_DIVASRV_Q_8M_V2_PCI 53
+#define CARDTYPE_DIVASRV_P_30M_V2_PCI 54
+ /* DIVA Server Voice V2 cards */
+#define CARDTYPE_DIVASRV_VOICE_Q_8M_V2_PCI 55
+#define CARDTYPE_DIVASRV_VOICE_P_30M_V2_PCI 56
+ /* Diva LAN */
+#define CARDTYPE_DIVAISDN_LAN 57
+#define CARDTYPE_DIVA_202_PCI_ST 58
+#define CARDTYPE_DIVA_202_PCI_U 59
+#define CARDTYPE_DIVASRV_B_2M_V2_PCI 60
+#define CARDTYPE_DIVASRV_B_2F_PCI 61
+#define CARDTYPE_DIVALOW_USBV2 62
+#define CARDTYPE_DIVASRV_VOICE_B_2M_V2_PCI 63
+#define CARDTYPE_DIVA_PRO_30_PCI_ST 64
+#define CARDTYPE_DIVA_CT_ST_V20 65
+/* Diva Mobile V.90 PC Card and Diva ISDN PC Card */
+#define CARDTYPE_DIVAMOBILE_V2_PCM 66
+#define CARDTYPE_DIVA_V2_PCM 67
+/* Re-badged Diva Pro PC Card */
+#define CARDTYPE_DIVA_PC_CARD 68
+ /* next free card type identifier */
+#define CARDTYPE_MAX 69
+/*
+ * The card families
+ */
+#define FAMILY_DIVA 1
+#define FAMILY_S 2
+#define FAMILY_MAESTRA 3
+#define FAMILY_MAX 4
+/*
+ * The basic card types
+ */
+#define CARD_DIVA 1 /* DSP based, old DSP */
+#define CARD_PRO 2 /* DSP based, new DSP */
+#define CARD_PICO 3 /* HSCX based */
+#define CARD_S 4 /* IDI on board based */
+#define CARD_SX 5 /* IDI on board based */
+#define CARD_SXN 6 /* IDI on board based */
+#define CARD_SCOM 7 /* IDI on board based */
+#define CARD_QUAD 8 /* IDI on board based */
+#define CARD_PR 9 /* IDI on board based */
+#define CARD_MAE 10 /* IDI on board based */
+#define CARD_MAEQ 11 /* IDI on board based */
+#define CARD_MAEP 12 /* IDI on board based */
+#define CARD_DIVALOW 13 /* IPAC based */
+#define CARD_CT 14 /* SCOUT based */
+#define CARD_DIVATA 15 /* DIVA TA */
+#define CARD_DIVALAN 16 /* DIVA LAN */
+#define CARD_MAE2 17 /* IDI on board based */
+#define CARD_MAX 18
+/*
+ * The internal card types of the S family
+ */
+#define CARD_I_NONE 0
+#define CARD_I_S 0
+#define CARD_I_SX 1
+#define CARD_I_SCOM 2
+#define CARD_I_QUAD 3
+#define CARD_I_PR 4
+/*
+ * The bus types we support
+ */
+#define BUS_ISA 1
+#define BUS_PCM 2
+#define BUS_PCI 3
+#define BUS_MCA 4
+#define BUS_USB 5
+#define BUS_COM 6
+#define BUS_LAN 7
+/*
+ * The chips we use for B-channel traffic
+ */
+#define CHIP_NONE 0
+#define CHIP_DSP 1
+#define CHIP_HSCX 2
+#define CHIP_IPAC 3
+#define CHIP_SCOUT 4
+#define CHIP_EXTERN 5
+#define CHIP_IPACX 6
+/*
+ * The structures where the card properties are aggregated by id
+ */
+typedef struct CARD_PROPERTIES
+{ char *Name; /* official marketing name */
+ unsigned short PnPId; /* plug and play ID (for non PCMIA cards) */
+ unsigned short Version; /* major and minor version no of the card */
+ unsigned char DescType; /* card type to set in the IDI descriptor */
+ unsigned char Family; /* basic family of the card */
+ unsigned short Features; /* features bits to set in the IDI desc. */
+ unsigned char Card; /* basic card type */
+ unsigned char IType; /* internal type of S cards (read from ram) */
+ unsigned char Bus; /* bus type this card is designed for */
+ unsigned char Chip; /* chipset used on card */
+ unsigned char Adapters; /* number of adapters on card */
+ unsigned char Channels; /* # of channels per adapter */
+ unsigned short E_info; /* # of ram entity info structs per adapter */
+ unsigned short SizeIo; /* size of IO window per adapter */
+ unsigned short SizeMem; /* size of memory window per adapter */
+} CARD_PROPERTIES;
+typedef struct CARD_RESOURCE
+{ unsigned char Int [10];
+ unsigned short IoFirst;
+ unsigned short IoStep;
+ unsigned short IoCnt;
+ unsigned long MemFirst;
+ unsigned long MemStep;
+ unsigned short MemCnt;
+} CARD_RESOURCE;
+/* test if the card of type 't' is a plug & play card */
+#define IS_PNP(t) \
+( \
+ ( \
+ CardProperties[t].Bus != BUS_ISA \
+ && \
+ CardProperties[t].Bus != BUS_MCA \
+ ) \
+ || \
+ ( \
+ CardProperties[t].Family != FAMILY_S \
+ && \
+ CardProperties[t].Card != CARD_DIVA \
+ ) \
+)
+/* extract IDI Descriptor info for card type 't' (p == DescType/Features) */
+#define IDI_PROP(t,p) (CardProperties[t].p)
+#if CARDTYPE_H_WANT_DATA
+#if CARDTYPE_H_WANT_IDI_DATA
+/* include "di_defs.h" for IDI adapter type and feature flag definitions */
+#include "di_defs.h"
+#else /*!CARDTYPE_H_WANT_IDI_DATA*/
+/* define IDI adapter types and feature flags here to prevent inclusion */
+#ifndef IDI_ADAPTER_S
+#define IDI_ADAPTER_S 1
+#define IDI_ADAPTER_PR 2
+#define IDI_ADAPTER_DIVA 3
+#define IDI_ADAPTER_MAESTRA 4
+#endif
+#ifndef DI_VOICE
+#define DI_VOICE 0x0 /* obsolete define */
+#define DI_FAX3 0x1
+#define DI_MODEM 0x2
+#define DI_POST 0x4
+#define DI_V110 0x8
+#define DI_V120 0x10
+#define DI_POTS 0x20
+#define DI_CODEC 0x40
+#define DI_MANAGE 0x80
+#define DI_V_42 0x0100
+#define DI_EXTD_FAX 0x0200 /* Extended FAX (ECM, 2D, T.6, Polling) */
+#define DI_AT_PARSER 0x0400 /* Build-in AT Parser in the L2 */
+#define DI_VOICE_OVER_IP 0x0800 /* Voice over IP support */
+#endif
+#endif /*CARDTYPE_H_WANT_IDI_DATA*/
+#define DI_V1x0 (DI_V110 | DI_V120)
+#define DI_NULL 0x0000
+#if defined(SOFT_DSP_SUPPORT)
+#define SOFT_DSP_ADD_FEATURES (DI_MODEM | DI_FAX3 | DI_AT_PARSER)
+#else
+#define SOFT_DSP_ADD_FEATURES 0
+#endif
+#if defined(SOFT_V110_SUPPORT)
+#define DI_SOFT_V110 DI_V110
+#else
+#define DI_SOFT_V110 0
+#endif
+/*--- CardProperties [Index=CARDTYPE_....] ---------------------------------*/
+CARD_PROPERTIES CardProperties [ ] =
+{
+{ /* 0 */
+ "Diva MCA", 0x6336, 0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3,
+ CARD_DIVA, CARD_I_NONE, BUS_MCA, CHIP_DSP,
+ 1, 2, 0, 8, 0
+},
+{ /* 1 */
+ "Diva ISA", 0x0000, 0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3,
+ CARD_DIVA, CARD_I_NONE, BUS_ISA, CHIP_DSP,
+ 1, 2, 0, 8, 0
+},
+{ /* 2 */
+ "Diva/PCM", 0x0000, 0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3,
+ CARD_DIVA, CARD_I_NONE, BUS_PCM, CHIP_DSP,
+ 1, 2, 0, 8, 0
+},
+{ /* 3 */
+ "Diva PRO ISA", 0x0031, 0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_CODEC,
+ CARD_PRO, CARD_I_NONE, BUS_ISA, CHIP_DSP,
+ 1, 2, 0, 8, 0
+},
+{ /* 4 */
+ "Diva PRO PC-Card", 0x0000, 0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM,
+ CARD_PRO, CARD_I_NONE, BUS_PCM, CHIP_DSP,
+ 1, 2, 0, 8, 0
+},
+{ /* 5 */
+ "Diva PICCOLA ISA", 0x0051, 0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
+ CARD_PICO, CARD_I_NONE, BUS_ISA, CHIP_HSCX,
+ 1, 2, 0, 8, 0
+},
+{ /* 6 */
+ "Diva PICCOLA PCM", 0x0000, 0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
+ CARD_PICO, CARD_I_NONE, BUS_PCM, CHIP_HSCX,
+ 1, 2, 0, 8, 0
+},
+{ /* 7 */
+ "Diva PRO 2.0 S/T PCI", 0xe001, 0x0200,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_POTS,
+ CARD_PRO, CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1, 2, 0, 8, 0
+},
+{ /* 8 */
+ "Diva 2.0 S/T PCI", 0xe002, 0x0200,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | DI_POTS | SOFT_DSP_ADD_FEATURES,
+ CARD_PICO, CARD_I_NONE, BUS_PCI, CHIP_HSCX,
+ 1, 2, 0, 8, 0
+},
+{ /* 9 */
+ "QUADRO ISA", 0x0000, 0x0100,
+ IDI_ADAPTER_S, FAMILY_S, DI_NULL,
+ CARD_QUAD, CARD_I_QUAD, BUS_ISA, CHIP_NONE,
+ 4, 2, 16, 0, 0x800
+},
+{ /* 10 */
+ "S ISA", 0x0000, 0x0100,
+ IDI_ADAPTER_S, FAMILY_S, DI_CODEC,
+ CARD_S, CARD_I_S, BUS_ISA, CHIP_NONE,
+ 1, 1, 16, 0, 0x800
+},
+{ /* 11 */
+ "S MCA", 0x6a93, 0x0100,
+ IDI_ADAPTER_S, FAMILY_S, DI_CODEC,
+ CARD_S, CARD_I_S, BUS_MCA, CHIP_NONE,
+ 1, 1, 16, 16, 0x400
+},
+{ /* 12 */
+ "SX ISA", 0x0000, 0x0100,
+ IDI_ADAPTER_S, FAMILY_S, DI_NULL,
+ CARD_SX, CARD_I_SX, BUS_ISA, CHIP_NONE,
+ 1, 2, 16, 0, 0x800
+},
+{ /* 13 */
+ "SX MCA", 0x6a93, 0x0100,
+ IDI_ADAPTER_S, FAMILY_S, DI_NULL,
+ CARD_SX, CARD_I_SX, BUS_MCA, CHIP_NONE,
+ 1, 2, 16, 16, 0x400
+},
+{ /* 14 */
+ "SXN ISA", 0x0000, 0x0100,
+ IDI_ADAPTER_S, FAMILY_S, DI_NULL,
+ CARD_SXN, CARD_I_SCOM, BUS_ISA, CHIP_NONE,
+ 1, 2, 16, 0, 0x800
+},
+{ /* 15 */
+ "SXN MCA", 0x6a93, 0x0100,
+ IDI_ADAPTER_S, FAMILY_S, DI_NULL,
+ CARD_SXN, CARD_I_SCOM, BUS_MCA, CHIP_NONE,
+ 1, 2, 16, 16, 0x400
+},
+{ /* 16 */
+ "SCOM ISA", 0x0000, 0x0100,
+ IDI_ADAPTER_S, FAMILY_S, DI_CODEC,
+ CARD_SCOM, CARD_I_SCOM, BUS_ISA, CHIP_NONE,
+ 1, 2, 16, 0, 0x800
+},
+{ /* 17 */
+ "SCOM MCA", 0x6a93, 0x0100,
+ IDI_ADAPTER_S, FAMILY_S, DI_CODEC,
+ CARD_SCOM, CARD_I_SCOM, BUS_MCA, CHIP_NONE,
+ 1, 2, 16, 16, 0x400
+},
+{ /* 18 */
+ "S2M ISA", 0x0000, 0x0100,
+ IDI_ADAPTER_PR, FAMILY_S, DI_NULL,
+ CARD_PR, CARD_I_PR, BUS_ISA, CHIP_NONE,
+ 1, 30, 256, 0, 0x4000
+},
+{ /* 19 */
+ "S2M MCA", 0x6abb, 0x0100,
+ IDI_ADAPTER_PR, FAMILY_S, DI_NULL,
+ CARD_PR, CARD_I_PR, BUS_MCA, CHIP_NONE,
+ 1, 30, 256, 16, 0x4000
+},
+{ /* 20 */
+ "Diva Server BRI-2M ISA", 0x0041, 0x0100,
+ IDI_ADAPTER_MAESTRA,FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM,
+ CARD_MAE, CARD_I_NONE, BUS_ISA, CHIP_DSP,
+ 1, 2, 16, 8, 0
+},
+{ /* 21 */
+ "Diva Server BRI-2M PCI", 0xE010, 0x0100,
+ IDI_ADAPTER_MAESTRA,FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM,
+ CARD_MAE, CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1, 2, 16, 8, 0
+},
+{ /* 22 */
+ "Diva Server 4BRI-8M PCI", 0xE012, 0x0100,
+ IDI_ADAPTER_MAESTRA,FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM,
+ CARD_MAEQ, CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 4, 2, 16, 8, 0
+},
+{ /* 23 */
+ "Diva Server PRI-30M PCI", 0xE014, 0x0100,
+ IDI_ADAPTER_MAESTRA,FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM,
+ CARD_MAEP, CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1, 30, 256, 8, 0
+},
+{ /* 24 */
+ "Diva Server PRI-2M PCI", 0xe014, 0x0100,
+ IDI_ADAPTER_MAESTRA,FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM,
+ CARD_MAEP, CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1, 30, 256, 8, 0
+},
+{ /* 25 */
+ "Diva Server PRI-9M PCI", 0x0000, 0x0100,
+ IDI_ADAPTER_MAESTRA,FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM,
+ CARD_MAEP, CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1, 30, 256, 8, 0
+},
+{ /* 26 */
+ "Diva 2.0 S/T ISA", 0x0071, 0x0200,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | DI_POTS | SOFT_DSP_ADD_FEATURES,
+ CARD_PICO, CARD_I_NONE, BUS_ISA, CHIP_HSCX,
+ 1, 2, 0, 8, 0
+},
+{ /* 27 */
+ "Diva 2.0 U ISA", 0x0091, 0x0200,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | DI_POTS | SOFT_DSP_ADD_FEATURES,
+ CARD_PICO, CARD_I_NONE, BUS_ISA, CHIP_HSCX,
+ 1, 2, 0, 8, 0
+},
+{ /* 28 */
+ "Diva 2.0 U PCI", 0xe004, 0x0200,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | DI_POTS | SOFT_DSP_ADD_FEATURES,
+ CARD_PICO, CARD_I_NONE, BUS_PCI, CHIP_HSCX,
+ 1, 2, 0, 8, 0
+},
+{ /* 29 */
+ "Diva PRO 2.0 S/T ISA", 0x0061, 0x0200,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_POTS,
+ CARD_PRO, CARD_I_NONE, BUS_ISA, CHIP_DSP,
+ 1, 2, 0, 8, 0
+},
+{ /* 30 */
+ "Diva PRO 2.0 U ISA", 0x0081, 0x0200,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_POTS,
+ CARD_PRO, CARD_I_NONE, BUS_ISA, CHIP_DSP,
+ 1, 2, 0, 8, 0
+},
+{ /* 31 */
+ "Diva PRO 2.0 U PCI", 0xe003, 0x0200,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_POTS,
+ CARD_PRO, CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1, 2, 0, 8, 0
+},
+{ /* 32 */
+ "Diva MOBILE", 0x0000, 0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
+ CARD_PICO, CARD_I_NONE, BUS_PCM, CHIP_HSCX,
+ 1, 2, 0, 8, 0
+},
+{ /* 33 */
+ "TDK DFI3600", 0x0000, 0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
+ CARD_PICO, CARD_I_NONE, BUS_PCM, CHIP_HSCX,
+ 1, 2, 0, 8, 0
+},
+{ /* 34 (OEM version of 4 - "Diva PRO PC-Card") */
+ "New Media ISDN", 0x0000, 0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM,
+ CARD_PRO, CARD_I_NONE, BUS_PCM, CHIP_DSP,
+ 1, 2, 0, 8, 0
+},
+{ /* 35 (OEM version of 7 - "Diva PRO 2.0 S/T PCI") */
+ "BT ExLane PCI", 0xe101, 0x0200,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_POTS,
+ CARD_PRO, CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1, 2, 0, 8, 0
+},
+{ /* 36 (OEM version of 29 - "Diva PRO 2.0 S/T ISA") */
+ "BT ExLane ISA", 0x1061, 0x0200,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_POTS,
+ CARD_PRO, CARD_I_NONE, BUS_ISA, CHIP_DSP,
+ 1, 2, 0, 8, 0
+},
+{ /* 37 */
+ "Diva 2.01 S/T ISA", 0x00A1, 0x0300,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
+ CARD_DIVALOW, CARD_I_NONE, BUS_ISA, CHIP_IPAC,
+ 1, 2, 0, 8, 0
+},
+{ /* 38 */
+ "Diva 2.01 U ISA", 0x00B1, 0x0300,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
+ CARD_DIVALOW, CARD_I_NONE, BUS_ISA, CHIP_IPAC,
+ 1, 2, 0, 8, 0
+},
+{ /* 39 */
+ "Diva 2.01 S/T PCI", 0xe005, 0x0300,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
+ CARD_DIVALOW, CARD_I_NONE, BUS_PCI, CHIP_IPAC,
+ 1, 2, 0, 8, 0
+},
+{ /* 40 no ID yet */
+ "Diva 2.01 U PCI", 0x0000, 0x0300,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
+ CARD_DIVALOW, CARD_I_NONE, BUS_PCI, CHIP_IPAC,
+ 1, 2, 0, 8, 0
+},
+{ /* 41 */
+ "Diva MOBILE V.90", 0x0000, 0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
+ CARD_PICO, CARD_I_NONE, BUS_PCM, CHIP_HSCX,
+ 1, 2, 0, 8, 0
+},
+{ /* 42 */
+ "TDK DFI3600 V.90", 0x0000, 0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
+ CARD_PICO, CARD_I_NONE, BUS_PCM, CHIP_HSCX,
+ 1, 2, 0, 8, 0
+},
+{ /* 43 */
+ "Diva Server PRI-23M PCI", 0xe014, 0x0100,
+ IDI_ADAPTER_MAESTRA,FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM,
+ CARD_MAEP, CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1, 30, 256, 8, 0
+},
+{ /* 44 */
+ "Diva 2.01 S/T USB", 0x1000, 0x0300,
+ IDI_ADAPTER_DIVA ,FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
+ CARD_DIVALOW, CARD_I_NONE, BUS_USB, CHIP_IPAC,
+ 1, 2, 0, 8, 0
+},
+{ /* 45 */
+ "Diva CT S/T PCI", 0xe006, 0x0300,
+ IDI_ADAPTER_DIVA ,FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_CODEC,
+ CARD_CT, CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1, 2, 0, 0, 0
+},
+{ /* 46 */
+ "Diva CT U PCI", 0xe007, 0x0300,
+ IDI_ADAPTER_DIVA ,FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_CODEC,
+ CARD_CT, CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1, 2, 0, 0, 0
+},
+{ /* 47 */
+ "Diva CT Lite S/T PCI", 0xe008, 0x0300,
+ IDI_ADAPTER_DIVA ,FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_CODEC,
+ CARD_CT, CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1, 2, 0, 0, 0
+},
+{ /* 48 */
+ "Diva CT Lite U PCI", 0xe009, 0x0300,
+ IDI_ADAPTER_DIVA ,FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_CODEC,
+ CARD_CT, CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1, 2, 0, 0, 0
+},
+{ /* 49 */
+ "Diva ISDN+V.90 PC Card", 0x8D8C, 0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_CODEC,
+ CARD_DIVALOW, CARD_I_NONE, BUS_PCM, CHIP_IPAC,
+ 1, 2, 0, 8, 0
+},
+{ /* 50 */
+ "Diva ISDN+V.90 PCI", 0xe00A, 0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
+ CARD_DIVALOW, CARD_I_NONE, BUS_PCI, CHIP_IPAC,
+ 1, 2, 0, 8, 0
+},
+{ /* 51 (DivaTA) no ID */
+ "Diva TA", 0x0000, 0x0300,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V110 | DI_FAX3 | SOFT_DSP_ADD_FEATURES,
+ CARD_DIVATA, CARD_I_NONE, BUS_COM, CHIP_EXTERN,
+ 1, 1, 0, 8, 0
+},
+{ /* 52 (Diva Server 4BRI-8M PCI adapter enabled for Voice) */
+ "Diva Server Voice 4BRI-8M PCI", 0xE016, 0x0100,
+ IDI_ADAPTER_MAESTRA,FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_VOICE_OVER_IP,
+ CARD_MAEQ, CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 4, 2, 16, 8, 0
+},
+{ /* 53 (Diva Server 4BRI 2.0 adapter) */
+ "Diva Server 4BRI-8M 2.0 PCI", 0xE013, 0x0200,
+ IDI_ADAPTER_MAESTRA,FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM,
+ CARD_MAEQ, CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 4, 2, 16, 8, 0
+},
+{ /* 54 (Diva Server PRI 2.0 adapter) */
+ "Diva Server PRI 2.0 PCI", 0xE015, 0x0200,
+ IDI_ADAPTER_MAESTRA,FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM,
+ CARD_MAEP, CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1, 30, 256, 8, 0
+},
+{ /* 55 (Diva Server 4BRI-8M 2.0 PCI adapter enabled for Voice) */
+ "Diva Server Voice 4BRI-8M 2.0 PCI", 0xE017, 0x0200,
+ IDI_ADAPTER_MAESTRA,FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_VOICE_OVER_IP,
+ CARD_MAEQ, CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 4, 2, 16, 8, 0
+},
+{ /* 56 (Diva Server PRI 2.0 PCI adapter enabled for Voice) */
+ "Diva Server Voice PRI 2.0 PCI", 0xE019, 0x0200,
+ IDI_ADAPTER_MAESTRA,FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_VOICE_OVER_IP,
+ CARD_MAEP, CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1, 30, 256, 8, 0
+},
+{
+ /* 57 (DivaLan ) no ID */
+ "Diva LAN", 0x0000, 0x0300,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V110 | DI_FAX3 | SOFT_DSP_ADD_FEATURES,
+ CARD_DIVALAN, CARD_I_NONE, BUS_LAN, CHIP_EXTERN,
+ 1, 1, 0, 8, 0
+},
+{ /* 58 */
+ "Diva 2.02 PCI S/T", 0xE00B, 0x0300,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES | DI_SOFT_V110,
+ CARD_DIVALOW, CARD_I_NONE, BUS_PCI, CHIP_IPACX,
+ 1, 2, 0, 8, 0
+},
+{ /* 59 */
+ "Diva 2.02 PCI U", 0xE00C, 0x0300,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
+ CARD_DIVALOW, CARD_I_NONE, BUS_PCI, CHIP_IPACX,
+ 1, 2, 0, 8, 0
+},
+{ /* 60 */
+ "Diva Server BRI-2M 2.0 PCI", 0xE018, 0x0200,
+ IDI_ADAPTER_MAESTRA,FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM,
+ CARD_MAE2, CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1, 2, 16, 8, 0
+},
+{ /* 61 (the previous name was Diva Server BRI-2F 2.0 PCI) */
+ "Diva Server 2FX", 0xE01A, 0x0200,
+ IDI_ADAPTER_MAESTRA,FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_SOFT_V110,
+ CARD_MAE2, CARD_I_NONE, BUS_PCI, CHIP_IPACX,
+ 1, 2, 16, 8, 0
+},
+{ /* 62 */
+ " Diva ISDN USB 2.0", 0x1003, 0x0300,
+ IDI_ADAPTER_DIVA ,FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
+ CARD_DIVALOW, CARD_I_NONE, BUS_USB, CHIP_IPACX,
+ 1, 2, 0, 8, 0
+},
+{ /* 63 (Diva Server BRI-2M 2.0 PCI adapter enabled for Voice) */
+ "Diva Server Voice BRI-2M 2.0 PCI", 0xE01B, 0x0200,
+ IDI_ADAPTER_MAESTRA,FAMILY_MAESTRA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_VOICE_OVER_IP,
+ CARD_MAE2, CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1, 2, 16, 8, 0
+},
+{ /* 64 */
+ "Diva Pro 3.0 PCI", 0xe00d, 0x0300,
+ IDI_ADAPTER_DIVA ,FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM,
+ CARD_PRO, CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1, 2, 0, 0, 0
+},
+{ /* 65 */
+ "Diva ISDN + CT 2.0", 0xE00E, 0x0300,
+ IDI_ADAPTER_DIVA ,FAMILY_DIVA, DI_V1x0 | DI_FAX3 | DI_MODEM | DI_CODEC,
+ CARD_CT, CARD_I_NONE, BUS_PCI, CHIP_DSP,
+ 1, 2, 0, 0, 0
+},
+{ /* 66 */
+ "Diva Mobile V.90 PC Card", 0x8331, 0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
+ CARD_PICO, CARD_I_NONE, BUS_PCM, CHIP_IPACX,
+ 1, 2, 0, 8, 0
+},
+{ /* 67 */
+ "Diva ISDN PC Card", 0x8311, 0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
+ CARD_PICO, CARD_I_NONE, BUS_PCM, CHIP_IPACX,
+ 1, 2, 0, 8, 0
+},
+{ /* 68 */
+ "Diva ISDN PC Card", 0x0000, 0x0100,
+ IDI_ADAPTER_DIVA, FAMILY_DIVA, DI_V120 | SOFT_DSP_ADD_FEATURES,
+ CARD_PRO, CARD_I_NONE, BUS_PCM, CHIP_DSP,
+ 1, 2, 0, 8, 0
+},
+} ;
+#if CARDTYPE_H_WANT_RESOURCE_DATA
+/*--- CardResource [Index=CARDTYPE_....] ---------------------------(GEI)-*/
+CARD_RESOURCE CardResource [ ] = {
+/* Interrupts IO-Address Mem-Address */
+/* 0*/ { 3,4,9,0,0,0,0,0,0,0, 0x200,0x20,16, 0x0,0x0,0 }, // DIVA MCA
+/* 1*/ { 3,4,9,10,11,12,0,0,0,0, 0x200,0x20,16, 0x0,0x0,0 }, // DIVA ISA
+/* 2*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // DIVA PCMCIA
+/* 3*/ { 3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16, 0x0,0x0,0 }, // DIVA PRO ISA
+/* 4*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // DIVA PRO PCMCIA
+/* 5*/ { 3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16, 0x0,0x0,0 }, // DIVA PICCOLA ISA
+/* 6*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // DIVA PICCOLA PCMCIA
+/* 7*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // DIVA PRO 2.0 PCI
+/* 8*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // DIVA 2.0 PCI
+/* 9*/ { 3,4,5,7,9,10,11,12,0,0, 0x0,0x0,0, 0x80000,0x2000,64 }, // QUADRO ISA
+/*10*/ { 3,4,9,10,11,12,0,0,0,0, 0x0,0x0,0, 0xc0000,0x2000,16 }, // S ISA
+/*11*/ { 3,4,9,0,0,0,0,0,0,0, 0xc00,0x10,16, 0xc0000,0x2000,16 }, // S MCA
+/*12*/ { 3,4,9,10,11,12,0,0,0,0, 0x0,0x0,0, 0xc0000,0x2000,16 }, // SX ISA
+/*13*/ { 3,4,9,0,0,0,0,0,0,0, 0xc00,0x10,16, 0xc0000,0x2000,16 }, // SX MCA
+/*14*/ { 3,4,5,7,9,10,11,12,0,0, 0x0,0x0,0, 0x80000,0x0800,256 }, // SXN ISA
+/*15*/ { 3,4,9,0,0,0,0,0,0,0, 0xc00,0x10,16, 0xc0000,0x2000,16 }, // SXN MCA
+/*16*/ { 3,4,5,7,9,10,11,12,0,0, 0x0,0x0,0, 0x80000,0x0800,256 }, // SCOM ISA
+/*17*/ { 3,4,9,0,0,0,0,0,0,0, 0xc00,0x10,16, 0xc0000,0x2000,16 }, // SCOM MCA
+/*18*/ { 3,4,5,7,9,10,11,12,0,0, 0x0,0x0,0, 0xc0000,0x4000,16 }, // S2M ISA
+/*19*/ { 3,4,9,0,0,0,0,0,0,0, 0xc00,0x10,16, 0xc0000,0x4000,16 }, // S2M MCA
+/*20*/ { 3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16, 0x0,0x0,0 }, // MAESTRA ISA
+/*21*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // MAESTRA PCI
+/*22*/ { 3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16, 0x0,0x0,0 }, // MAESTRA QUADRO ISA
+/*23*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x20,2048, 0x0,0x0,0 }, // MAESTRA QUADRO PCI
+/*24*/ { 3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16, 0x0,0x0,0 }, // MAESTRA PRIMARY ISA
+/*25*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // MAESTRA PRIMARY PCI
+/*26*/ { 3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16, 0x0,0x0,0 }, // DIVA 2.0 ISA
+/*27*/ { 3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16, 0x0,0x0,0 }, // DIVA 2.0 /U ISA
+/*28*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // DIVA 2.0 /U PCI
+/*29*/ { 3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16, 0x0,0x0,0 }, // DIVA PRO 2.0 ISA
+/*30*/ { 3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16, 0x0,0x0,0 }, // DIVA PRO 2.0 /U ISA
+/*31*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // DIVA PRO 2.0 /U PCI
+/*32*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // DIVA MOBILE
+/*33*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // TDK DFI3600 (same as DIVA MOBILE [32])
+/*34*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // New Media ISDN (same as DIVA PRO PCMCIA [4])
+/*35*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // BT ExLane PCI (same as DIVA PRO 2.0 PCI [7])
+/*36*/ { 3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16, 0x0,0x0,0 }, // BT ExLane ISA (same as DIVA PRO 2.0 ISA [29])
+/*37*/ { 3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16, 0x0,0x0,0 }, // DIVA 2.01 S/T ISA
+/*38*/ { 3,5,7,9,10,11,12,14,15,0, 0x200,0x20,16, 0x0,0x0,0 }, // DIVA 2.01 U ISA
+/*39*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // DIVA 2.01 S/T PCI
+/*40*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // DIVA 2.01 U PCI
+/*41*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // DIVA MOBILE V.90
+/*42*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // TDK DFI3600 V.90 (same as DIVA MOBILE V.90 [39])
+/*43*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x20,2048, 0x0,0x0,0 }, // DIVA Server PRI-23M PCI
+/*44*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // DIVA 2.01 S/T USB
+/*45*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // DIVA CT S/T PCI
+/*46*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // DIVA CT U PCI
+/*47*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // DIVA CT Lite S/T PCI
+/*48*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // DIVA CT Lite U PCI
+/*49*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // DIVA ISDN+V.90 PC Card
+/*50*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // DIVA ISDN+V.90 PCI
+/*51*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // DIVA TA
+/*52*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x20,2048, 0x0,0x0,0 }, // MAESTRA VOICE QUADRO PCI
+/*53*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x20,2048, 0x0,0x0,0 }, // MAESTRA VOICE QUADRO PCI
+/*54*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // MAESTRA VOICE PRIMARY PCI
+/*55*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x20,2048, 0x0,0x0,0 }, // MAESTRA VOICE QUADRO PCI
+/*56*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // MAESTRA VOICE PRIMARY PCI
+/*57*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // DIVA LAN
+/*58*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // DIVA 2.02 S/T PCI
+/*59*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // DIVA 2.02 U PCI
+/*60*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // Diva Server BRI-2M 2.0 PCI
+/*61*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // Diva Server BRI-2F PCI
+/*62*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // DIVA 2.01 S/T USB
+/*63*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // Diva Server Voice BRI-2M 2.0 PCI
+/*64*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // DIVA 3.0 PCI
+/*65*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // DIVA CT S/T PCI V2.0
+/*66*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // DIVA Mobile V.90 PC Card
+/*67*/ { 0,0,0,0,0,0,0,0,0,0, 0x0,0x0,0, 0x0,0x0,0 }, // DIVA ISDN PC Card
+/*68*/ { 3,4,5,7,9,10,11,12,14,15, 0x0,0x8,8192, 0x0,0x0,0 }, // DIVA ISDN PC Card
+};
+#endif /*CARDTYPE_H_WANT_RESOURCE_DATA*/
+#else /*!CARDTYPE_H_WANT_DATA*/
+extern CARD_PROPERTIES CardProperties [] ;
+extern CARD_RESOURCE CardResource [] ;
+#endif /*CARDTYPE_H_WANT_DATA*/
+/*
+ * all existing download files
+ */
+#define CARD_DSP_CNT 5
+#define CARD_PROT_CNT 9
+#define CARD_FT_UNKNOWN 0
+#define CARD_FT_B 1
+#define CARD_FT_D 2
+#define CARD_FT_S 3
+#define CARD_FT_M 4
+#define CARD_FT_NEW_DSP_COMBIFILE 5 /* File format of new DSP code (the DSP code powered by Telindus) */
+#define CARD_FILE_NONE 0
+#define CARD_B_S 1
+#define CARD_B_P 2
+#define CARD_D_K1 3
+#define CARD_D_K2 4
+#define CARD_D_H 5
+#define CARD_D_V 6
+#define CARD_D_M 7
+#define CARD_D_F 8
+#define CARD_P_S_E 9
+#define CARD_P_S_1 10
+#define CARD_P_S_B 11
+#define CARD_P_S_F 12
+#define CARD_P_S_A 13
+#define CARD_P_S_N 14
+#define CARD_P_S_5 15
+#define CARD_P_S_J 16
+#define CARD_P_SX_E 17
+#define CARD_P_SX_1 18
+#define CARD_P_SX_B 19
+#define CARD_P_SX_F 20
+#define CARD_P_SX_A 21
+#define CARD_P_SX_N 22
+#define CARD_P_SX_5 23
+#define CARD_P_SX_J 24
+#define CARD_P_SY_E 25
+#define CARD_P_SY_1 26
+#define CARD_P_SY_B 27
+#define CARD_P_SY_F 28
+#define CARD_P_SY_A 29
+#define CARD_P_SY_N 30
+#define CARD_P_SY_5 31
+#define CARD_P_SY_J 32
+#define CARD_P_SQ_E 33
+#define CARD_P_SQ_1 34
+#define CARD_P_SQ_B 35
+#define CARD_P_SQ_F 36
+#define CARD_P_SQ_A 37
+#define CARD_P_SQ_N 38
+#define CARD_P_SQ_5 39
+#define CARD_P_SQ_J 40
+#define CARD_P_P_E 41
+#define CARD_P_P_1 42
+#define CARD_P_P_B 43
+#define CARD_P_P_F 44
+#define CARD_P_P_A 45
+#define CARD_P_P_N 46
+#define CARD_P_P_5 47
+#define CARD_P_P_J 48
+#define CARD_P_M_E 49
+#define CARD_P_M_1 50
+#define CARD_P_M_B 51
+#define CARD_P_M_F 52
+#define CARD_P_M_A 53
+#define CARD_P_M_N 54
+#define CARD_P_M_5 55
+#define CARD_P_M_J 56
+#define CARD_P_S_S 57
+#define CARD_P_SX_S 58
+#define CARD_P_SY_S 59
+#define CARD_P_SQ_S 60
+#define CARD_P_P_S 61
+#define CARD_P_M_S 62
+#define CARD_D_NEW_DSP_COMBIFILE 63
+typedef struct CARD_FILES_DATA
+{
+ char * Name;
+ unsigned char Type;
+}
+CARD_FILES_DATA;
+typedef struct CARD_FILES
+{
+ unsigned char Boot;
+ unsigned char Dsp [CARD_DSP_CNT];
+ unsigned char DspTelindus;
+ unsigned char Prot [CARD_PROT_CNT];
+}
+CARD_FILES;
+#if CARDTYPE_H_WANT_DATA
+#if CARDTYPE_H_WANT_FILE_DATA
+CARD_FILES_DATA CardFData [] = {
+// Filename Filetype
+ 0, CARD_FT_UNKNOWN,
+ "didnload.bin", CARD_FT_B,
+ "diprload.bin", CARD_FT_B,
+ "didiva.bin", CARD_FT_D,
+ "didivapp.bin", CARD_FT_D,
+ "dihscx.bin", CARD_FT_D,
+ "div110.bin", CARD_FT_D,
+ "dimodem.bin", CARD_FT_D,
+ "difax.bin", CARD_FT_D,
+ "di_etsi.bin", CARD_FT_S,
+ "di_1tr6.bin", CARD_FT_S,
+ "di_belg.bin", CARD_FT_S,
+ "di_franc.bin", CARD_FT_S,
+ "di_atel.bin", CARD_FT_S,
+ "di_ni.bin", CARD_FT_S,
+ "di_5ess.bin", CARD_FT_S,
+ "di_japan.bin", CARD_FT_S,
+ "di_etsi.sx", CARD_FT_S,
+ "di_1tr6.sx", CARD_FT_S,
+ "di_belg.sx", CARD_FT_S,
+ "di_franc.sx", CARD_FT_S,
+ "di_atel.sx", CARD_FT_S,
+ "di_ni.sx", CARD_FT_S,
+ "di_5ess.sx", CARD_FT_S,
+ "di_japan.sx", CARD_FT_S,
+ "di_etsi.sy", CARD_FT_S,
+ "di_1tr6.sy", CARD_FT_S,
+ "di_belg.sy", CARD_FT_S,
+ "di_franc.sy", CARD_FT_S,
+ "di_atel.sy", CARD_FT_S,
+ "di_ni.sy", CARD_FT_S,
+ "di_5ess.sy", CARD_FT_S,
+ "di_japan.sy", CARD_FT_S,
+ "di_etsi.sq", CARD_FT_S,
+ "di_1tr6.sq", CARD_FT_S,
+ "di_belg.sq", CARD_FT_S,
+ "di_franc.sq", CARD_FT_S,
+ "di_atel.sq", CARD_FT_S,
+ "di_ni.sq", CARD_FT_S,
+ "di_5ess.sq", CARD_FT_S,
+ "di_japan.sq", CARD_FT_S,
+ "di_etsi.p", CARD_FT_S,
+ "di_1tr6.p", CARD_FT_S,
+ "di_belg.p", CARD_FT_S,
+ "di_franc.p", CARD_FT_S,
+ "di_atel.p", CARD_FT_S,
+ "di_ni.p", CARD_FT_S,
+ "di_5ess.p", CARD_FT_S,
+ "di_japan.p", CARD_FT_S,
+ "di_etsi.sm", CARD_FT_M,
+ "di_1tr6.sm", CARD_FT_M,
+ "di_belg.sm", CARD_FT_M,
+ "di_franc.sm", CARD_FT_M,
+ "di_atel.sm", CARD_FT_M,
+ "di_ni.sm", CARD_FT_M,
+ "di_5ess.sm", CARD_FT_M,
+ "di_japan.sm", CARD_FT_M,
+ "di_swed.bin", CARD_FT_S,
+ "di_swed.sx", CARD_FT_S,
+ "di_swed.sy", CARD_FT_S,
+ "di_swed.sq", CARD_FT_S,
+ "di_swed.p", CARD_FT_S,
+ "di_swed.sm", CARD_FT_M,
+ "didspdld.bin", CARD_FT_NEW_DSP_COMBIFILE
+};
+CARD_FILES CardFiles [] =
+{
+ { /* CARD_UNKNOWN */
+ CARD_FILE_NONE,
+ CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+ CARD_FILE_NONE,
+ CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+ CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+ CARD_FILE_NONE
+ },
+ { /* CARD_DIVA */
+ CARD_FILE_NONE,
+ CARD_D_K1, CARD_D_H, CARD_D_V, CARD_FILE_NONE, CARD_D_F,
+ CARD_D_NEW_DSP_COMBIFILE,
+ CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+ CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+ CARD_FILE_NONE
+ },
+ { /* CARD_PRO */
+ CARD_FILE_NONE,
+ CARD_D_K2, CARD_D_H, CARD_D_V, CARD_D_M, CARD_D_F,
+ CARD_D_NEW_DSP_COMBIFILE,
+ CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+ CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+ CARD_FILE_NONE
+ },
+ { /* CARD_PICO */
+ CARD_FILE_NONE,
+ CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+ CARD_FILE_NONE,
+ CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+ CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+ CARD_FILE_NONE
+ },
+ { /* CARD_S */
+ CARD_B_S,
+ CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+ CARD_FILE_NONE,
+ CARD_P_S_E, CARD_P_S_1, CARD_P_S_B, CARD_P_S_F,
+ CARD_P_S_A, CARD_P_S_N, CARD_P_S_5, CARD_P_S_J,
+ CARD_P_S_S
+ },
+ { /* CARD_SX */
+ CARD_B_S,
+ CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+ CARD_FILE_NONE,
+ CARD_P_SX_E, CARD_P_SX_1, CARD_P_SX_B, CARD_P_SX_F,
+ CARD_P_SX_A, CARD_P_SX_N, CARD_P_SX_5, CARD_P_SX_J,
+ CARD_P_SX_S
+ },
+ { /* CARD_SXN */
+ CARD_B_S,
+ CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+ CARD_FILE_NONE,
+ CARD_P_SY_E, CARD_P_SY_1, CARD_P_SY_B, CARD_P_SY_F,
+ CARD_P_SY_A, CARD_P_SY_N, CARD_P_SY_5, CARD_P_SY_J,
+ CARD_P_SY_S
+ },
+ { /* CARD_SCOM */
+ CARD_B_S,
+ CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+ CARD_FILE_NONE,
+ CARD_P_SY_E, CARD_P_SY_1, CARD_P_SY_B, CARD_P_SY_F,
+ CARD_P_SY_A, CARD_P_SY_N, CARD_P_SY_5, CARD_P_SY_J,
+ CARD_P_SY_S
+ },
+ { /* CARD_QUAD */
+ CARD_B_S,
+ CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+ CARD_FILE_NONE,
+ CARD_P_SQ_E, CARD_P_SQ_1, CARD_P_SQ_B, CARD_P_SQ_F,
+ CARD_P_SQ_A, CARD_P_SQ_N, CARD_P_SQ_5, CARD_P_SQ_J,
+ CARD_P_SQ_S
+ },
+ { /* CARD_PR */
+ CARD_B_P,
+ CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+ CARD_FILE_NONE,
+ CARD_P_P_E, CARD_P_P_1, CARD_P_P_B, CARD_P_P_F,
+ CARD_P_P_A, CARD_P_P_N, CARD_P_P_5, CARD_P_P_J,
+ CARD_P_P_S
+ },
+ { /* CARD_MAE */
+ CARD_FILE_NONE,
+ CARD_D_K2, CARD_D_H, CARD_D_V, CARD_D_M, CARD_D_F,
+ CARD_D_NEW_DSP_COMBIFILE,
+ CARD_P_M_E, CARD_P_M_1, CARD_P_M_B, CARD_P_M_F,
+ CARD_P_M_A, CARD_P_M_N, CARD_P_M_5, CARD_P_M_J,
+ CARD_P_M_S
+ },
+ { /* CARD_MAEQ */ /* currently not supported */
+ CARD_FILE_NONE,
+ CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+ CARD_FILE_NONE,
+ CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+ CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+ CARD_FILE_NONE
+ },
+ { /* CARD_MAEP */ /* currently not supported */
+ CARD_FILE_NONE,
+ CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+ CARD_FILE_NONE,
+ CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+ CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE, CARD_FILE_NONE,
+ CARD_FILE_NONE
+ }
+};
+#endif /*CARDTYPE_H_WANT_FILE_DATA*/
+#else /*!CARDTYPE_H_WANT_DATA*/
+extern CARD_FILES_DATA CardFData [] ;
+extern CARD_FILES CardFiles [] ;
+#endif /*CARDTYPE_H_WANT_DATA*/
+#endif /* _CARDTYPE_H_ */
diff --git a/drivers/isdn/hardware/eicon/cp_vers.h b/drivers/isdn/hardware/eicon/cp_vers.h
new file mode 100644
index 0000000..cb5ada3
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/cp_vers.h
@@ -0,0 +1,26 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+static char diva_capi_common_code_build[] = "102-28";
diff --git a/drivers/isdn/hardware/eicon/dadapter.c b/drivers/isdn/hardware/eicon/dadapter.c
new file mode 100644
index 0000000..8949789
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/dadapter.c
@@ -0,0 +1,366 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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 "platform.h"
+#include "pc.h"
+#include "debuglib.h"
+#include "di_defs.h"
+#include "divasync.h"
+#include "dadapter.h"
+/* --------------------------------------------------------------------------
+ Adapter array change notification framework
+ -------------------------------------------------------------------------- */
+typedef struct _didd_adapter_change_notification {
+ didd_adapter_change_callback_t callback;
+ void IDI_CALL_ENTITY_T * context;
+} didd_adapter_change_notification_t, \
+ * IDI_CALL_ENTITY_T pdidd_adapter_change_notification_t;
+#define DIVA_DIDD_MAX_NOTIFICATIONS 256
+static didd_adapter_change_notification_t\
+ NotificationTable[DIVA_DIDD_MAX_NOTIFICATIONS];
+/* --------------------------------------------------------------------------
+ Array to held adapter information
+ -------------------------------------------------------------------------- */
+static DESCRIPTOR HandleTable[NEW_MAX_DESCRIPTORS];
+static dword Adapters = 0; /* Number of adapters */
+/* --------------------------------------------------------------------------
+ Shadow IDI_DIMAINT
+ and 'shadow' debug stuff
+ -------------------------------------------------------------------------- */
+static void no_printf (unsigned char * format, ...)
+{
+#ifdef EBUG
+ va_list ap;
+ va_start (ap, format);
+ debug((format, ap));
+ va_end (ap);
+#endif
+}
+
+/* -------------------------------------------------------------------------
+ Portable debug Library
+ ------------------------------------------------------------------------- */
+#include "debuglib.c"
+
+static DESCRIPTOR MAdapter = {IDI_DIMAINT, /* Adapter Type */
+ 0x00, /* Channels */
+ 0x0000, /* Features */
+ (IDI_CALL)no_printf};
+/* --------------------------------------------------------------------------
+ DAdapter. Only IDI clients with buffer, that is huge enough to
+ get all descriptors will receive information about DAdapter
+ { byte type, byte channels, word features, IDI_CALL request }
+ -------------------------------------------------------------------------- */
+static void IDI_CALL_LINK_T diva_dadapter_request (ENTITY IDI_CALL_ENTITY_T *);
+static DESCRIPTOR DAdapter = {IDI_DADAPTER, /* Adapter Type */
+ 0x00, /* Channels */
+ 0x0000, /* Features */
+ diva_dadapter_request };
+/* --------------------------------------------------------------------------
+ LOCALS
+ -------------------------------------------------------------------------- */
+static dword diva_register_adapter_callback (\
+ didd_adapter_change_callback_t callback,
+ void IDI_CALL_ENTITY_T* context);
+static void diva_remove_adapter_callback (dword handle);
+static void diva_notify_adapter_change (DESCRIPTOR* d, int removal);
+static diva_os_spin_lock_t didd_spin;
+/* --------------------------------------------------------------------------
+ Should be called as first step, after driver init
+ -------------------------------------------------------------------------- */
+void diva_didd_load_time_init (void) {
+ memset (&HandleTable[0], 0x00, sizeof(HandleTable));
+ memset (&NotificationTable[0], 0x00, sizeof(NotificationTable));
+ diva_os_initialize_spin_lock (&didd_spin, "didd");
+}
+/* --------------------------------------------------------------------------
+ Should be called as last step, if driver does unload
+ -------------------------------------------------------------------------- */
+void diva_didd_load_time_finit (void) {
+ diva_os_destroy_spin_lock (&didd_spin, "didd");
+}
+/* --------------------------------------------------------------------------
+ Called in order to register new adapter in adapter array
+ return adapter handle (> 0) on success
+ return -1 adapter array overflow
+ -------------------------------------------------------------------------- */
+static int diva_didd_add_descriptor (DESCRIPTOR* d) {
+ diva_os_spin_lock_magic_t irql;
+ int i;
+ if (d->type == IDI_DIMAINT) {
+ if (d->request) {
+ MAdapter.request = d->request;
+ dprintf = (DIVA_DI_PRINTF)d->request;
+ diva_notify_adapter_change (&MAdapter, 0); /* Inserted */
+ DBG_TRC (("DIMAINT registered, dprintf=%08x", d->request))
+ } else {
+ DBG_TRC (("DIMAINT removed"))
+ diva_notify_adapter_change (&MAdapter, 1); /* About to remove */
+ MAdapter.request = (IDI_CALL)no_printf;
+ dprintf = no_printf;
+ }
+ return (NEW_MAX_DESCRIPTORS);
+ }
+ for (i = 0; i < NEW_MAX_DESCRIPTORS; i++) {
+ diva_os_enter_spin_lock (&didd_spin, &irql, "didd_add");
+ if (HandleTable[i].type == 0) {
+ memcpy (&HandleTable[i], d, sizeof(*d));
+ Adapters++;
+ diva_os_leave_spin_lock (&didd_spin, &irql, "didd_add");
+ diva_notify_adapter_change (d, 0); /* we have new adapter */
+ DBG_TRC (("Add adapter[%d], request=%08x", (i+1), d->request))
+ return (i+1);
+ }
+ diva_os_leave_spin_lock (&didd_spin, &irql, "didd_add");
+ }
+ DBG_ERR (("Can't add adapter, out of resources"))
+ return (-1);
+}
+/* --------------------------------------------------------------------------
+ Called in order to remove one registered adapter from array
+ return adapter handle (> 0) on success
+ return 0 on success
+ -------------------------------------------------------------------------- */
+static int diva_didd_remove_descriptor (IDI_CALL request) {
+ diva_os_spin_lock_magic_t irql;
+ int i;
+ if (request == MAdapter.request) {
+ DBG_TRC(("DIMAINT removed"))
+ dprintf = no_printf;
+ diva_notify_adapter_change (&MAdapter, 1); /* About to remove */
+ MAdapter.request = (IDI_CALL)no_printf;
+ return (0);
+ }
+ for (i = 0; (Adapters && (i < NEW_MAX_DESCRIPTORS)); i++) {
+ if (HandleTable[i].request == request) {
+ diva_notify_adapter_change (&HandleTable[i], 1); /* About to remove */
+ diva_os_enter_spin_lock (&didd_spin, &irql, "didd_rm");
+ memset (&HandleTable[i], 0x00, sizeof(HandleTable[0]));
+ Adapters--;
+ diva_os_leave_spin_lock (&didd_spin, &irql, "didd_rm");
+ DBG_TRC (("Remove adapter[%d], request=%08x", (i+1), request))
+ return (0);
+ }
+ }
+ DBG_ERR (("Invalid request=%08x, can't remove adapter", request))
+ return (-1);
+}
+/* --------------------------------------------------------------------------
+ Read adapter array
+ return 1 if not enough space to save all available adapters
+ -------------------------------------------------------------------------- */
+static int diva_didd_read_adapter_array (DESCRIPTOR* buffer, int length) {
+ diva_os_spin_lock_magic_t irql;
+ int src, dst;
+ memset (buffer, 0x00, length);
+ length /= sizeof(DESCRIPTOR);
+ DBG_TRC (("DIDD_Read, space = %d, Adapters = %d", length, Adapters+2))
+
+ diva_os_enter_spin_lock (&didd_spin, &irql, "didd_read");
+ for (src = 0, dst = 0;
+ (Adapters && (src < NEW_MAX_DESCRIPTORS) && (dst < length));
+ src++) {
+ if (HandleTable[src].type) {
+ memcpy (&buffer[dst], &HandleTable[src], sizeof(DESCRIPTOR));
+ dst++;
+ }
+ }
+ diva_os_leave_spin_lock (&didd_spin, &irql, "didd_read");
+ if (dst < length) {
+ memcpy (&buffer[dst], &MAdapter, sizeof(DESCRIPTOR));
+ dst++;
+ } else {
+ DBG_ERR (("Can't write DIMAINT. Array too small"))
+ }
+ if (dst < length) {
+ memcpy (&buffer[dst], &DAdapter, sizeof(DESCRIPTOR));
+ dst++;
+ } else {
+ DBG_ERR (("Can't write DADAPTER. Array too small"))
+ }
+ DBG_TRC (("Read %d adapters", dst))
+ return (dst == length);
+}
+/* --------------------------------------------------------------------------
+ DAdapter request function.
+ This function does process only synchronous requests, and is used
+ for reception/registration of new interfaces
+ -------------------------------------------------------------------------- */
+static void IDI_CALL_LINK_T diva_dadapter_request (\
+ ENTITY IDI_CALL_ENTITY_T *e) {
+ IDI_SYNC_REQ *syncReq = (IDI_SYNC_REQ *)e ;
+ if (e->Req) { /* We do not process it, also return error */
+ e->Rc = OUT_OF_RESOURCES;
+ DBG_ERR (("Can't process async request, Req=%02x", e->Req))
+ return;
+ }
+ /*
+ So, we process sync request
+ */
+ switch (e->Rc) {
+ case IDI_SYNC_REQ_DIDD_REGISTER_ADAPTER_NOTIFY: {
+ diva_didd_adapter_notify_t* pinfo = &syncReq->didd_notify.info;
+ pinfo->handle = diva_register_adapter_callback (\
+ (didd_adapter_change_callback_t)pinfo->callback,
+ (void IDI_CALL_ENTITY_T *)pinfo->context);
+ e->Rc = 0xff;
+ } break;
+ case IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER_NOTIFY: {
+ diva_didd_adapter_notify_t* pinfo = &syncReq->didd_notify.info;
+ diva_remove_adapter_callback (pinfo->handle);
+ e->Rc = 0xff;
+ } break;
+ case IDI_SYNC_REQ_DIDD_ADD_ADAPTER: {
+ diva_didd_add_adapter_t* pinfo = &syncReq->didd_add_adapter.info;
+ if (diva_didd_add_descriptor ((DESCRIPTOR*)pinfo->descriptor) < 0) {
+ e->Rc = OUT_OF_RESOURCES;
+ } else {
+ e->Rc = 0xff;
+ }
+ } break;
+ case IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER: {
+ diva_didd_remove_adapter_t* pinfo = &syncReq->didd_remove_adapter.info;
+ if (diva_didd_remove_descriptor ((IDI_CALL)pinfo->p_request) < 0) {
+ e->Rc = OUT_OF_RESOURCES;
+ } else {
+ e->Rc = 0xff;
+ }
+ } break;
+ case IDI_SYNC_REQ_DIDD_READ_ADAPTER_ARRAY: {
+ diva_didd_read_adapter_array_t* pinfo =\
+ &syncReq->didd_read_adapter_array.info;
+ if (diva_didd_read_adapter_array ((DESCRIPTOR*)pinfo->buffer,
+ (int)pinfo->length)) {
+ e->Rc = OUT_OF_RESOURCES;
+ } else {
+ e->Rc = 0xff;
+ }
+ } break;
+ default:
+ DBG_ERR (("Can't process sync request, Req=%02x", e->Rc))
+ e->Rc = OUT_OF_RESOURCES;
+ }
+}
+/* --------------------------------------------------------------------------
+ IDI client does register his notification function
+ -------------------------------------------------------------------------- */
+static dword diva_register_adapter_callback (\
+ didd_adapter_change_callback_t callback,
+ void IDI_CALL_ENTITY_T* context) {
+ diva_os_spin_lock_magic_t irql;
+ dword i;
+
+ for (i = 0; i < DIVA_DIDD_MAX_NOTIFICATIONS; i++) {
+ diva_os_enter_spin_lock (&didd_spin, &irql, "didd_nfy_add");
+ if (!NotificationTable[i].callback) {
+ NotificationTable[i].callback = callback;
+ NotificationTable[i].context = context;
+ diva_os_leave_spin_lock (&didd_spin, &irql, "didd_nfy_add");
+ DBG_TRC(("Register adapter notification[%d]=%08x", i+1, callback))
+ return (i+1);
+ }
+ diva_os_leave_spin_lock (&didd_spin, &irql, "didd_nfy_add");
+ }
+ DBG_ERR (("Can't register adapter notification, overflow"))
+ return (0);
+}
+/* --------------------------------------------------------------------------
+ IDI client does register his notification function
+ -------------------------------------------------------------------------- */
+static void diva_remove_adapter_callback (dword handle) {
+ diva_os_spin_lock_magic_t irql;
+ if (handle && ((--handle) < DIVA_DIDD_MAX_NOTIFICATIONS)) {
+ diva_os_enter_spin_lock (&didd_spin, &irql, "didd_nfy_rm");
+ NotificationTable[handle].callback = NULL;
+ NotificationTable[handle].context = NULL;
+ diva_os_leave_spin_lock (&didd_spin, &irql, "didd_nfy_rm");
+ DBG_TRC(("Remove adapter notification[%d]", (int)(handle+1)))
+ return;
+ }
+ DBG_ERR(("Can't remove adapter notification, handle=%d", handle))
+}
+/* --------------------------------------------------------------------------
+ Notify all client about adapter array change
+ Does suppose following behavior in the client side:
+ Step 1: Redister Notification
+ Step 2: Read Adapter Array
+ -------------------------------------------------------------------------- */
+static void diva_notify_adapter_change (DESCRIPTOR* d, int removal) {
+ int i, do_notify;
+ didd_adapter_change_notification_t nfy;
+ diva_os_spin_lock_magic_t irql;
+ for (i = 0; i < DIVA_DIDD_MAX_NOTIFICATIONS; i++) {
+ do_notify = 0;
+ diva_os_enter_spin_lock (&didd_spin, &irql, "didd_nfy");
+ if (NotificationTable[i].callback) {
+ memcpy (&nfy, &NotificationTable[i], sizeof(nfy));
+ do_notify = 1;
+ }
+ diva_os_leave_spin_lock (&didd_spin, &irql, "didd_nfy");
+ if (do_notify) {
+ (*(nfy.callback))(nfy.context, d, removal);
+ }
+ }
+}
+/* --------------------------------------------------------------------------
+ For all systems, that are linked by Kernel Mode Linker this is ONLY one
+ function thet should be exported by this device driver
+ IDI clients should look for IDI_DADAPTER, and use request function
+ of this adapter (sync request) in order to receive appropriate services:
+ - add new adapter
+ - remove existing adapter
+ - add adapter array notification
+ - remove adapter array notification
+ (read adapter is redundant in this case)
+ INPUT:
+ buffer - pointer to buffer that will receive adapter array
+ length - length (in bytes) of space in buffer
+ OUTPUT:
+ Adapter array will be written to memory described by 'buffer'
+ If the last adapter seen in the returned adapter array is
+ IDI_DADAPTER or if last adapter in array does have type '0', then
+ it was enougth space in buffer to accommodate all available
+ adapter descriptors
+ *NOTE 1 (debug interface):
+ The IDI adapter of type 'IDI_DIMAINT' does register as 'request'
+ famous 'dprintf' function (of type DI_PRINTF, please look
+ include/debuglib.c and include/debuglib.h) for details.
+ So dprintf is not exported from module debug module directly,
+ instead of this IDI_DIMAINT is registered.
+ Module load order will receive in this case:
+ 1. DIDD (this file)
+ 2. DIMAINT does load and register 'IDI_DIMAINT', at this step
+ DIDD should be able to get 'dprintf', save it, and
+ register with DIDD by means of 'dprintf' function.
+ 3. any other driver is loaded and is able to access adapter array
+ and debug interface
+ This approach does allow to load/unload debug interface on demand,
+ and save memory, it it is necessary.
+ -------------------------------------------------------------------------- */
+void IDI_CALL_LINK_T DIVA_DIDD_Read (void IDI_CALL_ENTITY_T * buffer,
+ int length) {
+ diva_didd_read_adapter_array (buffer, length);
+}
+
diff --git a/drivers/isdn/hardware/eicon/dadapter.h b/drivers/isdn/hardware/eicon/dadapter.h
new file mode 100644
index 0000000..3575ac9
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/dadapter.h
@@ -0,0 +1,34 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+#ifndef __DIVA_DIDD_DADAPTER_INC__
+#define __DIVA_DIDD_DADAPTER_INC__
+
+void diva_didd_load_time_init (void);
+void diva_didd_load_time_finit (void);
+
+#define NEW_MAX_DESCRIPTORS 64
+
+#endif
diff --git a/drivers/isdn/hardware/eicon/debug.c b/drivers/isdn/hardware/eicon/debug.c
new file mode 100644
index 0000000..84318ec
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/debug.c
@@ -0,0 +1,2133 @@
+#include "platform.h"
+#include "pc.h"
+#include "di_defs.h"
+#include "debug_if.h"
+#include "divasync.h"
+#include "kst_ifc.h"
+#include "maintidi.h"
+#include "man_defs.h"
+
+/*
+ LOCALS
+ */
+#define DBG_MAGIC (0x47114711L)
+
+static void DI_register (void *arg);
+static void DI_deregister (pDbgHandle hDbg);
+static void DI_format (int do_lock, word id, int type, char *format, va_list argument_list);
+static void DI_format_locked (word id, int type, char *format, va_list argument_list);
+static void DI_format_old (word id, char *format, va_list ap) { }
+static void DiProcessEventLog (unsigned short id, unsigned long msgID, va_list ap) { }
+static void single_p (byte * P, word * PLength, byte Id);
+static void diva_maint_xdi_cb (ENTITY* e);
+static word SuperTraceCreateReadReq (byte* P, const char* path);
+static int diva_mnt_cmp_nmbr (const char* nmbr);
+static void diva_free_dma_descriptor (IDI_CALL request, int nr);
+static int diva_get_dma_descriptor (IDI_CALL request, dword *dma_magic);
+void diva_mnt_internal_dprintf (dword drv_id, dword type, char* p, ...);
+
+static dword MaxDumpSize = 256 ;
+static dword MaxXlogSize = 2 + 128 ;
+static char TraceFilter[DIVA_MAX_SELECTIVE_FILTER_LENGTH+1];
+static int TraceFilterIdent = -1;
+static int TraceFilterChannel = -1;
+
+typedef struct _diva_maint_client {
+ dword sec;
+ dword usec;
+ pDbgHandle hDbg;
+ char drvName[128];
+ dword dbgMask;
+ dword last_dbgMask;
+ IDI_CALL request;
+ _DbgHandle_ Dbg;
+ int logical;
+ int channels;
+ diva_strace_library_interface_t* pIdiLib;
+ BUFFERS XData;
+ char xbuffer[2048+512];
+ byte* pmem;
+ int request_pending;
+ int dma_handle;
+} diva_maint_client_t;
+static diva_maint_client_t clients[MAX_DESCRIPTORS];
+
+static void diva_change_management_debug_mask (diva_maint_client_t* pC, dword old_mask);
+
+static void diva_maint_error (void* user_context,
+ diva_strace_library_interface_t* hLib,
+ int Adapter,
+ int error,
+ const char* file,
+ int line);
+static void diva_maint_state_change_notify (void* user_context,
+ diva_strace_library_interface_t* hLib,
+ int Adapter,
+ diva_trace_line_state_t* channel,
+ int notify_subject);
+static void diva_maint_trace_notify (void* user_context,
+ diva_strace_library_interface_t* hLib,
+ int Adapter,
+ void* xlog_buffer,
+ int length);
+
+
+
+typedef struct MSG_QUEUE {
+ dword Size; /* total size of queue (constant) */
+ byte *Base; /* lowest address (constant) */
+ byte *High; /* Base + Size (constant) */
+ byte *Head; /* first message in queue (if any) */
+ byte *Tail; /* first free position */
+ byte *Wrap; /* current wraparound position */
+ dword Count; /* current no of bytes in queue */
+} MSG_QUEUE;
+
+typedef struct MSG_HEAD {
+ volatile dword Size; /* size of data following MSG_HEAD */
+#define MSG_INCOMPLETE 0x8000 /* ored to Size until queueCompleteMsg */
+} MSG_HEAD;
+
+#define queueCompleteMsg(p) do{ ((MSG_HEAD *)p - 1)->Size &= ~MSG_INCOMPLETE; }while(0)
+#define queueCount(q) ((q)->Count)
+#define MSG_NEED(size) \
+ ( (sizeof(MSG_HEAD) + size + sizeof(dword) - 1) & ~(sizeof(dword) - 1) )
+
+static void queueInit (MSG_QUEUE *Q, byte *Buffer, dword sizeBuffer) {
+ Q->Size = sizeBuffer;
+ Q->Base = Q->Head = Q->Tail = Buffer;
+ Q->High = Buffer + sizeBuffer;
+ Q->Wrap = NULL;
+ Q->Count= 0;
+}
+
+static byte *queueAllocMsg (MSG_QUEUE *Q, word size) {
+ /* Allocate 'size' bytes at tail of queue which will be filled later
+ * directly with callers own message header info and/or message.
+ * An 'alloced' message is marked incomplete by oring the 'Size' field
+ * with MSG_INCOMPLETE.
+ * This must be reset via queueCompleteMsg() after the message is filled.
+ * As long as a message is marked incomplete queuePeekMsg() will return
+ * a 'queue empty' condition when it reaches such a message. */
+
+ MSG_HEAD *Msg;
+ word need = MSG_NEED(size);
+
+ if (Q->Tail == Q->Head) {
+ if (Q->Wrap || need > Q->Size) {
+ return NULL; /* full */
+ }
+ goto alloc; /* empty */
+ }
+
+ if (Q->Tail > Q->Head) {
+ if (Q->Tail + need <= Q->High) goto alloc; /* append */
+ if (Q->Base + need > Q->Head) {
+ return NULL; /* too much */
+ }
+ /* wraparound the queue (but not the message) */
+ Q->Wrap = Q->Tail;
+ Q->Tail = Q->Base;
+ goto alloc;
+ }
+
+ if (Q->Tail + need > Q->Head) {
+ return NULL; /* too much */
+ }
+
+alloc:
+ Msg = (MSG_HEAD *)Q->Tail;
+
+ Msg->Size = size | MSG_INCOMPLETE;
+
+ Q->Tail += need;
+ Q->Count += size;
+
+
+
+ return ((byte*)(Msg + 1));
+}
+
+static void queueFreeMsg (MSG_QUEUE *Q) {
+/* Free the message at head of queue */
+
+ word size = ((MSG_HEAD *)Q->Head)->Size & ~MSG_INCOMPLETE;
+
+ Q->Head += MSG_NEED(size);
+ Q->Count -= size;
+
+ if (Q->Wrap) {
+ if (Q->Head >= Q->Wrap) {
+ Q->Head = Q->Base;
+ Q->Wrap = NULL;
+ }
+ } else if (Q->Head >= Q->Tail) {
+ Q->Head = Q->Tail = Q->Base;
+ }
+}
+
+static byte *queuePeekMsg (MSG_QUEUE *Q, word *size) {
+ /* Show the first valid message in queue BUT DON'T free the message.
+ * After looking on the message contents it can be freed queueFreeMsg()
+ * or simply remain in message queue. */
+
+ MSG_HEAD *Msg = (MSG_HEAD *)Q->Head;
+
+ if (((byte *)Msg == Q->Tail && !Q->Wrap) ||
+ (Msg->Size & MSG_INCOMPLETE)) {
+ return NULL;
+ } else {
+ *size = Msg->Size;
+ return ((byte *)(Msg + 1));
+ }
+}
+
+/*
+ Message queue header
+ */
+static MSG_QUEUE* dbg_queue;
+static byte* dbg_base;
+static int external_dbg_queue;
+static diva_os_spin_lock_t dbg_q_lock;
+static diva_os_spin_lock_t dbg_adapter_lock;
+static int dbg_q_busy;
+static volatile dword dbg_sequence;
+static dword start_sec;
+static dword start_usec;
+
+/*
+ INTERFACE:
+ Initialize run time queue structures.
+ base: base of the message queue
+ length: length of the message queue
+ do_init: perfor queue reset
+
+ return: zero on success, -1 on error
+ */
+int diva_maint_init (byte* base, unsigned long length, int do_init) {
+ if (dbg_queue || (!base) || (length < (4096*4))) {
+ return (-1);
+ }
+
+ TraceFilter[0] = 0;
+ TraceFilterIdent = -1;
+ TraceFilterChannel = -1;
+
+ dbg_base = base;
+
+ diva_os_get_time (&start_sec, &start_usec);
+
+ *(dword*)base = (dword)DBG_MAGIC; /* Store Magic */
+ base += sizeof(dword);
+ length -= sizeof(dword);
+
+ *(dword*)base = 2048; /* Extension Field Length */
+ base += sizeof(dword);
+ length -= sizeof(dword);
+
+ strcpy (base, "KERNEL MODE BUFFER\n");
+ base += 2048;
+ length -= 2048;
+
+ *(dword*)base = 0; /* Terminate extension */
+ base += sizeof(dword);
+ length -= sizeof(dword);
+
+ *(void**)base = (void*)(base+sizeof(void*)); /* Store Base */
+ base += sizeof(void*);
+ length -= sizeof(void*);
+
+ dbg_queue = (MSG_QUEUE*)base;
+ queueInit (dbg_queue, base + sizeof(MSG_QUEUE), length - sizeof(MSG_QUEUE) - 512);
+ external_dbg_queue = 0;
+
+ if (!do_init) {
+ external_dbg_queue = 1; /* memory was located on the external device */
+ }
+
+
+ if (diva_os_initialize_spin_lock (&dbg_q_lock, "dbg_init")) {
+ dbg_queue = NULL;
+ dbg_base = NULL;
+ external_dbg_queue = 0;
+ return (-1);
+ }
+
+ if (diva_os_initialize_spin_lock (&dbg_adapter_lock, "dbg_init")) {
+ diva_os_destroy_spin_lock(&dbg_q_lock, "dbg_init");
+ dbg_queue = NULL;
+ dbg_base = NULL;
+ external_dbg_queue = 0;
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ INTERFACE:
+ Finit at unload time
+ return address of internal queue or zero if queue
+ was external
+ */
+void* diva_maint_finit (void) {
+ void* ret = (void*)dbg_base;
+ int i;
+
+ dbg_queue = NULL;
+ dbg_base = NULL;
+
+ if (ret) {
+ diva_os_destroy_spin_lock(&dbg_q_lock, "dbg_finit");
+ diva_os_destroy_spin_lock(&dbg_adapter_lock, "dbg_finit");
+ }
+
+ if (external_dbg_queue) {
+ ret = NULL;
+ }
+ external_dbg_queue = 0;
+
+ for (i = 1; i < ARRAY_SIZE(clients); i++) {
+ if (clients[i].pmem) {
+ diva_os_free (0, clients[i].pmem);
+ }
+ }
+
+ return (ret);
+}
+
+/*
+ INTERFACE:
+ Return amount of messages in debug queue
+ */
+dword diva_dbg_q_length (void) {
+ return (dbg_queue ? queueCount(dbg_queue) : 0);
+}
+
+/*
+ INTERFACE:
+ Lock message queue and return the pointer to the first
+ entry.
+ */
+diva_dbg_entry_head_t* diva_maint_get_message (word* size,
+ diva_os_spin_lock_magic_t* old_irql) {
+ diva_dbg_entry_head_t* pmsg = NULL;
+
+ diva_os_enter_spin_lock (&dbg_q_lock, old_irql, "read");
+ if (dbg_q_busy) {
+ diva_os_leave_spin_lock (&dbg_q_lock, old_irql, "read_busy");
+ return NULL;
+ }
+ dbg_q_busy = 1;
+
+ if (!(pmsg = (diva_dbg_entry_head_t*)queuePeekMsg (dbg_queue, size))) {
+ dbg_q_busy = 0;
+ diva_os_leave_spin_lock (&dbg_q_lock, old_irql, "read_empty");
+ }
+
+ return (pmsg);
+}
+
+/*
+ INTERFACE:
+ acknowledge last message and unlock queue
+ */
+void diva_maint_ack_message (int do_release,
+ diva_os_spin_lock_magic_t* old_irql) {
+ if (!dbg_q_busy) {
+ return;
+ }
+ if (do_release) {
+ queueFreeMsg (dbg_queue);
+ }
+ dbg_q_busy = 0;
+ diva_os_leave_spin_lock (&dbg_q_lock, old_irql, "read_ack");
+}
+
+
+/*
+ INTERFACE:
+ PRT COMP function used to register
+ with MAINT adapter or log in compatibility
+ mode in case older driver version is connected too
+ */
+void diva_maint_prtComp (char *format, ...) {
+ void *hDbg;
+ va_list ap;
+
+ if (!format)
+ return;
+
+ va_start(ap, format);
+
+ /*
+ register to new log driver functions
+ */
+ if ((format[0] == 0) && ((unsigned char)format[1] == 255)) {
+ hDbg = va_arg(ap, void *); /* ptr to DbgHandle */
+ DI_register (hDbg);
+ }
+
+ va_end (ap);
+}
+
+static void DI_register (void *arg) {
+ diva_os_spin_lock_magic_t old_irql;
+ dword sec, usec;
+ pDbgHandle hDbg ;
+ int id, free_id = -1, best_id = 0;
+
+ diva_os_get_time (&sec, &usec);
+
+ hDbg = (pDbgHandle)arg ;
+ /*
+ Check for bad args, specially for the old obsolete debug handle
+ */
+ if ((hDbg == NULL) ||
+ ((hDbg->id == 0) && (((_OldDbgHandle_ *)hDbg)->id == -1)) ||
+ (hDbg->Registered != 0)) {
+ return ;
+ }
+
+ diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "register");
+
+ for (id = 1; id < ARRAY_SIZE(clients); id++) {
+ if (clients[id].hDbg == hDbg) {
+ /*
+ driver already registered
+ */
+ diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "register");
+ return;
+ }
+ if (clients[id].hDbg) { /* slot is busy */
+ continue;
+ }
+ free_id = id;
+ if (!strcmp (clients[id].drvName, hDbg->drvName)) {
+ /*
+ This driver was already registered with this name
+ and slot is still free - reuse it
+ */
+ best_id = 1;
+ break;
+ }
+ if (!clients[id].hDbg) { /* slot is busy */
+ break;
+ }
+ }
+
+ if (free_id != -1) {
+ diva_dbg_entry_head_t* pmsg = NULL;
+ int len;
+ char tmp[256];
+ word size;
+
+ /*
+ Register new driver with id == free_id
+ */
+ clients[free_id].hDbg = hDbg;
+ clients[free_id].sec = sec;
+ clients[free_id].usec = usec;
+ strcpy (clients[free_id].drvName, hDbg->drvName);
+
+ clients[free_id].dbgMask = hDbg->dbgMask;
+ if (best_id) {
+ hDbg->dbgMask |= clients[free_id].last_dbgMask;
+ } else {
+ clients[free_id].last_dbgMask = 0;
+ }
+
+ hDbg->Registered = DBG_HANDLE_REG_NEW ;
+ hDbg->id = (byte)free_id;
+ hDbg->dbg_end = DI_deregister;
+ hDbg->dbg_prt = DI_format_locked;
+ hDbg->dbg_ev = DiProcessEventLog;
+ hDbg->dbg_irq = DI_format_locked;
+ if (hDbg->Version > 0) {
+ hDbg->dbg_old = DI_format_old;
+ }
+ hDbg->next = (pDbgHandle)DBG_MAGIC;
+
+ /*
+ Log driver register, MAINT driver ID is '0'
+ */
+ len = sprintf (tmp, "DIMAINT - drv # %d = '%s' registered",
+ free_id, hDbg->drvName);
+
+ while (!(pmsg = (diva_dbg_entry_head_t*)queueAllocMsg (dbg_queue,
+ (word)(len+1+sizeof(*pmsg))))) {
+ if ((pmsg = (diva_dbg_entry_head_t*)queuePeekMsg (dbg_queue, &size))) {
+ queueFreeMsg (dbg_queue);
+ } else {
+ break;
+ }
+ }
+
+ if (pmsg) {
+ pmsg->sequence = dbg_sequence++;
+ pmsg->time_sec = sec;
+ pmsg->time_usec = usec;
+ pmsg->facility = MSG_TYPE_STRING;
+ pmsg->dli = DLI_REG;
+ pmsg->drv_id = 0; /* id 0 - DIMAINT */
+ pmsg->di_cpu = 0;
+ pmsg->data_length = len+1;
+
+ memcpy (&pmsg[1], tmp, len+1);
+ queueCompleteMsg (pmsg);
+ diva_maint_wakeup_read();
+ }
+ }
+
+ diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "register");
+}
+
+static void DI_deregister (pDbgHandle hDbg) {
+ diva_os_spin_lock_magic_t old_irql, old_irql1;
+ dword sec, usec;
+ int i;
+ word size;
+ byte* pmem = NULL;
+
+ diva_os_get_time (&sec, &usec);
+
+ diva_os_enter_spin_lock (&dbg_adapter_lock, &old_irql1, "read");
+ diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "read");
+
+ for (i = 1; i < ARRAY_SIZE(clients); i++) {
+ if (clients[i].hDbg == hDbg) {
+ diva_dbg_entry_head_t* pmsg;
+ char tmp[256];
+ int len;
+
+ clients[i].hDbg = NULL;
+
+ hDbg->id = -1;
+ hDbg->dbgMask = 0;
+ hDbg->dbg_end = NULL;
+ hDbg->dbg_prt = NULL;
+ hDbg->dbg_irq = NULL;
+ if (hDbg->Version > 0)
+ hDbg->dbg_old = NULL;
+ hDbg->Registered = 0;
+ hDbg->next = NULL;
+
+ if (clients[i].pIdiLib) {
+ (*(clients[i].pIdiLib->DivaSTraceLibraryFinit))(clients[i].pIdiLib->hLib);
+ clients[i].pIdiLib = NULL;
+
+ pmem = clients[i].pmem;
+ clients[i].pmem = NULL;
+ }
+
+ /*
+ Log driver register, MAINT driver ID is '0'
+ */
+ len = sprintf (tmp, "DIMAINT - drv # %d = '%s' de-registered",
+ i, hDbg->drvName);
+
+ while (!(pmsg = (diva_dbg_entry_head_t*)queueAllocMsg (dbg_queue,
+ (word)(len+1+sizeof(*pmsg))))) {
+ if ((pmsg = (diva_dbg_entry_head_t*)queuePeekMsg (dbg_queue, &size))) {
+ queueFreeMsg (dbg_queue);
+ } else {
+ break;
+ }
+ }
+
+ if (pmsg) {
+ pmsg->sequence = dbg_sequence++;
+ pmsg->time_sec = sec;
+ pmsg->time_usec = usec;
+ pmsg->facility = MSG_TYPE_STRING;
+ pmsg->dli = DLI_REG;
+ pmsg->drv_id = 0; /* id 0 - DIMAINT */
+ pmsg->di_cpu = 0;
+ pmsg->data_length = len+1;
+
+ memcpy (&pmsg[1], tmp, len+1);
+ queueCompleteMsg (pmsg);
+ diva_maint_wakeup_read();
+ }
+
+ break;
+ }
+ }
+
+ diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "read_ack");
+ diva_os_leave_spin_lock (&dbg_adapter_lock, &old_irql1, "read_ack");
+
+ if (pmem) {
+ diva_os_free (0, pmem);
+ }
+}
+
+static void DI_format_locked (unsigned short id,
+ int type,
+ char *format,
+ va_list argument_list) {
+ DI_format (1, id, type, format, argument_list);
+}
+
+static void DI_format (int do_lock,
+ unsigned short id,
+ int type,
+ char *format,
+ va_list ap) {
+ diva_os_spin_lock_magic_t old_irql;
+ dword sec, usec;
+ diva_dbg_entry_head_t* pmsg = NULL;
+ dword length;
+ word size;
+ static char fmtBuf[MSG_FRAME_MAX_SIZE+sizeof(*pmsg)+1];
+ char *data;
+ unsigned short code;
+
+ if (diva_os_in_irq()) {
+ dbg_sequence++;
+ return;
+ }
+
+ if ((!format) ||
+ ((TraceFilter[0] != 0) && ((TraceFilterIdent < 0) || (TraceFilterChannel < 0)))) {
+ return;
+ }
+
+
+
+ diva_os_get_time (&sec, &usec);
+
+ if (do_lock) {
+ diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "format");
+ }
+
+ switch (type) {
+ case DLI_MXLOG :
+ case DLI_BLK :
+ case DLI_SEND:
+ case DLI_RECV:
+ if (!(length = va_arg(ap, unsigned long))) {
+ break;
+ }
+ if (length > MaxDumpSize) {
+ length = MaxDumpSize;
+ }
+ while (!(pmsg = (diva_dbg_entry_head_t*)queueAllocMsg (dbg_queue,
+ (word)length+sizeof(*pmsg)))) {
+ if ((pmsg = (diva_dbg_entry_head_t*)queuePeekMsg (dbg_queue, &size))) {
+ queueFreeMsg (dbg_queue);
+ } else {
+ break;
+ }
+ }
+ if (pmsg) {
+ memcpy (&pmsg[1], format, length);
+ pmsg->sequence = dbg_sequence++;
+ pmsg->time_sec = sec;
+ pmsg->time_usec = usec;
+ pmsg->facility = MSG_TYPE_BINARY ;
+ pmsg->dli = type; /* DLI_XXX */
+ pmsg->drv_id = id; /* driver MAINT id */
+ pmsg->di_cpu = 0;
+ pmsg->data_length = length;
+ queueCompleteMsg (pmsg);
+ }
+ break;
+
+ case DLI_XLOG: {
+ byte* p;
+ data = va_arg(ap, char*);
+ code = (unsigned short)va_arg(ap, unsigned int);
+ length = (unsigned long) va_arg(ap, unsigned int);
+
+ if (length > MaxXlogSize)
+ length = MaxXlogSize;
+
+ while (!(pmsg = (diva_dbg_entry_head_t*)queueAllocMsg (dbg_queue,
+ (word)length+sizeof(*pmsg)+2))) {
+ if ((pmsg = (diva_dbg_entry_head_t*)queuePeekMsg (dbg_queue, &size))) {
+ queueFreeMsg (dbg_queue);
+ } else {
+ break;
+ }
+ }
+ if (pmsg) {
+ p = (byte*)&pmsg[1];
+ p[0] = (char)(code) ;
+ p[1] = (char)(code >> 8) ;
+ if (data && length) {
+ memcpy (&p[2], &data[0], length) ;
+ }
+ length += 2 ;
+
+ pmsg->sequence = dbg_sequence++;
+ pmsg->time_sec = sec;
+ pmsg->time_usec = usec;
+ pmsg->facility = MSG_TYPE_BINARY ;
+ pmsg->dli = type; /* DLI_XXX */
+ pmsg->drv_id = id; /* driver MAINT id */
+ pmsg->di_cpu = 0;
+ pmsg->data_length = length;
+ queueCompleteMsg (pmsg);
+ }
+ } break;
+
+ case DLI_LOG :
+ case DLI_FTL :
+ case DLI_ERR :
+ case DLI_TRC :
+ case DLI_REG :
+ case DLI_MEM :
+ case DLI_SPL :
+ case DLI_IRP :
+ case DLI_TIM :
+ case DLI_TAPI:
+ case DLI_NDIS:
+ case DLI_CONN:
+ case DLI_STAT:
+ case DLI_PRV0:
+ case DLI_PRV1:
+ case DLI_PRV2:
+ case DLI_PRV3:
+ if ((length = (unsigned long)vsprintf (&fmtBuf[0], format, ap)) > 0) {
+ length += (sizeof(*pmsg)+1);
+
+ while (!(pmsg = (diva_dbg_entry_head_t*)queueAllocMsg (dbg_queue,
+ (word)length))) {
+ if ((pmsg = (diva_dbg_entry_head_t*)queuePeekMsg (dbg_queue, &size))) {
+ queueFreeMsg (dbg_queue);
+ } else {
+ break;
+ }
+ }
+
+ pmsg->sequence = dbg_sequence++;
+ pmsg->time_sec = sec;
+ pmsg->time_usec = usec;
+ pmsg->facility = MSG_TYPE_STRING;
+ pmsg->dli = type; /* DLI_XXX */
+ pmsg->drv_id = id; /* driver MAINT id */
+ pmsg->di_cpu = 0;
+ pmsg->data_length = length - sizeof(*pmsg);
+
+ memcpy (&pmsg[1], fmtBuf, pmsg->data_length);
+ queueCompleteMsg (pmsg);
+ }
+ break;
+
+ } /* switch type */
+
+
+ if (queueCount(dbg_queue)) {
+ diva_maint_wakeup_read();
+ }
+
+ if (do_lock) {
+ diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "format");
+ }
+}
+
+/*
+ Write driver ID and driver revision to callers buffer
+ */
+int diva_get_driver_info (dword id, byte* data, int data_length) {
+ diva_os_spin_lock_magic_t old_irql;
+ byte* p = data;
+ int to_copy;
+
+ if (!data || !id || (data_length < 17) ||
+ (id >= ARRAY_SIZE(clients))) {
+ return (-1);
+ }
+
+ diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "driver info");
+
+ if (clients[id].hDbg) {
+ *p++ = 1;
+ *p++ = (byte)clients[id].sec; /* save seconds */
+ *p++ = (byte)(clients[id].sec >> 8);
+ *p++ = (byte)(clients[id].sec >> 16);
+ *p++ = (byte)(clients[id].sec >> 24);
+
+ *p++ = (byte)(clients[id].usec/1000); /* save mseconds */
+ *p++ = (byte)((clients[id].usec/1000) >> 8);
+ *p++ = (byte)((clients[id].usec/1000) >> 16);
+ *p++ = (byte)((clients[id].usec/1000) >> 24);
+
+ data_length -= 9;
+
+ if ((to_copy = min(strlen(clients[id].drvName), (size_t)(data_length-1)))) {
+ memcpy (p, clients[id].drvName, to_copy);
+ p += to_copy;
+ data_length -= to_copy;
+ if ((data_length >= 4) && clients[id].hDbg->drvTag[0]) {
+ *p++ = '(';
+ data_length -= 1;
+ if ((to_copy = min(strlen(clients[id].hDbg->drvTag), (size_t)(data_length-2)))) {
+ memcpy (p, clients[id].hDbg->drvTag, to_copy);
+ p += to_copy;
+ data_length -= to_copy;
+ if (data_length >= 2) {
+ *p++ = ')';
+ data_length--;
+ }
+ }
+ }
+ }
+ }
+ *p++ = 0;
+
+ diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "driver info");
+
+ return (p - data);
+}
+
+int diva_get_driver_dbg_mask (dword id, byte* data) {
+ diva_os_spin_lock_magic_t old_irql;
+ int ret = -1;
+
+ if (!data || !id || (id >= ARRAY_SIZE(clients))) {
+ return (-1);
+ }
+ diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "driver info");
+
+ if (clients[id].hDbg) {
+ ret = 4;
+ *data++= (byte)(clients[id].hDbg->dbgMask);
+ *data++= (byte)(clients[id].hDbg->dbgMask >> 8);
+ *data++= (byte)(clients[id].hDbg->dbgMask >> 16);
+ *data++= (byte)(clients[id].hDbg->dbgMask >> 24);
+ }
+
+ diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "driver info");
+
+ return (ret);
+}
+
+int diva_set_driver_dbg_mask (dword id, dword mask) {
+ diva_os_spin_lock_magic_t old_irql, old_irql1;
+ int ret = -1;
+
+
+ if (!id || (id >= ARRAY_SIZE(clients))) {
+ return (-1);
+ }
+
+ diva_os_enter_spin_lock (&dbg_adapter_lock, &old_irql1, "dbg mask");
+ diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "dbg mask");
+
+ if (clients[id].hDbg) {
+ dword old_mask = clients[id].hDbg->dbgMask;
+ mask &= 0x7fffffff;
+ clients[id].hDbg->dbgMask = mask;
+ clients[id].last_dbgMask = (clients[id].hDbg->dbgMask | clients[id].dbgMask);
+ ret = 4;
+ diva_change_management_debug_mask (&clients[id], old_mask);
+ }
+
+
+ diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "dbg mask");
+
+ if (clients[id].request_pending) {
+ clients[id].request_pending = 0;
+ (*(clients[id].request))((ENTITY*)(*(clients[id].pIdiLib->DivaSTraceGetHandle))(clients[id].pIdiLib->hLib));
+ }
+
+ diva_os_leave_spin_lock (&dbg_adapter_lock, &old_irql1, "dbg mask");
+
+ return (ret);
+}
+
+static int diva_get_idi_adapter_info (IDI_CALL request, dword* serial, dword* logical) {
+ IDI_SYNC_REQ sync_req;
+
+ sync_req.xdi_logical_adapter_number.Req = 0;
+ sync_req.xdi_logical_adapter_number.Rc = IDI_SYNC_REQ_XDI_GET_LOGICAL_ADAPTER_NUMBER;
+ (*request)((ENTITY *)&sync_req);
+ *logical = sync_req.xdi_logical_adapter_number.info.logical_adapter_number;
+
+ sync_req.GetSerial.Req = 0;
+ sync_req.GetSerial.Rc = IDI_SYNC_REQ_GET_SERIAL;
+ sync_req.GetSerial.serial = 0;
+ (*request)((ENTITY *)&sync_req);
+ *serial = sync_req.GetSerial.serial;
+
+ return (0);
+}
+
+/*
+ Register XDI adapter as MAINT compatible driver
+ */
+void diva_mnt_add_xdi_adapter (const DESCRIPTOR* d) {
+ diva_os_spin_lock_magic_t old_irql, old_irql1;
+ dword sec, usec, logical, serial, org_mask;
+ int id, best_id = 0, free_id = -1;
+ char tmp[256];
+ diva_dbg_entry_head_t* pmsg = NULL;
+ int len;
+ word size;
+ byte* pmem;
+
+ diva_os_get_time (&sec, &usec);
+ diva_get_idi_adapter_info (d->request, &serial, &logical);
+ if (serial & 0xff000000) {
+ sprintf (tmp, "ADAPTER:%d SN:%u-%d",
+ (int)logical,
+ serial & 0x00ffffff,
+ (byte)(((serial & 0xff000000) >> 24) + 1));
+ } else {
+ sprintf (tmp, "ADAPTER:%d SN:%u", (int)logical, serial);
+ }
+
+ if (!(pmem = diva_os_malloc (0, DivaSTraceGetMemotyRequirement (d->channels)))) {
+ return;
+ }
+ memset (pmem, 0x00, DivaSTraceGetMemotyRequirement (d->channels));
+
+ diva_os_enter_spin_lock (&dbg_adapter_lock, &old_irql1, "register");
+ diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "register");
+
+ for (id = 1; id < ARRAY_SIZE(clients); id++) {
+ if (clients[id].hDbg && (clients[id].request == d->request)) {
+ diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "register");
+ diva_os_leave_spin_lock (&dbg_adapter_lock, &old_irql1, "register");
+ diva_os_free(0, pmem);
+ return;
+ }
+ if (clients[id].hDbg) { /* slot is busy */
+ continue;
+ }
+ if (free_id < 0) {
+ free_id = id;
+ }
+ if (!strcmp (clients[id].drvName, tmp)) {
+ /*
+ This driver was already registered with this name
+ and slot is still free - reuse it
+ */
+ free_id = id;
+ best_id = 1;
+ break;
+ }
+ }
+
+ if (free_id < 0) {
+ diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "register");
+ diva_os_leave_spin_lock (&dbg_adapter_lock, &old_irql1, "register");
+ diva_os_free (0, pmem);
+ return;
+ }
+
+ id = free_id;
+ clients[id].request = d->request;
+ clients[id].request_pending = 0;
+ clients[id].hDbg = &clients[id].Dbg;
+ clients[id].sec = sec;
+ clients[id].usec = usec;
+ strcpy (clients[id].drvName, tmp);
+ strcpy (clients[id].Dbg.drvName, tmp);
+ clients[id].Dbg.drvTag[0] = 0;
+ clients[id].logical = (int)logical;
+ clients[id].channels = (int)d->channels;
+ clients[id].dma_handle = -1;
+
+ clients[id].Dbg.dbgMask = 0;
+ clients[id].dbgMask = clients[id].Dbg.dbgMask;
+ if (id) {
+ clients[id].Dbg.dbgMask |= clients[free_id].last_dbgMask;
+ } else {
+ clients[id].last_dbgMask = 0;
+ }
+ clients[id].Dbg.Registered = DBG_HANDLE_REG_NEW;
+ clients[id].Dbg.id = (byte)id;
+ clients[id].Dbg.dbg_end = DI_deregister;
+ clients[id].Dbg.dbg_prt = DI_format_locked;
+ clients[id].Dbg.dbg_ev = DiProcessEventLog;
+ clients[id].Dbg.dbg_irq = DI_format_locked;
+ clients[id].Dbg.next = (pDbgHandle)DBG_MAGIC;
+
+ {
+ diva_trace_library_user_interface_t diva_maint_user_ifc = { &clients[id],
+ diva_maint_state_change_notify,
+ diva_maint_trace_notify,
+ diva_maint_error };
+
+ /*
+ Attach to adapter management interface
+ */
+ if ((clients[id].pIdiLib =
+ DivaSTraceLibraryCreateInstance ((int)logical, &diva_maint_user_ifc, pmem))) {
+ if (((*(clients[id].pIdiLib->DivaSTraceLibraryStart))(clients[id].pIdiLib->hLib))) {
+ diva_mnt_internal_dprintf (0, DLI_ERR, "Adapter(%d) Start failed", (int)logical);
+ (*(clients[id].pIdiLib->DivaSTraceLibraryFinit))(clients[id].pIdiLib->hLib);
+ clients[id].pIdiLib = NULL;
+ }
+ } else {
+ diva_mnt_internal_dprintf (0, DLI_ERR, "A(%d) management init failed", (int)logical);
+ }
+ }
+
+ if (!clients[id].pIdiLib) {
+ clients[id].request = NULL;
+ clients[id].request_pending = 0;
+ clients[id].hDbg = NULL;
+ diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "register");
+ diva_os_leave_spin_lock (&dbg_adapter_lock, &old_irql1, "register");
+ diva_os_free (0, pmem);
+ return;
+ }
+
+ /*
+ Log driver register, MAINT driver ID is '0'
+ */
+ len = sprintf (tmp, "DIMAINT - drv # %d = '%s' registered",
+ id, clients[id].Dbg.drvName);
+
+ while (!(pmsg = (diva_dbg_entry_head_t*)queueAllocMsg (dbg_queue,
+ (word)(len+1+sizeof(*pmsg))))) {
+ if ((pmsg = (diva_dbg_entry_head_t*)queuePeekMsg (dbg_queue, &size))) {
+ queueFreeMsg (dbg_queue);
+ } else {
+ break;
+ }
+ }
+
+ if (pmsg) {
+ pmsg->sequence = dbg_sequence++;
+ pmsg->time_sec = sec;
+ pmsg->time_usec = usec;
+ pmsg->facility = MSG_TYPE_STRING;
+ pmsg->dli = DLI_REG;
+ pmsg->drv_id = 0; /* id 0 - DIMAINT */
+ pmsg->di_cpu = 0;
+ pmsg->data_length = len+1;
+
+ memcpy (&pmsg[1], tmp, len+1);
+ queueCompleteMsg (pmsg);
+ diva_maint_wakeup_read();
+ }
+
+ org_mask = clients[id].Dbg.dbgMask;
+ clients[id].Dbg.dbgMask = 0;
+
+ diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "register");
+
+ if (clients[id].request_pending) {
+ clients[id].request_pending = 0;
+ (*(clients[id].request))((ENTITY*)(*(clients[id].pIdiLib->DivaSTraceGetHandle))(clients[id].pIdiLib->hLib));
+ }
+
+ diva_os_leave_spin_lock (&dbg_adapter_lock, &old_irql1, "register");
+
+ diva_set_driver_dbg_mask (id, org_mask);
+}
+
+/*
+ De-Register XDI adapter
+ */
+void diva_mnt_remove_xdi_adapter (const DESCRIPTOR* d) {
+ diva_os_spin_lock_magic_t old_irql, old_irql1;
+ dword sec, usec;
+ int i;
+ word size;
+ byte* pmem = NULL;
+
+ diva_os_get_time (&sec, &usec);
+
+ diva_os_enter_spin_lock (&dbg_adapter_lock, &old_irql1, "read");
+ diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "read");
+
+ for (i = 1; i < ARRAY_SIZE(clients); i++) {
+ if (clients[i].hDbg && (clients[i].request == d->request)) {
+ diva_dbg_entry_head_t* pmsg;
+ char tmp[256];
+ int len;
+
+ if (clients[i].pIdiLib) {
+ (*(clients[i].pIdiLib->DivaSTraceLibraryFinit))(clients[i].pIdiLib->hLib);
+ clients[i].pIdiLib = NULL;
+
+ pmem = clients[i].pmem;
+ clients[i].pmem = NULL;
+ }
+
+ clients[i].hDbg = NULL;
+ clients[i].request_pending = 0;
+ if (clients[i].dma_handle >= 0) {
+ /*
+ Free DMA handle
+ */
+ diva_free_dma_descriptor (clients[i].request, clients[i].dma_handle);
+ clients[i].dma_handle = -1;
+ }
+ clients[i].request = NULL;
+
+ /*
+ Log driver register, MAINT driver ID is '0'
+ */
+ len = sprintf (tmp, "DIMAINT - drv # %d = '%s' de-registered",
+ i, clients[i].Dbg.drvName);
+
+ memset (&clients[i].Dbg, 0x00, sizeof(clients[i].Dbg));
+
+ while (!(pmsg = (diva_dbg_entry_head_t*)queueAllocMsg (dbg_queue,
+ (word)(len+1+sizeof(*pmsg))))) {
+ if ((pmsg = (diva_dbg_entry_head_t*)queuePeekMsg (dbg_queue, &size))) {
+ queueFreeMsg (dbg_queue);
+ } else {
+ break;
+ }
+ }
+
+ if (pmsg) {
+ pmsg->sequence = dbg_sequence++;
+ pmsg->time_sec = sec;
+ pmsg->time_usec = usec;
+ pmsg->facility = MSG_TYPE_STRING;
+ pmsg->dli = DLI_REG;
+ pmsg->drv_id = 0; /* id 0 - DIMAINT */
+ pmsg->di_cpu = 0;
+ pmsg->data_length = len+1;
+
+ memcpy (&pmsg[1], tmp, len+1);
+ queueCompleteMsg (pmsg);
+ diva_maint_wakeup_read();
+ }
+
+ break;
+ }
+ }
+
+ diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "read_ack");
+ diva_os_leave_spin_lock (&dbg_adapter_lock, &old_irql1, "read_ack");
+
+ if (pmem) {
+ diva_os_free (0, pmem);
+ }
+}
+
+/* ----------------------------------------------------------------
+ Low level interface for management interface client
+ ---------------------------------------------------------------- */
+/*
+ Return handle to client structure
+ */
+void* SuperTraceOpenAdapter (int AdapterNumber) {
+ int i;
+
+ for (i = 1; i < ARRAY_SIZE(clients); i++) {
+ if (clients[i].hDbg && clients[i].request && (clients[i].logical == AdapterNumber)) {
+ return (&clients[i]);
+ }
+ }
+
+ return NULL;
+}
+
+int SuperTraceCloseAdapter (void* AdapterHandle) {
+ return (0);
+}
+
+int SuperTraceReadRequest (void* AdapterHandle, const char* name, byte* data) {
+ diva_maint_client_t* pC = (diva_maint_client_t*)AdapterHandle;
+
+ if (pC && pC->pIdiLib && pC->request) {
+ ENTITY* e = (ENTITY*)(*(pC->pIdiLib->DivaSTraceGetHandle))(pC->pIdiLib->hLib);
+ byte* xdata = (byte*)&pC->xbuffer[0];
+ char tmp = 0;
+ word length;
+
+ if (!strcmp(name, "\\")) { /* Read ROOT */
+ name = &tmp;
+ }
+ length = SuperTraceCreateReadReq (xdata, name);
+ single_p (xdata, &length, 0); /* End Of Message */
+
+ e->Req = MAN_READ;
+ e->ReqCh = 0;
+ e->X->PLength = length;
+ e->X->P = (byte*)xdata;
+
+ pC->request_pending = 1;
+
+ return (0);
+ }
+
+ return (-1);
+}
+
+int SuperTraceGetNumberOfChannels (void* AdapterHandle) {
+ if (AdapterHandle) {
+ diva_maint_client_t* pC = (diva_maint_client_t*)AdapterHandle;
+
+ return (pC->channels);
+ }
+
+ return (0);
+}
+
+int SuperTraceASSIGN (void* AdapterHandle, byte* data) {
+ diva_maint_client_t* pC = (diva_maint_client_t*)AdapterHandle;
+
+ if (pC && pC->pIdiLib && pC->request) {
+ ENTITY* e = (ENTITY*)(*(pC->pIdiLib->DivaSTraceGetHandle))(pC->pIdiLib->hLib);
+ IDI_SYNC_REQ* preq;
+ char buffer[((sizeof(preq->xdi_extended_features)+4) > sizeof(ENTITY)) ? (sizeof(preq->xdi_extended_features)+4) : sizeof(ENTITY)];
+ char features[4];
+ word assign_data_length = 1;
+
+ features[0] = 0;
+ pC->xbuffer[0] = 0;
+ preq = (IDI_SYNC_REQ*)&buffer[0];
+ preq->xdi_extended_features.Req = 0;
+ preq->xdi_extended_features.Rc = IDI_SYNC_REQ_XDI_GET_EXTENDED_FEATURES;
+ preq->xdi_extended_features.info.buffer_length_in_bytes = sizeof(features);
+ preq->xdi_extended_features.info.features = &features[0];
+
+ (*(pC->request))((ENTITY*)preq);
+
+ if ((features[0] & DIVA_XDI_EXTENDED_FEATURES_VALID) &&
+ (features[0] & DIVA_XDI_EXTENDED_FEATURE_MANAGEMENT_DMA)) {
+ dword uninitialized_var(rx_dma_magic);
+ if ((pC->dma_handle = diva_get_dma_descriptor (pC->request, &rx_dma_magic)) >= 0) {
+ pC->xbuffer[0] = LLI;
+ pC->xbuffer[1] = 8;
+ pC->xbuffer[2] = 0x40;
+ pC->xbuffer[3] = (byte)pC->dma_handle;
+ pC->xbuffer[4] = (byte)rx_dma_magic;
+ pC->xbuffer[5] = (byte)(rx_dma_magic >> 8);
+ pC->xbuffer[6] = (byte)(rx_dma_magic >> 16);
+ pC->xbuffer[7] = (byte)(rx_dma_magic >> 24);
+ pC->xbuffer[8] = (byte)DIVA_MAX_MANAGEMENT_TRANSFER_SIZE;
+ pC->xbuffer[9] = (byte)(DIVA_MAX_MANAGEMENT_TRANSFER_SIZE >> 8);
+ pC->xbuffer[10] = 0;
+
+ assign_data_length = 11;
+ }
+ } else {
+ pC->dma_handle = -1;
+ }
+
+ e->Id = MAN_ID;
+ e->callback = diva_maint_xdi_cb;
+ e->XNum = 1;
+ e->X = &pC->XData;
+ e->Req = ASSIGN;
+ e->ReqCh = 0;
+ e->X->PLength = assign_data_length;
+ e->X->P = (byte*)&pC->xbuffer[0];
+
+ pC->request_pending = 1;
+
+ return (0);
+ }
+
+ return (-1);
+}
+
+int SuperTraceREMOVE (void* AdapterHandle) {
+ diva_maint_client_t* pC = (diva_maint_client_t*)AdapterHandle;
+
+ if (pC && pC->pIdiLib && pC->request) {
+ ENTITY* e = (ENTITY*)(*(pC->pIdiLib->DivaSTraceGetHandle))(pC->pIdiLib->hLib);
+
+ e->XNum = 1;
+ e->X = &pC->XData;
+ e->Req = REMOVE;
+ e->ReqCh = 0;
+ e->X->PLength = 1;
+ e->X->P = (byte*)&pC->xbuffer[0];
+ pC->xbuffer[0] = 0;
+
+ pC->request_pending = 1;
+
+ return (0);
+ }
+
+ return (-1);
+}
+
+int SuperTraceTraceOnRequest(void* hAdapter, const char* name, byte* data) {
+ diva_maint_client_t* pC = (diva_maint_client_t*)hAdapter;
+
+ if (pC && pC->pIdiLib && pC->request) {
+ ENTITY* e = (ENTITY*)(*(pC->pIdiLib->DivaSTraceGetHandle))(pC->pIdiLib->hLib);
+ byte* xdata = (byte*)&pC->xbuffer[0];
+ char tmp = 0;
+ word length;
+
+ if (!strcmp(name, "\\")) { /* Read ROOT */
+ name = &tmp;
+ }
+ length = SuperTraceCreateReadReq (xdata, name);
+ single_p (xdata, &length, 0); /* End Of Message */
+ e->Req = MAN_EVENT_ON;
+ e->ReqCh = 0;
+ e->X->PLength = length;
+ e->X->P = (byte*)xdata;
+
+ pC->request_pending = 1;
+
+ return (0);
+ }
+
+ return (-1);
+}
+
+int SuperTraceWriteVar (void* AdapterHandle,
+ byte* data,
+ const char* name,
+ void* var,
+ byte type,
+ byte var_length) {
+ diva_maint_client_t* pC = (diva_maint_client_t*)AdapterHandle;
+
+ if (pC && pC->pIdiLib && pC->request) {
+ ENTITY* e = (ENTITY*)(*(pC->pIdiLib->DivaSTraceGetHandle))(pC->pIdiLib->hLib);
+ diva_man_var_header_t* pVar = (diva_man_var_header_t*)&pC->xbuffer[0];
+ word length = SuperTraceCreateReadReq ((byte*)pVar, name);
+
+ memcpy (&pC->xbuffer[length], var, var_length);
+ length += var_length;
+ pVar->length += var_length;
+ pVar->value_length = var_length;
+ pVar->type = type;
+ single_p ((byte*)pVar, &length, 0); /* End Of Message */
+
+ e->Req = MAN_WRITE;
+ e->ReqCh = 0;
+ e->X->PLength = length;
+ e->X->P = (byte*)pVar;
+
+ pC->request_pending = 1;
+
+ return (0);
+ }
+
+ return (-1);
+}
+
+int SuperTraceExecuteRequest (void* AdapterHandle,
+ const char* name,
+ byte* data) {
+ diva_maint_client_t* pC = (diva_maint_client_t*)AdapterHandle;
+
+ if (pC && pC->pIdiLib && pC->request) {
+ ENTITY* e = (ENTITY*)(*(pC->pIdiLib->DivaSTraceGetHandle))(pC->pIdiLib->hLib);
+ byte* xdata = (byte*)&pC->xbuffer[0];
+ word length;
+
+ length = SuperTraceCreateReadReq (xdata, name);
+ single_p (xdata, &length, 0); /* End Of Message */
+
+ e->Req = MAN_EXECUTE;
+ e->ReqCh = 0;
+ e->X->PLength = length;
+ e->X->P = (byte*)xdata;
+
+ pC->request_pending = 1;
+
+ return (0);
+ }
+
+ return (-1);
+}
+
+static word SuperTraceCreateReadReq (byte* P, const char* path) {
+ byte var_length;
+ byte* plen;
+
+ var_length = (byte)strlen (path);
+
+ *P++ = ESC;
+ plen = P++;
+ *P++ = 0x80; /* MAN_IE */
+ *P++ = 0x00; /* Type */
+ *P++ = 0x00; /* Attribute */
+ *P++ = 0x00; /* Status */
+ *P++ = 0x00; /* Variable Length */
+ *P++ = var_length;
+ memcpy (P, path, var_length);
+ P += var_length;
+ *plen = var_length + 0x06;
+
+ return ((word)(var_length + 0x08));
+}
+
+static void single_p (byte * P, word * PLength, byte Id) {
+ P[(*PLength)++] = Id;
+}
+
+static void diva_maint_xdi_cb (ENTITY* e) {
+ diva_strace_context_t* pLib = DIVAS_CONTAINING_RECORD(e,diva_strace_context_t,e);
+ diva_maint_client_t* pC;
+ diva_os_spin_lock_magic_t old_irql, old_irql1;
+
+
+ diva_os_enter_spin_lock (&dbg_adapter_lock, &old_irql1, "xdi_cb");
+ diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "xdi_cb");
+
+ pC = (diva_maint_client_t*)pLib->hAdapter;
+
+ if ((e->complete == 255) || (pC->dma_handle < 0)) {
+ if ((*(pLib->instance.DivaSTraceMessageInput))(&pLib->instance)) {
+ diva_mnt_internal_dprintf (0, DLI_ERR, "Trace internal library error");
+ }
+ } else {
+ /*
+ Process combined management interface indication
+ */
+ if ((*(pLib->instance.DivaSTraceMessageInput))(&pLib->instance)) {
+ diva_mnt_internal_dprintf (0, DLI_ERR, "Trace internal library error (DMA mode)");
+ }
+ }
+
+ diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "xdi_cb");
+
+
+ if (pC->request_pending) {
+ pC->request_pending = 0;
+ (*(pC->request))(e);
+ }
+
+ diva_os_leave_spin_lock (&dbg_adapter_lock, &old_irql1, "xdi_cb");
+}
+
+
+static void diva_maint_error (void* user_context,
+ diva_strace_library_interface_t* hLib,
+ int Adapter,
+ int error,
+ const char* file,
+ int line) {
+ diva_mnt_internal_dprintf (0, DLI_ERR,
+ "Trace library error(%d) A(%d) %s %d", error, Adapter, file, line);
+}
+
+static void print_ie (diva_trace_ie_t* ie, char* buffer, int length) {
+ int i;
+
+ buffer[0] = 0;
+
+ if (length > 32) {
+ for (i = 0; ((i < ie->length) && (length > 3)); i++) {
+ sprintf (buffer, "%02x", ie->data[i]);
+ buffer += 2;
+ length -= 2;
+ if (i < (ie->length-1)) {
+ strcpy (buffer, " ");
+ buffer++;
+ length--;
+ }
+ }
+ }
+}
+
+static void diva_maint_state_change_notify (void* user_context,
+ diva_strace_library_interface_t* hLib,
+ int Adapter,
+ diva_trace_line_state_t* channel,
+ int notify_subject) {
+ diva_maint_client_t* pC = (diva_maint_client_t*)user_context;
+ diva_trace_fax_state_t* fax = &channel->fax;
+ diva_trace_modem_state_t* modem = &channel->modem;
+ char tmp[256];
+
+ if (!pC->hDbg) {
+ return;
+ }
+
+ switch (notify_subject) {
+ case DIVA_SUPER_TRACE_NOTIFY_LINE_CHANGE: {
+ int view = (TraceFilter[0] == 0);
+ /*
+ Process selective Trace
+ */
+ if (channel->Line[0] == 'I' && channel->Line[1] == 'd' &&
+ channel->Line[2] == 'l' && channel->Line[3] == 'e') {
+ if ((TraceFilterIdent == pC->hDbg->id) && (TraceFilterChannel == (int)channel->ChannelNumber)) {
+ (*(hLib->DivaSTraceSetBChannel))(hLib, (int)channel->ChannelNumber, 0);
+ (*(hLib->DivaSTraceSetAudioTap))(hLib, (int)channel->ChannelNumber, 0);
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG, "Selective Trace OFF for Ch=%d",
+ (int)channel->ChannelNumber);
+ TraceFilterIdent = -1;
+ TraceFilterChannel = -1;
+ view = 1;
+ }
+ } else if (TraceFilter[0] && (TraceFilterIdent < 0) && !(diva_mnt_cmp_nmbr (&channel->RemoteAddress[0]) &&
+ diva_mnt_cmp_nmbr (&channel->LocalAddress[0]))) {
+
+ if ((pC->hDbg->dbgMask & DIVA_MGT_DBG_IFC_BCHANNEL) != 0) { /* Activate B-channel trace */
+ (*(hLib->DivaSTraceSetBChannel))(hLib, (int)channel->ChannelNumber, 1);
+ }
+ if ((pC->hDbg->dbgMask & DIVA_MGT_DBG_IFC_AUDIO) != 0) { /* Activate AudioTap Trace */
+ (*(hLib->DivaSTraceSetAudioTap))(hLib, (int)channel->ChannelNumber, 1);
+ }
+
+ TraceFilterIdent = pC->hDbg->id;
+ TraceFilterChannel = (int)channel->ChannelNumber;
+
+ if (TraceFilterIdent >= 0) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG, "Selective Trace ON for Ch=%d",
+ (int)channel->ChannelNumber);
+ view = 1;
+ }
+ }
+ if (view && (pC->hDbg->dbgMask & DIVA_MGT_DBG_LINE_EVENTS)) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT, "L Ch = %d",
+ (int)channel->ChannelNumber);
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT, "L Status = <%s>", &channel->Line[0]);
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT, "L Layer1 = <%s>", &channel->Framing[0]);
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT, "L Layer2 = <%s>", &channel->Layer2[0]);
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT, "L Layer3 = <%s>", &channel->Layer3[0]);
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT, "L RAddr = <%s>",
+ &channel->RemoteAddress[0]);
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT, "L RSAddr = <%s>",
+ &channel->RemoteSubAddress[0]);
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT, "L LAddr = <%s>",
+ &channel->LocalAddress[0]);
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT, "L LSAddr = <%s>",
+ &channel->LocalSubAddress[0]);
+ print_ie(&channel->call_BC, tmp, sizeof(tmp));
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT, "L BC = <%s>", tmp);
+ print_ie(&channel->call_HLC, tmp, sizeof(tmp));
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT, "L HLC = <%s>", tmp);
+ print_ie(&channel->call_LLC, tmp, sizeof(tmp));
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT, "L LLC = <%s>", tmp);
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT, "L CR = 0x%x", channel->CallReference);
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT, "L Disc = 0x%x",
+ channel->LastDisconnecCause);
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT, "L Owner = <%s>", &channel->UserID[0]);
+ }
+
+ } break;
+
+ case DIVA_SUPER_TRACE_NOTIFY_MODEM_CHANGE:
+ if (pC->hDbg->dbgMask & DIVA_MGT_DBG_MDM_PROGRESS) {
+ {
+ int ch = TraceFilterChannel;
+ int id = TraceFilterIdent;
+
+ if ((id >= 0) && (ch >= 0) && (id < ARRAY_SIZE(clients)) &&
+ (clients[id].Dbg.id == (byte)id) && (clients[id].pIdiLib == hLib)) {
+ if (ch != (int)modem->ChannelNumber) {
+ break;
+ }
+ } else if (TraceFilter[0] != 0) {
+ break;
+ }
+ }
+
+
+ diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Ch = %lu",
+ (int)modem->ChannelNumber);
+ diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Event = %lu", modem->Event);
+ diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Norm = %lu", modem->Norm);
+ diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Opts. = 0x%08x", modem->Options);
+ diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Tx = %lu Bps", modem->TxSpeed);
+ diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Rx = %lu Bps", modem->RxSpeed);
+ diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM RT = %lu mSec",
+ modem->RoundtripMsec);
+ diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Sr = %lu", modem->SymbolRate);
+ diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM Rxl = %d dBm", modem->RxLeveldBm);
+ diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM El = %d dBm", modem->EchoLeveldBm);
+ diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM SNR = %lu dB", modem->SNRdb);
+ diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM MAE = %lu", modem->MAE);
+ diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM LRet = %lu",
+ modem->LocalRetrains);
+ diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM RRet = %lu",
+ modem->RemoteRetrains);
+ diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM LRes = %lu", modem->LocalResyncs);
+ diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "MDM RRes = %lu",
+ modem->RemoteResyncs);
+ if (modem->Event == 3) {
+ diva_mnt_internal_dprintf(pC->hDbg->id,DLI_STAT,"MDM Disc = %lu", modem->DiscReason);
+ }
+ }
+ if ((modem->Event == 3) && (pC->hDbg->dbgMask & DIVA_MGT_DBG_MDM_STATISTICS)) {
+ (*(pC->pIdiLib->DivaSTraceGetModemStatistics))(pC->pIdiLib);
+ }
+ break;
+
+ case DIVA_SUPER_TRACE_NOTIFY_FAX_CHANGE:
+ if (pC->hDbg->dbgMask & DIVA_MGT_DBG_FAX_PROGRESS) {
+ {
+ int ch = TraceFilterChannel;
+ int id = TraceFilterIdent;
+
+ if ((id >= 0) && (ch >= 0) && (id < ARRAY_SIZE(clients)) &&
+ (clients[id].Dbg.id == (byte)id) && (clients[id].pIdiLib == hLib)) {
+ if (ch != (int)fax->ChannelNumber) {
+ break;
+ }
+ } else if (TraceFilter[0] != 0) {
+ break;
+ }
+ }
+
+ diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Ch = %lu",(int)fax->ChannelNumber);
+ diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Event = %lu", fax->Event);
+ diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Pages = %lu", fax->Page_Counter);
+ diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Feat. = 0x%08x", fax->Features);
+ diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX ID = <%s>", &fax->Station_ID[0]);
+ diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Saddr = <%s>", &fax->Subaddress[0]);
+ diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Pwd = <%s>", &fax->Password[0]);
+ diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Speed = %lu", fax->Speed);
+ diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Res. = 0x%08x", fax->Resolution);
+ diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Width = %lu", fax->Paper_Width);
+ diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Length= %lu", fax->Paper_Length);
+ diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX SLT = %lu", fax->Scanline_Time);
+ if (fax->Event == 3) {
+ diva_mnt_internal_dprintf(pC->hDbg->id, DLI_STAT, "FAX Disc = %lu", fax->Disc_Reason);
+ }
+ }
+ if ((fax->Event == 3) && (pC->hDbg->dbgMask & DIVA_MGT_DBG_FAX_STATISTICS)) {
+ (*(pC->pIdiLib->DivaSTraceGetFaxStatistics))(pC->pIdiLib);
+ }
+ break;
+
+ case DIVA_SUPER_TRACE_INTERFACE_CHANGE:
+ if (pC->hDbg->dbgMask & DIVA_MGT_DBG_IFC_EVENTS) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT,
+ "Layer 1 -> [%s]", channel->pInterface->Layer1);
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_STAT,
+ "Layer 2 -> [%s]", channel->pInterface->Layer2);
+ }
+ break;
+
+ case DIVA_SUPER_TRACE_NOTIFY_STAT_CHANGE:
+ if (pC->hDbg->dbgMask & DIVA_MGT_DBG_IFC_STATISTICS) {
+ /*
+ Incoming Statistics
+ */
+ if (channel->pInterfaceStat->inc.Calls) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "Inc Calls =%lu", channel->pInterfaceStat->inc.Calls);
+ }
+ if (channel->pInterfaceStat->inc.Connected) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "Inc Connected =%lu", channel->pInterfaceStat->inc.Connected);
+ }
+ if (channel->pInterfaceStat->inc.User_Busy) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "Inc Busy =%lu", channel->pInterfaceStat->inc.User_Busy);
+ }
+ if (channel->pInterfaceStat->inc.Call_Rejected) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "Inc Rejected =%lu", channel->pInterfaceStat->inc.Call_Rejected);
+ }
+ if (channel->pInterfaceStat->inc.Wrong_Number) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "Inc Wrong Nr =%lu", channel->pInterfaceStat->inc.Wrong_Number);
+ }
+ if (channel->pInterfaceStat->inc.Incompatible_Dst) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "Inc Incomp. Dest =%lu", channel->pInterfaceStat->inc.Incompatible_Dst);
+ }
+ if (channel->pInterfaceStat->inc.Out_of_Order) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "Inc Out of Order =%lu", channel->pInterfaceStat->inc.Out_of_Order);
+ }
+ if (channel->pInterfaceStat->inc.Ignored) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "Inc Ignored =%lu", channel->pInterfaceStat->inc.Ignored);
+ }
+
+ /*
+ Outgoing Statistics
+ */
+ if (channel->pInterfaceStat->outg.Calls) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "Outg Calls =%lu", channel->pInterfaceStat->outg.Calls);
+ }
+ if (channel->pInterfaceStat->outg.Connected) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "Outg Connected =%lu", channel->pInterfaceStat->outg.Connected);
+ }
+ if (channel->pInterfaceStat->outg.User_Busy) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "Outg Busy =%lu", channel->pInterfaceStat->outg.User_Busy);
+ }
+ if (channel->pInterfaceStat->outg.No_Answer) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "Outg No Answer =%lu", channel->pInterfaceStat->outg.No_Answer);
+ }
+ if (channel->pInterfaceStat->outg.Wrong_Number) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "Outg Wrong Nr =%lu", channel->pInterfaceStat->outg.Wrong_Number);
+ }
+ if (channel->pInterfaceStat->outg.Call_Rejected) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "Outg Rejected =%lu", channel->pInterfaceStat->outg.Call_Rejected);
+ }
+ if (channel->pInterfaceStat->outg.Other_Failures) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "Outg Other Failures =%lu", channel->pInterfaceStat->outg.Other_Failures);
+ }
+ }
+ break;
+
+ case DIVA_SUPER_TRACE_NOTIFY_MDM_STAT_CHANGE:
+ if (channel->pInterfaceStat->mdm.Disc_Normal) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "MDM Disc Normal = %lu", channel->pInterfaceStat->mdm.Disc_Normal);
+ }
+ if (channel->pInterfaceStat->mdm.Disc_Unspecified) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "MDM Disc Unsp. = %lu", channel->pInterfaceStat->mdm.Disc_Unspecified);
+ }
+ if (channel->pInterfaceStat->mdm.Disc_Busy_Tone) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "MDM Disc Busy Tone = %lu", channel->pInterfaceStat->mdm.Disc_Busy_Tone);
+ }
+ if (channel->pInterfaceStat->mdm.Disc_Congestion) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "MDM Disc Congestion = %lu", channel->pInterfaceStat->mdm.Disc_Congestion);
+ }
+ if (channel->pInterfaceStat->mdm.Disc_Carr_Wait) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "MDM Disc Carrier Wait = %lu", channel->pInterfaceStat->mdm.Disc_Carr_Wait);
+ }
+ if (channel->pInterfaceStat->mdm.Disc_Trn_Timeout) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "MDM Disc Trn. T.o. = %lu", channel->pInterfaceStat->mdm.Disc_Trn_Timeout);
+ }
+ if (channel->pInterfaceStat->mdm.Disc_Incompat) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "MDM Disc Incompatible = %lu", channel->pInterfaceStat->mdm.Disc_Incompat);
+ }
+ if (channel->pInterfaceStat->mdm.Disc_Frame_Rej) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "MDM Disc Frame Reject = %lu", channel->pInterfaceStat->mdm.Disc_Frame_Rej);
+ }
+ if (channel->pInterfaceStat->mdm.Disc_V42bis) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "MDM Disc V.42bis = %lu", channel->pInterfaceStat->mdm.Disc_V42bis);
+ }
+ break;
+
+ case DIVA_SUPER_TRACE_NOTIFY_FAX_STAT_CHANGE:
+ if (channel->pInterfaceStat->fax.Disc_Normal) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "FAX Disc Normal = %lu", channel->pInterfaceStat->fax.Disc_Normal);
+ }
+ if (channel->pInterfaceStat->fax.Disc_Not_Ident) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "FAX Disc Not Ident. = %lu", channel->pInterfaceStat->fax.Disc_Not_Ident);
+ }
+ if (channel->pInterfaceStat->fax.Disc_No_Response) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "FAX Disc No Response = %lu", channel->pInterfaceStat->fax.Disc_No_Response);
+ }
+ if (channel->pInterfaceStat->fax.Disc_Retries) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "FAX Disc Max Retries = %lu", channel->pInterfaceStat->fax.Disc_Retries);
+ }
+ if (channel->pInterfaceStat->fax.Disc_Unexp_Msg) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "FAX Unexp. Msg. = %lu", channel->pInterfaceStat->fax.Disc_Unexp_Msg);
+ }
+ if (channel->pInterfaceStat->fax.Disc_No_Polling) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "FAX Disc No Polling = %lu", channel->pInterfaceStat->fax.Disc_No_Polling);
+ }
+ if (channel->pInterfaceStat->fax.Disc_Training) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "FAX Disc Training = %lu", channel->pInterfaceStat->fax.Disc_Training);
+ }
+ if (channel->pInterfaceStat->fax.Disc_Unexpected) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "FAX Disc Unexpected = %lu", channel->pInterfaceStat->fax.Disc_Unexpected);
+ }
+ if (channel->pInterfaceStat->fax.Disc_Application) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "FAX Disc Application = %lu", channel->pInterfaceStat->fax.Disc_Application);
+ }
+ if (channel->pInterfaceStat->fax.Disc_Incompat) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "FAX Disc Incompatible = %lu", channel->pInterfaceStat->fax.Disc_Incompat);
+ }
+ if (channel->pInterfaceStat->fax.Disc_No_Command) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "FAX Disc No Command = %lu", channel->pInterfaceStat->fax.Disc_No_Command);
+ }
+ if (channel->pInterfaceStat->fax.Disc_Long_Msg) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "FAX Disc Long Msg. = %lu", channel->pInterfaceStat->fax.Disc_Long_Msg);
+ }
+ if (channel->pInterfaceStat->fax.Disc_Supervisor) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "FAX Disc Supervisor = %lu", channel->pInterfaceStat->fax.Disc_Supervisor);
+ }
+ if (channel->pInterfaceStat->fax.Disc_SUB_SEP_PWD) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "FAX Disc SUP SEP PWD = %lu", channel->pInterfaceStat->fax.Disc_SUB_SEP_PWD);
+ }
+ if (channel->pInterfaceStat->fax.Disc_Invalid_Msg) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "FAX Disc Invalid Msg. = %lu", channel->pInterfaceStat->fax.Disc_Invalid_Msg);
+ }
+ if (channel->pInterfaceStat->fax.Disc_Page_Coding) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "FAX Disc Page Coding = %lu", channel->pInterfaceStat->fax.Disc_Page_Coding);
+ }
+ if (channel->pInterfaceStat->fax.Disc_App_Timeout) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "FAX Disc Appl. T.o. = %lu", channel->pInterfaceStat->fax.Disc_App_Timeout);
+ }
+ if (channel->pInterfaceStat->fax.Disc_Unspecified) {
+ diva_mnt_internal_dprintf (pC->hDbg->id, DLI_LOG,
+ "FAX Disc Unspec. = %lu", channel->pInterfaceStat->fax.Disc_Unspecified);
+ }
+ break;
+ }
+}
+
+/*
+ Receive trace information from the Management Interface and store it in the
+ internal trace buffer with MSG_TYPE_MLOG as is, without any filtering.
+ Event Filtering and formatting is done in Management Interface self.
+ */
+static void diva_maint_trace_notify (void* user_context,
+ diva_strace_library_interface_t* hLib,
+ int Adapter,
+ void* xlog_buffer,
+ int length) {
+ diva_maint_client_t* pC = (diva_maint_client_t*)user_context;
+ diva_dbg_entry_head_t* pmsg;
+ word size;
+ dword sec, usec;
+ int ch = TraceFilterChannel;
+ int id = TraceFilterIdent;
+
+ /*
+ Selective trace
+ */
+ if ((id >= 0) && (ch >= 0) && (id < ARRAY_SIZE(clients)) &&
+ (clients[id].Dbg.id == (byte)id) && (clients[id].pIdiLib == hLib)) {
+ const char* p = NULL;
+ int ch_value = -1;
+ MI_XLOG_HDR *TrcData = (MI_XLOG_HDR *)xlog_buffer;
+
+ if (Adapter != clients[id].logical) {
+ return; /* Ignore all trace messages from other adapters */
+ }
+
+ if (TrcData->code == 24) {
+ p = (char*)&TrcData->code;
+ p += 2;
+ }
+
+ /*
+ All L1 messages start as [dsp,ch], so we can filter this information
+ and filter out all messages that use different channel
+ */
+ if (p && p[0] == '[') {
+ if (p[2] == ',') {
+ p += 3;
+ ch_value = *p - '0';
+ } else if (p[3] == ',') {
+ p += 4;
+ ch_value = *p - '0';
+ }
+ if (ch_value >= 0) {
+ if (p[2] == ']') {
+ ch_value = ch_value * 10 + p[1] - '0';
+ }
+ if (ch_value != ch) {
+ return; /* Ignore other channels */
+ }
+ }
+ }
+
+ } else if (TraceFilter[0] != 0) {
+ return; /* Ignore trace if trace filter is activated, but idle */
+ }
+
+ diva_os_get_time (&sec, &usec);
+
+ while (!(pmsg = (diva_dbg_entry_head_t*)queueAllocMsg (dbg_queue,
+ (word)length+sizeof(*pmsg)))) {
+ if ((pmsg = (diva_dbg_entry_head_t*)queuePeekMsg (dbg_queue, &size))) {
+ queueFreeMsg (dbg_queue);
+ } else {
+ break;
+ }
+ }
+ if (pmsg) {
+ memcpy (&pmsg[1], xlog_buffer, length);
+ pmsg->sequence = dbg_sequence++;
+ pmsg->time_sec = sec;
+ pmsg->time_usec = usec;
+ pmsg->facility = MSG_TYPE_MLOG;
+ pmsg->dli = pC->logical;
+ pmsg->drv_id = pC->hDbg->id;
+ pmsg->di_cpu = 0;
+ pmsg->data_length = length;
+ queueCompleteMsg (pmsg);
+ if (queueCount(dbg_queue)) {
+ diva_maint_wakeup_read();
+ }
+ }
+}
+
+
+/*
+ Convert MAINT trace mask to management interface trace mask/work/facility and
+ issue command to management interface
+ */
+static void diva_change_management_debug_mask (diva_maint_client_t* pC, dword old_mask) {
+ if (pC->request && pC->hDbg && pC->pIdiLib) {
+ dword changed = pC->hDbg->dbgMask ^ old_mask;
+
+ if (changed & DIVA_MGT_DBG_TRACE) {
+ (*(pC->pIdiLib->DivaSTraceSetInfo))(pC->pIdiLib,
+ (pC->hDbg->dbgMask & DIVA_MGT_DBG_TRACE) != 0);
+ }
+ if (changed & DIVA_MGT_DBG_DCHAN) {
+ (*(pC->pIdiLib->DivaSTraceSetDChannel))(pC->pIdiLib,
+ (pC->hDbg->dbgMask & DIVA_MGT_DBG_DCHAN) != 0);
+ }
+ if (!TraceFilter[0]) {
+ if (changed & DIVA_MGT_DBG_IFC_BCHANNEL) {
+ int i, state = ((pC->hDbg->dbgMask & DIVA_MGT_DBG_IFC_BCHANNEL) != 0);
+
+ for (i = 0; i < pC->channels; i++) {
+ (*(pC->pIdiLib->DivaSTraceSetBChannel))(pC->pIdiLib, i+1, state);
+ }
+ }
+ if (changed & DIVA_MGT_DBG_IFC_AUDIO) {
+ int i, state = ((pC->hDbg->dbgMask & DIVA_MGT_DBG_IFC_AUDIO) != 0);
+
+ for (i = 0; i < pC->channels; i++) {
+ (*(pC->pIdiLib->DivaSTraceSetAudioTap))(pC->pIdiLib, i+1, state);
+ }
+ }
+ }
+ }
+}
+
+
+void diva_mnt_internal_dprintf (dword drv_id, dword type, char* fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ DI_format (0, (word)drv_id, (int)type, fmt, ap);
+ va_end(ap);
+}
+
+/*
+ Shutdown all adapters before driver removal
+ */
+int diva_mnt_shutdown_xdi_adapters (void) {
+ diva_os_spin_lock_magic_t old_irql, old_irql1;
+ int i, fret = 0;
+ byte * pmem;
+
+
+ for (i = 1; i < ARRAY_SIZE(clients); i++) {
+ pmem = NULL;
+
+ diva_os_enter_spin_lock (&dbg_adapter_lock, &old_irql1, "unload");
+ diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "unload");
+
+ if (clients[i].hDbg && clients[i].pIdiLib && clients[i].request) {
+ if ((*(clients[i].pIdiLib->DivaSTraceLibraryStop))(clients[i].pIdiLib) == 1) {
+ /*
+ Adapter removal complete
+ */
+ if (clients[i].pIdiLib) {
+ (*(clients[i].pIdiLib->DivaSTraceLibraryFinit))(clients[i].pIdiLib->hLib);
+ clients[i].pIdiLib = NULL;
+
+ pmem = clients[i].pmem;
+ clients[i].pmem = NULL;
+ }
+ clients[i].hDbg = NULL;
+ clients[i].request_pending = 0;
+
+ if (clients[i].dma_handle >= 0) {
+ /*
+ Free DMA handle
+ */
+ diva_free_dma_descriptor (clients[i].request, clients[i].dma_handle);
+ clients[i].dma_handle = -1;
+ }
+ clients[i].request = NULL;
+ } else {
+ fret = -1;
+ }
+ }
+
+ diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "unload");
+ if (clients[i].hDbg && clients[i].pIdiLib && clients[i].request && clients[i].request_pending) {
+ clients[i].request_pending = 0;
+ (*(clients[i].request))((ENTITY*)(*(clients[i].pIdiLib->DivaSTraceGetHandle))(clients[i].pIdiLib->hLib));
+ if (clients[i].dma_handle >= 0) {
+ diva_free_dma_descriptor (clients[i].request, clients[i].dma_handle);
+ clients[i].dma_handle = -1;
+ }
+ }
+ diva_os_leave_spin_lock (&dbg_adapter_lock, &old_irql1, "unload");
+
+ if (pmem) {
+ diva_os_free (0, pmem);
+ }
+ }
+
+ return (fret);
+}
+
+/*
+ Set/Read the trace filter used for selective tracing.
+ Affects B- and Audio Tap trace mask at run time
+ */
+int diva_set_trace_filter (int filter_length, const char* filter) {
+ diva_os_spin_lock_magic_t old_irql, old_irql1;
+ int i, ch, on, client_b_on, client_atap_on;
+
+ diva_os_enter_spin_lock (&dbg_adapter_lock, &old_irql1, "dbg mask");
+ diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "write_filter");
+
+ if (filter_length <= DIVA_MAX_SELECTIVE_FILTER_LENGTH) {
+ memcpy (&TraceFilter[0], filter, filter_length);
+ if (TraceFilter[filter_length]) {
+ TraceFilter[filter_length] = 0;
+ }
+ if (TraceFilter[0] == '*') {
+ TraceFilter[0] = 0;
+ }
+ } else {
+ filter_length = -1;
+ }
+
+ TraceFilterIdent = -1;
+ TraceFilterChannel = -1;
+
+ on = (TraceFilter[0] == 0);
+
+ for (i = 1; i < ARRAY_SIZE(clients); i++) {
+ if (clients[i].hDbg && clients[i].pIdiLib && clients[i].request) {
+ client_b_on = on && ((clients[i].hDbg->dbgMask & DIVA_MGT_DBG_IFC_BCHANNEL) != 0);
+ client_atap_on = on && ((clients[i].hDbg->dbgMask & DIVA_MGT_DBG_IFC_AUDIO) != 0);
+ for (ch = 0; ch < clients[i].channels; ch++) {
+ (*(clients[i].pIdiLib->DivaSTraceSetBChannel))(clients[i].pIdiLib->hLib, ch+1, client_b_on);
+ (*(clients[i].pIdiLib->DivaSTraceSetAudioTap))(clients[i].pIdiLib->hLib, ch+1, client_atap_on);
+ }
+ }
+ }
+
+ for (i = 1; i < ARRAY_SIZE(clients); i++) {
+ if (clients[i].hDbg && clients[i].pIdiLib && clients[i].request && clients[i].request_pending) {
+ diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "write_filter");
+ clients[i].request_pending = 0;
+ (*(clients[i].request))((ENTITY*)(*(clients[i].pIdiLib->DivaSTraceGetHandle))(clients[i].pIdiLib->hLib));
+ diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "write_filter");
+ }
+ }
+
+ diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "write_filter");
+ diva_os_leave_spin_lock (&dbg_adapter_lock, &old_irql1, "dbg mask");
+
+ return (filter_length);
+}
+
+int diva_get_trace_filter (int max_length, char* filter) {
+ diva_os_spin_lock_magic_t old_irql;
+ int len;
+
+ diva_os_enter_spin_lock (&dbg_q_lock, &old_irql, "read_filter");
+ len = strlen (&TraceFilter[0]) + 1;
+ if (max_length >= len) {
+ memcpy (filter, &TraceFilter[0], len);
+ }
+ diva_os_leave_spin_lock (&dbg_q_lock, &old_irql, "read_filter");
+
+ return (len);
+}
+
+static int diva_dbg_cmp_key (const char* ref, const char* key) {
+ while (*key && (*ref++ == *key++));
+ return (!*key && !*ref);
+}
+
+/*
+ In case trace filter starts with "C" character then
+ all following characters are interpreted as command.
+ Followings commands are available:
+ - single, trace single call at time, independent from CPN/CiPN
+ */
+static int diva_mnt_cmp_nmbr (const char* nmbr) {
+ const char* ref = &TraceFilter[0];
+ int ref_len = strlen(&TraceFilter[0]), nmbr_len = strlen(nmbr);
+
+ if (ref[0] == 'C') {
+ if (diva_dbg_cmp_key (&ref[1], "single")) {
+ return (0);
+ }
+ return (-1);
+ }
+
+ if (!ref_len || (ref_len > nmbr_len)) {
+ return (-1);
+ }
+
+ nmbr = nmbr + nmbr_len - 1;
+ ref = ref + ref_len - 1;
+
+ while (ref_len--) {
+ if (*nmbr-- != *ref--) {
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+static int diva_get_dma_descriptor (IDI_CALL request, dword *dma_magic) {
+ ENTITY e;
+ IDI_SYNC_REQ* pReq = (IDI_SYNC_REQ*)&e;
+
+ if (!request) {
+ return (-1);
+ }
+
+ pReq->xdi_dma_descriptor_operation.Req = 0;
+ pReq->xdi_dma_descriptor_operation.Rc = IDI_SYNC_REQ_DMA_DESCRIPTOR_OPERATION;
+
+ pReq->xdi_dma_descriptor_operation.info.operation = IDI_SYNC_REQ_DMA_DESCRIPTOR_ALLOC;
+ pReq->xdi_dma_descriptor_operation.info.descriptor_number = -1;
+ pReq->xdi_dma_descriptor_operation.info.descriptor_address = NULL;
+ pReq->xdi_dma_descriptor_operation.info.descriptor_magic = 0;
+
+ (*request)((ENTITY*)pReq);
+
+ if (!pReq->xdi_dma_descriptor_operation.info.operation &&
+ (pReq->xdi_dma_descriptor_operation.info.descriptor_number >= 0) &&
+ pReq->xdi_dma_descriptor_operation.info.descriptor_magic) {
+ *dma_magic = pReq->xdi_dma_descriptor_operation.info.descriptor_magic;
+ return (pReq->xdi_dma_descriptor_operation.info.descriptor_number);
+ } else {
+ return (-1);
+ }
+}
+
+static void diva_free_dma_descriptor (IDI_CALL request, int nr) {
+ ENTITY e;
+ IDI_SYNC_REQ* pReq = (IDI_SYNC_REQ*)&e;
+
+ if (!request || (nr < 0)) {
+ return;
+ }
+
+ pReq->xdi_dma_descriptor_operation.Req = 0;
+ pReq->xdi_dma_descriptor_operation.Rc = IDI_SYNC_REQ_DMA_DESCRIPTOR_OPERATION;
+
+ pReq->xdi_dma_descriptor_operation.info.operation = IDI_SYNC_REQ_DMA_DESCRIPTOR_FREE;
+ pReq->xdi_dma_descriptor_operation.info.descriptor_number = nr;
+ pReq->xdi_dma_descriptor_operation.info.descriptor_address = NULL;
+ pReq->xdi_dma_descriptor_operation.info.descriptor_magic = 0;
+
+ (*request)((ENTITY*)pReq);
+}
+
diff --git a/drivers/isdn/hardware/eicon/debug_if.h b/drivers/isdn/hardware/eicon/debug_if.h
new file mode 100644
index 0000000..4db739d
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/debug_if.h
@@ -0,0 +1,90 @@
+/*
+ *
+ Copyright (c) Eicon Technology Corporation, 2000.
+ *
+ This source file is supplied for the use with Eicon
+ Technology Corporation's range of DIVA Server Adapters.
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+#ifndef __DIVA_DEBUG_IF_H__
+#define __DIVA_DEBUG_IF_H__
+#define MSG_TYPE_DRV_ID 0x0001
+#define MSG_TYPE_FLAGS 0x0002
+#define MSG_TYPE_STRING 0x0003
+#define MSG_TYPE_BINARY 0x0004
+#define MSG_TYPE_MLOG 0x0005
+
+#define MSG_FRAME_MAX_SIZE 2150
+
+typedef struct _diva_dbg_entry_head {
+ dword sequence;
+ dword time_sec;
+ dword time_usec;
+ dword facility;
+ dword dli;
+ dword drv_id;
+ dword di_cpu;
+ dword data_length;
+} diva_dbg_entry_head_t;
+
+int diva_maint_init (byte* base, unsigned long length, int do_init);
+void* diva_maint_finit (void);
+dword diva_dbg_q_length (void);
+diva_dbg_entry_head_t* diva_maint_get_message (word* size,
+ diva_os_spin_lock_magic_t* old_irql);
+void diva_maint_ack_message (int do_release,
+ diva_os_spin_lock_magic_t* old_irql);
+void diva_maint_prtComp (char *format, ...);
+void diva_maint_wakeup_read (void);
+int diva_get_driver_info (dword id, byte* data, int data_length);
+int diva_get_driver_dbg_mask (dword id, byte* data);
+int diva_set_driver_dbg_mask (dword id, dword mask);
+void diva_mnt_remove_xdi_adapter (const DESCRIPTOR* d);
+void diva_mnt_add_xdi_adapter (const DESCRIPTOR* d);
+int diva_mnt_shutdown_xdi_adapters (void);
+
+#define DIVA_MAX_SELECTIVE_FILTER_LENGTH 127
+int diva_set_trace_filter (int filter_length, const char* filter);
+int diva_get_trace_filter (int max_length, char* filter);
+
+
+#define DITRACE_CMD_GET_DRIVER_INFO 1
+#define DITRACE_READ_DRIVER_DBG_MASK 2
+#define DITRACE_WRITE_DRIVER_DBG_MASK 3
+#define DITRACE_READ_TRACE_ENTRY 4
+#define DITRACE_READ_TRACE_ENTRYS 5
+#define DITRACE_WRITE_SELECTIVE_TRACE_FILTER 6
+#define DITRACE_READ_SELECTIVE_TRACE_FILTER 7
+
+/*
+ Trace lavels for debug via management interface
+ */
+#define DIVA_MGT_DBG_TRACE 0x00000001 /* All trace messages from the card */
+#define DIVA_MGT_DBG_DCHAN 0x00000002 /* All D-channel relater trace messages */
+#define DIVA_MGT_DBG_MDM_PROGRESS 0x00000004 /* Modem progress events */
+#define DIVA_MGT_DBG_FAX_PROGRESS 0x00000008 /* Fax progress events */
+#define DIVA_MGT_DBG_IFC_STATISTICS 0x00000010 /* Interface call statistics */
+#define DIVA_MGT_DBG_MDM_STATISTICS 0x00000020 /* Global modem statistics */
+#define DIVA_MGT_DBG_FAX_STATISTICS 0x00000040 /* Global call statistics */
+#define DIVA_MGT_DBG_LINE_EVENTS 0x00000080 /* Line state events */
+#define DIVA_MGT_DBG_IFC_EVENTS 0x00000100 /* Interface/L1/L2 state events */
+#define DIVA_MGT_DBG_IFC_BCHANNEL 0x00000200 /* B-Channel trace for all channels */
+#define DIVA_MGT_DBG_IFC_AUDIO 0x00000400 /* Audio Tap trace for all channels */
+
+# endif /* DEBUG_IF___H */
+
+
diff --git a/drivers/isdn/hardware/eicon/debuglib.c b/drivers/isdn/hardware/eicon/debuglib.c
new file mode 100644
index 0000000..e39c5c1
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/debuglib.c
@@ -0,0 +1,156 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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 "debuglib.h"
+
+#ifdef DIVA_NO_DEBUGLIB
+static DIVA_DI_PRINTF dprintf;
+#else /* DIVA_NO_DEBUGLIB */
+
+_DbgHandle_ myDriverDebugHandle = { 0 /*!Registered*/, DBG_HANDLE_VERSION };
+DIVA_DI_PRINTF dprintf = no_printf;
+/*****************************************************************************/
+#define DBG_FUNC(name) \
+void \
+myDbgPrint_##name (char *format, ...) \
+{ va_list ap ; \
+ if ( myDriverDebugHandle.dbg_prt ) \
+ { va_start (ap, format) ; \
+ (myDriverDebugHandle.dbg_prt) \
+ (myDriverDebugHandle.id, DLI_##name, format, ap) ; \
+ va_end (ap) ; \
+} }
+DBG_FUNC(LOG)
+DBG_FUNC(FTL)
+DBG_FUNC(ERR)
+DBG_FUNC(TRC)
+DBG_FUNC(MXLOG)
+DBG_FUNC(FTL_MXLOG)
+void
+myDbgPrint_EVL (long msgID, ...)
+{ va_list ap ;
+ if ( myDriverDebugHandle.dbg_ev )
+ { va_start (ap, msgID) ;
+ (myDriverDebugHandle.dbg_ev)
+ (myDriverDebugHandle.id, (unsigned long)msgID, ap) ;
+ va_end (ap) ;
+} }
+DBG_FUNC(REG)
+DBG_FUNC(MEM)
+DBG_FUNC(SPL)
+DBG_FUNC(IRP)
+DBG_FUNC(TIM)
+DBG_FUNC(BLK)
+DBG_FUNC(TAPI)
+DBG_FUNC(NDIS)
+DBG_FUNC(CONN)
+DBG_FUNC(STAT)
+DBG_FUNC(SEND)
+DBG_FUNC(RECV)
+DBG_FUNC(PRV0)
+DBG_FUNC(PRV1)
+DBG_FUNC(PRV2)
+DBG_FUNC(PRV3)
+/*****************************************************************************/
+int
+DbgRegister (char *drvName, char *drvTag, unsigned long dbgMask)
+{
+ int len;
+/*
+ * deregister (if already registered) and zero out myDriverDebugHandle
+ */
+ DbgDeregister () ;
+/*
+ * initialize the debug handle
+ */
+ myDriverDebugHandle.Version = DBG_HANDLE_VERSION ;
+ myDriverDebugHandle.id = -1 ;
+ myDriverDebugHandle.dbgMask = dbgMask | (DL_EVL | DL_FTL | DL_LOG) ;
+ len = strlen (drvName) ;
+ memcpy (myDriverDebugHandle.drvName, drvName,
+ (len < sizeof(myDriverDebugHandle.drvName)) ?
+ len : sizeof(myDriverDebugHandle.drvName) - 1) ;
+ len = strlen (drvTag) ;
+ memcpy (myDriverDebugHandle.drvTag, drvTag,
+ (len < sizeof(myDriverDebugHandle.drvTag)) ?
+ len : sizeof(myDriverDebugHandle.drvTag) - 1) ;
+/*
+ * Try to register debugging via old (and only) interface
+ */
+ dprintf("\000\377", &myDriverDebugHandle) ;
+ if ( myDriverDebugHandle.dbg_prt )
+ {
+ return (1) ;
+ }
+/*
+ * Check if we registered with an old maint driver (see debuglib.h)
+ */
+ if ( myDriverDebugHandle.dbg_end != NULL
+ /* location of 'dbg_prt' in _OldDbgHandle_ struct */
+ && (myDriverDebugHandle.regTime.LowPart ||
+ myDriverDebugHandle.regTime.HighPart ) )
+ /* same location as in _OldDbgHandle_ struct */
+ {
+ dprintf("%s: Cannot log to old maint driver !", drvName) ;
+ myDriverDebugHandle.dbg_end =
+ ((_OldDbgHandle_ *)&myDriverDebugHandle)->dbg_end ;
+ DbgDeregister () ;
+ }
+ return (0) ;
+}
+/*****************************************************************************/
+void
+DbgSetLevel (unsigned long dbgMask)
+{
+ myDriverDebugHandle.dbgMask = dbgMask | (DL_EVL | DL_FTL | DL_LOG) ;
+}
+/*****************************************************************************/
+void
+DbgDeregister (void)
+{
+ if ( myDriverDebugHandle.dbg_end )
+ {
+ (myDriverDebugHandle.dbg_end)(&myDriverDebugHandle) ;
+ }
+ memset (&myDriverDebugHandle, 0, sizeof(myDriverDebugHandle)) ;
+}
+void xdi_dbg_xlog (char* x, ...) {
+ va_list ap;
+ va_start (ap, x);
+ if (myDriverDebugHandle.dbg_end &&
+ (myDriverDebugHandle.dbg_irq || myDriverDebugHandle.dbg_old) &&
+ (myDriverDebugHandle.dbgMask & DL_STAT)) {
+ if (myDriverDebugHandle.dbg_irq) {
+ (*(myDriverDebugHandle.dbg_irq))(myDriverDebugHandle.id,
+ (x[0] != 0) ? DLI_TRC : DLI_XLOG, x, ap);
+ } else {
+ (*(myDriverDebugHandle.dbg_old))(myDriverDebugHandle.id, x, ap);
+ }
+ }
+ va_end(ap);
+}
+/*****************************************************************************/
+#endif /* DIVA_NO_DEBUGLIB */
diff --git a/drivers/isdn/hardware/eicon/debuglib.h b/drivers/isdn/hardware/eicon/debuglib.h
new file mode 100644
index 0000000..016410c
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/debuglib.h
@@ -0,0 +1,322 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+#if !defined(__DEBUGLIB_H__)
+#define __DEBUGLIB_H__
+#include <stdarg.h>
+/*
+ * define global debug priorities
+ */
+#define DL_LOG 0x00000001 /* always worth mentioning */
+#define DL_FTL 0x00000002 /* always sampled error */
+#define DL_ERR 0x00000004 /* any kind of error */
+#define DL_TRC 0x00000008 /* verbose information */
+#define DL_XLOG 0x00000010 /* old xlog info */
+#define DL_MXLOG 0x00000020 /* maestra xlog info */
+#define DL_FTL_MXLOG 0x00000021 /* fatal maestra xlog info */
+#define DL_EVL 0x00000080 /* special NT eventlog msg */
+#define DL_COMPAT (DL_MXLOG | DL_XLOG)
+#define DL_PRIOR_MASK (DL_EVL | DL_COMPAT | DL_TRC | DL_ERR | DL_FTL | DL_LOG)
+#define DLI_LOG 0x0100
+#define DLI_FTL 0x0200
+#define DLI_ERR 0x0300
+#define DLI_TRC 0x0400
+#define DLI_XLOG 0x0500
+#define DLI_MXLOG 0x0600
+#define DLI_FTL_MXLOG 0x0600
+#define DLI_EVL 0x0800
+/*
+ * define OS (operating system interface) debuglevel
+ */
+#define DL_REG 0x00000100 /* init/query registry */
+#define DL_MEM 0x00000200 /* memory management */
+#define DL_SPL 0x00000400 /* event/spinlock handling */
+#define DL_IRP 0x00000800 /* I/O request handling */
+#define DL_TIM 0x00001000 /* timer/watchdog handling */
+#define DL_BLK 0x00002000 /* raw data block contents */
+#define DL_OS_MASK (DL_BLK | DL_TIM | DL_IRP | DL_SPL | DL_MEM | DL_REG)
+#define DLI_REG 0x0900
+#define DLI_MEM 0x0A00
+#define DLI_SPL 0x0B00
+#define DLI_IRP 0x0C00
+#define DLI_TIM 0x0D00
+#define DLI_BLK 0x0E00
+/*
+ * define ISDN (connection interface) debuglevel
+ */
+#define DL_TAPI 0x00010000 /* debug TAPI interface */
+#define DL_NDIS 0x00020000 /* debug NDIS interface */
+#define DL_CONN 0x00040000 /* connection handling */
+#define DL_STAT 0x00080000 /* trace state machines */
+#define DL_SEND 0x00100000 /* trace raw xmitted data */
+#define DL_RECV 0x00200000 /* trace raw received data */
+#define DL_DATA (DL_SEND | DL_RECV)
+#define DL_ISDN_MASK (DL_DATA | DL_STAT | DL_CONN | DL_NDIS | DL_TAPI)
+#define DLI_TAPI 0x1100
+#define DLI_NDIS 0x1200
+#define DLI_CONN 0x1300
+#define DLI_STAT 0x1400
+#define DLI_SEND 0x1500
+#define DLI_RECV 0x1600
+/*
+ * define some private (unspecified) debuglevel
+ */
+#define DL_PRV0 0x01000000
+#define DL_PRV1 0x02000000
+#define DL_PRV2 0x04000000
+#define DL_PRV3 0x08000000
+#define DL_PRIV_MASK (DL_PRV0 | DL_PRV1 | DL_PRV2 | DL_PRV3)
+#define DLI_PRV0 0x1900
+#define DLI_PRV1 0x1A00
+#define DLI_PRV2 0x1B00
+#define DLI_PRV3 0x1C00
+#define DT_INDEX(x) ((x) & 0x000F)
+#define DL_INDEX(x) ((((x) >> 8) & 0x00FF) - 1)
+#define DLI_NAME(x) ((x) & 0xFF00)
+/*
+ * Debug mask for kernel mode tracing, if set the output is also sent to
+ * the system debug function. Requires that the project is compiled
+ * with _KERNEL_DBG_PRINT_
+ */
+#define DL_TO_KERNEL 0x40000000
+
+#ifdef DIVA_NO_DEBUGLIB
+#define myDbgPrint_LOG(x...) do { } while(0);
+#define myDbgPrint_FTL(x...) do { } while(0);
+#define myDbgPrint_ERR(x...) do { } while(0);
+#define myDbgPrint_TRC(x...) do { } while(0);
+#define myDbgPrint_MXLOG(x...) do { } while(0);
+#define myDbgPrint_EVL(x...) do { } while(0);
+#define myDbgPrint_REG(x...) do { } while(0);
+#define myDbgPrint_MEM(x...) do { } while(0);
+#define myDbgPrint_SPL(x...) do { } while(0);
+#define myDbgPrint_IRP(x...) do { } while(0);
+#define myDbgPrint_TIM(x...) do { } while(0);
+#define myDbgPrint_BLK(x...) do { } while(0);
+#define myDbgPrint_TAPI(x...) do { } while(0);
+#define myDbgPrint_NDIS(x...) do { } while(0);
+#define myDbgPrint_CONN(x...) do { } while(0);
+#define myDbgPrint_STAT(x...) do { } while(0);
+#define myDbgPrint_SEND(x...) do { } while(0);
+#define myDbgPrint_RECV(x...) do { } while(0);
+#define myDbgPrint_PRV0(x...) do { } while(0);
+#define myDbgPrint_PRV1(x...) do { } while(0);
+#define myDbgPrint_PRV2(x...) do { } while(0);
+#define myDbgPrint_PRV3(x...) do { } while(0);
+#define DBG_TEST(func,args) do { } while(0);
+#define DBG_EVL_ID(args) do { } while(0);
+
+#else /* DIVA_NO_DEBUGLIB */
+/*
+ * define low level macros for formatted & raw debugging
+ */
+#define DBG_DECL(func) extern void myDbgPrint_##func (char *, ...) ;
+DBG_DECL(LOG)
+DBG_DECL(FTL)
+DBG_DECL(ERR)
+DBG_DECL(TRC)
+DBG_DECL(MXLOG)
+DBG_DECL(FTL_MXLOG)
+extern void myDbgPrint_EVL (long, ...) ;
+DBG_DECL(REG)
+DBG_DECL(MEM)
+DBG_DECL(SPL)
+DBG_DECL(IRP)
+DBG_DECL(TIM)
+DBG_DECL(BLK)
+DBG_DECL(TAPI)
+DBG_DECL(NDIS)
+DBG_DECL(CONN)
+DBG_DECL(STAT)
+DBG_DECL(SEND)
+DBG_DECL(RECV)
+DBG_DECL(PRV0)
+DBG_DECL(PRV1)
+DBG_DECL(PRV2)
+DBG_DECL(PRV3)
+#ifdef _KERNEL_DBG_PRINT_
+/*
+ * tracing to maint and kernel if selected in the trace mask.
+ */
+#define DBG_TEST(func,args) \
+{ if ( (myDriverDebugHandle.dbgMask) & (unsigned long)DL_##func ) \
+ { \
+ if ( (myDriverDebugHandle.dbgMask) & DL_TO_KERNEL ) \
+ {DbgPrint args; DbgPrint ("\r\n");} \
+ myDbgPrint_##func args ; \
+} }
+#else
+/*
+ * Standard tracing to maint driver.
+ */
+#define DBG_TEST(func,args) \
+{ if ( (myDriverDebugHandle.dbgMask) & (unsigned long)DL_##func ) \
+ { myDbgPrint_##func args ; \
+} }
+#endif
+/*
+ * For event level debug use a separate define, the parameter are
+ * different and cause compiler errors on some systems.
+ */
+#define DBG_EVL_ID(args) \
+{ if ( (myDriverDebugHandle.dbgMask) & (unsigned long)DL_EVL ) \
+ { myDbgPrint_EVL args ; \
+} }
+
+#endif /* DIVA_NO_DEBUGLIB */
+
+#define DBG_LOG(args) DBG_TEST(LOG, args)
+#define DBG_FTL(args) DBG_TEST(FTL, args)
+#define DBG_ERR(args) DBG_TEST(ERR, args)
+#define DBG_TRC(args) DBG_TEST(TRC, args)
+#define DBG_MXLOG(args) DBG_TEST(MXLOG, args)
+#define DBG_FTL_MXLOG(args) DBG_TEST(FTL_MXLOG, args)
+#define DBG_EVL(args) DBG_EVL_ID(args)
+#define DBG_REG(args) DBG_TEST(REG, args)
+#define DBG_MEM(args) DBG_TEST(MEM, args)
+#define DBG_SPL(args) DBG_TEST(SPL, args)
+#define DBG_IRP(args) DBG_TEST(IRP, args)
+#define DBG_TIM(args) DBG_TEST(TIM, args)
+#define DBG_BLK(args) DBG_TEST(BLK, args)
+#define DBG_TAPI(args) DBG_TEST(TAPI, args)
+#define DBG_NDIS(args) DBG_TEST(NDIS, args)
+#define DBG_CONN(args) DBG_TEST(CONN, args)
+#define DBG_STAT(args) DBG_TEST(STAT, args)
+#define DBG_SEND(args) DBG_TEST(SEND, args)
+#define DBG_RECV(args) DBG_TEST(RECV, args)
+#define DBG_PRV0(args) DBG_TEST(PRV0, args)
+#define DBG_PRV1(args) DBG_TEST(PRV1, args)
+#define DBG_PRV2(args) DBG_TEST(PRV2, args)
+#define DBG_PRV3(args) DBG_TEST(PRV3, args)
+/*
+ * prototypes for debug register/deregister functions in "debuglib.c"
+ */
+#ifdef DIVA_NO_DEBUGLIB
+#define DbgRegister(name,tag, mask) do { } while(0)
+#define DbgDeregister() do { } while(0)
+#define DbgSetLevel(mask) do { } while(0)
+#else
+extern DIVA_DI_PRINTF dprintf;
+extern int DbgRegister (char *drvName, char *drvTag, unsigned long dbgMask) ;
+extern void DbgDeregister (void) ;
+extern void DbgSetLevel (unsigned long dbgMask) ;
+#endif
+/*
+ * driver internal structure for debug handling;
+ * in client drivers this structure is maintained in "debuglib.c",
+ * in the debug driver "debug.c" maintains a chain of such structs.
+ */
+typedef struct _DbgHandle_ *pDbgHandle ;
+typedef void ( * DbgEnd) (pDbgHandle) ;
+typedef void ( * DbgLog) (unsigned short, int, char *, va_list) ;
+typedef void ( * DbgOld) (unsigned short, char *, va_list) ;
+typedef void ( * DbgEv) (unsigned short, unsigned long, va_list) ;
+typedef void ( * DbgIrq) (unsigned short, int, char *, va_list) ;
+typedef struct _DbgHandle_
+{ char Registered ; /* driver successfull registered */
+#define DBG_HANDLE_REG_NEW 0x01 /* this (new) structure */
+#define DBG_HANDLE_REG_OLD 0x7f /* old structure (see below) */
+ char Version; /* version of this structure */
+#define DBG_HANDLE_VERSION 1 /* contains dbg_old function now */
+#define DBG_HANDLE_VER_EXT 2 /* pReserved points to extended info*/
+ short id ; /* internal id of registered driver */
+ struct _DbgHandle_ *next ; /* ptr to next registered driver */
+ struct /*LARGE_INTEGER*/ {
+ unsigned long LowPart;
+ long HighPart;
+ } regTime ; /* timestamp for registration */
+ void *pIrp ; /* ptr to pending i/o request */
+ unsigned long dbgMask ; /* current debug mask */
+ char drvName[16] ; /* ASCII name of registered driver */
+ char drvTag[64] ; /* revision string */
+ DbgEnd dbg_end ; /* function for debug closing */
+ DbgLog dbg_prt ; /* function for debug appending */
+ DbgOld dbg_old ; /* function for old debug appending */
+ DbgEv dbg_ev ; /* function for Windows NT Eventlog */
+ DbgIrq dbg_irq ; /* function for irql checked debug */
+ void *pReserved3 ;
+} _DbgHandle_ ;
+extern _DbgHandle_ myDriverDebugHandle ;
+typedef struct _OldDbgHandle_
+{ struct _OldDbgHandle_ *next ;
+ void *pIrp ;
+ long regTime[2] ;
+ unsigned long dbgMask ;
+ short id ;
+ char drvName[78] ;
+ DbgEnd dbg_end ;
+ DbgLog dbg_prt ;
+} _OldDbgHandle_ ;
+/* the differences in DbgHandles
+ old: tmp: new:
+ 0 long next char Registered char Registered
+ char filler char Version
+ short id short id
+ 4 long pIrp long regTime.lo long next
+ 8 long regTime.lo long regTime.hi long regTime.lo
+ 12 long regTime.hi long next long regTime.hi
+ 16 long dbgMask long pIrp long pIrp
+ 20 short id long dbgMask long dbgMask
+ 22 char drvName[78] ..
+ 24 .. char drvName[16] char drvName[16]
+ 40 .. char drvTag[64] char drvTag[64]
+ 100 void *dbg_end .. ..
+ 104 void *dbg_prt void *dbg_end void *dbg_end
+ 108 .. void *dbg_prt void *dbg_prt
+ 112 .. .. void *dbg_old
+ 116 .. .. void *dbg_ev
+ 120 .. .. void *dbg_irq
+ 124 .. .. void *pReserved3
+ ( new->id == 0 && *((short *)&new->dbgMask) == -1 ) identifies "old",
+ new->Registered and new->Version overlay old->next,
+ new->next overlays old->pIrp, new->regTime matches old->regTime and
+ thus these fields can be maintained in new struct whithout trouble;
+ id, dbgMask, drvName, dbg_end and dbg_prt need special handling !
+*/
+#define DBG_EXT_TYPE_CARD_TRACE 0x00000001
+typedef struct
+{
+ unsigned long ExtendedType;
+ union
+ {
+ /* DBG_EXT_TYPE_CARD_TRACE */
+ struct
+ {
+ void ( * MaskChangedNotify) (void *pContext);
+ unsigned long ModuleTxtMask;
+ unsigned long DebugLevel;
+ unsigned long B_ChannelMask;
+ unsigned long LogBufferSize;
+ } CardTrace;
+ }Data;
+} _DbgExtendedInfo_;
+#ifndef DIVA_NO_DEBUGLIB
+/* -------------------------------------------------------------
+ Function used for xlog-style debug
+ ------------------------------------------------------------- */
+#define XDI_USE_XLOG 1
+void xdi_dbg_xlog (char* x, ...);
+#endif /* DIVA_NO_DEBUGLIB */
+#endif /* __DEBUGLIB_H__ */
diff --git a/drivers/isdn/hardware/eicon/dfifo.h b/drivers/isdn/hardware/eicon/dfifo.h
new file mode 100644
index 0000000..9a109c7
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/dfifo.h
@@ -0,0 +1,54 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+#ifndef __DIVA_IDI_DFIFO_INC__
+#define __DIVA_IDI_DFIFO_INC__
+#define DIVA_DFIFO_CACHE_SZ 64 /* Used to isolate pipe from
+ rest of the world
+ should be divisible by 4
+ */
+#define DIVA_DFIFO_RAW_SZ (2512*8)
+#define DIVA_DFIFO_DATA_SZ 68
+#define DIVA_DFIFO_HDR_SZ 4
+#define DIVA_DFIFO_SEGMENT_SZ (DIVA_DFIFO_DATA_SZ+DIVA_DFIFO_HDR_SZ)
+#define DIVA_DFIFO_SEGMENTS ((DIVA_DFIFO_RAW_SZ)/(DIVA_DFIFO_SEGMENT_SZ)+1)
+#define DIVA_DFIFO_MEM_SZ (\
+ (DIVA_DFIFO_SEGMENT_SZ)*(DIVA_DFIFO_SEGMENTS)+\
+ (DIVA_DFIFO_CACHE_SZ)*2\
+ )
+#define DIVA_DFIFO_STEP DIVA_DFIFO_SEGMENT_SZ
+/* -------------------------------------------------------------------------
+ Block header layout is:
+ byte[0] -> flags
+ byte[1] -> length of data in block
+ byte[2] -> reserved
+ byte[4] -> reserved
+ ------------------------------------------------------------------------- */
+#define DIVA_DFIFO_WRAP 0x80 /* This is the last block in fifo */
+#define DIVA_DFIFO_READY 0x40 /* This block is ready for processing */
+#define DIVA_DFIFO_LAST 0x20 /* This block is last in message */
+#define DIVA_DFIFO_AUTO 0x10 /* Don't look for 'ready', don't ack */
+int diva_dfifo_create (void* start, int length);
+#endif
diff --git a/drivers/isdn/hardware/eicon/di.c b/drivers/isdn/hardware/eicon/di.c
new file mode 100644
index 0000000..10760b3
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/di.c
@@ -0,0 +1,835 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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 "platform.h"
+#include "pc.h"
+#include "pr_pc.h"
+#include "di_defs.h"
+#include "di.h"
+#if !defined USE_EXTENDED_DEBUGS
+ #include "dimaint.h"
+#else
+ #define dprintf
+#endif
+#include "io.h"
+#include "dfifo.h"
+#define PR_RAM ((struct pr_ram *)0)
+#define RAM ((struct dual *)0)
+/*------------------------------------------------------------------*/
+/* local function prototypes */
+/*------------------------------------------------------------------*/
+void pr_out(ADAPTER * a);
+byte pr_dpc(ADAPTER * a);
+static byte pr_ready(ADAPTER * a);
+static byte isdn_rc(ADAPTER *, byte, byte, byte, word, dword, dword);
+static byte isdn_ind(ADAPTER *, byte, byte, byte, PBUFFER *, byte, word);
+/* -----------------------------------------------------------------
+ Functions used for the extended XDI Debug
+ macros
+ global convergence counter (used by all adapters)
+ Look by the implementation part of the functions
+ about the parameters.
+ If you change the dubugging parameters, then you should update
+ the aididbg.doc in the IDI doc's.
+ ----------------------------------------------------------------- */
+#if defined(XDI_USE_XLOG)
+#define XDI_A_NR(_x_) ((byte)(((ISDN_ADAPTER *)(_x_->io))->ANum))
+static void xdi_xlog (byte *msg, word code, int length);
+static byte xdi_xlog_sec = 0;
+#else
+#define XDI_A_NR(_x_) ((byte)0)
+#endif
+static void xdi_xlog_rc_event (byte Adapter,
+ byte Id, byte Ch, byte Rc, byte cb, byte type);
+static void xdi_xlog_request (byte Adapter, byte Id,
+ byte Ch, byte Req, byte type);
+static void xdi_xlog_ind (byte Adapter,
+ byte Id,
+ byte Ch,
+ byte Ind,
+ byte rnr_valid,
+ byte rnr,
+ byte type);
+/*------------------------------------------------------------------*/
+/* output function */
+/*------------------------------------------------------------------*/
+void pr_out(ADAPTER * a)
+{
+ byte e_no;
+ ENTITY * this = NULL;
+ BUFFERS *X;
+ word length;
+ word i;
+ word clength;
+ REQ * ReqOut;
+ byte more;
+ byte ReadyCount;
+ byte ReqCount;
+ byte Id;
+ dtrc(dprintf("pr_out"));
+ /* while a request is pending ... */
+ e_no = look_req(a);
+ if(!e_no)
+ {
+ dtrc(dprintf("no_req"));
+ return;
+ }
+ ReadyCount = pr_ready(a);
+ if(!ReadyCount)
+ {
+ dtrc(dprintf("not_ready"));
+ return;
+ }
+ ReqCount = 0;
+ while(e_no && ReadyCount) {
+ next_req(a);
+ this = entity_ptr(a, e_no);
+#ifdef USE_EXTENDED_DEBUGS
+ if ( !this )
+ {
+ DBG_FTL(("XDI: [%02x] !A%d ==> NULL entity ptr - try to ignore",
+ xdi_xlog_sec++, (int)((ISDN_ADAPTER *)a->io)->ANum))
+ e_no = look_req(a) ;
+ ReadyCount-- ;
+ continue ;
+ }
+ {
+ DBG_TRC((">A%d Id=0x%x Req=0x%x", ((ISDN_ADAPTER *)a->io)->ANum, this->Id, this->Req))
+ }
+#else
+ dbug(dprintf("out:Req=%x,Id=%x,Ch=%x",this->Req,this->Id,this->ReqCh));
+#endif
+ /* get address of next available request buffer */
+ ReqOut = (REQ *)&PR_RAM->B[a->ram_inw(a, &PR_RAM->NextReq)];
+#if defined(DIVA_ISTREAM)
+ if (!(a->tx_stream[this->Id] &&
+ this->Req == N_DATA)) {
+#endif
+ /* now copy the data from the current data buffer into the */
+ /* adapters request buffer */
+ length = 0;
+ i = this->XCurrent;
+ X = PTR_X(a,this);
+ while(i<this->XNum && length<270) {
+ clength = min((word)(270-length),(word)(X[i].PLength-this->XOffset));
+ a->ram_out_buffer(a,
+ &ReqOut->XBuffer.P[length],
+ PTR_P(a,this,&X[i].P[this->XOffset]),
+ clength);
+ length +=clength;
+ this->XOffset +=clength;
+ if(this->XOffset==X[i].PLength) {
+ this->XCurrent = (byte)++i;
+ this->XOffset = 0;
+ }
+ }
+#if defined(DIVA_ISTREAM)
+ } else { /* Use CMA extension in order to transfer data to the card */
+ i = this->XCurrent;
+ X = PTR_X(a,this);
+ while (i < this->XNum) {
+ diva_istream_write (a,
+ this->Id,
+ PTR_P(a,this,&X[i].P[0]),
+ X[i].PLength,
+ ((i+1) == this->XNum),
+ 0, 0);
+ this->XCurrent = (byte)++i;
+ }
+ length = 0;
+ }
+#endif
+ a->ram_outw(a, &ReqOut->XBuffer.length, length);
+ a->ram_out(a, &ReqOut->ReqId, this->Id);
+ a->ram_out(a, &ReqOut->ReqCh, this->ReqCh);
+ /* if it's a specific request (no ASSIGN) ... */
+ if(this->Id &0x1f) {
+ /* if buffers are left in the list of data buffers do */
+ /* do chaining (LL_MDATA, N_MDATA) */
+ this->More++;
+ if(i<this->XNum && this->MInd) {
+ xdi_xlog_request (XDI_A_NR(a), this->Id, this->ReqCh, this->MInd,
+ a->IdTypeTable[this->No]);
+ a->ram_out(a, &ReqOut->Req, this->MInd);
+ more = true;
+ }
+ else {
+ xdi_xlog_request (XDI_A_NR(a), this->Id, this->ReqCh, this->Req,
+ a->IdTypeTable[this->No]);
+ this->More |=XMOREF;
+ a->ram_out(a, &ReqOut->Req, this->Req);
+ more = false;
+ if (a->FlowControlIdTable[this->ReqCh] == this->Id)
+ a->FlowControlSkipTable[this->ReqCh] = true;
+ /*
+ Note that remove request was sent to the card
+ */
+ if (this->Req == REMOVE) {
+ a->misc_flags_table[e_no] |= DIVA_MISC_FLAGS_REMOVE_PENDING;
+ }
+ }
+ /* if we did chaining, this entity is put back into the */
+ /* request queue */
+ if(more) {
+ req_queue(a,this->No);
+ }
+ }
+ /* else it's a ASSIGN */
+ else {
+ /* save the request code used for buffer chaining */
+ this->MInd = 0;
+ if (this->Id==BLLC_ID) this->MInd = LL_MDATA;
+ if (this->Id==NL_ID ||
+ this->Id==TASK_ID ||
+ this->Id==MAN_ID
+ ) this->MInd = N_MDATA;
+ /* send the ASSIGN */
+ a->IdTypeTable[this->No] = this->Id;
+ xdi_xlog_request (XDI_A_NR(a),this->Id,this->ReqCh,this->Req, this->Id);
+ this->More |=XMOREF;
+ a->ram_out(a, &ReqOut->Req, this->Req);
+ /* save the reference of the ASSIGN */
+ assign_queue(a, this->No, a->ram_inw(a, &ReqOut->Reference));
+ }
+ a->ram_outw(a, &PR_RAM->NextReq, a->ram_inw(a, &ReqOut->next));
+ ReadyCount--;
+ ReqCount++;
+ e_no = look_req(a);
+ }
+ /* send the filled request buffers to the ISDN adapter */
+ a->ram_out(a, &PR_RAM->ReqInput,
+ (byte)(a->ram_in(a, &PR_RAM->ReqInput) + ReqCount));
+ /* if it is a 'unreturncoded' UREMOVE request, remove the */
+ /* Id from our table after sending the request */
+ if(this && (this->Req==UREMOVE) && this->Id) {
+ Id = this->Id;
+ e_no = a->IdTable[Id];
+ free_entity(a, e_no);
+ for (i = 0; i < 256; i++)
+ {
+ if (a->FlowControlIdTable[i] == Id)
+ a->FlowControlIdTable[i] = 0;
+ }
+ a->IdTable[Id] = 0;
+ this->Id = 0;
+ }
+}
+static byte pr_ready(ADAPTER * a)
+{
+ byte ReadyCount;
+ ReadyCount = (byte)(a->ram_in(a, &PR_RAM->ReqOutput) -
+ a->ram_in(a, &PR_RAM->ReqInput));
+ if(!ReadyCount) {
+ if(!a->ReadyInt) {
+ a->ram_inc(a, &PR_RAM->ReadyInt);
+ a->ReadyInt++;
+ }
+ }
+ return ReadyCount;
+}
+/*------------------------------------------------------------------*/
+/* isdn interrupt handler */
+/*------------------------------------------------------------------*/
+byte pr_dpc(ADAPTER * a)
+{
+ byte Count;
+ RC * RcIn;
+ IND * IndIn;
+ byte c;
+ byte RNRId;
+ byte Rc;
+ byte Ind;
+ /* if return codes are available ... */
+ if((Count = a->ram_in(a, &PR_RAM->RcOutput)) != 0) {
+ dtrc(dprintf("#Rc=%x",Count));
+ /* get the buffer address of the first return code */
+ RcIn = (RC *)&PR_RAM->B[a->ram_inw(a, &PR_RAM->NextRc)];
+ /* for all return codes do ... */
+ while(Count--) {
+ if((Rc=a->ram_in(a, &RcIn->Rc)) != 0) {
+ dword tmp[2];
+ /*
+ Get extended information, associated with return code
+ */
+ a->ram_in_buffer(a,
+ &RcIn->Reserved2[0],
+ (byte*)&tmp[0],
+ 8);
+ /* call return code handler, if it is not our return code */
+ /* the handler returns 2 */
+ /* for all return codes we process, we clear the Rc field */
+ isdn_rc(a,
+ Rc,
+ a->ram_in(a, &RcIn->RcId),
+ a->ram_in(a, &RcIn->RcCh),
+ a->ram_inw(a, &RcIn->Reference),
+ tmp[0], /* type of extended information */
+ tmp[1]); /* extended information */
+ a->ram_out(a, &RcIn->Rc, 0);
+ }
+ /* get buffer address of next return code */
+ RcIn = (RC *)&PR_RAM->B[a->ram_inw(a, &RcIn->next)];
+ }
+ /* clear all return codes (no chaining!) */
+ a->ram_out(a, &PR_RAM->RcOutput ,0);
+ /* call output function */
+ pr_out(a);
+ }
+ /* clear RNR flag */
+ RNRId = 0;
+ /* if indications are available ... */
+ if((Count = a->ram_in(a, &PR_RAM->IndOutput)) != 0) {
+ dtrc(dprintf("#Ind=%x",Count));
+ /* get the buffer address of the first indication */
+ IndIn = (IND *)&PR_RAM->B[a->ram_inw(a, &PR_RAM->NextInd)];
+ /* for all indications do ... */
+ while(Count--) {
+ /* if the application marks an indication as RNR, all */
+ /* indications from the same Id delivered in this interrupt */
+ /* are marked RNR */
+ if(RNRId && RNRId==a->ram_in(a, &IndIn->IndId)) {
+ a->ram_out(a, &IndIn->Ind, 0);
+ a->ram_out(a, &IndIn->RNR, true);
+ }
+ else {
+ Ind = a->ram_in(a, &IndIn->Ind);
+ if(Ind) {
+ RNRId = 0;
+ /* call indication handler, a return value of 2 means chain */
+ /* a return value of 1 means RNR */
+ /* for all indications we process, we clear the Ind field */
+ c = isdn_ind(a,
+ Ind,
+ a->ram_in(a, &IndIn->IndId),
+ a->ram_in(a, &IndIn->IndCh),
+ &IndIn->RBuffer,
+ a->ram_in(a, &IndIn->MInd),
+ a->ram_inw(a, &IndIn->MLength));
+ if(c==1) {
+ dtrc(dprintf("RNR"));
+ a->ram_out(a, &IndIn->Ind, 0);
+ RNRId = a->ram_in(a, &IndIn->IndId);
+ a->ram_out(a, &IndIn->RNR, true);
+ }
+ }
+ }
+ /* get buffer address of next indication */
+ IndIn = (IND *)&PR_RAM->B[a->ram_inw(a, &IndIn->next)];
+ }
+ a->ram_out(a, &PR_RAM->IndOutput, 0);
+ }
+ return false;
+}
+byte scom_test_int(ADAPTER * a)
+{
+ return a->ram_in(a,(void *)0x3fe);
+}
+void scom_clear_int(ADAPTER * a)
+{
+ a->ram_out(a,(void *)0x3fe,0);
+}
+/*------------------------------------------------------------------*/
+/* return code handler */
+/*------------------------------------------------------------------*/
+byte isdn_rc(ADAPTER * a,
+ byte Rc,
+ byte Id,
+ byte Ch,
+ word Ref,
+ dword extended_info_type,
+ dword extended_info)
+{
+ ENTITY * this;
+ byte e_no;
+ word i;
+ int cancel_rc;
+#ifdef USE_EXTENDED_DEBUGS
+ {
+ DBG_TRC(("<A%d Id=0x%x Rc=0x%x", ((ISDN_ADAPTER *)a->io)->ANum, Id, Rc))
+ }
+#else
+ dbug(dprintf("isdn_rc(Rc=%x,Id=%x,Ch=%x)",Rc,Id,Ch));
+#endif
+ /* check for ready interrupt */
+ if(Rc==READY_INT) {
+ xdi_xlog_rc_event (XDI_A_NR(a), Id, Ch, Rc, 0, 0);
+ if(a->ReadyInt) {
+ a->ReadyInt--;
+ return 0;
+ }
+ return 2;
+ }
+ /* if we know this Id ... */
+ e_no = a->IdTable[Id];
+ if(e_no) {
+ this = entity_ptr(a,e_no);
+ xdi_xlog_rc_event (XDI_A_NR(a), Id, Ch, Rc, 0, a->IdTypeTable[this->No]);
+ this->RcCh = Ch;
+ /* if it is a return code to a REMOVE request, remove the */
+ /* Id from our table */
+ if ((a->misc_flags_table[e_no] & DIVA_MISC_FLAGS_REMOVE_PENDING) &&
+ (Rc==OK)) {
+ if (a->IdTypeTable[e_no] == NL_ID) {
+ if (a->RcExtensionSupported &&
+ (extended_info_type != DIVA_RC_TYPE_REMOVE_COMPLETE)) {
+ dtrc(dprintf("XDI: N-REMOVE, A(%02x) Id:%02x, ignore RC=OK",
+ XDI_A_NR(a),Id));
+ return (0);
+ }
+ if (extended_info_type == DIVA_RC_TYPE_REMOVE_COMPLETE)
+ a->RcExtensionSupported = true;
+ }
+ a->misc_flags_table[e_no] &= ~DIVA_MISC_FLAGS_REMOVE_PENDING;
+ a->misc_flags_table[e_no] &= ~DIVA_MISC_FLAGS_NO_RC_CANCELLING;
+ free_entity(a, e_no);
+ for (i = 0; i < 256; i++)
+ {
+ if (a->FlowControlIdTable[i] == Id)
+ a->FlowControlIdTable[i] = 0;
+ }
+ a->IdTable[Id] = 0;
+ this->Id = 0;
+ /* ---------------------------------------------------------------
+ If we send N_DISC or N_DISK_ACK after we have received OK_FC
+ then the card will respond with OK_FC and later with RC==OK.
+ If we send N_REMOVE in this state we will receive only RC==OK
+ This will create the state in that the XDI is waiting for the
+ additional RC and does not delivery the RC to the client. This
+ code corrects the counter of outstanding RC's in this case.
+ --------------------------------------------------------------- */
+ if ((this->More & XMOREC) > 1) {
+ this->More &= ~XMOREC;
+ this->More |= 1;
+ dtrc(dprintf("XDI: correct MORE on REMOVE A(%02x) Id:%02x",
+ XDI_A_NR(a),Id));
+ }
+ }
+ if (Rc==OK_FC) {
+ a->FlowControlIdTable[Ch] = Id;
+ a->FlowControlSkipTable[Ch] = false;
+ this->Rc = Rc;
+ this->More &= ~(XBUSY | XMOREC);
+ this->complete=0xff;
+ xdi_xlog_rc_event (XDI_A_NR(a), Id, Ch, Rc, 1, a->IdTypeTable[this->No]);
+ CALLBACK(a, this);
+ return 0;
+ }
+ /*
+ New protocol code sends return codes that comes from release
+ of flow control condition marked with DIVA_RC_TYPE_OK_FC extended
+ information element type.
+ If like return code arrives then application is able to process
+ all return codes self and XDI should not cances return codes.
+ This return code does not decrement XMOREC partial return code
+ counter due to fact that it was no request for this return code,
+ also XMOREC was not incremented.
+ */
+ if (extended_info_type == DIVA_RC_TYPE_OK_FC) {
+ a->misc_flags_table[e_no] |= DIVA_MISC_FLAGS_NO_RC_CANCELLING;
+ this->Rc = Rc;
+ this->complete=0xff;
+ xdi_xlog_rc_event (XDI_A_NR(a), Id, Ch, Rc, 1, a->IdTypeTable[this->No]);
+ DBG_TRC(("XDI OK_FC A(%02x) Id:%02x Ch:%02x Rc:%02x",
+ XDI_A_NR(a), Id, Ch, Rc))
+ CALLBACK(a, this);
+ return 0;
+ }
+ cancel_rc = !(a->misc_flags_table[e_no] & DIVA_MISC_FLAGS_NO_RC_CANCELLING);
+ if (cancel_rc && (a->FlowControlIdTable[Ch] == Id))
+ {
+ a->FlowControlIdTable[Ch] = 0;
+ if ((Rc != OK) || !a->FlowControlSkipTable[Ch])
+ {
+ this->Rc = Rc;
+ if (Ch == this->ReqCh)
+ {
+ this->More &=~(XBUSY | XMOREC);
+ this->complete=0xff;
+ }
+ xdi_xlog_rc_event (XDI_A_NR(a), Id, Ch, Rc, 1, a->IdTypeTable[this->No]);
+ CALLBACK(a, this);
+ }
+ return 0;
+ }
+ if (this->More &XMOREC)
+ this->More--;
+ /* call the application callback function */
+ if (((!cancel_rc) || (this->More & XMOREF)) && !(this->More & XMOREC)) {
+ this->Rc = Rc;
+ this->More &=~XBUSY;
+ this->complete=0xff;
+ xdi_xlog_rc_event (XDI_A_NR(a), Id, Ch, Rc, 1, a->IdTypeTable[this->No]);
+ CALLBACK(a, this);
+ }
+ return 0;
+ }
+ /* if it's an ASSIGN return code check if it's a return */
+ /* code to an ASSIGN request from us */
+ if((Rc &0xf0)==ASSIGN_RC) {
+ e_no = get_assign(a, Ref);
+ if(e_no) {
+ this = entity_ptr(a,e_no);
+ this->Id = Id;
+ xdi_xlog_rc_event (XDI_A_NR(a), Id, Ch, Rc, 2, a->IdTypeTable[this->No]);
+ /* call the application callback function */
+ this->Rc = Rc;
+ this->More &=~XBUSY;
+ this->complete=0xff;
+#if defined(DIVA_ISTREAM) /* { */
+ if ((Rc == ASSIGN_OK) && a->ram_offset &&
+ (a->IdTypeTable[this->No] == NL_ID) &&
+ ((extended_info_type == DIVA_RC_TYPE_RX_DMA) ||
+ (extended_info_type == DIVA_RC_TYPE_CMA_PTR)) &&
+ extended_info) {
+ dword offset = (*(a->ram_offset)) (a);
+ dword tmp[2];
+ extended_info -= offset;
+#ifdef PLATFORM_GT_32BIT
+ a->ram_in_dw(a, (void*)ULongToPtr(extended_info), (dword*)&tmp[0], 2);
+#else
+ a->ram_in_dw(a, (void*)extended_info, (dword*)&tmp[0], 2);
+#endif
+ a->tx_stream[Id] = tmp[0];
+ a->rx_stream[Id] = tmp[1];
+ if (extended_info_type == DIVA_RC_TYPE_RX_DMA) {
+ DBG_TRC(("Id=0x%x RxDMA=%08x:%08x",
+ Id, a->tx_stream[Id], a->rx_stream[Id]))
+ a->misc_flags_table[this->No] |= DIVA_MISC_FLAGS_RX_DMA;
+ } else {
+ DBG_TRC(("Id=0x%x CMA=%08x:%08x",
+ Id, a->tx_stream[Id], a->rx_stream[Id]))
+ a->misc_flags_table[this->No] &= ~DIVA_MISC_FLAGS_RX_DMA;
+ a->rx_pos[Id] = 0;
+ a->rx_stream[Id] -= offset;
+ }
+ a->tx_pos[Id] = 0;
+ a->tx_stream[Id] -= offset;
+ } else {
+ a->tx_stream[Id] = 0;
+ a->rx_stream[Id] = 0;
+ a->misc_flags_table[this->No] &= ~DIVA_MISC_FLAGS_RX_DMA;
+ }
+#endif /* } */
+ CALLBACK(a, this);
+ if(Rc==ASSIGN_OK) {
+ a->IdTable[Id] = e_no;
+ }
+ else
+ {
+ free_entity(a, e_no);
+ for (i = 0; i < 256; i++)
+ {
+ if (a->FlowControlIdTable[i] == Id)
+ a->FlowControlIdTable[i] = 0;
+ }
+ a->IdTable[Id] = 0;
+ this->Id = 0;
+ }
+ return 1;
+ }
+ }
+ return 2;
+}
+/*------------------------------------------------------------------*/
+/* indication handler */
+/*------------------------------------------------------------------*/
+byte isdn_ind(ADAPTER * a,
+ byte Ind,
+ byte Id,
+ byte Ch,
+ PBUFFER * RBuffer,
+ byte MInd,
+ word MLength)
+{
+ ENTITY * this;
+ word clength;
+ word offset;
+ BUFFERS *R;
+ byte* cma = NULL;
+#ifdef USE_EXTENDED_DEBUGS
+ {
+ DBG_TRC(("<A%d Id=0x%x Ind=0x%x", ((ISDN_ADAPTER *)a->io)->ANum, Id, Ind))
+ }
+#else
+ dbug(dprintf("isdn_ind(Ind=%x,Id=%x,Ch=%x)",Ind,Id,Ch));
+#endif
+ if(a->IdTable[Id]) {
+ this = entity_ptr(a,a->IdTable[Id]);
+ this->IndCh = Ch;
+ xdi_xlog_ind (XDI_A_NR(a), Id, Ch, Ind,
+ 0/* rnr_valid */, 0 /* rnr */, a->IdTypeTable[this->No]);
+ /* if the Receive More flag is not yet set, this is the */
+ /* first buffer of the packet */
+ if(this->RCurrent==0xff) {
+ /* check for receive buffer chaining */
+ if(Ind==this->MInd) {
+ this->complete = 0;
+ this->Ind = MInd;
+ }
+ else {
+ this->complete = 1;
+ this->Ind = Ind;
+ }
+ /* call the application callback function for the receive */
+ /* look ahead */
+ this->RLength = MLength;
+#if defined(DIVA_ISTREAM)
+ if ((a->rx_stream[this->Id] ||
+ (a->misc_flags_table[this->No] & DIVA_MISC_FLAGS_RX_DMA)) &&
+ ((Ind == N_DATA) ||
+ (a->protocol_capabilities & PROTCAP_CMA_ALLPR))) {
+ PISDN_ADAPTER IoAdapter = (PISDN_ADAPTER)a->io ;
+ if (a->misc_flags_table[this->No] & DIVA_MISC_FLAGS_RX_DMA) {
+#if defined(DIVA_IDI_RX_DMA)
+ dword d;
+ diva_get_dma_map_entry (\
+ (struct _diva_dma_map_entry*)IoAdapter->dma_map,
+ (int)a->rx_stream[this->Id], (void**)&cma, &d);
+#else
+ cma = &a->stream_buffer[0];
+ cma[0] = cma[1] = cma[2] = cma[3] = 0;
+#endif
+ this->RLength = MLength = (word)*(dword*)cma;
+ cma += 4;
+ } else {
+ int final = 0;
+ cma = &a->stream_buffer[0];
+ this->RLength = MLength = (word)diva_istream_read (a,
+ Id,
+ cma,
+ sizeof(a->stream_buffer),
+ &final, NULL, NULL);
+ }
+ IoAdapter->RBuffer.length = min(MLength, (word)270);
+ if (IoAdapter->RBuffer.length != MLength) {
+ this->complete = 0;
+ } else {
+ this->complete = 1;
+ }
+ memcpy (IoAdapter->RBuffer.P, cma, IoAdapter->RBuffer.length) ;
+ this->RBuffer = (DBUFFER *)&IoAdapter->RBuffer ;
+ }
+#endif
+ if (!cma) {
+ a->ram_look_ahead(a, RBuffer, this);
+ }
+ this->RNum = 0;
+ CALLBACK(a, this);
+ /* map entity ptr, selector could be re-mapped by call to */
+ /* IDI from within callback */
+ this = entity_ptr(a,a->IdTable[Id]);
+ xdi_xlog_ind (XDI_A_NR(a), Id, Ch, Ind,
+ 1/* rnr_valid */, this->RNR/* rnr */, a->IdTypeTable[this->No]);
+ /* check for RNR */
+ if(this->RNR==1) {
+ this->RNR = 0;
+ return 1;
+ }
+ /* if no buffers are provided by the application, the */
+ /* application want to copy the data itself including */
+ /* N_MDATA/LL_MDATA chaining */
+ if(!this->RNR && !this->RNum) {
+ xdi_xlog_ind (XDI_A_NR(a), Id, Ch, Ind,
+ 2/* rnr_valid */, 0/* rnr */, a->IdTypeTable[this->No]);
+ return 0;
+ }
+ /* if there is no RNR, set the More flag */
+ this->RCurrent = 0;
+ this->ROffset = 0;
+ }
+ if(this->RNR==2) {
+ if(Ind!=this->MInd) {
+ this->RCurrent = 0xff;
+ this->RNR = 0;
+ }
+ return 0;
+ }
+ /* if we have received buffers from the application, copy */
+ /* the data into these buffers */
+ offset = 0;
+ R = PTR_R(a,this);
+ do {
+ if(this->ROffset==R[this->RCurrent].PLength) {
+ this->ROffset = 0;
+ this->RCurrent++;
+ }
+ if (cma) {
+ clength = min(MLength, (word)(R[this->RCurrent].PLength-this->ROffset));
+ } else {
+ clength = min(a->ram_inw(a, &RBuffer->length)-offset,
+ R[this->RCurrent].PLength-this->ROffset);
+ }
+ if(R[this->RCurrent].P) {
+ if (cma) {
+ memcpy (PTR_P(a,this,&R[this->RCurrent].P[this->ROffset]),
+ &cma[offset],
+ clength);
+ } else {
+ a->ram_in_buffer(a,
+ &RBuffer->P[offset],
+ PTR_P(a,this,&R[this->RCurrent].P[this->ROffset]),
+ clength);
+ }
+ }
+ offset +=clength;
+ this->ROffset +=clength;
+ if (cma) {
+ if (offset >= MLength) {
+ break;
+ }
+ continue;
+ }
+ } while(offset<(a->ram_inw(a, &RBuffer->length)));
+ /* if it's the last buffer of the packet, call the */
+ /* application callback function for the receive complete */
+ /* call */
+ if(Ind!=this->MInd) {
+ R[this->RCurrent].PLength = this->ROffset;
+ if(this->ROffset) this->RCurrent++;
+ this->RNum = this->RCurrent;
+ this->RCurrent = 0xff;
+ this->Ind = Ind;
+ this->complete = 2;
+ xdi_xlog_ind (XDI_A_NR(a), Id, Ch, Ind,
+ 3/* rnr_valid */, 0/* rnr */, a->IdTypeTable[this->No]);
+ CALLBACK(a, this);
+ }
+ return 0;
+ }
+ return 2;
+}
+#if defined(XDI_USE_XLOG)
+/* -----------------------------------------------------------
+ This function works in the same way as xlog on the
+ active board
+ ----------------------------------------------------------- */
+static void xdi_xlog (byte *msg, word code, int length) {
+ xdi_dbg_xlog ("\x00\x02", msg, code, length);
+}
+#endif
+/* -----------------------------------------------------------
+ This function writes the information about the Return Code
+ processing in the trace buffer. Trace ID is 221.
+ INPUT:
+ Adapter - system unicue adapter number (0 ... 255)
+ Id - Id of the entity that had sent this return code
+ Ch - Channel of the entity that had sent this return code
+ Rc - return code value
+ cb: (0...2)
+ switch (cb) {
+ case 0: printf ("DELIVERY"); break;
+ case 1: printf ("CALLBACK"); break;
+ case 2: printf ("ASSIGN"); break;
+ }
+ DELIVERY - have entered isdn_rc with this RC
+ CALLBACK - about to make callback to the application
+ for this RC
+ ASSIGN - about to make callback for RC that is result
+ of ASSIGN request. It is no DELIVERY message
+ before of this message
+ type - the Id that was sent by the ASSIGN of this entity.
+ This should be global Id like NL_ID, DSIG_ID, MAN_ID.
+ An unknown Id will cause "?-" in the front of the request.
+ In this case the log.c is to be extended.
+ ----------------------------------------------------------- */
+static void xdi_xlog_rc_event (byte Adapter,
+ byte Id, byte Ch, byte Rc, byte cb, byte type) {
+#if defined(XDI_USE_XLOG)
+ word LogInfo[4];
+ PUT_WORD(&LogInfo[0], ((word)Adapter | (word)(xdi_xlog_sec++ << 8)));
+ PUT_WORD(&LogInfo[1], ((word)Id | (word)(Ch << 8)));
+ PUT_WORD(&LogInfo[2], ((word)Rc | (word)(type << 8)));
+ PUT_WORD(&LogInfo[3], cb);
+ xdi_xlog ((byte*)&LogInfo[0], 221, sizeof(LogInfo));
+#endif
+}
+/* ------------------------------------------------------------------------
+ This function writes the information about the request processing
+ in the trace buffer. Trace ID is 220.
+ INPUT:
+ Adapter - system unicue adapter number (0 ... 255)
+ Id - Id of the entity that had sent this request
+ Ch - Channel of the entity that had sent this request
+ Req - Code of the request
+ type - the Id that was sent by the ASSIGN of this entity.
+ This should be global Id like NL_ID, DSIG_ID, MAN_ID.
+ An unknown Id will cause "?-" in the front of the request.
+ In this case the log.c is to be extended.
+ ------------------------------------------------------------------------ */
+static void xdi_xlog_request (byte Adapter, byte Id,
+ byte Ch, byte Req, byte type) {
+#if defined(XDI_USE_XLOG)
+ word LogInfo[3];
+ PUT_WORD(&LogInfo[0], ((word)Adapter | (word)(xdi_xlog_sec++ << 8)));
+ PUT_WORD(&LogInfo[1], ((word)Id | (word)(Ch << 8)));
+ PUT_WORD(&LogInfo[2], ((word)Req | (word)(type << 8)));
+ xdi_xlog ((byte*)&LogInfo[0], 220, sizeof(LogInfo));
+#endif
+}
+/* ------------------------------------------------------------------------
+ This function writes the information about the indication processing
+ in the trace buffer. Trace ID is 222.
+ INPUT:
+ Adapter - system unicue adapter number (0 ... 255)
+ Id - Id of the entity that had sent this indication
+ Ch - Channel of the entity that had sent this indication
+ Ind - Code of the indication
+ rnr_valid: (0 .. 3) supported
+ switch (rnr_valid) {
+ case 0: printf ("DELIVERY"); break;
+ case 1: printf ("RNR=%d", rnr);
+ case 2: printf ("RNum=0");
+ case 3: printf ("COMPLETE");
+ }
+ DELIVERY - indication entered isdn_rc function
+ RNR=... - application had returned RNR=... after the
+ look ahead callback
+ RNum=0 - aplication had not returned any buffer to copy
+ this indication and will copy it self
+ COMPLETE - XDI had copied the data to the buffers provided
+ bu the application and is about to issue the
+ final callback
+ rnr: Look case 1 of the rnr_valid
+ type: the Id that was sent by the ASSIGN of this entity. This should
+ be global Id like NL_ID, DSIG_ID, MAN_ID. An unknown Id will
+ cause "?-" in the front of the request. In this case the
+ log.c is to be extended.
+ ------------------------------------------------------------------------ */
+static void xdi_xlog_ind (byte Adapter,
+ byte Id,
+ byte Ch,
+ byte Ind,
+ byte rnr_valid,
+ byte rnr,
+ byte type) {
+#if defined(XDI_USE_XLOG)
+ word LogInfo[4];
+ PUT_WORD(&LogInfo[0], ((word)Adapter | (word)(xdi_xlog_sec++ << 8)));
+ PUT_WORD(&LogInfo[1], ((word)Id | (word)(Ch << 8)));
+ PUT_WORD(&LogInfo[2], ((word)Ind | (word)(type << 8)));
+ PUT_WORD(&LogInfo[3], ((word)rnr | (word)(rnr_valid << 8)));
+ xdi_xlog ((byte*)&LogInfo[0], 222, sizeof(LogInfo));
+#endif
+}
diff --git a/drivers/isdn/hardware/eicon/di.h b/drivers/isdn/hardware/eicon/di.h
new file mode 100644
index 0000000..dcf37b1
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/di.h
@@ -0,0 +1,118 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+/*
+ * some macros for detailed trace management
+ */
+#include "di_dbg.h"
+/*****************************************************************************/
+#define XMOREC 0x1f
+#define XMOREF 0x20
+#define XBUSY 0x40
+#define RMORE 0x80
+#define DIVA_MISC_FLAGS_REMOVE_PENDING 0x01
+#define DIVA_MISC_FLAGS_NO_RC_CANCELLING 0x02
+#define DIVA_MISC_FLAGS_RX_DMA 0x04
+ /* structure for all information we have to keep on a per */
+ /* adapater basis */
+typedef struct adapter_s ADAPTER;
+struct adapter_s {
+ void * io;
+ byte IdTable[256];
+ byte IdTypeTable[256];
+ byte FlowControlIdTable[256];
+ byte FlowControlSkipTable[256];
+ byte ReadyInt;
+ byte RcExtensionSupported;
+ byte misc_flags_table[256];
+ dword protocol_capabilities;
+ byte ( * ram_in)(ADAPTER * a, void * adr);
+ word ( * ram_inw)(ADAPTER * a, void * adr);
+ void (* ram_in_buffer)(ADAPTER * a, void * adr, void * P, word length);
+ void (* ram_look_ahead)(ADAPTER * a, PBUFFER * RBuffer, ENTITY * e);
+ void ( * ram_out)(ADAPTER * a, void * adr, byte data);
+ void ( * ram_outw)(ADAPTER * a, void * adr, word data);
+ void (* ram_out_buffer)(ADAPTER * a, void * adr, void * P, word length);
+ void ( * ram_inc)(ADAPTER * a, void * adr);
+#if defined(DIVA_ISTREAM)
+ dword rx_stream[256];
+ dword tx_stream[256];
+ word tx_pos[256];
+ word rx_pos[256];
+ byte stream_buffer[2512];
+ dword ( * ram_offset)(ADAPTER * a);
+ void ( * ram_out_dw) (ADAPTER *a,
+ void *addr,
+ const dword* data,
+ int dwords);
+ void ( * ram_in_dw) (ADAPTER *a,
+ void *addr,
+ dword* data,
+ int dwords);
+ void ( * istream_wakeup)(ADAPTER* a);
+#else
+ byte stream_buffer[4];
+#endif
+};
+/*------------------------------------------------------------------*/
+/* public functions of IDI common code */
+/*------------------------------------------------------------------*/
+void pr_out(ADAPTER * a);
+byte pr_dpc(ADAPTER * a);
+byte scom_test_int(ADAPTER * a);
+void scom_clear_int(ADAPTER * a);
+/*------------------------------------------------------------------*/
+/* OS specific functions used by IDI common code */
+/*------------------------------------------------------------------*/
+void free_entity(ADAPTER * a, byte e_no);
+void assign_queue(ADAPTER * a, byte e_no, word ref);
+byte get_assign(ADAPTER * a, word ref);
+void req_queue(ADAPTER * a, byte e_no);
+byte look_req(ADAPTER * a);
+void next_req(ADAPTER * a);
+ENTITY * entity_ptr(ADAPTER * a, byte e_no);
+#if defined(DIVA_ISTREAM)
+struct _diva_xdi_stream_interface;
+void diva_xdi_provide_istream_info (ADAPTER* a,
+ struct _diva_xdi_stream_interface* pI);
+void pr_stream (ADAPTER * a);
+int diva_istream_write (void* context,
+ int Id,
+ void* data,
+ int length,
+ int final,
+ byte usr1,
+ byte usr2);
+int diva_istream_read (void* context,
+ int Id,
+ void* data,
+ int max_length,
+ int* final,
+ byte* usr1,
+ byte* usr2);
+#if defined(DIVA_IDI_RX_DMA)
+#include "diva_dma.h"
+#endif
+#endif
diff --git a/drivers/isdn/hardware/eicon/di_dbg.h b/drivers/isdn/hardware/eicon/di_dbg.h
new file mode 100644
index 0000000..d576ff3
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/di_dbg.h
@@ -0,0 +1,37 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+#ifndef __DIVA_DI_DBG_INC__
+#define __DIVA_DI_DBG_INC__
+#if !defined (dtrc)
+#define dtrc(a)
+#endif
+#if !defined (dbug)
+#define dbug(a)
+#endif
+#if !defined USE_EXTENDED_DEBUGS
+extern void (*dprintf)(char*, ...);
+#endif
+#endif
diff --git a/drivers/isdn/hardware/eicon/di_defs.h b/drivers/isdn/hardware/eicon/di_defs.h
new file mode 100644
index 0000000..4c2f612
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/di_defs.h
@@ -0,0 +1,181 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+#ifndef _DI_DEFS_
+#define _DI_DEFS_
+ /* typedefs for our data structures */
+typedef struct get_name_s GET_NAME;
+/* The entity_s structure is used to pass all
+ parameters between application and IDI */
+typedef struct entity_s ENTITY;
+typedef struct buffers_s BUFFERS;
+typedef struct postcall_s POSTCALL;
+typedef struct get_para_s GET_PARA;
+#define BOARD_NAME_LENGTH 9
+#define IDI_CALL_LINK_T
+#define IDI_CALL_ENTITY_T
+/* typedef void ( * IDI_CALL)(ENTITY *); */
+/* --------------------------------------------------------
+ IDI_CALL
+ -------------------------------------------------------- */
+typedef void (IDI_CALL_LINK_T * IDI_CALL)(ENTITY IDI_CALL_ENTITY_T *);
+typedef struct {
+ word length; /* length of data/parameter field */
+ byte P[270]; /* data/parameter field */
+} DBUFFER;
+struct get_name_s {
+ word command; /* command = 0x0100 */
+ byte name[BOARD_NAME_LENGTH];
+};
+struct postcall_s {
+ word command; /* command = 0x0300 */
+ word dummy; /* not used */
+ void ( * callback)(void *); /* call back */
+ void *context; /* context pointer */
+};
+#define REQ_PARA 0x0600 /* request command line parameters */
+#define REQ_PARA_LEN 1 /* number of data bytes */
+#define L1_STARTUP_DOWN_POS 0 /* '-y' command line parameter in......*/
+#define L1_STARTUP_DOWN_MSK 0x01 /* first byte position (index 0) with value 0x01 */
+struct get_para_s {
+ word command; /* command = 0x0600 */
+ byte len; /* max length of para field in bytes */
+ byte para[REQ_PARA_LEN]; /* parameter field */
+};
+struct buffers_s {
+ word PLength;
+ byte * P;
+};
+struct entity_s {
+ byte Req; /* pending request */
+ byte Rc; /* return code received */
+ byte Ind; /* indication received */
+ byte ReqCh; /* channel of current Req */
+ byte RcCh; /* channel of current Rc */
+ byte IndCh; /* channel of current Ind */
+ byte Id; /* ID used by this entity */
+ byte GlobalId; /* reserved field */
+ byte XNum; /* number of X-buffers */
+ byte RNum; /* number of R-buffers */
+ BUFFERS * X; /* pointer to X-buffer list */
+ BUFFERS * R; /* pointer to R-buffer list */
+ word RLength; /* length of current R-data */
+ DBUFFER * RBuffer; /* buffer of current R-data */
+ byte RNR; /* receive not ready flag */
+ byte complete; /* receive complete status */
+ IDI_CALL callback;
+ word user[2];
+ /* fields used by the driver internally */
+ byte No; /* entity number */
+ byte reserved2; /* reserved field */
+ byte More; /* R/X More flags */
+ byte MInd; /* MDATA coding for this ID */
+ byte XCurrent; /* current transmit buffer */
+ byte RCurrent; /* current receive buffer */
+ word XOffset; /* offset in x-buffer */
+ word ROffset; /* offset in r-buffer */
+};
+typedef struct {
+ byte type;
+ byte channels;
+ word features;
+ IDI_CALL request;
+} DESCRIPTOR;
+ /* descriptor type field coding */
+#define IDI_ADAPTER_S 1
+#define IDI_ADAPTER_PR 2
+#define IDI_ADAPTER_DIVA 3
+#define IDI_ADAPTER_MAESTRA 4
+#define IDI_VADAPTER 0x40
+#define IDI_DRIVER 0x80
+#define IDI_DADAPTER 0xfd
+#define IDI_DIDDPNP 0xfe
+#define IDI_DIMAINT 0xff
+ /* Hardware IDs ISA PNP */
+#define HW_ID_DIVA_PRO 3 /* same as IDI_ADAPTER_DIVA */
+#define HW_ID_MAESTRA 4 /* same as IDI_ADAPTER_MAESTRA */
+#define HW_ID_PICCOLA 5
+#define HW_ID_DIVA_PRO20 6
+#define HW_ID_DIVA20 7
+#define HW_ID_DIVA_PRO20_U 8
+#define HW_ID_DIVA20_U 9
+#define HW_ID_DIVA30 10
+#define HW_ID_DIVA30_U 11
+ /* Hardware IDs PCI */
+#define HW_ID_EICON_PCI 0x1133
+#define HW_ID_SIEMENS_PCI 0x8001 /* unused SubVendor ID for Siemens Cornet-N cards */
+#define HW_ID_PROTTYPE_CORNETN 0x0014 /* SubDevice ID for Siemens Cornet-N cards */
+#define HW_ID_FUJITSU_SIEMENS_PCI 0x110A /* SubVendor ID for Fujitsu Siemens */
+#define HW_ID_GS03_PCI 0x0021 /* SubDevice ID for Fujitsu Siemens ISDN S0 card */
+#define HW_ID_DIVA_PRO20_PCI 0xe001
+#define HW_ID_DIVA20_PCI 0xe002
+#define HW_ID_DIVA_PRO20_PCI_U 0xe003
+#define HW_ID_DIVA20_PCI_U 0xe004
+#define HW_ID_DIVA201_PCI 0xe005
+#define HW_ID_DIVA_CT_ST 0xe006
+#define HW_ID_DIVA_CT_U 0xe007
+#define HW_ID_DIVA_CTL_ST 0xe008
+#define HW_ID_DIVA_CTL_U 0xe009
+#define HW_ID_DIVA_ISDN_V90_PCI 0xe00a
+#define HW_ID_DIVA202_PCI_ST 0xe00b
+#define HW_ID_DIVA202_PCI_U 0xe00c
+#define HW_ID_DIVA_PRO30_PCI 0xe00d
+#define HW_ID_MAESTRA_PCI 0xe010
+#define HW_ID_MAESTRAQ_PCI 0xe012
+#define HW_ID_DSRV_Q8M_V2_PCI 0xe013
+#define HW_ID_MAESTRAP_PCI 0xe014
+#define HW_ID_DSRV_P30M_V2_PCI 0xe015
+#define HW_ID_DSRV_VOICE_Q8M_PCI 0xe016
+#define HW_ID_DSRV_VOICE_Q8M_V2_PCI 0xe017
+#define HW_ID_DSRV_B2M_V2_PCI 0xe018
+#define HW_ID_DSRV_VOICE_P30M_V2_PCI 0xe019
+#define HW_ID_DSRV_B2F_PCI 0xe01a
+#define HW_ID_DSRV_VOICE_B2M_V2_PCI 0xe01b
+ /* Hardware IDs USB */
+#define EICON_USB_VENDOR_ID 0x071D
+#define HW_ID_DIVA_USB_REV1 0x1000
+#define HW_ID_DIVA_USB_REV2 0x1003
+#define HW_ID_TELEDAT_SURF_USB_REV2 0x1004
+#define HW_ID_TELEDAT_SURF_USB_REV1 0x2000
+/* --------------------------------------------------------------------------
+ Adapter array change notification framework
+ -------------------------------------------------------------------------- */
+typedef void (IDI_CALL_LINK_T* didd_adapter_change_callback_t)( void IDI_CALL_ENTITY_T * context, DESCRIPTOR* adapter, int removal);
+/* -------------------------------------------------------------------------- */
+#define DI_VOICE 0x0 /* obsolete define */
+#define DI_FAX3 0x1
+#define DI_MODEM 0x2
+#define DI_POST 0x4
+#define DI_V110 0x8
+#define DI_V120 0x10
+#define DI_POTS 0x20
+#define DI_CODEC 0x40
+#define DI_MANAGE 0x80
+#define DI_V_42 0x0100
+#define DI_EXTD_FAX 0x0200 /* Extended FAX (ECM, 2D, T.6, Polling) */
+#define DI_AT_PARSER 0x0400 /* Build-in AT Parser in the L2 */
+#define DI_VOICE_OVER_IP 0x0800 /* Voice over IP support */
+typedef void (IDI_CALL_LINK_T* _IDI_CALL)(void*, ENTITY*);
+#endif
diff --git a/drivers/isdn/hardware/eicon/did_vers.h b/drivers/isdn/hardware/eicon/did_vers.h
new file mode 100644
index 0000000..538c590
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/did_vers.h
@@ -0,0 +1,26 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+static char diva_didd_common_code_build[] = "102-51";
diff --git a/drivers/isdn/hardware/eicon/diddfunc.c b/drivers/isdn/hardware/eicon/diddfunc.c
new file mode 100644
index 0000000..3029234
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/diddfunc.c
@@ -0,0 +1,115 @@
+/* $Id: diddfunc.c,v 1.14.6.2 2004/08/28 20:03:53 armin Exp $
+ *
+ * DIDD Interface module for Eicon active cards.
+ *
+ * Functions are in dadapter.c
+ *
+ * Copyright 2002-2003 by Armin Schindler (mac@melware.de)
+ * Copyright 2002-2003 Cytronics & Melware (info@melware.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include "platform.h"
+#include "di_defs.h"
+#include "dadapter.h"
+#include "divasync.h"
+
+#define DBG_MINIMUM (DL_LOG + DL_FTL + DL_ERR)
+#define DBG_DEFAULT (DBG_MINIMUM + DL_XLOG + DL_REG)
+
+
+extern void DIVA_DIDD_Read(void *, int);
+extern char *DRIVERRELEASE_DIDD;
+static dword notify_handle;
+static DESCRIPTOR _DAdapter;
+
+/*
+ * didd callback function
+ */
+static void *didd_callback(void *context, DESCRIPTOR * adapter,
+ int removal)
+{
+ if (adapter->type == IDI_DADAPTER) {
+ DBG_ERR(("Notification about IDI_DADAPTER change ! Oops."))
+ return (NULL);
+ } else if (adapter->type == IDI_DIMAINT) {
+ if (removal) {
+ DbgDeregister();
+ } else {
+ DbgRegister("DIDD", DRIVERRELEASE_DIDD, DBG_DEFAULT);
+ }
+ }
+ return (NULL);
+}
+
+/*
+ * connect to didd
+ */
+static int DIVA_INIT_FUNCTION connect_didd(void)
+{
+ int x = 0;
+ int dadapter = 0;
+ IDI_SYNC_REQ req;
+ DESCRIPTOR DIDD_Table[MAX_DESCRIPTORS];
+
+ DIVA_DIDD_Read(DIDD_Table, sizeof(DIDD_Table));
+
+ for (x = 0; x < MAX_DESCRIPTORS; x++) {
+ if (DIDD_Table[x].type == IDI_DADAPTER) { /* DADAPTER found */
+ dadapter = 1;
+ memcpy(&_DAdapter, &DIDD_Table[x], sizeof(_DAdapter));
+ req.didd_notify.e.Req = 0;
+ req.didd_notify.e.Rc =
+ IDI_SYNC_REQ_DIDD_REGISTER_ADAPTER_NOTIFY;
+ req.didd_notify.info.callback = (void *)didd_callback;
+ req.didd_notify.info.context = NULL;
+ _DAdapter.request((ENTITY *) & req);
+ if (req.didd_notify.e.Rc != 0xff)
+ return (0);
+ notify_handle = req.didd_notify.info.handle;
+ } else if (DIDD_Table[x].type == IDI_DIMAINT) { /* MAINT found */
+ DbgRegister("DIDD", DRIVERRELEASE_DIDD, DBG_DEFAULT);
+ }
+ }
+ return (dadapter);
+}
+
+/*
+ * disconnect from didd
+ */
+static void DIVA_EXIT_FUNCTION disconnect_didd(void)
+{
+ IDI_SYNC_REQ req;
+
+ req.didd_notify.e.Req = 0;
+ req.didd_notify.e.Rc = IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER_NOTIFY;
+ req.didd_notify.info.handle = notify_handle;
+ _DAdapter.request((ENTITY *) & req);
+}
+
+/*
+ * init
+ */
+int DIVA_INIT_FUNCTION diddfunc_init(void)
+{
+ diva_didd_load_time_init();
+
+ if (!connect_didd()) {
+ DBG_ERR(("init: failed to connect to DIDD."))
+ diva_didd_load_time_finit();
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * finit
+ */
+void DIVA_EXIT_FUNCTION diddfunc_finit(void)
+{
+ DbgDeregister();
+ disconnect_didd();
+ diva_didd_load_time_finit();
+}
diff --git a/drivers/isdn/hardware/eicon/diva.c b/drivers/isdn/hardware/eicon/diva.c
new file mode 100644
index 0000000..1403a54
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/diva.c
@@ -0,0 +1,657 @@
+/* $Id: diva.c,v 1.21.4.1 2004/05/08 14:33:43 armin Exp $ */
+
+#define CARDTYPE_H_WANT_DATA 1
+#define CARDTYPE_H_WANT_IDI_DATA 0
+#define CARDTYPE_H_WANT_RESOURCE_DATA 0
+#define CARDTYPE_H_WANT_FILE_DATA 0
+
+#include "platform.h"
+#include "debuglib.h"
+#include "cardtype.h"
+#include "pc.h"
+#include "di_defs.h"
+#include "di.h"
+#include "io.h"
+#include "pc_maint.h"
+#include "xdi_msg.h"
+#include "xdi_adapter.h"
+#include "diva_pci.h"
+#include "diva.h"
+
+#ifdef CONFIG_ISDN_DIVAS_PRIPCI
+#include "os_pri.h"
+#endif
+#ifdef CONFIG_ISDN_DIVAS_BRIPCI
+#include "os_bri.h"
+#include "os_4bri.h"
+#endif
+
+PISDN_ADAPTER IoAdapters[MAX_ADAPTER];
+extern IDI_CALL Requests[MAX_ADAPTER];
+extern int create_adapter_proc(diva_os_xdi_adapter_t * a);
+extern void remove_adapter_proc(diva_os_xdi_adapter_t * a);
+
+#define DivaIdiReqFunc(N) \
+static void DivaIdiRequest##N(ENTITY *e) \
+{ if ( IoAdapters[N] ) (* IoAdapters[N]->DIRequest)(IoAdapters[N], e) ; }
+
+/*
+** Create own 32 Adapters
+*/
+DivaIdiReqFunc(0)
+DivaIdiReqFunc(1)
+DivaIdiReqFunc(2)
+DivaIdiReqFunc(3)
+DivaIdiReqFunc(4)
+DivaIdiReqFunc(5)
+DivaIdiReqFunc(6)
+DivaIdiReqFunc(7)
+DivaIdiReqFunc(8)
+DivaIdiReqFunc(9)
+DivaIdiReqFunc(10)
+DivaIdiReqFunc(11)
+DivaIdiReqFunc(12)
+DivaIdiReqFunc(13)
+DivaIdiReqFunc(14)
+DivaIdiReqFunc(15)
+DivaIdiReqFunc(16)
+DivaIdiReqFunc(17)
+DivaIdiReqFunc(18)
+DivaIdiReqFunc(19)
+DivaIdiReqFunc(20)
+DivaIdiReqFunc(21)
+DivaIdiReqFunc(22)
+DivaIdiReqFunc(23)
+DivaIdiReqFunc(24)
+DivaIdiReqFunc(25)
+DivaIdiReqFunc(26)
+DivaIdiReqFunc(27)
+DivaIdiReqFunc(28)
+DivaIdiReqFunc(29)
+DivaIdiReqFunc(30)
+DivaIdiReqFunc(31)
+
+/*
+** LOCALS
+*/
+static LIST_HEAD(adapter_queue);
+
+typedef struct _diva_get_xlog {
+ word command;
+ byte req;
+ byte rc;
+ byte data[sizeof(struct mi_pc_maint)];
+} diva_get_xlog_t;
+
+typedef struct _diva_supported_cards_info {
+ int CardOrdinal;
+ diva_init_card_proc_t init_card;
+} diva_supported_cards_info_t;
+
+static diva_supported_cards_info_t divas_supported_cards[] = {
+#ifdef CONFIG_ISDN_DIVAS_PRIPCI
+ /*
+ PRI Cards
+ */
+ {CARDTYPE_DIVASRV_P_30M_PCI, diva_pri_init_card},
+ /*
+ PRI Rev.2 Cards
+ */
+ {CARDTYPE_DIVASRV_P_30M_V2_PCI, diva_pri_init_card},
+ /*
+ PRI Rev.2 VoIP Cards
+ */
+ {CARDTYPE_DIVASRV_VOICE_P_30M_V2_PCI, diva_pri_init_card},
+#endif
+#ifdef CONFIG_ISDN_DIVAS_BRIPCI
+ /*
+ 4BRI Rev 1 Cards
+ */
+ {CARDTYPE_DIVASRV_Q_8M_PCI, diva_4bri_init_card},
+ {CARDTYPE_DIVASRV_VOICE_Q_8M_PCI, diva_4bri_init_card},
+ /*
+ 4BRI Rev 2 Cards
+ */
+ {CARDTYPE_DIVASRV_Q_8M_V2_PCI, diva_4bri_init_card},
+ {CARDTYPE_DIVASRV_VOICE_Q_8M_V2_PCI, diva_4bri_init_card},
+ /*
+ 4BRI Based BRI Rev 2 Cards
+ */
+ {CARDTYPE_DIVASRV_B_2M_V2_PCI, diva_4bri_init_card},
+ {CARDTYPE_DIVASRV_B_2F_PCI, diva_4bri_init_card},
+ {CARDTYPE_DIVASRV_VOICE_B_2M_V2_PCI, diva_4bri_init_card},
+ /*
+ BRI
+ */
+ {CARDTYPE_MAESTRA_PCI, diva_bri_init_card},
+#endif
+
+ /*
+ EOL
+ */
+ {-1}
+};
+
+static void diva_init_request_array(void);
+static void *divas_create_pci_card(int handle, void *pci_dev_handle);
+
+static diva_os_spin_lock_t adapter_lock;
+
+static int diva_find_free_adapters(int base, int nr)
+{
+ int i;
+
+ for (i = 0; i < nr; i++) {
+ if (IoAdapters[base + i]) {
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+static diva_os_xdi_adapter_t *diva_q_get_next(struct list_head * what)
+{
+ diva_os_xdi_adapter_t *a = NULL;
+
+ if (what && (what->next != &adapter_queue))
+ a = list_entry(what->next, diva_os_xdi_adapter_t, link);
+
+ return(a);
+}
+
+/* --------------------------------------------------------------------------
+ Add card to the card list
+ -------------------------------------------------------------------------- */
+void *diva_driver_add_card(void *pdev, unsigned long CardOrdinal)
+{
+ diva_os_spin_lock_magic_t old_irql;
+ diva_os_xdi_adapter_t *pdiva, *pa;
+ int i, j, max, nr;
+
+ for (i = 0; divas_supported_cards[i].CardOrdinal != -1; i++) {
+ if (divas_supported_cards[i].CardOrdinal == CardOrdinal) {
+ if (!(pdiva = divas_create_pci_card(i, pdev))) {
+ return NULL;
+ }
+ switch (CardOrdinal) {
+ case CARDTYPE_DIVASRV_Q_8M_PCI:
+ case CARDTYPE_DIVASRV_VOICE_Q_8M_PCI:
+ case CARDTYPE_DIVASRV_Q_8M_V2_PCI:
+ case CARDTYPE_DIVASRV_VOICE_Q_8M_V2_PCI:
+ max = MAX_ADAPTER - 4;
+ nr = 4;
+ break;
+
+ default:
+ max = MAX_ADAPTER;
+ nr = 1;
+ }
+
+ diva_os_enter_spin_lock(&adapter_lock, &old_irql, "add card");
+
+ for (i = 0; i < max; i++) {
+ if (!diva_find_free_adapters(i, nr)) {
+ pdiva->controller = i + 1;
+ pdiva->xdi_adapter.ANum = pdiva->controller;
+ IoAdapters[i] = &pdiva->xdi_adapter;
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "add card");
+ create_adapter_proc(pdiva); /* add adapter to proc file system */
+
+ DBG_LOG(("add %s:%d",
+ CardProperties
+ [CardOrdinal].Name,
+ pdiva->controller))
+
+ diva_os_enter_spin_lock(&adapter_lock, &old_irql, "add card");
+ pa = pdiva;
+ for (j = 1; j < nr; j++) { /* slave adapters, if any */
+ pa = diva_q_get_next(&pa->link);
+ if (pa && !pa->interface.cleanup_adapter_proc) {
+ pa->controller = i + 1 + j;
+ pa->xdi_adapter.ANum = pa->controller;
+ IoAdapters[i + j] = &pa->xdi_adapter;
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "add card");
+ DBG_LOG(("add slave adapter (%d)",
+ pa->controller))
+ create_adapter_proc(pa); /* add adapter to proc file system */
+ diva_os_enter_spin_lock(&adapter_lock, &old_irql, "add card");
+ } else {
+ DBG_ERR(("slave adapter problem"))
+ break;
+ }
+ }
+
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "add card");
+ return (pdiva);
+ }
+ }
+
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "add card");
+
+ /*
+ Not able to add adapter - remove it and return error
+ */
+ DBG_ERR(("can not alloc request array"))
+ diva_driver_remove_card(pdiva);
+
+ return NULL;
+ }
+ }
+
+ return NULL;
+}
+
+/* --------------------------------------------------------------------------
+ Called on driver load, MAIN, main, DriverEntry
+ -------------------------------------------------------------------------- */
+int divasa_xdi_driver_entry(void)
+{
+ diva_os_initialize_spin_lock(&adapter_lock, "adapter");
+ memset(&IoAdapters[0], 0x00, sizeof(IoAdapters));
+ diva_init_request_array();
+
+ return (0);
+}
+
+/* --------------------------------------------------------------------------
+ Remove adapter from list
+ -------------------------------------------------------------------------- */
+static diva_os_xdi_adapter_t *get_and_remove_from_queue(void)
+{
+ diva_os_spin_lock_magic_t old_irql;
+ diva_os_xdi_adapter_t *a = NULL;
+
+ diva_os_enter_spin_lock(&adapter_lock, &old_irql, "driver_unload");
+
+ if (!list_empty(&adapter_queue)) {
+ a = list_entry(adapter_queue.next, diva_os_xdi_adapter_t, link);
+ list_del(adapter_queue.next);
+ }
+
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "driver_unload");
+ return (a);
+}
+
+/* --------------------------------------------------------------------------
+ Remove card from the card list
+ -------------------------------------------------------------------------- */
+void diva_driver_remove_card(void *pdiva)
+{
+ diva_os_spin_lock_magic_t old_irql;
+ diva_os_xdi_adapter_t *a[4];
+ diva_os_xdi_adapter_t *pa;
+ int i;
+
+ pa = a[0] = (diva_os_xdi_adapter_t *) pdiva;
+ a[1] = a[2] = a[3] = NULL;
+
+ diva_os_enter_spin_lock(&adapter_lock, &old_irql, "remode adapter");
+
+ for (i = 1; i < 4; i++) {
+ if ((pa = diva_q_get_next(&pa->link))
+ && !pa->interface.cleanup_adapter_proc) {
+ a[i] = pa;
+ } else {
+ break;
+ }
+ }
+
+ for (i = 0; ((i < 4) && a[i]); i++) {
+ list_del(&a[i]->link);
+ }
+
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "driver_unload");
+
+ (*(a[0]->interface.cleanup_adapter_proc)) (a[0]);
+
+ for (i = 0; i < 4; i++) {
+ if (a[i]) {
+ if (a[i]->controller) {
+ DBG_LOG(("remove adapter (%d)",
+ a[i]->controller)) IoAdapters[a[i]->controller - 1] = NULL;
+ remove_adapter_proc(a[i]);
+ }
+ diva_os_free(0, a[i]);
+ }
+ }
+}
+
+/* --------------------------------------------------------------------------
+ Create diva PCI adapter and init internal adapter structures
+ -------------------------------------------------------------------------- */
+static void *divas_create_pci_card(int handle, void *pci_dev_handle)
+{
+ diva_supported_cards_info_t *pI = &divas_supported_cards[handle];
+ diva_os_spin_lock_magic_t old_irql;
+ diva_os_xdi_adapter_t *a;
+
+ DBG_LOG(("found %d-%s", pI->CardOrdinal, CardProperties[pI->CardOrdinal].Name))
+
+ if (!(a = (diva_os_xdi_adapter_t *) diva_os_malloc(0, sizeof(*a)))) {
+ DBG_ERR(("A: can't alloc adapter"));
+ return NULL;
+ }
+
+ memset(a, 0x00, sizeof(*a));
+
+ a->CardIndex = handle;
+ a->CardOrdinal = pI->CardOrdinal;
+ a->Bus = DIVAS_XDI_ADAPTER_BUS_PCI;
+ a->xdi_adapter.cardType = a->CardOrdinal;
+ a->resources.pci.bus = diva_os_get_pci_bus(pci_dev_handle);
+ a->resources.pci.func = diva_os_get_pci_func(pci_dev_handle);
+ a->resources.pci.hdev = pci_dev_handle;
+
+ /*
+ Add master adapter first, so slave adapters will receive higher
+ numbers as master adapter
+ */
+ diva_os_enter_spin_lock(&adapter_lock, &old_irql, "found_pci_card");
+ list_add_tail(&a->link, &adapter_queue);
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "found_pci_card");
+
+ if ((*(pI->init_card)) (a)) {
+ diva_os_enter_spin_lock(&adapter_lock, &old_irql, "found_pci_card");
+ list_del(&a->link);
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "found_pci_card");
+ diva_os_free(0, a);
+ DBG_ERR(("A: can't get adapter resources"));
+ return NULL;
+ }
+
+ return (a);
+}
+
+/* --------------------------------------------------------------------------
+ Called on driver unload FINIT, finit, Unload
+ -------------------------------------------------------------------------- */
+void divasa_xdi_driver_unload(void)
+{
+ diva_os_xdi_adapter_t *a;
+
+ while ((a = get_and_remove_from_queue())) {
+ if (a->interface.cleanup_adapter_proc) {
+ (*(a->interface.cleanup_adapter_proc)) (a);
+ }
+ if (a->controller) {
+ IoAdapters[a->controller - 1] = NULL;
+ remove_adapter_proc(a);
+ }
+ diva_os_free(0, a);
+ }
+ diva_os_destroy_spin_lock(&adapter_lock, "adapter");
+}
+
+/*
+** Receive and process command from user mode utility
+*/
+void *diva_xdi_open_adapter(void *os_handle, const void __user *src,
+ int length,
+ divas_xdi_copy_from_user_fn_t cp_fn)
+{
+ diva_xdi_um_cfg_cmd_t msg;
+ diva_os_xdi_adapter_t *a = NULL;
+ diva_os_spin_lock_magic_t old_irql;
+ struct list_head *tmp;
+
+ if (length < sizeof(diva_xdi_um_cfg_cmd_t)) {
+ DBG_ERR(("A: A(?) open, msg too small (%d < %d)",
+ length, sizeof(diva_xdi_um_cfg_cmd_t)))
+ return NULL;
+ }
+ if ((*cp_fn) (os_handle, &msg, src, sizeof(msg)) <= 0) {
+ DBG_ERR(("A: A(?) open, write error"))
+ return NULL;
+ }
+ diva_os_enter_spin_lock(&adapter_lock, &old_irql, "open_adapter");
+ list_for_each(tmp, &adapter_queue) {
+ a = list_entry(tmp, diva_os_xdi_adapter_t, link);
+ if (a->controller == (int)msg.adapter)
+ break;
+ a = NULL;
+ }
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "open_adapter");
+
+ if (!a) {
+ DBG_ERR(("A: A(%d) open, adapter not found", msg.adapter))
+ }
+
+ return (a);
+}
+
+/*
+** Easy cleanup mailbox status
+*/
+void diva_xdi_close_adapter(void *adapter, void *os_handle)
+{
+ diva_os_xdi_adapter_t *a = (diva_os_xdi_adapter_t *) adapter;
+
+ a->xdi_mbox.status &= ~DIVA_XDI_MBOX_BUSY;
+ if (a->xdi_mbox.data) {
+ diva_os_free(0, a->xdi_mbox.data);
+ a->xdi_mbox.data = NULL;
+ }
+}
+
+int
+diva_xdi_write(void *adapter, void *os_handle, const void __user *src,
+ int length, divas_xdi_copy_from_user_fn_t cp_fn)
+{
+ diva_os_xdi_adapter_t *a = (diva_os_xdi_adapter_t *) adapter;
+ void *data;
+
+ if (a->xdi_mbox.status & DIVA_XDI_MBOX_BUSY) {
+ DBG_ERR(("A: A(%d) write, mbox busy", a->controller))
+ return (-1);
+ }
+
+ if (length < sizeof(diva_xdi_um_cfg_cmd_t)) {
+ DBG_ERR(("A: A(%d) write, message too small (%d < %d)",
+ a->controller, length,
+ sizeof(diva_xdi_um_cfg_cmd_t)))
+ return (-3);
+ }
+
+ if (!(data = diva_os_malloc(0, length))) {
+ DBG_ERR(("A: A(%d) write, ENOMEM", a->controller))
+ return (-2);
+ }
+
+ length = (*cp_fn) (os_handle, data, src, length);
+ if (length > 0) {
+ if ((*(a->interface.cmd_proc))
+ (a, (diva_xdi_um_cfg_cmd_t *) data, length)) {
+ length = -3;
+ }
+ } else {
+ DBG_ERR(("A: A(%d) write error (%d)", a->controller,
+ length))
+ }
+
+ diva_os_free(0, data);
+
+ return (length);
+}
+
+/*
+** Write answers to user mode utility, if any
+*/
+int
+diva_xdi_read(void *adapter, void *os_handle, void __user *dst,
+ int max_length, divas_xdi_copy_to_user_fn_t cp_fn)
+{
+ diva_os_xdi_adapter_t *a = (diva_os_xdi_adapter_t *) adapter;
+ int ret;
+
+ if (!(a->xdi_mbox.status & DIVA_XDI_MBOX_BUSY)) {
+ DBG_ERR(("A: A(%d) rx mbox empty", a->controller))
+ return (-1);
+ }
+ if (!a->xdi_mbox.data) {
+ a->xdi_mbox.status &= ~DIVA_XDI_MBOX_BUSY;
+ DBG_ERR(("A: A(%d) rx ENOMEM", a->controller))
+ return (-2);
+ }
+
+ if (max_length < a->xdi_mbox.data_length) {
+ DBG_ERR(("A: A(%d) rx buffer too short(%d < %d)",
+ a->controller, max_length,
+ a->xdi_mbox.data_length))
+ return (-3);
+ }
+
+ ret = (*cp_fn) (os_handle, dst, a->xdi_mbox.data,
+ a->xdi_mbox.data_length);
+ if (ret > 0) {
+ diva_os_free(0, a->xdi_mbox.data);
+ a->xdi_mbox.data = NULL;
+ a->xdi_mbox.status &= ~DIVA_XDI_MBOX_BUSY;
+ }
+
+ return (ret);
+}
+
+
+irqreturn_t diva_os_irq_wrapper(int irq, void *context)
+{
+ diva_os_xdi_adapter_t *a = context;
+ diva_xdi_clear_interrupts_proc_t clear_int_proc;
+
+ if (!a || !a->xdi_adapter.diva_isr_handler)
+ return IRQ_NONE;
+
+ if ((clear_int_proc = a->clear_interrupts_proc)) {
+ (*clear_int_proc) (a);
+ a->clear_interrupts_proc = NULL;
+ return IRQ_HANDLED;
+ }
+
+ (*(a->xdi_adapter.diva_isr_handler)) (&a->xdi_adapter);
+ return IRQ_HANDLED;
+}
+
+static void diva_init_request_array(void)
+{
+ Requests[0] = DivaIdiRequest0;
+ Requests[1] = DivaIdiRequest1;
+ Requests[2] = DivaIdiRequest2;
+ Requests[3] = DivaIdiRequest3;
+ Requests[4] = DivaIdiRequest4;
+ Requests[5] = DivaIdiRequest5;
+ Requests[6] = DivaIdiRequest6;
+ Requests[7] = DivaIdiRequest7;
+ Requests[8] = DivaIdiRequest8;
+ Requests[9] = DivaIdiRequest9;
+ Requests[10] = DivaIdiRequest10;
+ Requests[11] = DivaIdiRequest11;
+ Requests[12] = DivaIdiRequest12;
+ Requests[13] = DivaIdiRequest13;
+ Requests[14] = DivaIdiRequest14;
+ Requests[15] = DivaIdiRequest15;
+ Requests[16] = DivaIdiRequest16;
+ Requests[17] = DivaIdiRequest17;
+ Requests[18] = DivaIdiRequest18;
+ Requests[19] = DivaIdiRequest19;
+ Requests[20] = DivaIdiRequest20;
+ Requests[21] = DivaIdiRequest21;
+ Requests[22] = DivaIdiRequest22;
+ Requests[23] = DivaIdiRequest23;
+ Requests[24] = DivaIdiRequest24;
+ Requests[25] = DivaIdiRequest25;
+ Requests[26] = DivaIdiRequest26;
+ Requests[27] = DivaIdiRequest27;
+ Requests[28] = DivaIdiRequest28;
+ Requests[29] = DivaIdiRequest29;
+ Requests[30] = DivaIdiRequest30;
+ Requests[31] = DivaIdiRequest31;
+}
+
+void diva_xdi_display_adapter_features(int card)
+{
+ dword features;
+ if (!card || ((card - 1) >= MAX_ADAPTER) || !IoAdapters[card - 1]) {
+ return;
+ }
+ card--;
+ features = IoAdapters[card]->Properties.Features;
+
+ DBG_LOG(("FEATURES FOR ADAPTER: %d", card + 1))
+ DBG_LOG((" DI_FAX3 : %s",
+ (features & DI_FAX3) ? "Y" : "N"))
+ DBG_LOG((" DI_MODEM : %s",
+ (features & DI_MODEM) ? "Y" : "N"))
+ DBG_LOG((" DI_POST : %s",
+ (features & DI_POST) ? "Y" : "N"))
+ DBG_LOG((" DI_V110 : %s",
+ (features & DI_V110) ? "Y" : "N"))
+ DBG_LOG((" DI_V120 : %s",
+ (features & DI_V120) ? "Y" : "N"))
+ DBG_LOG((" DI_POTS : %s",
+ (features & DI_POTS) ? "Y" : "N"))
+ DBG_LOG((" DI_CODEC : %s",
+ (features & DI_CODEC) ? "Y" : "N"))
+ DBG_LOG((" DI_MANAGE : %s",
+ (features & DI_MANAGE) ? "Y" : "N"))
+ DBG_LOG((" DI_V_42 : %s",
+ (features & DI_V_42) ? "Y" : "N"))
+ DBG_LOG((" DI_EXTD_FAX : %s",
+ (features & DI_EXTD_FAX) ? "Y" : "N"))
+ DBG_LOG((" DI_AT_PARSER : %s",
+ (features & DI_AT_PARSER) ? "Y" : "N"))
+ DBG_LOG((" DI_VOICE_OVER_IP : %s",
+ (features & DI_VOICE_OVER_IP) ? "Y" : "N"))
+}
+
+void diva_add_slave_adapter(diva_os_xdi_adapter_t * a)
+{
+ diva_os_spin_lock_magic_t old_irql;
+
+ diva_os_enter_spin_lock(&adapter_lock, &old_irql, "add_slave");
+ list_add_tail(&a->link, &adapter_queue);
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "add_slave");
+}
+
+int diva_card_read_xlog(diva_os_xdi_adapter_t * a)
+{
+ diva_get_xlog_t *req;
+ byte *data;
+
+ if (!a->xdi_adapter.Initialized || !a->xdi_adapter.DIRequest) {
+ return (-1);
+ }
+ if (!(data = diva_os_malloc(0, sizeof(struct mi_pc_maint)))) {
+ return (-1);
+ }
+ memset(data, 0x00, sizeof(struct mi_pc_maint));
+
+ if (!(req = diva_os_malloc(0, sizeof(*req)))) {
+ diva_os_free(0, data);
+ return (-1);
+ }
+ req->command = 0x0400;
+ req->req = LOG;
+ req->rc = 0x00;
+
+ (*(a->xdi_adapter.DIRequest)) (&a->xdi_adapter, (ENTITY *) req);
+
+ if (!req->rc || req->req) {
+ diva_os_free(0, data);
+ diva_os_free(0, req);
+ return (-1);
+ }
+
+ memcpy(data, &req->req, sizeof(struct mi_pc_maint));
+
+ diva_os_free(0, req);
+
+ a->xdi_mbox.data_length = sizeof(struct mi_pc_maint);
+ a->xdi_mbox.data = data;
+ a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
+
+ return (0);
+}
+
+void xdiFreeFile(void *handle)
+{
+}
diff --git a/drivers/isdn/hardware/eicon/diva.h b/drivers/isdn/hardware/eicon/diva.h
new file mode 100644
index 0000000..e979085
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/diva.h
@@ -0,0 +1,31 @@
+/* $Id: diva.h,v 1.1.2.2 2001/02/08 12:25:43 armin Exp $ */
+
+#ifndef __DIVA_XDI_OS_PART_H__
+#define __DIVA_XDI_OS_PART_H__
+
+
+int divasa_xdi_driver_entry(void);
+void divasa_xdi_driver_unload(void);
+void *diva_driver_add_card(void *pdev, unsigned long CardOrdinal);
+void diva_driver_remove_card(void *pdiva);
+
+typedef int (*divas_xdi_copy_to_user_fn_t) (void *os_handle, void __user *dst,
+ const void *src, int length);
+
+typedef int (*divas_xdi_copy_from_user_fn_t) (void *os_handle, void *dst,
+ const void __user *src, int length);
+
+int diva_xdi_read(void *adapter, void *os_handle, void __user *dst,
+ int max_length, divas_xdi_copy_to_user_fn_t cp_fn);
+
+int diva_xdi_write(void *adapter, void *os_handle, const void __user *src,
+ int length, divas_xdi_copy_from_user_fn_t cp_fn);
+
+void *diva_xdi_open_adapter(void *os_handle, const void __user *src,
+ int length,
+ divas_xdi_copy_from_user_fn_t cp_fn);
+
+void diva_xdi_close_adapter(void *adapter, void *os_handle);
+
+
+#endif
diff --git a/drivers/isdn/hardware/eicon/diva_didd.c b/drivers/isdn/hardware/eicon/diva_didd.c
new file mode 100644
index 0000000..993b14c
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/diva_didd.c
@@ -0,0 +1,149 @@
+/* $Id: diva_didd.c,v 1.13.6.4 2005/02/11 19:40:25 armin Exp $
+ *
+ * DIDD Interface module for Eicon active cards.
+ *
+ * Functions are in dadapter.c
+ *
+ * Copyright 2002-2003 by Armin Schindler (mac@melware.de)
+ * Copyright 2002-2003 Cytronics & Melware (info@melware.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <net/net_namespace.h>
+
+#include "platform.h"
+#include "di_defs.h"
+#include "dadapter.h"
+#include "divasync.h"
+#include "did_vers.h"
+
+static char *main_revision = "$Revision: 1.13.6.4 $";
+
+static char *DRIVERNAME =
+ "Eicon DIVA - DIDD table (http://www.melware.net)";
+static char *DRIVERLNAME = "divadidd";
+char *DRIVERRELEASE_DIDD = "2.0";
+
+MODULE_DESCRIPTION("DIDD table driver for diva drivers");
+MODULE_AUTHOR("Cytronics & Melware, Eicon Networks");
+MODULE_SUPPORTED_DEVICE("Eicon diva drivers");
+MODULE_LICENSE("GPL");
+
+#define DBG_MINIMUM (DL_LOG + DL_FTL + DL_ERR)
+#define DBG_DEFAULT (DBG_MINIMUM + DL_XLOG + DL_REG)
+
+extern int diddfunc_init(void);
+extern void diddfunc_finit(void);
+
+extern void DIVA_DIDD_Read(void *, int);
+
+static struct proc_dir_entry *proc_didd;
+struct proc_dir_entry *proc_net_eicon = NULL;
+
+EXPORT_SYMBOL(DIVA_DIDD_Read);
+EXPORT_SYMBOL(proc_net_eicon);
+
+static char *getrev(const char *revision)
+{
+ char *rev;
+ char *p;
+ if ((p = strchr(revision, ':'))) {
+ rev = p + 2;
+ p = strchr(rev, '$');
+ *--p = 0;
+ } else
+ rev = "1.0";
+ return rev;
+}
+
+static int
+proc_read(char *page, char **start, off_t off, int count, int *eof,
+ void *data)
+{
+ int len = 0;
+ char tmprev[32];
+
+ strcpy(tmprev, main_revision);
+ len += sprintf(page + len, "%s\n", DRIVERNAME);
+ len += sprintf(page + len, "name : %s\n", DRIVERLNAME);
+ len += sprintf(page + len, "release : %s\n", DRIVERRELEASE_DIDD);
+ len += sprintf(page + len, "build : %s(%s)\n",
+ diva_didd_common_code_build, DIVA_BUILD);
+ len += sprintf(page + len, "revision : %s\n", getrev(tmprev));
+
+ if (off + count >= len)
+ *eof = 1;
+ if (len < off)
+ return 0;
+ *start = page + off;
+ return ((count < len - off) ? count : len - off);
+}
+
+static int DIVA_INIT_FUNCTION create_proc(void)
+{
+ proc_net_eicon = proc_mkdir("eicon", init_net.proc_net);
+
+ if (proc_net_eicon) {
+ if ((proc_didd =
+ create_proc_entry(DRIVERLNAME, S_IFREG | S_IRUGO,
+ proc_net_eicon))) {
+ proc_didd->read_proc = proc_read;
+ }
+ return (1);
+ }
+ return (0);
+}
+
+static void remove_proc(void)
+{
+ remove_proc_entry(DRIVERLNAME, proc_net_eicon);
+ remove_proc_entry("eicon", init_net.proc_net);
+}
+
+static int DIVA_INIT_FUNCTION divadidd_init(void)
+{
+ char tmprev[32];
+ int ret = 0;
+
+ printk(KERN_INFO "%s\n", DRIVERNAME);
+ printk(KERN_INFO "%s: Rel:%s Rev:", DRIVERLNAME, DRIVERRELEASE_DIDD);
+ strcpy(tmprev, main_revision);
+ printk("%s Build:%s(%s)\n", getrev(tmprev),
+ diva_didd_common_code_build, DIVA_BUILD);
+
+ if (!create_proc()) {
+ printk(KERN_ERR "%s: could not create proc entry\n",
+ DRIVERLNAME);
+ ret = -EIO;
+ goto out;
+ }
+
+ if (!diddfunc_init()) {
+ printk(KERN_ERR "%s: failed to connect to DIDD.\n",
+ DRIVERLNAME);
+#ifdef MODULE
+ remove_proc();
+#endif
+ ret = -EIO;
+ goto out;
+ }
+
+ out:
+ return (ret);
+}
+
+static void DIVA_EXIT_FUNCTION divadidd_exit(void)
+{
+ diddfunc_finit();
+ remove_proc();
+ printk(KERN_INFO "%s: module unloaded.\n", DRIVERLNAME);
+}
+
+module_init(divadidd_init);
+module_exit(divadidd_exit);
diff --git a/drivers/isdn/hardware/eicon/diva_dma.c b/drivers/isdn/hardware/eicon/diva_dma.c
new file mode 100644
index 0000000..f53a740
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/diva_dma.c
@@ -0,0 +1,94 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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 "platform.h"
+#include "diva_dma.h"
+/*
+ Every entry has length of PAGE_SIZE
+ and represents one single physical page
+ */
+struct _diva_dma_map_entry {
+ int busy;
+ dword phys_bus_addr; /* 32bit address as seen by the card */
+ void* local_ram_addr; /* local address as seen by the host */
+ void* addr_handle; /* handle uset to free allocated memory */
+};
+/*
+ Create local mapping structure and init it to default state
+ */
+struct _diva_dma_map_entry* diva_alloc_dma_map (void* os_context, int nentries) {
+ diva_dma_map_entry_t* pmap = diva_os_malloc(0, sizeof(*pmap)*(nentries+1));
+ if (pmap)
+ memset (pmap, 0, sizeof(*pmap)*(nentries+1));
+ return pmap;
+}
+/*
+ Free local map (context should be freed before) if any
+ */
+void diva_free_dma_mapping (struct _diva_dma_map_entry* pmap) {
+ if (pmap) {
+ diva_os_free (0, pmap);
+ }
+}
+/*
+ Set information saved on the map entry
+ */
+void diva_init_dma_map_entry (struct _diva_dma_map_entry* pmap,
+ int nr, void* virt, dword phys,
+ void* addr_handle) {
+ pmap[nr].phys_bus_addr = phys;
+ pmap[nr].local_ram_addr = virt;
+ pmap[nr].addr_handle = addr_handle;
+}
+/*
+ Allocate one single entry in the map
+ */
+int diva_alloc_dma_map_entry (struct _diva_dma_map_entry* pmap) {
+ int i;
+ for (i = 0; (pmap && pmap[i].local_ram_addr); i++) {
+ if (!pmap[i].busy) {
+ pmap[i].busy = 1;
+ return (i);
+ }
+ }
+ return (-1);
+}
+/*
+ Free one single entry in the map
+ */
+void diva_free_dma_map_entry (struct _diva_dma_map_entry* pmap, int nr) {
+ pmap[nr].busy = 0;
+}
+/*
+ Get information saved on the map entry
+ */
+void diva_get_dma_map_entry (struct _diva_dma_map_entry* pmap, int nr,
+ void** pvirt, dword* pphys) {
+ *pphys = pmap[nr].phys_bus_addr;
+ *pvirt = pmap[nr].local_ram_addr;
+}
+void* diva_get_entry_handle (struct _diva_dma_map_entry* pmap, int nr) {
+ return (pmap[nr].addr_handle);
+}
diff --git a/drivers/isdn/hardware/eicon/diva_dma.h b/drivers/isdn/hardware/eicon/diva_dma.h
new file mode 100644
index 0000000..dff8072
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/diva_dma.h
@@ -0,0 +1,48 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+#ifndef __DIVA_DMA_MAPPING_IFC_H__
+#define __DIVA_DMA_MAPPING_IFC_H__
+typedef struct _diva_dma_map_entry diva_dma_map_entry_t;
+struct _diva_dma_map_entry* diva_alloc_dma_map (void* os_context, int nentries);
+void diva_init_dma_map_entry (struct _diva_dma_map_entry* pmap,
+ int nr, void* virt, dword phys,
+ void* addr_handle);
+int diva_alloc_dma_map_entry (struct _diva_dma_map_entry* pmap);
+void diva_free_dma_map_entry (struct _diva_dma_map_entry* pmap, int entry);
+void diva_get_dma_map_entry (struct _diva_dma_map_entry* pmap, int nr,
+ void** pvirt, dword* pphys);
+void diva_free_dma_mapping (struct _diva_dma_map_entry* pmap);
+/*
+ Functionality to be implemented by OS wrapper
+ and running in process context
+ */
+void diva_init_dma_map (void* hdev,
+ struct _diva_dma_map_entry** ppmap,
+ int nentries);
+void diva_free_dma_map (void* hdev,
+ struct _diva_dma_map_entry* pmap);
+void* diva_get_entry_handle (struct _diva_dma_map_entry* pmap, int nr);
+#endif
diff --git a/drivers/isdn/hardware/eicon/diva_pci.h b/drivers/isdn/hardware/eicon/diva_pci.h
new file mode 100644
index 0000000..cc0d510
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/diva_pci.h
@@ -0,0 +1,19 @@
+/* $Id: diva_pci.h,v 1.6 2003/01/04 15:29:45 schindler Exp $ */
+
+#ifndef __DIVA_PCI_INTERFACE_H__
+#define __DIVA_PCI_INTERFACE_H__
+
+void __iomem *divasa_remap_pci_bar(diva_os_xdi_adapter_t *a,
+ int id,
+ unsigned long bar,
+ unsigned long area_length);
+void divasa_unmap_pci_bar(void __iomem *bar);
+unsigned long divasa_get_pci_irq(unsigned char bus,
+ unsigned char func, void *pci_dev_handle);
+unsigned long divasa_get_pci_bar(unsigned char bus,
+ unsigned char func,
+ int bar, void *pci_dev_handle);
+byte diva_os_get_pci_bus(void *pci_dev_handle);
+byte diva_os_get_pci_func(void *pci_dev_handle);
+
+#endif
diff --git a/drivers/isdn/hardware/eicon/divacapi.h b/drivers/isdn/hardware/eicon/divacapi.h
new file mode 100644
index 0000000..9f5b680
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/divacapi.h
@@ -0,0 +1,1360 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+
+/*#define DEBUG */
+
+
+
+
+
+
+
+
+
+
+
+#define IMPLEMENT_DTMF 1
+#define IMPLEMENT_LINE_INTERCONNECT2 1
+#define IMPLEMENT_ECHO_CANCELLER 1
+#define IMPLEMENT_RTP 1
+#define IMPLEMENT_T38 1
+#define IMPLEMENT_FAX_SUB_SEP_PWD 1
+#define IMPLEMENT_V18 1
+#define IMPLEMENT_DTMF_TONE 1
+#define IMPLEMENT_PIAFS 1
+#define IMPLEMENT_FAX_PAPER_FORMATS 1
+#define IMPLEMENT_VOWN 1
+#define IMPLEMENT_CAPIDTMF 1
+#define IMPLEMENT_FAX_NONSTANDARD 1
+#define VSWITCH_SUPPORT 1
+
+
+#define IMPLEMENT_LINE_INTERCONNECT 0
+#define IMPLEMENT_MARKED_OK_AFTER_FC 1
+
+#include "capidtmf.h"
+
+/*------------------------------------------------------------------*/
+/* Common API internal definitions */
+/*------------------------------------------------------------------*/
+
+#define MAX_APPL 240
+#define MAX_NCCI 127
+
+#define MSG_IN_QUEUE_SIZE ((4096 + 3) & 0xfffc) /* must be multiple of 4 */
+
+
+#define MSG_IN_OVERHEAD sizeof(APPL *)
+
+#define MAX_NL_CHANNEL 255
+#define MAX_DATA_B3 8
+#define MAX_DATA_ACK MAX_DATA_B3
+#define MAX_MULTI_IE 6
+#define MAX_MSG_SIZE 256
+#define MAX_MSG_PARMS 10
+#define MAX_CPN_MASK_SIZE 16
+#define MAX_MSN_CONFIG 10
+#define EXT_CONTROLLER 0x80
+#define CODEC 0x01
+#define CODEC_PERMANENT 0x02
+#define ADV_VOICE 0x03
+#define MAX_CIP_TYPES 5 /* kind of CIP types for group optimization */
+#define C_IND_MASK_DWORDS ((MAX_APPL+32) >> 5)
+
+
+#define FAX_CONNECT_INFO_BUFFER_SIZE 256
+#define NCPI_BUFFER_SIZE 256
+
+#define MAX_CHANNELS_PER_PLCI 8
+#define MAX_INTERNAL_COMMAND_LEVELS 4
+#define INTERNAL_REQ_BUFFER_SIZE 272
+
+#define INTERNAL_IND_BUFFER_SIZE 768
+
+#define DTMF_PARAMETER_BUFFER_SIZE 12
+#define ADV_VOICE_COEF_BUFFER_SIZE 50
+
+#define LI_PLCI_B_QUEUE_ENTRIES 256
+
+
+
+typedef struct _APPL APPL;
+typedef struct _PLCI PLCI;
+typedef struct _NCCI NCCI;
+typedef struct _DIVA_CAPI_ADAPTER DIVA_CAPI_ADAPTER;
+typedef struct _DATA_B3_DESC DATA_B3_DESC;
+typedef struct _DATA_ACK_DESC DATA_ACK_DESC;
+typedef struct manufacturer_profile_s MANUFACTURER_PROFILE;
+typedef struct fax_ncpi_s FAX_NCPI;
+typedef struct api_parse_s API_PARSE;
+typedef struct api_save_s API_SAVE;
+typedef struct msn_config_s MSN_CONFIG;
+typedef struct msn_config_max_s MSN_CONFIG_MAX;
+typedef struct msn_ld_s MSN_LD;
+
+struct manufacturer_profile_s {
+ dword private_options;
+ dword rtp_primary_payloads;
+ dword rtp_additional_payloads;
+};
+
+struct fax_ncpi_s {
+ word options;
+ word format;
+};
+
+struct msn_config_s {
+ byte msn[MAX_CPN_MASK_SIZE];
+};
+
+struct msn_config_max_s {
+ MSN_CONFIG msn_conf[MAX_MSN_CONFIG];
+};
+
+struct msn_ld_s {
+ dword low;
+ dword high;
+};
+
+struct api_parse_s {
+ word length;
+ byte * info;
+};
+
+struct api_save_s {
+ API_PARSE parms[MAX_MSG_PARMS+1];
+ byte info[MAX_MSG_SIZE];
+};
+
+struct _DATA_B3_DESC {
+ word Handle;
+ word Number;
+ word Flags;
+ word Length;
+ void * P;
+};
+
+struct _DATA_ACK_DESC {
+ word Handle;
+ word Number;
+};
+
+typedef void (* t_std_internal_command)(dword Id, PLCI *plci, byte Rc);
+
+/************************************************************************/
+/* Don't forget to adapt dos.asm after changing the _APPL structure!!!! */
+struct _APPL {
+ word Id;
+ word NullCREnable;
+ word CDEnable;
+ dword S_Handle;
+
+
+
+
+
+
+ LIST_ENTRY s_function;
+ dword s_context;
+ word s_count;
+ APPL * s_next;
+ byte * xbuffer_used;
+ void ** xbuffer_internal;
+ void ** xbuffer_ptr;
+
+
+
+
+
+
+ byte * queue;
+ word queue_size;
+ word queue_free;
+ word queue_read;
+ word queue_write;
+ word queue_signal;
+ byte msg_lost;
+ byte appl_flags;
+ word Number;
+
+ word MaxBuffer;
+ byte MaxNCCI;
+ byte MaxNCCIData;
+ word MaxDataLength;
+ word NCCIDataFlowCtrlTimer;
+ byte * ReceiveBuffer;
+ word * DataNCCI;
+ word * DataFlags;
+};
+
+
+struct _PLCI {
+ ENTITY Sig;
+ ENTITY NL;
+ word RNum;
+ word RFlags;
+ BUFFERS RData[2];
+ BUFFERS XData[1];
+ BUFFERS NData[2];
+
+ DIVA_CAPI_ADAPTER *adapter;
+ APPL *appl;
+ PLCI *relatedPTYPLCI;
+ byte Id;
+ byte State;
+ byte sig_req;
+ byte nl_req;
+ byte SuppState;
+ byte channels;
+ byte tel;
+ byte B1_resource;
+ byte B2_prot;
+ byte B3_prot;
+
+ word command;
+ word m_command;
+ word internal_command;
+ word number;
+ word req_in_start;
+ word req_in;
+ word req_out;
+ word msg_in_write_pos;
+ word msg_in_read_pos;
+ word msg_in_wrap_pos;
+
+ void * data_sent_ptr;
+ byte data_sent;
+ byte send_disc;
+ byte sig_global_req;
+ byte sig_remove_id;
+ byte nl_global_req;
+ byte nl_remove_id;
+ byte b_channel;
+ byte adv_nl;
+ byte manufacturer;
+ byte call_dir;
+ byte hook_state;
+ byte spoofed_msg;
+ byte ptyState;
+ byte cr_enquiry;
+ word hangup_flow_ctrl_timer;
+
+ word ncci_ring_list;
+ byte inc_dis_ncci_table[MAX_CHANNELS_PER_PLCI];
+ t_std_internal_command internal_command_queue[MAX_INTERNAL_COMMAND_LEVELS];
+ dword c_ind_mask_table[C_IND_MASK_DWORDS];
+ dword group_optimization_mask_table[C_IND_MASK_DWORDS];
+ byte RBuffer[200];
+ dword msg_in_queue[MSG_IN_QUEUE_SIZE/sizeof(dword)];
+ API_SAVE saved_msg;
+ API_SAVE B_protocol;
+ byte fax_connect_info_length;
+ byte fax_connect_info_buffer[FAX_CONNECT_INFO_BUFFER_SIZE];
+ byte fax_edata_ack_length;
+ word nsf_control_bits;
+ byte ncpi_state;
+ byte ncpi_buffer[NCPI_BUFFER_SIZE];
+
+ byte internal_req_buffer[INTERNAL_REQ_BUFFER_SIZE];
+ byte internal_ind_buffer[INTERNAL_IND_BUFFER_SIZE + 3];
+ dword requested_options_conn;
+ dword requested_options;
+ word B1_facilities;
+ API_SAVE *adjust_b_parms_msg;
+ word adjust_b_facilities;
+ word adjust_b_command;
+ word adjust_b_ncci;
+ word adjust_b_mode;
+ word adjust_b_state;
+ byte adjust_b_restore;
+
+ byte dtmf_rec_active;
+ word dtmf_rec_pulse_ms;
+ word dtmf_rec_pause_ms;
+ byte dtmf_send_requests;
+ word dtmf_send_pulse_ms;
+ word dtmf_send_pause_ms;
+ word dtmf_cmd;
+ word dtmf_msg_number_queue[8];
+ byte dtmf_parameter_length;
+ byte dtmf_parameter_buffer[DTMF_PARAMETER_BUFFER_SIZE];
+
+
+ t_capidtmf_state capidtmf_state;
+
+
+ byte li_bchannel_id; /* BRI: 1..2, PRI: 1..32 */
+ byte li_channel_bits;
+ byte li_notify_update;
+ word li_cmd;
+ word li_write_command;
+ word li_write_channel;
+ word li_plci_b_write_pos;
+ word li_plci_b_read_pos;
+ word li_plci_b_req_pos;
+ dword li_plci_b_queue[LI_PLCI_B_QUEUE_ENTRIES];
+
+
+ word ec_cmd;
+ word ec_idi_options;
+ word ec_tail_length;
+
+
+ byte tone_last_indication_code;
+
+ byte vswitchstate;
+ byte vsprot;
+ byte vsprotdialect;
+ byte notifiedcall; /* Flag if it is a spoofed call */
+
+ int rx_dma_descriptor;
+ dword rx_dma_magic;
+};
+
+
+struct _NCCI {
+ byte data_out;
+ byte data_pending;
+ byte data_ack_out;
+ byte data_ack_pending;
+ DATA_B3_DESC DBuffer[MAX_DATA_B3];
+ DATA_ACK_DESC DataAck[MAX_DATA_ACK];
+};
+
+
+struct _DIVA_CAPI_ADAPTER {
+ IDI_CALL request;
+ byte Id;
+ byte max_plci;
+ byte max_listen;
+ byte listen_active;
+ PLCI *plci;
+ byte ch_ncci[MAX_NL_CHANNEL+1];
+ byte ncci_ch[MAX_NCCI+1];
+ byte ncci_plci[MAX_NCCI+1];
+ byte ncci_state[MAX_NCCI+1];
+ byte ncci_next[MAX_NCCI+1];
+ NCCI ncci[MAX_NCCI+1];
+
+ byte ch_flow_control[MAX_NL_CHANNEL+1]; /* Used by XON protocol */
+ byte ch_flow_control_pending;
+ byte ch_flow_plci[MAX_NL_CHANNEL+1];
+ int last_flow_control_ch;
+
+ dword Info_Mask[MAX_APPL];
+ dword CIP_Mask[MAX_APPL];
+
+ dword Notification_Mask[MAX_APPL];
+ PLCI *codec_listen[MAX_APPL];
+ dword requested_options_table[MAX_APPL];
+ API_PROFILE profile;
+ MANUFACTURER_PROFILE man_profile;
+ dword manufacturer_features;
+
+ byte AdvCodecFLAG;
+ PLCI *AdvCodecPLCI;
+ PLCI *AdvSignalPLCI;
+ APPL *AdvSignalAppl;
+ byte TelOAD[23];
+ byte TelOSA[23];
+ byte scom_appl_disable;
+ PLCI *automatic_lawPLCI;
+ byte automatic_law;
+ byte u_law;
+
+ byte adv_voice_coef_length;
+ byte adv_voice_coef_buffer[ADV_VOICE_COEF_BUFFER_SIZE];
+
+ byte li_pri;
+ byte li_channels;
+ word li_base;
+
+ byte adapter_disabled;
+ byte group_optimization_enabled; /* use application groups if enabled */
+ dword sdram_bar;
+ byte flag_dynamic_l1_down; /* for hunt groups:down layer 1 if no appl present*/
+ byte FlowControlIdTable[256];
+ byte FlowControlSkipTable[256];
+ void* os_card; /* pointer to associated OS dependent adapter structure */
+};
+
+
+/*------------------------------------------------------------------*/
+/* Application flags */
+/*------------------------------------------------------------------*/
+
+#define APPL_FLAG_OLD_LI_SPEC 0x01
+#define APPL_FLAG_PRIV_EC_SPEC 0x02
+
+
+/*------------------------------------------------------------------*/
+/* API parameter definitions */
+/*------------------------------------------------------------------*/
+
+#define X75_TTX 1 /* x.75 for ttx */
+#define TRF 2 /* transparent with hdlc framing */
+#define TRF_IN 3 /* transparent with hdlc fr. inc. */
+#define SDLC 4 /* sdlc, sna layer-2 */
+#define X75_BTX 5 /* x.75 for btx */
+#define LAPD 6 /* lapd (Q.921) */
+#define X25_L2 7 /* x.25 layer-2 */
+#define V120_L2 8 /* V.120 layer-2 protocol */
+#define V42_IN 9 /* V.42 layer-2 protocol, incomming */
+#define V42 10 /* V.42 layer-2 protocol */
+#define MDM_ATP 11 /* AT Parser built in the L2 */
+#define X75_V42BIS 12 /* ISO7776 (X.75 SLP) modified to support V.42 bis compression */
+#define RTPL2_IN 13 /* RTP layer-2 protocol, incomming */
+#define RTPL2 14 /* RTP layer-2 protocol */
+#define V120_V42BIS 15 /* V.120 layer-2 protocol supporting V.42 bis compression */
+
+#define T70NL 1
+#define X25PLP 2
+#define T70NLX 3
+#define TRANSPARENT_NL 4
+#define ISO8208 5
+#define T30 6
+
+
+/*------------------------------------------------------------------*/
+/* FAX interface to IDI */
+/*------------------------------------------------------------------*/
+
+#define CAPI_MAX_HEAD_LINE_SPACE 89
+#define CAPI_MAX_DATE_TIME_LENGTH 18
+
+#define T30_MAX_STATION_ID_LENGTH 20
+#define T30_MAX_SUBADDRESS_LENGTH 20
+#define T30_MAX_PASSWORD_LENGTH 20
+
+typedef struct t30_info_s T30_INFO;
+struct t30_info_s {
+ byte code;
+ byte rate_div_2400;
+ byte resolution;
+ byte data_format;
+ byte pages_low;
+ byte pages_high;
+ byte operating_mode;
+ byte control_bits_low;
+ byte control_bits_high;
+ byte feature_bits_low;
+ byte feature_bits_high;
+ byte recording_properties;
+ byte universal_6;
+ byte universal_7;
+ byte station_id_len;
+ byte head_line_len;
+ byte station_id[T30_MAX_STATION_ID_LENGTH];
+/* byte head_line[]; */
+/* byte sub_sep_length; */
+/* byte sub_sep_field[]; */
+/* byte pwd_length; */
+/* byte pwd_field[]; */
+/* byte nsf_info_length; */
+/* byte nsf_info_field[]; */
+};
+
+
+#define T30_RESOLUTION_R8_0385 0x00
+#define T30_RESOLUTION_R8_0770_OR_200 0x01
+#define T30_RESOLUTION_R8_1540 0x02
+#define T30_RESOLUTION_R16_1540_OR_400 0x04
+#define T30_RESOLUTION_R4_0385_OR_100 0x08
+#define T30_RESOLUTION_300_300 0x10
+#define T30_RESOLUTION_INCH_BASED 0x40
+#define T30_RESOLUTION_METRIC_BASED 0x80
+
+#define T30_RECORDING_WIDTH_ISO_A4 0
+#define T30_RECORDING_WIDTH_ISO_B4 1
+#define T30_RECORDING_WIDTH_ISO_A3 2
+#define T30_RECORDING_WIDTH_COUNT 3
+
+#define T30_RECORDING_LENGTH_ISO_A4 0
+#define T30_RECORDING_LENGTH_ISO_B4 1
+#define T30_RECORDING_LENGTH_UNLIMITED 2
+#define T30_RECORDING_LENGTH_COUNT 3
+
+#define T30_MIN_SCANLINE_TIME_00_00_00 0
+#define T30_MIN_SCANLINE_TIME_05_05_05 1
+#define T30_MIN_SCANLINE_TIME_10_05_05 2
+#define T30_MIN_SCANLINE_TIME_10_10_10 3
+#define T30_MIN_SCANLINE_TIME_20_10_10 4
+#define T30_MIN_SCANLINE_TIME_20_20_20 5
+#define T30_MIN_SCANLINE_TIME_40_20_20 6
+#define T30_MIN_SCANLINE_TIME_40_40_40 7
+#define T30_MIN_SCANLINE_TIME_RES_8 8
+#define T30_MIN_SCANLINE_TIME_RES_9 9
+#define T30_MIN_SCANLINE_TIME_RES_10 10
+#define T30_MIN_SCANLINE_TIME_10_10_05 11
+#define T30_MIN_SCANLINE_TIME_20_10_05 12
+#define T30_MIN_SCANLINE_TIME_20_20_10 13
+#define T30_MIN_SCANLINE_TIME_40_20_10 14
+#define T30_MIN_SCANLINE_TIME_40_40_20 15
+#define T30_MIN_SCANLINE_TIME_COUNT 16
+
+#define T30_DATA_FORMAT_SFF 0
+#define T30_DATA_FORMAT_ASCII 1
+#define T30_DATA_FORMAT_NATIVE 2
+#define T30_DATA_FORMAT_COUNT 3
+
+
+#define T30_OPERATING_MODE_STANDARD 0
+#define T30_OPERATING_MODE_CLASS2 1
+#define T30_OPERATING_MODE_CLASS1 2
+#define T30_OPERATING_MODE_CAPI 3
+#define T30_OPERATING_MODE_CAPI_NEG 4
+#define T30_OPERATING_MODE_COUNT 5
+
+ /* EDATA transmit messages */
+#define EDATA_T30_DIS 0x01
+#define EDATA_T30_FTT 0x02
+#define EDATA_T30_MCF 0x03
+#define EDATA_T30_PARAMETERS 0x04
+
+ /* EDATA receive messages */
+#define EDATA_T30_DCS 0x81
+#define EDATA_T30_TRAIN_OK 0x82
+#define EDATA_T30_EOP 0x83
+#define EDATA_T30_MPS 0x84
+#define EDATA_T30_EOM 0x85
+#define EDATA_T30_DTC 0x86
+#define EDATA_T30_PAGE_END 0x87 /* Indicates end of page data. Reserved, but not implemented ! */
+#define EDATA_T30_EOP_CAPI 0x88
+
+
+#define T30_SUCCESS 0
+#define T30_ERR_NO_DIS_RECEIVED 1
+#define T30_ERR_TIMEOUT_NO_RESPONSE 2
+#define T30_ERR_RETRY_NO_RESPONSE 3
+#define T30_ERR_TOO_MANY_REPEATS 4
+#define T30_ERR_UNEXPECTED_MESSAGE 5
+#define T30_ERR_UNEXPECTED_DCN 6
+#define T30_ERR_DTC_UNSUPPORTED 7
+#define T30_ERR_ALL_RATES_FAILED 8
+#define T30_ERR_TOO_MANY_TRAINS 9
+#define T30_ERR_RECEIVE_CORRUPTED 10
+#define T30_ERR_UNEXPECTED_DISC 11
+#define T30_ERR_APPLICATION_DISC 12
+#define T30_ERR_INCOMPATIBLE_DIS 13
+#define T30_ERR_INCOMPATIBLE_DCS 14
+#define T30_ERR_TIMEOUT_NO_COMMAND 15
+#define T30_ERR_RETRY_NO_COMMAND 16
+#define T30_ERR_TIMEOUT_COMMAND_TOO_LONG 17
+#define T30_ERR_TIMEOUT_RESPONSE_TOO_LONG 18
+#define T30_ERR_NOT_IDENTIFIED 19
+#define T30_ERR_SUPERVISORY_TIMEOUT 20
+#define T30_ERR_TOO_LONG_SCAN_LINE 21
+/* #define T30_ERR_RETRY_NO_PAGE_AFTER_MPS 22 */
+#define T30_ERR_RETRY_NO_PAGE_RECEIVED 23
+#define T30_ERR_RETRY_NO_DCS_AFTER_FTT 24
+#define T30_ERR_RETRY_NO_DCS_AFTER_EOM 25
+#define T30_ERR_RETRY_NO_DCS_AFTER_MPS 26
+#define T30_ERR_RETRY_NO_DCN_AFTER_MCF 27
+#define T30_ERR_RETRY_NO_DCN_AFTER_RTN 28
+#define T30_ERR_RETRY_NO_CFR 29
+#define T30_ERR_RETRY_NO_MCF_AFTER_EOP 30
+#define T30_ERR_RETRY_NO_MCF_AFTER_EOM 31
+#define T30_ERR_RETRY_NO_MCF_AFTER_MPS 32
+#define T30_ERR_SUB_SEP_UNSUPPORTED 33
+#define T30_ERR_PWD_UNSUPPORTED 34
+#define T30_ERR_SUB_SEP_PWD_UNSUPPORTED 35
+#define T30_ERR_INVALID_COMMAND_FRAME 36
+#define T30_ERR_UNSUPPORTED_PAGE_CODING 37
+#define T30_ERR_INVALID_PAGE_CODING 38
+#define T30_ERR_INCOMPATIBLE_PAGE_CONFIG 39
+#define T30_ERR_TIMEOUT_FROM_APPLICATION 40
+#define T30_ERR_V34FAX_NO_REACTION_ON_MARK 41
+#define T30_ERR_V34FAX_TRAINING_TIMEOUT 42
+#define T30_ERR_V34FAX_UNEXPECTED_V21 43
+#define T30_ERR_V34FAX_PRIMARY_CTS_ON 44
+#define T30_ERR_V34FAX_TURNAROUND_POLLING 45
+#define T30_ERR_V34FAX_V8_INCOMPATIBILITY 46
+
+
+#define T30_CONTROL_BIT_DISABLE_FINE 0x0001
+#define T30_CONTROL_BIT_ENABLE_ECM 0x0002
+#define T30_CONTROL_BIT_ECM_64_BYTES 0x0004
+#define T30_CONTROL_BIT_ENABLE_2D_CODING 0x0008
+#define T30_CONTROL_BIT_ENABLE_T6_CODING 0x0010
+#define T30_CONTROL_BIT_ENABLE_UNCOMPR 0x0020
+#define T30_CONTROL_BIT_ACCEPT_POLLING 0x0040
+#define T30_CONTROL_BIT_REQUEST_POLLING 0x0080
+#define T30_CONTROL_BIT_MORE_DOCUMENTS 0x0100
+#define T30_CONTROL_BIT_ACCEPT_SUBADDRESS 0x0200
+#define T30_CONTROL_BIT_ACCEPT_SEL_POLLING 0x0400
+#define T30_CONTROL_BIT_ACCEPT_PASSWORD 0x0800
+#define T30_CONTROL_BIT_ENABLE_V34FAX 0x1000
+#define T30_CONTROL_BIT_EARLY_CONNECT 0x2000
+
+#define T30_CONTROL_BIT_ALL_FEATURES (T30_CONTROL_BIT_ENABLE_ECM | T30_CONTROL_BIT_ENABLE_2D_CODING | T30_CONTROL_BIT_ENABLE_T6_CODING | T30_CONTROL_BIT_ENABLE_UNCOMPR | T30_CONTROL_BIT_ENABLE_V34FAX)
+
+#define T30_FEATURE_BIT_FINE 0x0001
+#define T30_FEATURE_BIT_ECM 0x0002
+#define T30_FEATURE_BIT_ECM_64_BYTES 0x0004
+#define T30_FEATURE_BIT_2D_CODING 0x0008
+#define T30_FEATURE_BIT_T6_CODING 0x0010
+#define T30_FEATURE_BIT_UNCOMPR_ENABLED 0x0020
+#define T30_FEATURE_BIT_POLLING 0x0040
+#define T30_FEATURE_BIT_MORE_DOCUMENTS 0x0100
+#define T30_FEATURE_BIT_V34FAX 0x1000
+
+
+#define T30_NSF_CONTROL_BIT_ENABLE_NSF 0x0001
+#define T30_NSF_CONTROL_BIT_RAW_INFO 0x0002
+#define T30_NSF_CONTROL_BIT_NEGOTIATE_IND 0x0004
+#define T30_NSF_CONTROL_BIT_NEGOTIATE_RESP 0x0008
+
+#define T30_NSF_ELEMENT_NSF_FIF 0x00
+#define T30_NSF_ELEMENT_NSC_FIF 0x01
+#define T30_NSF_ELEMENT_NSS_FIF 0x02
+#define T30_NSF_ELEMENT_COMPANY_NAME 0x03
+
+
+/*------------------------------------------------------------------*/
+/* Analog modem definitions */
+/*------------------------------------------------------------------*/
+
+typedef struct async_s ASYNC_FORMAT;
+struct async_s {
+ unsigned pe: 1;
+ unsigned parity:2;
+ unsigned spare: 2;
+ unsigned stp: 1;
+ unsigned ch_len:2; /* 3th octett in CAI */
+};
+
+
+/*------------------------------------------------------------------*/
+/* PLCI/NCCI states */
+/*------------------------------------------------------------------*/
+
+#define IDLE 0
+#define OUTG_CON_PENDING 1
+#define INC_CON_PENDING 2
+#define INC_CON_ALERT 3
+#define INC_CON_ACCEPT 4
+#define INC_ACT_PENDING 5
+#define LISTENING 6
+#define CONNECTED 7
+#define OUTG_DIS_PENDING 8
+#define INC_DIS_PENDING 9
+#define LOCAL_CONNECT 10
+#define INC_RES_PENDING 11
+#define OUTG_RES_PENDING 12
+#define SUSPENDING 13
+#define ADVANCED_VOICE_SIG 14
+#define ADVANCED_VOICE_NOSIG 15
+#define RESUMING 16
+#define INC_CON_CONNECTED_ALERT 17
+#define OUTG_REJ_PENDING 18
+
+
+/*------------------------------------------------------------------*/
+/* auxilliary states for supplementary services */
+/*------------------------------------------------------------------*/
+
+#define IDLE 0
+#define HOLD_REQUEST 1
+#define HOLD_INDICATE 2
+#define CALL_HELD 3
+#define RETRIEVE_REQUEST 4
+#define RETRIEVE_INDICATION 5
+
+/*------------------------------------------------------------------*/
+/* Capi IE + Msg types */
+/*------------------------------------------------------------------*/
+#define ESC_CAUSE 0x800|CAU /* Escape cause element */
+#define ESC_MSGTYPE 0x800|MSGTYPEIE /* Escape message type */
+#define ESC_CHI 0x800|CHI /* Escape channel id */
+#define ESC_LAW 0x800|BC /* Escape law info */
+#define ESC_CR 0x800|CRIE /* Escape CallReference */
+#define ESC_PROFILE 0x800|PROFILEIE /* Escape profile */
+#define ESC_SSEXT 0x800|SSEXTIE /* Escape Supplem. Serv.*/
+#define ESC_VSWITCH 0x800|VSWITCHIE /* Escape VSwitch */
+#define CST 0x14 /* Call State i.e. */
+#define PI 0x1E /* Progress Indicator */
+#define NI 0x27 /* Notification Ind */
+#define CONN_NR 0x4C /* Connected Number */
+#define CONG_RNR 0xBF /* Congestion RNR */
+#define CONG_RR 0xB0 /* Congestion RR */
+#define RESERVED 0xFF /* Res. for future use */
+#define ON_BOARD_CODEC 0x02 /* external controller */
+#define HANDSET 0x04 /* Codec+Handset(Pro11) */
+#define HOOK_SUPPORT 0x01 /* activate Hook signal */
+#define SCR 0x7a /* unscreened number */
+
+#define HOOK_OFF_REQ 0x9001 /* internal conn req */
+#define HOOK_ON_REQ 0x9002 /* internal disc req */
+#define SUSPEND_REQ 0x9003 /* internal susp req */
+#define RESUME_REQ 0x9004 /* internal resume req */
+#define USELAW_REQ 0x9005 /* internal law req */
+#define LISTEN_SIG_ASSIGN_PEND 0x9006
+#define PERM_LIST_REQ 0x900a /* permanent conn DCE */
+#define C_HOLD_REQ 0x9011
+#define C_RETRIEVE_REQ 0x9012
+#define C_NCR_FAC_REQ 0x9013
+#define PERM_COD_ASSIGN 0x9014
+#define PERM_COD_CALL 0x9015
+#define PERM_COD_HOOK 0x9016
+#define PERM_COD_CONN_PEND 0x9017 /* wait for connect_con */
+#define PTY_REQ_PEND 0x9018
+#define CD_REQ_PEND 0x9019
+#define CF_START_PEND 0x901a
+#define CF_STOP_PEND 0x901b
+#define ECT_REQ_PEND 0x901c
+#define GETSERV_REQ_PEND 0x901d
+#define BLOCK_PLCI 0x901e
+#define INTERR_NUMBERS_REQ_PEND 0x901f
+#define INTERR_DIVERSION_REQ_PEND 0x9020
+#define MWI_ACTIVATE_REQ_PEND 0x9021
+#define MWI_DEACTIVATE_REQ_PEND 0x9022
+#define SSEXT_REQ_COMMAND 0x9023
+#define SSEXT_NC_REQ_COMMAND 0x9024
+#define START_L1_SIG_ASSIGN_PEND 0x9025
+#define REM_L1_SIG_ASSIGN_PEND 0x9026
+#define CONF_BEGIN_REQ_PEND 0x9027
+#define CONF_ADD_REQ_PEND 0x9028
+#define CONF_SPLIT_REQ_PEND 0x9029
+#define CONF_DROP_REQ_PEND 0x902a
+#define CONF_ISOLATE_REQ_PEND 0x902b
+#define CONF_REATTACH_REQ_PEND 0x902c
+#define VSWITCH_REQ_PEND 0x902d
+#define GET_MWI_STATE 0x902e
+#define CCBS_REQUEST_REQ_PEND 0x902f
+#define CCBS_DEACTIVATE_REQ_PEND 0x9030
+#define CCBS_INTERROGATE_REQ_PEND 0x9031
+
+#define NO_INTERNAL_COMMAND 0
+#define DTMF_COMMAND_1 1
+#define DTMF_COMMAND_2 2
+#define DTMF_COMMAND_3 3
+#define MIXER_COMMAND_1 4
+#define MIXER_COMMAND_2 5
+#define MIXER_COMMAND_3 6
+#define ADV_VOICE_COMMAND_CONNECT_1 7
+#define ADV_VOICE_COMMAND_CONNECT_2 8
+#define ADV_VOICE_COMMAND_CONNECT_3 9
+#define ADV_VOICE_COMMAND_DISCONNECT_1 10
+#define ADV_VOICE_COMMAND_DISCONNECT_2 11
+#define ADV_VOICE_COMMAND_DISCONNECT_3 12
+#define ADJUST_B_RESTORE_1 13
+#define ADJUST_B_RESTORE_2 14
+#define RESET_B3_COMMAND_1 15
+#define SELECT_B_COMMAND_1 16
+#define FAX_CONNECT_INFO_COMMAND_1 17
+#define FAX_CONNECT_INFO_COMMAND_2 18
+#define FAX_ADJUST_B23_COMMAND_1 19
+#define FAX_ADJUST_B23_COMMAND_2 20
+#define EC_COMMAND_1 21
+#define EC_COMMAND_2 22
+#define EC_COMMAND_3 23
+#define RTP_CONNECT_B3_REQ_COMMAND_1 24
+#define RTP_CONNECT_B3_REQ_COMMAND_2 25
+#define RTP_CONNECT_B3_REQ_COMMAND_3 26
+#define RTP_CONNECT_B3_RES_COMMAND_1 27
+#define RTP_CONNECT_B3_RES_COMMAND_2 28
+#define RTP_CONNECT_B3_RES_COMMAND_3 29
+#define HOLD_SAVE_COMMAND_1 30
+#define RETRIEVE_RESTORE_COMMAND_1 31
+#define FAX_DISCONNECT_COMMAND_1 32
+#define FAX_DISCONNECT_COMMAND_2 33
+#define FAX_DISCONNECT_COMMAND_3 34
+#define FAX_EDATA_ACK_COMMAND_1 35
+#define FAX_EDATA_ACK_COMMAND_2 36
+#define FAX_CONNECT_ACK_COMMAND_1 37
+#define FAX_CONNECT_ACK_COMMAND_2 38
+#define STD_INTERNAL_COMMAND_COUNT 39
+
+#define UID 0x2d /* User Id for Mgmt */
+
+#define CALL_DIR_OUT 0x01 /* call direction of initial call */
+#define CALL_DIR_IN 0x02
+#define CALL_DIR_ORIGINATE 0x04 /* DTE/DCE direction according to */
+#define CALL_DIR_ANSWER 0x08 /* state of B-Channel Operation */
+#define CALL_DIR_FORCE_OUTG_NL 0x10 /* for RESET_B3 reconnect, after DISC_B3... */
+
+#define AWAITING_MANUF_CON 0x80 /* command spoofing flags */
+#define SPOOFING_REQUIRED 0xff
+#define AWAITING_SELECT_B 0xef
+
+/*------------------------------------------------------------------*/
+/* B_CTRL / DSP_CTRL */
+/*------------------------------------------------------------------*/
+
+#define DSP_CTRL_OLD_SET_MIXER_COEFFICIENTS 0x01
+#define DSP_CTRL_SET_BCHANNEL_PASSIVATION_BRI 0x02
+#define DSP_CTRL_SET_DTMF_PARAMETERS 0x03
+
+#define MANUFACTURER_FEATURE_SLAVE_CODEC 0x00000001L
+#define MANUFACTURER_FEATURE_FAX_MORE_DOCUMENTS 0x00000002L
+#define MANUFACTURER_FEATURE_HARDDTMF 0x00000004L
+#define MANUFACTURER_FEATURE_SOFTDTMF_SEND 0x00000008L
+#define MANUFACTURER_FEATURE_DTMF_PARAMETERS 0x00000010L
+#define MANUFACTURER_FEATURE_SOFTDTMF_RECEIVE 0x00000020L
+#define MANUFACTURER_FEATURE_FAX_SUB_SEP_PWD 0x00000040L
+#define MANUFACTURER_FEATURE_V18 0x00000080L
+#define MANUFACTURER_FEATURE_MIXER_CH_CH 0x00000100L
+#define MANUFACTURER_FEATURE_MIXER_CH_PC 0x00000200L
+#define MANUFACTURER_FEATURE_MIXER_PC_CH 0x00000400L
+#define MANUFACTURER_FEATURE_MIXER_PC_PC 0x00000800L
+#define MANUFACTURER_FEATURE_ECHO_CANCELLER 0x00001000L
+#define MANUFACTURER_FEATURE_RTP 0x00002000L
+#define MANUFACTURER_FEATURE_T38 0x00004000L
+#define MANUFACTURER_FEATURE_TRANSP_DELIVERY_CONF 0x00008000L
+#define MANUFACTURER_FEATURE_XONOFF_FLOW_CONTROL 0x00010000L
+#define MANUFACTURER_FEATURE_OOB_CHANNEL 0x00020000L
+#define MANUFACTURER_FEATURE_IN_BAND_CHANNEL 0x00040000L
+#define MANUFACTURER_FEATURE_IN_BAND_FEATURE 0x00080000L
+#define MANUFACTURER_FEATURE_PIAFS 0x00100000L
+#define MANUFACTURER_FEATURE_DTMF_TONE 0x00200000L
+#define MANUFACTURER_FEATURE_FAX_PAPER_FORMATS 0x00400000L
+#define MANUFACTURER_FEATURE_OK_FC_LABEL 0x00800000L
+#define MANUFACTURER_FEATURE_VOWN 0x01000000L
+#define MANUFACTURER_FEATURE_XCONNECT 0x02000000L
+#define MANUFACTURER_FEATURE_DMACONNECT 0x04000000L
+#define MANUFACTURER_FEATURE_AUDIO_TAP 0x08000000L
+#define MANUFACTURER_FEATURE_FAX_NONSTANDARD 0x10000000L
+
+/*------------------------------------------------------------------*/
+/* DTMF interface to IDI */
+/*------------------------------------------------------------------*/
+
+
+#define DTMF_DIGIT_TONE_LOW_GROUP_697_HZ 0x00
+#define DTMF_DIGIT_TONE_LOW_GROUP_770_HZ 0x01
+#define DTMF_DIGIT_TONE_LOW_GROUP_852_HZ 0x02
+#define DTMF_DIGIT_TONE_LOW_GROUP_941_HZ 0x03
+#define DTMF_DIGIT_TONE_LOW_GROUP_MASK 0x03
+#define DTMF_DIGIT_TONE_HIGH_GROUP_1209_HZ 0x00
+#define DTMF_DIGIT_TONE_HIGH_GROUP_1336_HZ 0x04
+#define DTMF_DIGIT_TONE_HIGH_GROUP_1477_HZ 0x08
+#define DTMF_DIGIT_TONE_HIGH_GROUP_1633_HZ 0x0c
+#define DTMF_DIGIT_TONE_HIGH_GROUP_MASK 0x0c
+#define DTMF_DIGIT_TONE_CODE_0 0x07
+#define DTMF_DIGIT_TONE_CODE_1 0x00
+#define DTMF_DIGIT_TONE_CODE_2 0x04
+#define DTMF_DIGIT_TONE_CODE_3 0x08
+#define DTMF_DIGIT_TONE_CODE_4 0x01
+#define DTMF_DIGIT_TONE_CODE_5 0x05
+#define DTMF_DIGIT_TONE_CODE_6 0x09
+#define DTMF_DIGIT_TONE_CODE_7 0x02
+#define DTMF_DIGIT_TONE_CODE_8 0x06
+#define DTMF_DIGIT_TONE_CODE_9 0x0a
+#define DTMF_DIGIT_TONE_CODE_STAR 0x03
+#define DTMF_DIGIT_TONE_CODE_HASHMARK 0x0b
+#define DTMF_DIGIT_TONE_CODE_A 0x0c
+#define DTMF_DIGIT_TONE_CODE_B 0x0d
+#define DTMF_DIGIT_TONE_CODE_C 0x0e
+#define DTMF_DIGIT_TONE_CODE_D 0x0f
+
+#define DTMF_UDATA_REQUEST_SEND_DIGITS 16
+#define DTMF_UDATA_REQUEST_ENABLE_RECEIVER 17
+#define DTMF_UDATA_REQUEST_DISABLE_RECEIVER 18
+#define DTMF_UDATA_INDICATION_DIGITS_SENT 16
+#define DTMF_UDATA_INDICATION_DIGITS_RECEIVED 17
+#define DTMF_UDATA_INDICATION_MODEM_CALLING_TONE 18
+#define DTMF_UDATA_INDICATION_FAX_CALLING_TONE 19
+#define DTMF_UDATA_INDICATION_ANSWER_TONE 20
+
+#define UDATA_REQUEST_MIXER_TAP_DATA 27
+#define UDATA_INDICATION_MIXER_TAP_DATA 27
+
+#define DTMF_LISTEN_ACTIVE_FLAG 0x01
+#define DTMF_SEND_DIGIT_FLAG 0x01
+
+
+/*------------------------------------------------------------------*/
+/* Mixer interface to IDI */
+/*------------------------------------------------------------------*/
+
+
+#define LI2_FLAG_PCCONNECT_A_B 0x40000000
+#define LI2_FLAG_PCCONNECT_B_A 0x80000000
+
+#define MIXER_BCHANNELS_BRI 2
+#define MIXER_IC_CHANNELS_BRI MIXER_BCHANNELS_BRI
+#define MIXER_IC_CHANNEL_BASE MIXER_BCHANNELS_BRI
+#define MIXER_CHANNELS_BRI (MIXER_BCHANNELS_BRI + MIXER_IC_CHANNELS_BRI)
+#define MIXER_CHANNELS_PRI 32
+
+typedef struct li_config_s LI_CONFIG;
+
+struct xconnect_card_address_s {
+ dword low;
+ dword high;
+};
+
+struct xconnect_transfer_address_s {
+ struct xconnect_card_address_s card_address;
+ dword offset;
+};
+
+struct li_config_s {
+ DIVA_CAPI_ADAPTER *adapter;
+ PLCI *plci;
+ struct xconnect_transfer_address_s send_b;
+ struct xconnect_transfer_address_s send_pc;
+ byte *flag_table; /* dword aligned and sized */
+ byte *coef_table; /* dword aligned and sized */
+ byte channel;
+ byte curchnl;
+ byte chflags;
+};
+
+extern LI_CONFIG *li_config_table;
+extern word li_total_channels;
+
+#define LI_CHANNEL_INVOLVED 0x01
+#define LI_CHANNEL_ACTIVE 0x02
+#define LI_CHANNEL_TX_DATA 0x04
+#define LI_CHANNEL_RX_DATA 0x08
+#define LI_CHANNEL_CONFERENCE 0x10
+#define LI_CHANNEL_ADDRESSES_SET 0x80
+
+#define LI_CHFLAG_MONITOR 0x01
+#define LI_CHFLAG_MIX 0x02
+#define LI_CHFLAG_LOOP 0x04
+
+#define LI_FLAG_INTERCONNECT 0x01
+#define LI_FLAG_MONITOR 0x02
+#define LI_FLAG_MIX 0x04
+#define LI_FLAG_PCCONNECT 0x08
+#define LI_FLAG_CONFERENCE 0x10
+#define LI_FLAG_ANNOUNCEMENT 0x20
+
+#define LI_COEF_CH_CH 0x01
+#define LI_COEF_CH_PC 0x02
+#define LI_COEF_PC_CH 0x04
+#define LI_COEF_PC_PC 0x08
+#define LI_COEF_CH_CH_SET 0x10
+#define LI_COEF_CH_PC_SET 0x20
+#define LI_COEF_PC_CH_SET 0x40
+#define LI_COEF_PC_PC_SET 0x80
+
+#define LI_REQ_SILENT_UPDATE 0xffff
+
+#define LI_PLCI_B_LAST_FLAG ((dword) 0x80000000L)
+#define LI_PLCI_B_DISC_FLAG ((dword) 0x40000000L)
+#define LI_PLCI_B_SKIP_FLAG ((dword) 0x20000000L)
+#define LI_PLCI_B_FLAG_MASK ((dword) 0xe0000000L)
+
+#define UDATA_REQUEST_SET_MIXER_COEFS_BRI 24
+#define UDATA_REQUEST_SET_MIXER_COEFS_PRI_SYNC 25
+#define UDATA_REQUEST_SET_MIXER_COEFS_PRI_ASYN 26
+#define UDATA_INDICATION_MIXER_COEFS_SET 24
+
+#define MIXER_FEATURE_ENABLE_TX_DATA 0x0001
+#define MIXER_FEATURE_ENABLE_RX_DATA 0x0002
+
+#define MIXER_COEF_LINE_CHANNEL_MASK 0x1f
+#define MIXER_COEF_LINE_FROM_PC_FLAG 0x20
+#define MIXER_COEF_LINE_TO_PC_FLAG 0x40
+#define MIXER_COEF_LINE_ROW_FLAG 0x80
+
+#define UDATA_REQUEST_XCONNECT_FROM 28
+#define UDATA_INDICATION_XCONNECT_FROM 28
+#define UDATA_REQUEST_XCONNECT_TO 29
+#define UDATA_INDICATION_XCONNECT_TO 29
+
+#define XCONNECT_CHANNEL_PORT_B 0x0000
+#define XCONNECT_CHANNEL_PORT_PC 0x8000
+#define XCONNECT_CHANNEL_PORT_MASK 0x8000
+#define XCONNECT_CHANNEL_NUMBER_MASK 0x7fff
+#define XCONNECT_CHANNEL_PORT_COUNT 2
+
+#define XCONNECT_SUCCESS 0x0000
+#define XCONNECT_ERROR 0x0001
+
+
+/*------------------------------------------------------------------*/
+/* Echo canceller interface to IDI */
+/*------------------------------------------------------------------*/
+
+
+#define PRIVATE_ECHO_CANCELLER 0
+
+#define PRIV_SELECTOR_ECHO_CANCELLER 255
+
+#define EC_ENABLE_OPERATION 1
+#define EC_DISABLE_OPERATION 2
+#define EC_FREEZE_COEFFICIENTS 3
+#define EC_RESUME_COEFFICIENT_UPDATE 4
+#define EC_RESET_COEFFICIENTS 5
+
+#define EC_DISABLE_NON_LINEAR_PROCESSING 0x0001
+#define EC_DO_NOT_REQUIRE_REVERSALS 0x0002
+#define EC_DETECT_DISABLE_TONE 0x0004
+
+#define EC_SUCCESS 0
+#define EC_UNSUPPORTED_OPERATION 1
+
+#define EC_BYPASS_DUE_TO_CONTINUOUS_2100HZ 1
+#define EC_BYPASS_DUE_TO_REVERSED_2100HZ 2
+#define EC_BYPASS_RELEASED 3
+
+#define DSP_CTRL_SET_LEC_PARAMETERS 0x05
+
+#define LEC_ENABLE_ECHO_CANCELLER 0x0001
+#define LEC_ENABLE_2100HZ_DETECTOR 0x0002
+#define LEC_REQUIRE_2100HZ_REVERSALS 0x0004
+#define LEC_MANUAL_DISABLE 0x0008
+#define LEC_ENABLE_NONLINEAR_PROCESSING 0x0010
+#define LEC_FREEZE_COEFFICIENTS 0x0020
+#define LEC_RESET_COEFFICIENTS 0x8000
+
+#define LEC_MAX_SUPPORTED_TAIL_LENGTH 32
+
+#define LEC_UDATA_INDICATION_DISABLE_DETECT 9
+
+#define LEC_DISABLE_TYPE_CONTIGNUOUS_2100HZ 0x00
+#define LEC_DISABLE_TYPE_REVERSED_2100HZ 0x01
+#define LEC_DISABLE_RELEASED 0x02
+
+
+/*------------------------------------------------------------------*/
+/* RTP interface to IDI */
+/*------------------------------------------------------------------*/
+
+
+#define B1_RTP 31
+#define B2_RTP 31
+#define B3_RTP 31
+
+#define PRIVATE_RTP 1
+
+#define RTP_PRIM_PAYLOAD_PCMU_8000 0
+#define RTP_PRIM_PAYLOAD_1016_8000 1
+#define RTP_PRIM_PAYLOAD_G726_32_8000 2
+#define RTP_PRIM_PAYLOAD_GSM_8000 3
+#define RTP_PRIM_PAYLOAD_G723_8000 4
+#define RTP_PRIM_PAYLOAD_DVI4_8000 5
+#define RTP_PRIM_PAYLOAD_DVI4_16000 6
+#define RTP_PRIM_PAYLOAD_LPC_8000 7
+#define RTP_PRIM_PAYLOAD_PCMA_8000 8
+#define RTP_PRIM_PAYLOAD_G722_16000 9
+#define RTP_PRIM_PAYLOAD_QCELP_8000 12
+#define RTP_PRIM_PAYLOAD_G728_8000 14
+#define RTP_PRIM_PAYLOAD_G729_8000 18
+#define RTP_PRIM_PAYLOAD_GSM_HR_8000 30
+#define RTP_PRIM_PAYLOAD_GSM_EFR_8000 31
+
+#define RTP_ADD_PAYLOAD_BASE 32
+#define RTP_ADD_PAYLOAD_RED 32
+#define RTP_ADD_PAYLOAD_CN_8000 33
+#define RTP_ADD_PAYLOAD_DTMF 34
+
+#define RTP_SUCCESS 0
+#define RTP_ERR_SSRC_OR_PAYLOAD_CHANGE 1
+
+#define UDATA_REQUEST_RTP_RECONFIGURE 64
+#define UDATA_INDICATION_RTP_CHANGE 65
+#define BUDATA_REQUEST_QUERY_RTCP_REPORT 1
+#define BUDATA_INDICATION_RTCP_REPORT 1
+
+#define RTP_CONNECT_OPTION_DISC_ON_SSRC_CHANGE 0x00000001L
+#define RTP_CONNECT_OPTION_DISC_ON_PT_CHANGE 0x00000002L
+#define RTP_CONNECT_OPTION_DISC_ON_UNKNOWN_PT 0x00000004L
+#define RTP_CONNECT_OPTION_NO_SILENCE_TRANSMIT 0x00010000L
+
+#define RTP_PAYLOAD_OPTION_VOICE_ACTIVITY_DETECT 0x0001
+#define RTP_PAYLOAD_OPTION_DISABLE_POST_FILTER 0x0002
+#define RTP_PAYLOAD_OPTION_G723_LOW_CODING_RATE 0x0100
+
+#define RTP_PACKET_FILTER_IGNORE_UNKNOWN_SSRC 0x00000001L
+
+#define RTP_CHANGE_FLAG_SSRC_CHANGE 0x00000001L
+#define RTP_CHANGE_FLAG_PAYLOAD_TYPE_CHANGE 0x00000002L
+#define RTP_CHANGE_FLAG_UNKNOWN_PAYLOAD_TYPE 0x00000004L
+
+
+/*------------------------------------------------------------------*/
+/* T.38 interface to IDI */
+/*------------------------------------------------------------------*/
+
+
+#define B1_T38 30
+#define B2_T38 30
+#define B3_T38 30
+
+#define PRIVATE_T38 2
+
+
+/*------------------------------------------------------------------*/
+/* PIAFS interface to IDI */
+/*------------------------------------------------------------------*/
+
+
+#define B1_PIAFS 29
+#define B2_PIAFS 29
+
+#define PRIVATE_PIAFS 29
+
+/*
+ B2 configuration for PIAFS:
++---------------------+------+-----------------------------------------+
+| PIAFS Protocol | byte | Bit 1 - Protocol Speed |
+| Speed configuration | | 0 - 32K |
+| | | 1 - 64K (default) |
+| | | Bit 2 - Variable Protocol Speed |
+| | | 0 - Speed is fix |
+| | | 1 - Speed is variable (default) |
++---------------------+------+-----------------------------------------+
+| Direction | word | Enable compression/decompression for |
+| | | 0: All direction |
+| | | 1: disable outgoing data |
+| | | 2: disable incomming data |
+| | | 3: disable both direction (default) |
++---------------------+------+-----------------------------------------+
+| Number of code | word | Parameter P1 of V.42bis in accordance |
+| words | | with V.42bis |
++---------------------+------+-----------------------------------------+
+| Maximum String | word | Parameter P2 of V.42bis in accordance |
+| Length | | with V.42bis |
++---------------------+------+-----------------------------------------+
+| control (UDATA) | byte | enable PIAFS control communication |
+| abilities | | |
++---------------------+------+-----------------------------------------+
+*/
+#define PIAFS_UDATA_ABILITIES 0x80
+
+/*------------------------------------------------------------------*/
+/* FAX SUB/SEP/PWD extension */
+/*------------------------------------------------------------------*/
+
+
+#define PRIVATE_FAX_SUB_SEP_PWD 3
+
+
+
+/*------------------------------------------------------------------*/
+/* V.18 extension */
+/*------------------------------------------------------------------*/
+
+
+#define PRIVATE_V18 4
+
+
+
+/*------------------------------------------------------------------*/
+/* DTMF TONE extension */
+/*------------------------------------------------------------------*/
+
+
+#define DTMF_GET_SUPPORTED_DETECT_CODES 0xf8
+#define DTMF_GET_SUPPORTED_SEND_CODES 0xf9
+#define DTMF_LISTEN_TONE_START 0xfa
+#define DTMF_LISTEN_TONE_STOP 0xfb
+#define DTMF_SEND_TONE 0xfc
+#define DTMF_LISTEN_MF_START 0xfd
+#define DTMF_LISTEN_MF_STOP 0xfe
+#define DTMF_SEND_MF 0xff
+
+#define DTMF_MF_DIGIT_TONE_CODE_1 0x10
+#define DTMF_MF_DIGIT_TONE_CODE_2 0x11
+#define DTMF_MF_DIGIT_TONE_CODE_3 0x12
+#define DTMF_MF_DIGIT_TONE_CODE_4 0x13
+#define DTMF_MF_DIGIT_TONE_CODE_5 0x14
+#define DTMF_MF_DIGIT_TONE_CODE_6 0x15
+#define DTMF_MF_DIGIT_TONE_CODE_7 0x16
+#define DTMF_MF_DIGIT_TONE_CODE_8 0x17
+#define DTMF_MF_DIGIT_TONE_CODE_9 0x18
+#define DTMF_MF_DIGIT_TONE_CODE_0 0x19
+#define DTMF_MF_DIGIT_TONE_CODE_K1 0x1a
+#define DTMF_MF_DIGIT_TONE_CODE_K2 0x1b
+#define DTMF_MF_DIGIT_TONE_CODE_KP 0x1c
+#define DTMF_MF_DIGIT_TONE_CODE_S1 0x1d
+#define DTMF_MF_DIGIT_TONE_CODE_ST 0x1e
+
+#define DTMF_DIGIT_CODE_COUNT 16
+#define DTMF_MF_DIGIT_CODE_BASE DSP_DTMF_DIGIT_CODE_COUNT
+#define DTMF_MF_DIGIT_CODE_COUNT 15
+#define DTMF_TOTAL_DIGIT_CODE_COUNT (DSP_MF_DIGIT_CODE_BASE + DSP_MF_DIGIT_CODE_COUNT)
+
+#define DTMF_TONE_DIGIT_BASE 0x80
+
+#define DTMF_SIGNAL_NO_TONE (DTMF_TONE_DIGIT_BASE + 0)
+#define DTMF_SIGNAL_UNIDENTIFIED_TONE (DTMF_TONE_DIGIT_BASE + 1)
+
+#define DTMF_SIGNAL_DIAL_TONE (DTMF_TONE_DIGIT_BASE + 2)
+#define DTMF_SIGNAL_PABX_INTERNAL_DIAL_TONE (DTMF_TONE_DIGIT_BASE + 3)
+#define DTMF_SIGNAL_SPECIAL_DIAL_TONE (DTMF_TONE_DIGIT_BASE + 4) /* stutter dial tone */
+#define DTMF_SIGNAL_SECOND_DIAL_TONE (DTMF_TONE_DIGIT_BASE + 5)
+#define DTMF_SIGNAL_RINGING_TONE (DTMF_TONE_DIGIT_BASE + 6)
+#define DTMF_SIGNAL_SPECIAL_RINGING_TONE (DTMF_TONE_DIGIT_BASE + 7)
+#define DTMF_SIGNAL_BUSY_TONE (DTMF_TONE_DIGIT_BASE + 8)
+#define DTMF_SIGNAL_CONGESTION_TONE (DTMF_TONE_DIGIT_BASE + 9) /* reorder tone */
+#define DTMF_SIGNAL_SPECIAL_INFORMATION_TONE (DTMF_TONE_DIGIT_BASE + 10)
+#define DTMF_SIGNAL_COMFORT_TONE (DTMF_TONE_DIGIT_BASE + 11)
+#define DTMF_SIGNAL_HOLD_TONE (DTMF_TONE_DIGIT_BASE + 12)
+#define DTMF_SIGNAL_RECORD_TONE (DTMF_TONE_DIGIT_BASE + 13)
+#define DTMF_SIGNAL_CALLER_WAITING_TONE (DTMF_TONE_DIGIT_BASE + 14)
+#define DTMF_SIGNAL_CALL_WAITING_TONE (DTMF_TONE_DIGIT_BASE + 15)
+#define DTMF_SIGNAL_PAY_TONE (DTMF_TONE_DIGIT_BASE + 16)
+#define DTMF_SIGNAL_POSITIVE_INDICATION_TONE (DTMF_TONE_DIGIT_BASE + 17)
+#define DTMF_SIGNAL_NEGATIVE_INDICATION_TONE (DTMF_TONE_DIGIT_BASE + 18)
+#define DTMF_SIGNAL_WARNING_TONE (DTMF_TONE_DIGIT_BASE + 19)
+#define DTMF_SIGNAL_INTRUSION_TONE (DTMF_TONE_DIGIT_BASE + 20)
+#define DTMF_SIGNAL_CALLING_CARD_SERVICE_TONE (DTMF_TONE_DIGIT_BASE + 21)
+#define DTMF_SIGNAL_PAYPHONE_RECOGNITION_TONE (DTMF_TONE_DIGIT_BASE + 22)
+#define DTMF_SIGNAL_CPE_ALERTING_SIGNAL (DTMF_TONE_DIGIT_BASE + 23)
+#define DTMF_SIGNAL_OFF_HOOK_WARNING_TONE (DTMF_TONE_DIGIT_BASE + 24)
+
+#define DTMF_SIGNAL_INTERCEPT_TONE (DTMF_TONE_DIGIT_BASE + 63)
+
+#define DTMF_SIGNAL_MODEM_CALLING_TONE (DTMF_TONE_DIGIT_BASE + 64)
+#define DTMF_SIGNAL_FAX_CALLING_TONE (DTMF_TONE_DIGIT_BASE + 65)
+#define DTMF_SIGNAL_ANSWER_TONE (DTMF_TONE_DIGIT_BASE + 66)
+#define DTMF_SIGNAL_REVERSED_ANSWER_TONE (DTMF_TONE_DIGIT_BASE + 67)
+#define DTMF_SIGNAL_ANSAM_TONE (DTMF_TONE_DIGIT_BASE + 68)
+#define DTMF_SIGNAL_REVERSED_ANSAM_TONE (DTMF_TONE_DIGIT_BASE + 69)
+#define DTMF_SIGNAL_BELL103_ANSWER_TONE (DTMF_TONE_DIGIT_BASE + 70)
+#define DTMF_SIGNAL_FAX_FLAGS (DTMF_TONE_DIGIT_BASE + 71)
+#define DTMF_SIGNAL_G2_FAX_GROUP_ID (DTMF_TONE_DIGIT_BASE + 72)
+#define DTMF_SIGNAL_HUMAN_SPEECH (DTMF_TONE_DIGIT_BASE + 73)
+#define DTMF_SIGNAL_ANSWERING_MACHINE_390 (DTMF_TONE_DIGIT_BASE + 74)
+
+#define DTMF_MF_LISTEN_ACTIVE_FLAG 0x02
+#define DTMF_SEND_MF_FLAG 0x02
+#define DTMF_TONE_LISTEN_ACTIVE_FLAG 0x04
+#define DTMF_SEND_TONE_FLAG 0x04
+
+#define PRIVATE_DTMF_TONE 5
+
+
+/*------------------------------------------------------------------*/
+/* FAX paper format extension */
+/*------------------------------------------------------------------*/
+
+
+#define PRIVATE_FAX_PAPER_FORMATS 6
+
+
+
+/*------------------------------------------------------------------*/
+/* V.OWN extension */
+/*------------------------------------------------------------------*/
+
+
+#define PRIVATE_VOWN 7
+
+
+
+/*------------------------------------------------------------------*/
+/* FAX non-standard facilities extension */
+/*------------------------------------------------------------------*/
+
+
+#define PRIVATE_FAX_NONSTANDARD 8
+
+
+
+/*------------------------------------------------------------------*/
+/* Advanced voice */
+/*------------------------------------------------------------------*/
+
+#define ADV_VOICE_WRITE_ACTIVATION 0
+#define ADV_VOICE_WRITE_DEACTIVATION 1
+#define ADV_VOICE_WRITE_UPDATE 2
+
+#define ADV_VOICE_OLD_COEF_COUNT 6
+#define ADV_VOICE_NEW_COEF_BASE (ADV_VOICE_OLD_COEF_COUNT * sizeof(word))
+
+/*------------------------------------------------------------------*/
+/* B1 resource switching */
+/*------------------------------------------------------------------*/
+
+#define B1_FACILITY_LOCAL 0x01
+#define B1_FACILITY_MIXER 0x02
+#define B1_FACILITY_DTMFX 0x04
+#define B1_FACILITY_DTMFR 0x08
+#define B1_FACILITY_VOICE 0x10
+#define B1_FACILITY_EC 0x20
+
+#define ADJUST_B_MODE_SAVE 0x0001
+#define ADJUST_B_MODE_REMOVE_L23 0x0002
+#define ADJUST_B_MODE_SWITCH_L1 0x0004
+#define ADJUST_B_MODE_NO_RESOURCE 0x0008
+#define ADJUST_B_MODE_ASSIGN_L23 0x0010
+#define ADJUST_B_MODE_USER_CONNECT 0x0020
+#define ADJUST_B_MODE_CONNECT 0x0040
+#define ADJUST_B_MODE_RESTORE 0x0080
+
+#define ADJUST_B_START 0
+#define ADJUST_B_SAVE_MIXER_1 1
+#define ADJUST_B_SAVE_DTMF_1 2
+#define ADJUST_B_REMOVE_L23_1 3
+#define ADJUST_B_REMOVE_L23_2 4
+#define ADJUST_B_SAVE_EC_1 5
+#define ADJUST_B_SAVE_DTMF_PARAMETER_1 6
+#define ADJUST_B_SAVE_VOICE_1 7
+#define ADJUST_B_SWITCH_L1_1 8
+#define ADJUST_B_SWITCH_L1_2 9
+#define ADJUST_B_RESTORE_VOICE_1 10
+#define ADJUST_B_RESTORE_VOICE_2 11
+#define ADJUST_B_RESTORE_DTMF_PARAMETER_1 12
+#define ADJUST_B_RESTORE_DTMF_PARAMETER_2 13
+#define ADJUST_B_RESTORE_EC_1 14
+#define ADJUST_B_RESTORE_EC_2 15
+#define ADJUST_B_ASSIGN_L23_1 16
+#define ADJUST_B_ASSIGN_L23_2 17
+#define ADJUST_B_CONNECT_1 18
+#define ADJUST_B_CONNECT_2 19
+#define ADJUST_B_CONNECT_3 20
+#define ADJUST_B_CONNECT_4 21
+#define ADJUST_B_RESTORE_DTMF_1 22
+#define ADJUST_B_RESTORE_DTMF_2 23
+#define ADJUST_B_RESTORE_MIXER_1 24
+#define ADJUST_B_RESTORE_MIXER_2 25
+#define ADJUST_B_RESTORE_MIXER_3 26
+#define ADJUST_B_RESTORE_MIXER_4 27
+#define ADJUST_B_RESTORE_MIXER_5 28
+#define ADJUST_B_RESTORE_MIXER_6 29
+#define ADJUST_B_RESTORE_MIXER_7 30
+#define ADJUST_B_END 31
+
+/*------------------------------------------------------------------*/
+/* XON Protocol def's */
+/*------------------------------------------------------------------*/
+#define N_CH_XOFF 0x01
+#define N_XON_SENT 0x02
+#define N_XON_REQ 0x04
+#define N_XON_CONNECT_IND 0x08
+#define N_RX_FLOW_CONTROL_MASK 0x3f
+#define N_OK_FC_PENDING 0x80
+#define N_TX_FLOW_CONTROL_MASK 0xc0
+
+/*------------------------------------------------------------------*/
+/* NCPI state */
+/*------------------------------------------------------------------*/
+#define NCPI_VALID_CONNECT_B3_IND 0x01
+#define NCPI_VALID_CONNECT_B3_ACT 0x02
+#define NCPI_VALID_DISC_B3_IND 0x04
+#define NCPI_CONNECT_B3_ACT_SENT 0x08
+#define NCPI_NEGOTIATE_B3_SENT 0x10
+#define NCPI_MDM_CTS_ON_RECEIVED 0x40
+#define NCPI_MDM_DCD_ON_RECEIVED 0x80
+
+/*------------------------------------------------------------------*/
diff --git a/drivers/isdn/hardware/eicon/divamnt.c b/drivers/isdn/hardware/eicon/divamnt.c
new file mode 100644
index 0000000..1e85f74
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/divamnt.c
@@ -0,0 +1,257 @@
+/* $Id: divamnt.c,v 1.32.6.10 2005/02/11 19:40:25 armin Exp $
+ *
+ * Driver for Eicon DIVA Server ISDN cards.
+ * Maint module
+ *
+ * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
+ * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/poll.h>
+#include <linux/smp_lock.h>
+#include <asm/uaccess.h>
+
+#include "platform.h"
+#include "di_defs.h"
+#include "divasync.h"
+#include "debug_if.h"
+
+static char *main_revision = "$Revision: 1.32.6.10 $";
+
+static int major;
+
+MODULE_DESCRIPTION("Maint driver for Eicon DIVA Server cards");
+MODULE_AUTHOR("Cytronics & Melware, Eicon Networks");
+MODULE_SUPPORTED_DEVICE("DIVA card driver");
+MODULE_LICENSE("GPL");
+
+static int buffer_length = 128;
+module_param(buffer_length, int, 0);
+static unsigned long diva_dbg_mem = 0;
+module_param(diva_dbg_mem, ulong, 0);
+
+static char *DRIVERNAME =
+ "Eicon DIVA - MAINT module (http://www.melware.net)";
+static char *DRIVERLNAME = "diva_mnt";
+static char *DEVNAME = "DivasMAINT";
+char *DRIVERRELEASE_MNT = "2.0";
+
+static wait_queue_head_t msgwaitq;
+static unsigned long opened;
+static struct timeval start_time;
+
+extern int mntfunc_init(int *, void **, unsigned long);
+extern void mntfunc_finit(void);
+extern int maint_read_write(void __user *buf, int count);
+
+/*
+ * helper functions
+ */
+static char *getrev(const char *revision)
+{
+ char *rev;
+ char *p;
+
+ if ((p = strchr(revision, ':'))) {
+ rev = p + 2;
+ p = strchr(rev, '$');
+ *--p = 0;
+ } else
+ rev = "1.0";
+
+ return rev;
+}
+
+/*
+ * kernel/user space copy functions
+ */
+int diva_os_copy_to_user(void *os_handle, void __user *dst, const void *src,
+ int length)
+{
+ return (copy_to_user(dst, src, length));
+}
+int diva_os_copy_from_user(void *os_handle, void *dst, const void __user *src,
+ int length)
+{
+ return (copy_from_user(dst, src, length));
+}
+
+/*
+ * get time
+ */
+void diva_os_get_time(dword * sec, dword * usec)
+{
+ struct timeval tv;
+
+ do_gettimeofday(&tv);
+
+ if (tv.tv_sec > start_time.tv_sec) {
+ if (start_time.tv_usec > tv.tv_usec) {
+ tv.tv_sec--;
+ tv.tv_usec += 1000000;
+ }
+ *sec = (dword) (tv.tv_sec - start_time.tv_sec);
+ *usec = (dword) (tv.tv_usec - start_time.tv_usec);
+ } else if (tv.tv_sec == start_time.tv_sec) {
+ *sec = 0;
+ if (start_time.tv_usec < tv.tv_usec) {
+ *usec = (dword) (tv.tv_usec - start_time.tv_usec);
+ } else {
+ *usec = 0;
+ }
+ } else {
+ *sec = (dword) tv.tv_sec;
+ *usec = (dword) tv.tv_usec;
+ }
+}
+
+/*
+ * device node operations
+ */
+static unsigned int maint_poll(struct file *file, poll_table * wait)
+{
+ unsigned int mask = 0;
+
+ poll_wait(file, &msgwaitq, wait);
+ mask = POLLOUT | POLLWRNORM;
+ if (file->private_data || diva_dbg_q_length()) {
+ mask |= POLLIN | POLLRDNORM;
+ }
+ return (mask);
+}
+
+static int maint_open(struct inode *ino, struct file *filep)
+{
+ int ret;
+
+ lock_kernel();
+ /* only one open is allowed, so we test
+ it atomically */
+ if (test_and_set_bit(0, &opened))
+ ret = -EBUSY;
+ else {
+ filep->private_data = NULL;
+ ret = nonseekable_open(ino, filep);
+ }
+ unlock_kernel();
+ return ret;
+}
+
+static int maint_close(struct inode *ino, struct file *filep)
+{
+ if (filep->private_data) {
+ diva_os_free(0, filep->private_data);
+ filep->private_data = NULL;
+ }
+
+ /* clear 'used' flag */
+ clear_bit(0, &opened);
+
+ return (0);
+}
+
+static ssize_t divas_maint_write(struct file *file, const char __user *buf,
+ size_t count, loff_t * ppos)
+{
+ return (maint_read_write((char __user *) buf, (int) count));
+}
+
+static ssize_t divas_maint_read(struct file *file, char __user *buf,
+ size_t count, loff_t * ppos)
+{
+ return (maint_read_write(buf, (int) count));
+}
+
+static const struct file_operations divas_maint_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = divas_maint_read,
+ .write = divas_maint_write,
+ .poll = maint_poll,
+ .open = maint_open,
+ .release = maint_close
+};
+
+static void divas_maint_unregister_chrdev(void)
+{
+ unregister_chrdev(major, DEVNAME);
+}
+
+static int DIVA_INIT_FUNCTION divas_maint_register_chrdev(void)
+{
+ if ((major = register_chrdev(0, DEVNAME, &divas_maint_fops)) < 0)
+ {
+ printk(KERN_ERR "%s: failed to create /dev entry.\n",
+ DRIVERLNAME);
+ return (0);
+ }
+
+ return (1);
+}
+
+/*
+ * wake up reader
+ */
+void diva_maint_wakeup_read(void)
+{
+ wake_up_interruptible(&msgwaitq);
+}
+
+/*
+ * Driver Load
+ */
+static int DIVA_INIT_FUNCTION maint_init(void)
+{
+ char tmprev[50];
+ int ret = 0;
+ void *buffer = NULL;
+
+ do_gettimeofday(&start_time);
+ init_waitqueue_head(&msgwaitq);
+
+ printk(KERN_INFO "%s\n", DRIVERNAME);
+ printk(KERN_INFO "%s: Rel:%s Rev:", DRIVERLNAME, DRIVERRELEASE_MNT);
+ strcpy(tmprev, main_revision);
+ printk("%s Build: %s \n", getrev(tmprev), DIVA_BUILD);
+
+ if (!divas_maint_register_chrdev()) {
+ ret = -EIO;
+ goto out;
+ }
+
+ if (!(mntfunc_init(&buffer_length, &buffer, diva_dbg_mem))) {
+ printk(KERN_ERR "%s: failed to connect to DIDD.\n",
+ DRIVERLNAME);
+ divas_maint_unregister_chrdev();
+ ret = -EIO;
+ goto out;
+ }
+
+ printk(KERN_INFO "%s: trace buffer = %p - %d kBytes, %s (Major: %d)\n",
+ DRIVERLNAME, buffer, (buffer_length / 1024),
+ (diva_dbg_mem == 0) ? "internal" : "external", major);
+
+ out:
+ return (ret);
+}
+
+/*
+** Driver Unload
+*/
+static void DIVA_EXIT_FUNCTION maint_exit(void)
+{
+ divas_maint_unregister_chrdev();
+ mntfunc_finit();
+
+ printk(KERN_INFO "%s: module unloaded.\n", DRIVERLNAME);
+}
+
+module_init(maint_init);
+module_exit(maint_exit);
+
diff --git a/drivers/isdn/hardware/eicon/divasfunc.c b/drivers/isdn/hardware/eicon/divasfunc.c
new file mode 100644
index 0000000..d36a4c0
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/divasfunc.c
@@ -0,0 +1,238 @@
+/* $Id: divasfunc.c,v 1.23.4.2 2004/08/28 20:03:53 armin Exp $
+ *
+ * Low level driver for Eicon DIVA Server ISDN cards.
+ *
+ * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
+ * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include "platform.h"
+#include "di_defs.h"
+#include "pc.h"
+#include "di.h"
+#include "io.h"
+#include "divasync.h"
+#include "diva.h"
+#include "xdi_vers.h"
+
+#define DBG_MINIMUM (DL_LOG + DL_FTL + DL_ERR)
+#define DBG_DEFAULT (DBG_MINIMUM + DL_XLOG + DL_REG)
+
+static int debugmask;
+
+extern void DIVA_DIDD_Read(void *, int);
+
+extern PISDN_ADAPTER IoAdapters[MAX_ADAPTER];
+
+extern char *DRIVERRELEASE_DIVAS;
+
+static dword notify_handle;
+static DESCRIPTOR DAdapter;
+static DESCRIPTOR MAdapter;
+
+/* --------------------------------------------------------------------------
+ MAINT driver connector section
+ -------------------------------------------------------------------------- */
+static void no_printf(unsigned char *x, ...)
+{
+ /* dummy debug function */
+}
+
+#include "debuglib.c"
+
+/*
+ * get the adapters serial number
+ */
+void diva_get_vserial_number(PISDN_ADAPTER IoAdapter, char *buf)
+{
+ int contr = 0;
+
+ if ((contr = ((IoAdapter->serialNo & 0xff000000) >> 24))) {
+ sprintf(buf, "%d-%d",
+ IoAdapter->serialNo & 0x00ffffff, contr + 1);
+ } else {
+ sprintf(buf, "%d", IoAdapter->serialNo);
+ }
+}
+
+/*
+ * register a new adapter
+ */
+void diva_xdi_didd_register_adapter(int card)
+{
+ DESCRIPTOR d;
+ IDI_SYNC_REQ req;
+
+ if (card && ((card - 1) < MAX_ADAPTER) &&
+ IoAdapters[card - 1] && Requests[card - 1]) {
+ d.type = IoAdapters[card - 1]->Properties.DescType;
+ d.request = Requests[card - 1];
+ d.channels = IoAdapters[card - 1]->Properties.Channels;
+ d.features = IoAdapters[card - 1]->Properties.Features;
+ DBG_TRC(("DIDD register A(%d) channels=%d", card,
+ d.channels))
+ /* workaround for different Name in structure */
+ strlcpy(IoAdapters[card - 1]->Name,
+ IoAdapters[card - 1]->Properties.Name,
+ sizeof(IoAdapters[card - 1]->Name));
+ req.didd_remove_adapter.e.Req = 0;
+ req.didd_add_adapter.e.Rc = IDI_SYNC_REQ_DIDD_ADD_ADAPTER;
+ req.didd_add_adapter.info.descriptor = (void *) &d;
+ DAdapter.request((ENTITY *) & req);
+ if (req.didd_add_adapter.e.Rc != 0xff) {
+ DBG_ERR(("DIDD register A(%d) failed !", card))
+ }
+ IoAdapters[card - 1]->os_trap_nfy_Fnc = NULL;
+ }
+}
+
+/*
+ * remove an adapter
+ */
+void diva_xdi_didd_remove_adapter(int card)
+{
+ IDI_SYNC_REQ req;
+ ADAPTER *a = &IoAdapters[card - 1]->a;
+
+ IoAdapters[card - 1]->os_trap_nfy_Fnc = NULL;
+ DBG_TRC(("DIDD de-register A(%d)", card))
+ req.didd_remove_adapter.e.Req = 0;
+ req.didd_remove_adapter.e.Rc = IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER;
+ req.didd_remove_adapter.info.p_request =
+ (IDI_CALL) Requests[card - 1];
+ DAdapter.request((ENTITY *) & req);
+ memset(&(a->IdTable), 0x00, 256);
+}
+
+/*
+ * start debug
+ */
+static void start_dbg(void)
+{
+ DbgRegister("DIVAS", DRIVERRELEASE_DIVAS, (debugmask) ? debugmask : DBG_DEFAULT);
+ DBG_LOG(("DIVA ISDNXDI BUILD (%s[%s]-%s-%s)",
+ DIVA_BUILD, diva_xdi_common_code_build, __DATE__,
+ __TIME__))
+}
+
+/*
+ * stop debug
+ */
+static void stop_dbg(void)
+{
+ DbgDeregister();
+ memset(&MAdapter, 0, sizeof(MAdapter));
+ dprintf = no_printf;
+}
+
+/*
+ * didd callback function
+ */
+static void *didd_callback(void *context, DESCRIPTOR * adapter,
+ int removal)
+{
+ if (adapter->type == IDI_DADAPTER) {
+ DBG_ERR(("Notification about IDI_DADAPTER change ! Oops."));
+ return (NULL);
+ }
+
+ if (adapter->type == IDI_DIMAINT) {
+ if (removal) {
+ stop_dbg();
+ } else {
+ memcpy(&MAdapter, adapter, sizeof(MAdapter));
+ dprintf = (DIVA_DI_PRINTF) MAdapter.request;
+ start_dbg();
+ }
+ }
+ return (NULL);
+}
+
+/*
+ * connect to didd
+ */
+static int DIVA_INIT_FUNCTION connect_didd(void)
+{
+ int x = 0;
+ int dadapter = 0;
+ IDI_SYNC_REQ req;
+ DESCRIPTOR DIDD_Table[MAX_DESCRIPTORS];
+
+ DIVA_DIDD_Read(DIDD_Table, sizeof(DIDD_Table));
+
+ for (x = 0; x < MAX_DESCRIPTORS; x++) {
+ if (DIDD_Table[x].type == IDI_DADAPTER) { /* DADAPTER found */
+ dadapter = 1;
+ memcpy(&DAdapter, &DIDD_Table[x], sizeof(DAdapter));
+ req.didd_notify.e.Req = 0;
+ req.didd_notify.e.Rc =
+ IDI_SYNC_REQ_DIDD_REGISTER_ADAPTER_NOTIFY;
+ req.didd_notify.info.callback = (void *)didd_callback;
+ req.didd_notify.info.context = NULL;
+ DAdapter.request((ENTITY *) & req);
+ if (req.didd_notify.e.Rc != 0xff) {
+ stop_dbg();
+ return (0);
+ }
+ notify_handle = req.didd_notify.info.handle;
+ } else if (DIDD_Table[x].type == IDI_DIMAINT) { /* MAINT found */
+ memcpy(&MAdapter, &DIDD_Table[x], sizeof(DAdapter));
+ dprintf = (DIVA_DI_PRINTF) MAdapter.request;
+ start_dbg();
+ }
+ }
+
+ if (!dadapter) {
+ stop_dbg();
+ }
+
+ return (dadapter);
+}
+
+/*
+ * disconnect from didd
+ */
+static void disconnect_didd(void)
+{
+ IDI_SYNC_REQ req;
+
+ stop_dbg();
+
+ req.didd_notify.e.Req = 0;
+ req.didd_notify.e.Rc = IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER_NOTIFY;
+ req.didd_notify.info.handle = notify_handle;
+ DAdapter.request((ENTITY *) & req);
+}
+
+/*
+ * init
+ */
+int DIVA_INIT_FUNCTION divasfunc_init(int dbgmask)
+{
+ char *version;
+
+ debugmask = dbgmask;
+
+ if (!connect_didd()) {
+ DBG_ERR(("divasfunc: failed to connect to DIDD."))
+ return (0);
+ }
+
+ version = diva_xdi_common_code_build;
+
+ divasa_xdi_driver_entry();
+
+ return (1);
+}
+
+/*
+ * exit
+ */
+void divasfunc_exit(void)
+{
+ divasa_xdi_driver_unload();
+ disconnect_didd();
+}
diff --git a/drivers/isdn/hardware/eicon/divasi.c b/drivers/isdn/hardware/eicon/divasi.c
new file mode 100644
index 0000000..f4969fe
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/divasi.c
@@ -0,0 +1,578 @@
+/* $Id: divasi.c,v 1.25.6.2 2005/01/31 12:22:20 armin Exp $
+ *
+ * Driver for Eicon DIVA Server ISDN cards.
+ * User Mode IDI Interface
+ *
+ * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
+ * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/skbuff.h>
+#include <linux/smp_lock.h>
+#include <asm/uaccess.h>
+
+#include "platform.h"
+#include "di_defs.h"
+#include "divasync.h"
+#include "um_xdi.h"
+#include "um_idi.h"
+
+static char *main_revision = "$Revision: 1.25.6.2 $";
+
+static int major;
+
+MODULE_DESCRIPTION("User IDI Interface for Eicon ISDN cards");
+MODULE_AUTHOR("Cytronics & Melware, Eicon Networks");
+MODULE_SUPPORTED_DEVICE("DIVA card driver");
+MODULE_LICENSE("GPL");
+
+typedef struct _diva_um_idi_os_context {
+ wait_queue_head_t read_wait;
+ wait_queue_head_t close_wait;
+ struct timer_list diva_timer_id;
+ int aborted;
+ int adapter_nr;
+} diva_um_idi_os_context_t;
+
+static char *DRIVERNAME = "Eicon DIVA - User IDI (http://www.melware.net)";
+static char *DRIVERLNAME = "diva_idi";
+static char *DEVNAME = "DivasIDI";
+char *DRIVERRELEASE_IDI = "2.0";
+
+extern int idifunc_init(void);
+extern void idifunc_finit(void);
+
+/*
+ * helper functions
+ */
+static char *getrev(const char *revision)
+{
+ char *rev;
+ char *p;
+ if ((p = strchr(revision, ':'))) {
+ rev = p + 2;
+ p = strchr(rev, '$');
+ *--p = 0;
+ } else
+ rev = "1.0";
+ return rev;
+}
+
+/*
+ * LOCALS
+ */
+static ssize_t um_idi_read(struct file *file, char __user *buf, size_t count,
+ loff_t * offset);
+static ssize_t um_idi_write(struct file *file, const char __user *buf,
+ size_t count, loff_t * offset);
+static unsigned int um_idi_poll(struct file *file, poll_table * wait);
+static int um_idi_open(struct inode *inode, struct file *file);
+static int um_idi_release(struct inode *inode, struct file *file);
+static int remove_entity(void *entity);
+static void diva_um_timer_function(unsigned long data);
+
+/*
+ * proc entry
+ */
+extern struct proc_dir_entry *proc_net_eicon;
+static struct proc_dir_entry *um_idi_proc_entry = NULL;
+
+static int
+um_idi_proc_read(char *page, char **start, off_t off, int count, int *eof,
+ void *data)
+{
+ int len = 0;
+ char tmprev[32];
+
+ len += sprintf(page + len, "%s\n", DRIVERNAME);
+ len += sprintf(page + len, "name : %s\n", DRIVERLNAME);
+ len += sprintf(page + len, "release : %s\n", DRIVERRELEASE_IDI);
+ strcpy(tmprev, main_revision);
+ len += sprintf(page + len, "revision : %s\n", getrev(tmprev));
+ len += sprintf(page + len, "build : %s\n", DIVA_BUILD);
+ len += sprintf(page + len, "major : %d\n", major);
+
+ if (off + count >= len)
+ *eof = 1;
+ if (len < off)
+ return 0;
+ *start = page + off;
+ return ((count < len - off) ? count : len - off);
+}
+
+static int DIVA_INIT_FUNCTION create_um_idi_proc(void)
+{
+ um_idi_proc_entry = create_proc_entry(DRIVERLNAME,
+ S_IFREG | S_IRUGO | S_IWUSR,
+ proc_net_eicon);
+ if (!um_idi_proc_entry)
+ return (0);
+
+ um_idi_proc_entry->read_proc = um_idi_proc_read;
+ um_idi_proc_entry->owner = THIS_MODULE;
+
+ return (1);
+}
+
+static void remove_um_idi_proc(void)
+{
+ if (um_idi_proc_entry) {
+ remove_proc_entry(DRIVERLNAME, proc_net_eicon);
+ um_idi_proc_entry = NULL;
+ }
+}
+
+static const struct file_operations divas_idi_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = um_idi_read,
+ .write = um_idi_write,
+ .poll = um_idi_poll,
+ .open = um_idi_open,
+ .release = um_idi_release
+};
+
+static void divas_idi_unregister_chrdev(void)
+{
+ unregister_chrdev(major, DEVNAME);
+}
+
+static int DIVA_INIT_FUNCTION divas_idi_register_chrdev(void)
+{
+ if ((major = register_chrdev(0, DEVNAME, &divas_idi_fops)) < 0)
+ {
+ printk(KERN_ERR "%s: failed to create /dev entry.\n",
+ DRIVERLNAME);
+ return (0);
+ }
+
+ return (1);
+}
+
+/*
+** Driver Load
+*/
+static int DIVA_INIT_FUNCTION divasi_init(void)
+{
+ char tmprev[50];
+ int ret = 0;
+
+ printk(KERN_INFO "%s\n", DRIVERNAME);
+ printk(KERN_INFO "%s: Rel:%s Rev:", DRIVERLNAME, DRIVERRELEASE_IDI);
+ strcpy(tmprev, main_revision);
+ printk("%s Build: %s\n", getrev(tmprev), DIVA_BUILD);
+
+ if (!divas_idi_register_chrdev()) {
+ ret = -EIO;
+ goto out;
+ }
+
+ if (!create_um_idi_proc()) {
+ divas_idi_unregister_chrdev();
+ printk(KERN_ERR "%s: failed to create proc entry.\n",
+ DRIVERLNAME);
+ ret = -EIO;
+ goto out;
+ }
+
+ if (!(idifunc_init())) {
+ remove_um_idi_proc();
+ divas_idi_unregister_chrdev();
+ printk(KERN_ERR "%s: failed to connect to DIDD.\n",
+ DRIVERLNAME);
+ ret = -EIO;
+ goto out;
+ }
+ printk(KERN_INFO "%s: started with major %d\n", DRIVERLNAME, major);
+
+ out:
+ return (ret);
+}
+
+
+/*
+** Driver Unload
+*/
+static void DIVA_EXIT_FUNCTION divasi_exit(void)
+{
+ idifunc_finit();
+ remove_um_idi_proc();
+ divas_idi_unregister_chrdev();
+
+ printk(KERN_INFO "%s: module unloaded.\n", DRIVERLNAME);
+}
+
+module_init(divasi_init);
+module_exit(divasi_exit);
+
+
+/*
+ * FILE OPERATIONS
+ */
+
+static int
+divas_um_idi_copy_to_user(void *os_handle, void *dst, const void *src,
+ int length)
+{
+ memcpy(dst, src, length);
+ return (length);
+}
+
+static ssize_t
+um_idi_read(struct file *file, char __user *buf, size_t count, loff_t * offset)
+{
+ diva_um_idi_os_context_t *p_os;
+ int ret = -EINVAL;
+ void *data;
+
+ if (!file->private_data) {
+ return (-ENODEV);
+ }
+
+ if (!
+ (p_os =
+ (diva_um_idi_os_context_t *) diva_um_id_get_os_context(file->
+ private_data)))
+ {
+ return (-ENODEV);
+ }
+ if (p_os->aborted) {
+ return (-ENODEV);
+ }
+
+ if (!(data = diva_os_malloc(0, count))) {
+ return (-ENOMEM);
+ }
+
+ ret = diva_um_idi_read(file->private_data,
+ file, data, count,
+ divas_um_idi_copy_to_user);
+ switch (ret) {
+ case 0: /* no message available */
+ ret = (-EAGAIN);
+ break;
+ case (-1): /* adapter was removed */
+ ret = (-ENODEV);
+ break;
+ case (-2): /* message_length > length of user buffer */
+ ret = (-EFAULT);
+ break;
+ }
+
+ if (ret > 0) {
+ if (copy_to_user(buf, data, ret)) {
+ ret = (-EFAULT);
+ }
+ }
+
+ diva_os_free(0, data);
+ DBG_TRC(("read: ret %d", ret));
+ return (ret);
+}
+
+
+static int
+divas_um_idi_copy_from_user(void *os_handle, void *dst, const void *src,
+ int length)
+{
+ memcpy(dst, src, length);
+ return (length);
+}
+
+static int um_idi_open_adapter(struct file *file, int adapter_nr)
+{
+ diva_um_idi_os_context_t *p_os;
+ void *e =
+ divas_um_idi_create_entity((dword) adapter_nr, (void *) file);
+
+ if (!(file->private_data = e)) {
+ return (0);
+ }
+ p_os = (diva_um_idi_os_context_t *) diva_um_id_get_os_context(e);
+ init_waitqueue_head(&p_os->read_wait);
+ init_waitqueue_head(&p_os->close_wait);
+ init_timer(&p_os->diva_timer_id);
+ p_os->diva_timer_id.function = (void *) diva_um_timer_function;
+ p_os->diva_timer_id.data = (unsigned long) p_os;
+ p_os->aborted = 0;
+ p_os->adapter_nr = adapter_nr;
+ return (1);
+}
+
+static ssize_t
+um_idi_write(struct file *file, const char __user *buf, size_t count,
+ loff_t * offset)
+{
+ diva_um_idi_os_context_t *p_os;
+ int ret = -EINVAL;
+ void *data;
+ int adapter_nr = 0;
+
+ if (!file->private_data) {
+ /* the first write() selects the adapter_nr */
+ if (count == sizeof(int)) {
+ if (copy_from_user
+ ((void *) &adapter_nr, buf,
+ count)) return (-EFAULT);
+ if (!(um_idi_open_adapter(file, adapter_nr)))
+ return (-ENODEV);
+ return (count);
+ } else
+ return (-ENODEV);
+ }
+
+ if (!(p_os =
+ (diva_um_idi_os_context_t *) diva_um_id_get_os_context(file->
+ private_data)))
+ {
+ return (-ENODEV);
+ }
+ if (p_os->aborted) {
+ return (-ENODEV);
+ }
+
+ if (!(data = diva_os_malloc(0, count))) {
+ return (-ENOMEM);
+ }
+
+ if (copy_from_user(data, buf, count)) {
+ ret = -EFAULT;
+ } else {
+ ret = diva_um_idi_write(file->private_data,
+ file, data, count,
+ divas_um_idi_copy_from_user);
+ switch (ret) {
+ case 0: /* no space available */
+ ret = (-EAGAIN);
+ break;
+ case (-1): /* adapter was removed */
+ ret = (-ENODEV);
+ break;
+ case (-2): /* length of user buffer > max message_length */
+ ret = (-EFAULT);
+ break;
+ }
+ }
+ diva_os_free(0, data);
+ DBG_TRC(("write: ret %d", ret));
+ return (ret);
+}
+
+static unsigned int um_idi_poll(struct file *file, poll_table * wait)
+{
+ diva_um_idi_os_context_t *p_os;
+
+ if (!file->private_data) {
+ return (POLLERR);
+ }
+
+ if ((!(p_os =
+ (diva_um_idi_os_context_t *)
+ diva_um_id_get_os_context(file->private_data)))
+ || p_os->aborted) {
+ return (POLLERR);
+ }
+
+ poll_wait(file, &p_os->read_wait, wait);
+
+ if (p_os->aborted) {
+ return (POLLERR);
+ }
+
+ switch (diva_user_mode_idi_ind_ready(file->private_data, file)) {
+ case (-1):
+ return (POLLERR);
+
+ case 0:
+ return (0);
+ }
+
+ return (POLLIN | POLLRDNORM);
+}
+
+static int um_idi_open(struct inode *inode, struct file *file)
+{
+ cycle_kernel_lock();
+ return (0);
+}
+
+
+static int um_idi_release(struct inode *inode, struct file *file)
+{
+ diva_um_idi_os_context_t *p_os;
+ unsigned int adapter_nr;
+ int ret = 0;
+
+ if (!(file->private_data)) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ if (!(p_os =
+ (diva_um_idi_os_context_t *) diva_um_id_get_os_context(file->private_data))) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ adapter_nr = p_os->adapter_nr;
+
+ if ((ret = remove_entity(file->private_data))) {
+ goto out;
+ }
+
+ if (divas_um_idi_delete_entity
+ ((int) adapter_nr, file->private_data)) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ out:
+ return (ret);
+}
+
+int diva_os_get_context_size(void)
+{
+ return (sizeof(diva_um_idi_os_context_t));
+}
+
+void diva_os_wakeup_read(void *os_context)
+{
+ diva_um_idi_os_context_t *p_os =
+ (diva_um_idi_os_context_t *) os_context;
+ wake_up_interruptible(&p_os->read_wait);
+}
+
+void diva_os_wakeup_close(void *os_context)
+{
+ diva_um_idi_os_context_t *p_os =
+ (diva_um_idi_os_context_t *) os_context;
+ wake_up_interruptible(&p_os->close_wait);
+}
+
+static
+void diva_um_timer_function(unsigned long data)
+{
+ diva_um_idi_os_context_t *p_os = (diva_um_idi_os_context_t *) data;
+
+ p_os->aborted = 1;
+ wake_up_interruptible(&p_os->read_wait);
+ wake_up_interruptible(&p_os->close_wait);
+ DBG_ERR(("entity removal watchdog"))
+}
+
+/*
+** If application exits without entity removal this function will remove
+** entity and block until removal is complete
+*/
+static int remove_entity(void *entity)
+{
+ struct task_struct *curtask = current;
+ diva_um_idi_os_context_t *p_os;
+
+ diva_um_idi_stop_wdog(entity);
+
+ if (!entity) {
+ DBG_FTL(("Zero entity on remove"))
+ return (0);
+ }
+
+ if (!(p_os =
+ (diva_um_idi_os_context_t *)
+ diva_um_id_get_os_context(entity))) {
+ DBG_FTL(("Zero entity os context on remove"))
+ return (0);
+ }
+
+ if (!divas_um_idi_entity_assigned(entity) || p_os->aborted) {
+ /*
+ Entity is not assigned, also can be removed
+ */
+ return (0);
+ }
+
+ DBG_TRC(("E(%08x) check remove", entity))
+
+ /*
+ If adapter not answers on remove request inside of
+ 10 Sec, then adapter is dead
+ */
+ diva_um_idi_start_wdog(entity);
+
+ {
+ DECLARE_WAITQUEUE(wait, curtask);
+
+ add_wait_queue(&p_os->close_wait, &wait);
+ for (;;) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (!divas_um_idi_entity_start_remove(entity)
+ || p_os->aborted) {
+ break;
+ }
+ schedule();
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&p_os->close_wait, &wait);
+ }
+
+ DBG_TRC(("E(%08x) start remove", entity))
+ {
+ DECLARE_WAITQUEUE(wait, curtask);
+
+ add_wait_queue(&p_os->close_wait, &wait);
+ for (;;) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (!divas_um_idi_entity_assigned(entity)
+ || p_os->aborted) {
+ break;
+ }
+ schedule();
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&p_os->close_wait, &wait);
+ }
+
+ DBG_TRC(("E(%08x) remove complete, aborted:%d", entity,
+ p_os->aborted))
+
+ diva_um_idi_stop_wdog(entity);
+
+ p_os->aborted = 0;
+
+ return (0);
+}
+
+/*
+ * timer watchdog
+ */
+void diva_um_idi_start_wdog(void *entity)
+{
+ diva_um_idi_os_context_t *p_os;
+
+ if (entity &&
+ ((p_os =
+ (diva_um_idi_os_context_t *)
+ diva_um_id_get_os_context(entity)))) {
+ mod_timer(&p_os->diva_timer_id, jiffies + 10 * HZ);
+ }
+}
+
+void diva_um_idi_stop_wdog(void *entity)
+{
+ diva_um_idi_os_context_t *p_os;
+
+ if (entity &&
+ ((p_os =
+ (diva_um_idi_os_context_t *)
+ diva_um_id_get_os_context(entity)))) {
+ del_timer(&p_os->diva_timer_id);
+ }
+}
diff --git a/drivers/isdn/hardware/eicon/divasmain.c b/drivers/isdn/hardware/eicon/divasmain.c
new file mode 100644
index 0000000..fbbcb27
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/divasmain.c
@@ -0,0 +1,850 @@
+/* $Id: divasmain.c,v 1.55.4.6 2005/02/09 19:28:20 armin Exp $
+ *
+ * Low level driver for Eicon DIVA Server ISDN cards.
+ *
+ * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
+ * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <linux/ioport.h>
+#include <linux/workqueue.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/poll.h>
+#include <linux/kmod.h>
+#include <linux/smp_lock.h>
+
+#include "platform.h"
+#undef ID_MASK
+#undef N_DATA
+#include "pc.h"
+#include "di_defs.h"
+#include "divasync.h"
+#include "diva.h"
+#include "di.h"
+#include "io.h"
+#include "xdi_msg.h"
+#include "xdi_adapter.h"
+#include "xdi_vers.h"
+#include "diva_dma.h"
+#include "diva_pci.h"
+
+static char *main_revision = "$Revision: 1.55.4.6 $";
+
+static int major;
+
+static int dbgmask;
+
+MODULE_DESCRIPTION("Kernel driver for Eicon DIVA Server cards");
+MODULE_AUTHOR("Cytronics & Melware, Eicon Networks");
+MODULE_LICENSE("GPL");
+
+module_param(dbgmask, int, 0);
+MODULE_PARM_DESC(dbgmask, "initial debug mask");
+
+static char *DRIVERNAME =
+ "Eicon DIVA Server driver (http://www.melware.net)";
+static char *DRIVERLNAME = "divas";
+static char *DEVNAME = "Divas";
+char *DRIVERRELEASE_DIVAS = "2.0";
+
+extern irqreturn_t diva_os_irq_wrapper(int irq, void *context);
+extern int create_divas_proc(void);
+extern void remove_divas_proc(void);
+extern void diva_get_vserial_number(PISDN_ADAPTER IoAdapter, char *buf);
+extern int divasfunc_init(int dbgmask);
+extern void divasfunc_exit(void);
+
+typedef struct _diva_os_thread_dpc {
+ struct tasklet_struct divas_task;
+ diva_os_soft_isr_t *psoft_isr;
+} diva_os_thread_dpc_t;
+
+/* --------------------------------------------------------------------------
+ PCI driver interface section
+ -------------------------------------------------------------------------- */
+/*
+ vendor, device Vendor and device ID to match (or PCI_ANY_ID)
+ subvendor, Subsystem vendor and device ID to match (or PCI_ANY_ID)
+ subdevice
+ class, Device class to match. The class_mask tells which bits
+ class_mask of the class are honored during the comparison.
+ driver_data Data private to the driver.
+ */
+
+#if !defined(PCI_DEVICE_ID_EICON_MAESTRAP_2)
+#define PCI_DEVICE_ID_EICON_MAESTRAP_2 0xE015
+#endif
+
+#if !defined(PCI_DEVICE_ID_EICON_4BRI_VOIP)
+#define PCI_DEVICE_ID_EICON_4BRI_VOIP 0xE016
+#endif
+
+#if !defined(PCI_DEVICE_ID_EICON_4BRI_2_VOIP)
+#define PCI_DEVICE_ID_EICON_4BRI_2_VOIP 0xE017
+#endif
+
+#if !defined(PCI_DEVICE_ID_EICON_BRI2M_2)
+#define PCI_DEVICE_ID_EICON_BRI2M_2 0xE018
+#endif
+
+#if !defined(PCI_DEVICE_ID_EICON_MAESTRAP_2_VOIP)
+#define PCI_DEVICE_ID_EICON_MAESTRAP_2_VOIP 0xE019
+#endif
+
+#if !defined(PCI_DEVICE_ID_EICON_2F)
+#define PCI_DEVICE_ID_EICON_2F 0xE01A
+#endif
+
+#if !defined(PCI_DEVICE_ID_EICON_BRI2M_2_VOIP)
+#define PCI_DEVICE_ID_EICON_BRI2M_2_VOIP 0xE01B
+#endif
+
+/*
+ This table should be sorted by PCI device ID
+ */
+static struct pci_device_id divas_pci_tbl[] = {
+/* Diva Server BRI-2M PCI 0xE010 */
+ {PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_MAESTRA,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, CARDTYPE_MAESTRA_PCI},
+/* Diva Server 4BRI-8M PCI 0xE012 */
+ {PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_MAESTRAQ,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, CARDTYPE_DIVASRV_Q_8M_PCI},
+/* Diva Server 4BRI-8M 2.0 PCI 0xE013 */
+ {PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_MAESTRAQ_U,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, CARDTYPE_DIVASRV_Q_8M_V2_PCI},
+/* Diva Server PRI-30M PCI 0xE014 */
+ {PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_MAESTRAP,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, CARDTYPE_DIVASRV_P_30M_PCI},
+/* Diva Server PRI 2.0 adapter 0xE015 */
+ {PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_MAESTRAP_2,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, CARDTYPE_DIVASRV_P_30M_V2_PCI},
+/* Diva Server Voice 4BRI-8M PCI 0xE016 */
+ {PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_4BRI_VOIP,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, CARDTYPE_DIVASRV_VOICE_Q_8M_PCI},
+/* Diva Server Voice 4BRI-8M 2.0 PCI 0xE017 */
+ {PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_4BRI_2_VOIP,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, CARDTYPE_DIVASRV_VOICE_Q_8M_V2_PCI},
+/* Diva Server BRI-2M 2.0 PCI 0xE018 */
+ {PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_BRI2M_2,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, CARDTYPE_DIVASRV_B_2M_V2_PCI},
+/* Diva Server Voice PRI 2.0 PCI 0xE019 */
+ {PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_MAESTRAP_2_VOIP,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ CARDTYPE_DIVASRV_VOICE_P_30M_V2_PCI},
+/* Diva Server 2FX 0xE01A */
+ {PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_2F,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, CARDTYPE_DIVASRV_B_2F_PCI},
+/* Diva Server Voice BRI-2M 2.0 PCI 0xE01B */
+ {PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_BRI2M_2_VOIP,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, CARDTYPE_DIVASRV_VOICE_B_2M_V2_PCI},
+ {0,} /* 0 terminated list. */
+};
+MODULE_DEVICE_TABLE(pci, divas_pci_tbl);
+
+static int divas_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent);
+static void __devexit divas_remove_one(struct pci_dev *pdev);
+
+static struct pci_driver diva_pci_driver = {
+ .name = "divas",
+ .probe = divas_init_one,
+ .remove = __devexit_p(divas_remove_one),
+ .id_table = divas_pci_tbl,
+};
+
+/*********************************************************
+ ** little helper functions
+ *********************************************************/
+static char *getrev(const char *revision)
+{
+ char *rev;
+ char *p;
+ if ((p = strchr(revision, ':'))) {
+ rev = p + 2;
+ p = strchr(rev, '$');
+ *--p = 0;
+ } else
+ rev = "1.0";
+ return rev;
+}
+
+void diva_log_info(unsigned char *format, ...)
+{
+ va_list args;
+ unsigned char line[160];
+
+ va_start(args, format);
+ vsnprintf(line, sizeof(line), format, args);
+ va_end(args);
+
+ printk(KERN_INFO "%s: %s\n", DRIVERLNAME, line);
+}
+
+void divas_get_version(char *p)
+{
+ char tmprev[32];
+
+ strcpy(tmprev, main_revision);
+ sprintf(p, "%s: %s(%s) %s(%s) major=%d\n", DRIVERLNAME, DRIVERRELEASE_DIVAS,
+ getrev(tmprev), diva_xdi_common_code_build, DIVA_BUILD, major);
+}
+
+/* --------------------------------------------------------------------------
+ PCI Bus services
+ -------------------------------------------------------------------------- */
+byte diva_os_get_pci_bus(void *pci_dev_handle)
+{
+ struct pci_dev *pdev = (struct pci_dev *) pci_dev_handle;
+ return ((byte) pdev->bus->number);
+}
+
+byte diva_os_get_pci_func(void *pci_dev_handle)
+{
+ struct pci_dev *pdev = (struct pci_dev *) pci_dev_handle;
+ return ((byte) pdev->devfn);
+}
+
+unsigned long divasa_get_pci_irq(unsigned char bus, unsigned char func,
+ void *pci_dev_handle)
+{
+ unsigned char irq = 0;
+ struct pci_dev *dev = (struct pci_dev *) pci_dev_handle;
+
+ irq = dev->irq;
+
+ return ((unsigned long) irq);
+}
+
+unsigned long divasa_get_pci_bar(unsigned char bus, unsigned char func,
+ int bar, void *pci_dev_handle)
+{
+ unsigned long ret = 0;
+ struct pci_dev *dev = (struct pci_dev *) pci_dev_handle;
+
+ if (bar < 6) {
+ ret = dev->resource[bar].start;
+ }
+
+ DBG_TRC(("GOT BAR[%d]=%08x", bar, ret));
+
+ {
+ unsigned long type = (ret & 0x00000001);
+ if (type & PCI_BASE_ADDRESS_SPACE_IO) {
+ DBG_TRC((" I/O"));
+ ret &= PCI_BASE_ADDRESS_IO_MASK;
+ } else {
+ DBG_TRC((" memory"));
+ ret &= PCI_BASE_ADDRESS_MEM_MASK;
+ }
+ DBG_TRC((" final=%08x", ret));
+ }
+
+ return (ret);
+}
+
+void PCIwrite(byte bus, byte func, int offset, void *data, int length,
+ void *pci_dev_handle)
+{
+ struct pci_dev *dev = (struct pci_dev *) pci_dev_handle;
+
+ switch (length) {
+ case 1: /* byte */
+ pci_write_config_byte(dev, offset,
+ *(unsigned char *) data);
+ break;
+ case 2: /* word */
+ pci_write_config_word(dev, offset,
+ *(unsigned short *) data);
+ break;
+ case 4: /* dword */
+ pci_write_config_dword(dev, offset,
+ *(unsigned int *) data);
+ break;
+
+ default: /* buffer */
+ if (!(length % 4) && !(length & 0x03)) { /* Copy as dword */
+ dword *p = (dword *) data;
+ length /= 4;
+
+ while (length--) {
+ pci_write_config_dword(dev, offset,
+ *(unsigned int *)
+ p++);
+ }
+ } else { /* copy as byte stream */
+ byte *p = (byte *) data;
+
+ while (length--) {
+ pci_write_config_byte(dev, offset,
+ *(unsigned char *)
+ p++);
+ }
+ }
+ }
+}
+
+void PCIread(byte bus, byte func, int offset, void *data, int length,
+ void *pci_dev_handle)
+{
+ struct pci_dev *dev = (struct pci_dev *) pci_dev_handle;
+
+ switch (length) {
+ case 1: /* byte */
+ pci_read_config_byte(dev, offset, (unsigned char *) data);
+ break;
+ case 2: /* word */
+ pci_read_config_word(dev, offset, (unsigned short *) data);
+ break;
+ case 4: /* dword */
+ pci_read_config_dword(dev, offset, (unsigned int *) data);
+ break;
+
+ default: /* buffer */
+ if (!(length % 4) && !(length & 0x03)) { /* Copy as dword */
+ dword *p = (dword *) data;
+ length /= 4;
+
+ while (length--) {
+ pci_read_config_dword(dev, offset,
+ (unsigned int *)
+ p++);
+ }
+ } else { /* copy as byte stream */
+ byte *p = (byte *) data;
+
+ while (length--) {
+ pci_read_config_byte(dev, offset,
+ (unsigned char *)
+ p++);
+ }
+ }
+ }
+}
+
+/*
+ Init map with DMA pages. It is not problem if some allocations fail -
+ the channels that will not get one DMA page will use standard PIO
+ interface
+ */
+static void *diva_pci_alloc_consistent(struct pci_dev *hwdev,
+ size_t size,
+ dma_addr_t * dma_handle,
+ void **addr_handle)
+{
+ void *addr = pci_alloc_consistent(hwdev, size, dma_handle);
+
+ *addr_handle = addr;
+
+ return (addr);
+}
+
+void diva_init_dma_map(void *hdev,
+ struct _diva_dma_map_entry **ppmap, int nentries)
+{
+ struct pci_dev *pdev = (struct pci_dev *) hdev;
+ struct _diva_dma_map_entry *pmap =
+ diva_alloc_dma_map(hdev, nentries);
+
+ if (pmap) {
+ int i;
+ dma_addr_t dma_handle;
+ void *cpu_addr;
+ void *addr_handle;
+
+ for (i = 0; i < nentries; i++) {
+ if (!(cpu_addr = diva_pci_alloc_consistent(pdev,
+ PAGE_SIZE,
+ &dma_handle,
+ &addr_handle)))
+ {
+ break;
+ }
+ diva_init_dma_map_entry(pmap, i, cpu_addr,
+ (dword) dma_handle,
+ addr_handle);
+ DBG_TRC(("dma map alloc [%d]=(%08lx:%08x:%08lx)",
+ i, (unsigned long) cpu_addr,
+ (dword) dma_handle,
+ (unsigned long) addr_handle))}
+ }
+
+ *ppmap = pmap;
+}
+
+/*
+ Free all contained in the map entries and memory used by the map
+ Should be always called after adapter removal from DIDD array
+ */
+void diva_free_dma_map(void *hdev, struct _diva_dma_map_entry *pmap)
+{
+ struct pci_dev *pdev = (struct pci_dev *) hdev;
+ int i;
+ dword phys_addr;
+ void *cpu_addr;
+ dma_addr_t dma_handle;
+ void *addr_handle;
+
+ for (i = 0; (pmap != NULL); i++) {
+ diva_get_dma_map_entry(pmap, i, &cpu_addr, &phys_addr);
+ if (!cpu_addr) {
+ break;
+ }
+ addr_handle = diva_get_entry_handle(pmap, i);
+ dma_handle = (dma_addr_t) phys_addr;
+ pci_free_consistent(pdev, PAGE_SIZE, addr_handle,
+ dma_handle);
+ DBG_TRC(("dma map free [%d]=(%08lx:%08x:%08lx)", i,
+ (unsigned long) cpu_addr, (dword) dma_handle,
+ (unsigned long) addr_handle))
+ }
+
+ diva_free_dma_mapping(pmap);
+}
+
+
+/*********************************************************
+ ** I/O port utilities
+ *********************************************************/
+
+int
+diva_os_register_io_port(void *adapter, int on, unsigned long port,
+ unsigned long length, const char *name, int id)
+{
+ if (on) {
+ if (!request_region(port, length, name)) {
+ DBG_ERR(("A: I/O: can't register port=%08x", port))
+ return (-1);
+ }
+ } else {
+ release_region(port, length);
+ }
+ return (0);
+}
+
+void __iomem *divasa_remap_pci_bar(diva_os_xdi_adapter_t *a, int id, unsigned long bar, unsigned long area_length)
+{
+ void __iomem *ret = ioremap(bar, area_length);
+ DBG_TRC(("remap(%08x)->%p", bar, ret));
+ return (ret);
+}
+
+void divasa_unmap_pci_bar(void __iomem *bar)
+{
+ if (bar) {
+ iounmap(bar);
+ }
+}
+
+/*********************************************************
+ ** I/O port access
+ *********************************************************/
+byte __inline__ inpp(void __iomem *addr)
+{
+ return (inb((unsigned long) addr));
+}
+
+word __inline__ inppw(void __iomem *addr)
+{
+ return (inw((unsigned long) addr));
+}
+
+void __inline__ inppw_buffer(void __iomem *addr, void *P, int length)
+{
+ insw((unsigned long) addr, (word *) P, length >> 1);
+}
+
+void __inline__ outppw_buffer(void __iomem *addr, void *P, int length)
+{
+ outsw((unsigned long) addr, (word *) P, length >> 1);
+}
+
+void __inline__ outppw(void __iomem *addr, word w)
+{
+ outw(w, (unsigned long) addr);
+}
+
+void __inline__ outpp(void __iomem *addr, word p)
+{
+ outb(p, (unsigned long) addr);
+}
+
+/* --------------------------------------------------------------------------
+ IRQ request / remove
+ -------------------------------------------------------------------------- */
+int diva_os_register_irq(void *context, byte irq, const char *name)
+{
+ int result = request_irq(irq, diva_os_irq_wrapper,
+ IRQF_DISABLED | IRQF_SHARED, name, context);
+ return (result);
+}
+
+void diva_os_remove_irq(void *context, byte irq)
+{
+ free_irq(irq, context);
+}
+
+/* --------------------------------------------------------------------------
+ DPC framework implementation
+ -------------------------------------------------------------------------- */
+static void diva_os_dpc_proc(unsigned long context)
+{
+ diva_os_thread_dpc_t *psoft_isr = (diva_os_thread_dpc_t *) context;
+ diva_os_soft_isr_t *pisr = psoft_isr->psoft_isr;
+
+ (*(pisr->callback)) (pisr, pisr->callback_context);
+}
+
+int diva_os_initialize_soft_isr(diva_os_soft_isr_t * psoft_isr,
+ diva_os_soft_isr_callback_t callback,
+ void *callback_context)
+{
+ diva_os_thread_dpc_t *pdpc;
+
+ pdpc = (diva_os_thread_dpc_t *) diva_os_malloc(0, sizeof(*pdpc));
+ if (!(psoft_isr->object = pdpc)) {
+ return (-1);
+ }
+ memset(pdpc, 0x00, sizeof(*pdpc));
+ psoft_isr->callback = callback;
+ psoft_isr->callback_context = callback_context;
+ pdpc->psoft_isr = psoft_isr;
+ tasklet_init(&pdpc->divas_task, diva_os_dpc_proc, (unsigned long)pdpc);
+
+ return (0);
+}
+
+int diva_os_schedule_soft_isr(diva_os_soft_isr_t * psoft_isr)
+{
+ if (psoft_isr && psoft_isr->object) {
+ diva_os_thread_dpc_t *pdpc =
+ (diva_os_thread_dpc_t *) psoft_isr->object;
+
+ tasklet_schedule(&pdpc->divas_task);
+ }
+
+ return (1);
+}
+
+int diva_os_cancel_soft_isr(diva_os_soft_isr_t * psoft_isr)
+{
+ return (0);
+}
+
+void diva_os_remove_soft_isr(diva_os_soft_isr_t * psoft_isr)
+{
+ if (psoft_isr && psoft_isr->object) {
+ diva_os_thread_dpc_t *pdpc =
+ (diva_os_thread_dpc_t *) psoft_isr->object;
+ void *mem;
+
+ tasklet_kill(&pdpc->divas_task);
+ flush_scheduled_work();
+ mem = psoft_isr->object;
+ psoft_isr->object = NULL;
+ diva_os_free(0, mem);
+ }
+}
+
+/*
+ * kernel/user space copy functions
+ */
+static int
+xdi_copy_to_user(void *os_handle, void __user *dst, const void *src, int length)
+{
+ if (copy_to_user(dst, src, length)) {
+ return (-EFAULT);
+ }
+ return (length);
+}
+
+static int
+xdi_copy_from_user(void *os_handle, void *dst, const void __user *src, int length)
+{
+ if (copy_from_user(dst, src, length)) {
+ return (-EFAULT);
+ }
+ return (length);
+}
+
+/*
+ * device node operations
+ */
+static int divas_open(struct inode *inode, struct file *file)
+{
+ cycle_kernel_lock();
+ return (0);
+}
+
+static int divas_release(struct inode *inode, struct file *file)
+{
+ if (file->private_data) {
+ diva_xdi_close_adapter(file->private_data, file);
+ }
+ return (0);
+}
+
+static ssize_t divas_write(struct file *file, const char __user *buf,
+ size_t count, loff_t * ppos)
+{
+ int ret = -EINVAL;
+
+ if (!file->private_data) {
+ file->private_data = diva_xdi_open_adapter(file, buf,
+ count,
+ xdi_copy_from_user);
+ }
+ if (!file->private_data) {
+ return (-ENODEV);
+ }
+
+ ret = diva_xdi_write(file->private_data, file,
+ buf, count, xdi_copy_from_user);
+ switch (ret) {
+ case -1: /* Message should be removed from rx mailbox first */
+ ret = -EBUSY;
+ break;
+ case -2: /* invalid adapter was specified in this call */
+ ret = -ENOMEM;
+ break;
+ case -3:
+ ret = -ENXIO;
+ break;
+ }
+ DBG_TRC(("write: ret %d", ret));
+ return (ret);
+}
+
+static ssize_t divas_read(struct file *file, char __user *buf,
+ size_t count, loff_t * ppos)
+{
+ int ret = -EINVAL;
+
+ if (!file->private_data) {
+ file->private_data = diva_xdi_open_adapter(file, buf,
+ count,
+ xdi_copy_from_user);
+ }
+ if (!file->private_data) {
+ return (-ENODEV);
+ }
+
+ ret = diva_xdi_read(file->private_data, file,
+ buf, count, xdi_copy_to_user);
+ switch (ret) {
+ case -1: /* RX mailbox is empty */
+ ret = -EAGAIN;
+ break;
+ case -2: /* no memory, mailbox was cleared, last command is failed */
+ ret = -ENOMEM;
+ break;
+ case -3: /* can't copy to user, retry */
+ ret = -EFAULT;
+ break;
+ }
+ DBG_TRC(("read: ret %d", ret));
+ return (ret);
+}
+
+static unsigned int divas_poll(struct file *file, poll_table * wait)
+{
+ if (!file->private_data) {
+ return (POLLERR);
+ }
+ return (POLLIN | POLLRDNORM);
+}
+
+static const struct file_operations divas_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = divas_read,
+ .write = divas_write,
+ .poll = divas_poll,
+ .open = divas_open,
+ .release = divas_release
+};
+
+static void divas_unregister_chrdev(void)
+{
+ unregister_chrdev(major, DEVNAME);
+}
+
+static int DIVA_INIT_FUNCTION divas_register_chrdev(void)
+{
+ if ((major = register_chrdev(0, DEVNAME, &divas_fops)) < 0)
+ {
+ printk(KERN_ERR "%s: failed to create /dev entry.\n",
+ DRIVERLNAME);
+ return (0);
+ }
+
+ return (1);
+}
+
+/* --------------------------------------------------------------------------
+ PCI driver section
+ -------------------------------------------------------------------------- */
+static int __devinit divas_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ void *pdiva = NULL;
+ u8 pci_latency;
+ u8 new_latency = 32;
+
+ DBG_TRC(("%s bus: %08x fn: %08x insertion.\n",
+ CardProperties[ent->driver_data].Name,
+ pdev->bus->number, pdev->devfn))
+ printk(KERN_INFO "%s: %s bus: %08x fn: %08x insertion.\n",
+ DRIVERLNAME, CardProperties[ent->driver_data].Name,
+ pdev->bus->number, pdev->devfn);
+
+ if (pci_enable_device(pdev)) {
+ DBG_TRC(("%s: %s bus: %08x fn: %08x device init failed.\n",
+ DRIVERLNAME,
+ CardProperties[ent->driver_data].Name,
+ pdev->bus->number,
+ pdev->devfn))
+ printk(KERN_ERR
+ "%s: %s bus: %08x fn: %08x device init failed.\n",
+ DRIVERLNAME,
+ CardProperties[ent->driver_data].
+ Name, pdev->bus->number,
+ pdev->devfn);
+ return (-EIO);
+ }
+
+ pci_set_master(pdev);
+
+ pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &pci_latency);
+ if (!pci_latency) {
+ DBG_TRC(("%s: bus: %08x fn: %08x fix latency.\n",
+ DRIVERLNAME, pdev->bus->number, pdev->devfn))
+ printk(KERN_INFO
+ "%s: bus: %08x fn: %08x fix latency.\n",
+ DRIVERLNAME, pdev->bus->number, pdev->devfn);
+ pci_write_config_byte(pdev, PCI_LATENCY_TIMER, new_latency);
+ }
+
+ if (!(pdiva = diva_driver_add_card(pdev, ent->driver_data))) {
+ DBG_TRC(("%s: %s bus: %08x fn: %08x card init failed.\n",
+ DRIVERLNAME,
+ CardProperties[ent->driver_data].Name,
+ pdev->bus->number,
+ pdev->devfn))
+ printk(KERN_ERR
+ "%s: %s bus: %08x fn: %08x card init failed.\n",
+ DRIVERLNAME,
+ CardProperties[ent->driver_data].
+ Name, pdev->bus->number,
+ pdev->devfn);
+ return (-EIO);
+ }
+
+ pci_set_drvdata(pdev, pdiva);
+
+ return (0);
+}
+
+static void __devexit divas_remove_one(struct pci_dev *pdev)
+{
+ void *pdiva = pci_get_drvdata(pdev);
+
+ DBG_TRC(("bus: %08x fn: %08x removal.\n",
+ pdev->bus->number, pdev->devfn))
+ printk(KERN_INFO "%s: bus: %08x fn: %08x removal.\n",
+ DRIVERLNAME, pdev->bus->number, pdev->devfn);
+
+ if (pdiva) {
+ diva_driver_remove_card(pdiva);
+ }
+
+}
+
+/* --------------------------------------------------------------------------
+ Driver Load / Startup
+ -------------------------------------------------------------------------- */
+static int DIVA_INIT_FUNCTION divas_init(void)
+{
+ char tmprev[50];
+ int ret = 0;
+
+ printk(KERN_INFO "%s\n", DRIVERNAME);
+ printk(KERN_INFO "%s: Rel:%s Rev:", DRIVERLNAME, DRIVERRELEASE_DIVAS);
+ strcpy(tmprev, main_revision);
+ printk("%s Build: %s(%s)\n", getrev(tmprev),
+ diva_xdi_common_code_build, DIVA_BUILD);
+ printk(KERN_INFO "%s: support for: ", DRIVERLNAME);
+#ifdef CONFIG_ISDN_DIVAS_BRIPCI
+ printk("BRI/PCI ");
+#endif
+#ifdef CONFIG_ISDN_DIVAS_PRIPCI
+ printk("PRI/PCI ");
+#endif
+ printk("adapters\n");
+
+ if (!divasfunc_init(dbgmask)) {
+ printk(KERN_ERR "%s: failed to connect to DIDD.\n",
+ DRIVERLNAME);
+ ret = -EIO;
+ goto out;
+ }
+
+ if (!divas_register_chrdev()) {
+#ifdef MODULE
+ divasfunc_exit();
+#endif
+ ret = -EIO;
+ goto out;
+ }
+
+ if (!create_divas_proc()) {
+#ifdef MODULE
+ divas_unregister_chrdev();
+ divasfunc_exit();
+#endif
+ printk(KERN_ERR "%s: failed to create proc entry.\n",
+ DRIVERLNAME);
+ ret = -EIO;
+ goto out;
+ }
+
+ if ((ret = pci_register_driver(&diva_pci_driver))) {
+#ifdef MODULE
+ remove_divas_proc();
+ divas_unregister_chrdev();
+ divasfunc_exit();
+#endif
+ printk(KERN_ERR "%s: failed to init pci driver.\n",
+ DRIVERLNAME);
+ goto out;
+ }
+ printk(KERN_INFO "%s: started with major %d\n", DRIVERLNAME, major);
+
+ out:
+ return (ret);
+}
+
+/* --------------------------------------------------------------------------
+ Driver Unload
+ -------------------------------------------------------------------------- */
+static void DIVA_EXIT_FUNCTION divas_exit(void)
+{
+ pci_unregister_driver(&diva_pci_driver);
+ remove_divas_proc();
+ divas_unregister_chrdev();
+ divasfunc_exit();
+
+ printk(KERN_INFO "%s: module unloaded.\n", DRIVERLNAME);
+}
+
+module_init(divas_init);
+module_exit(divas_exit);
diff --git a/drivers/isdn/hardware/eicon/divasproc.c b/drivers/isdn/hardware/eicon/divasproc.c
new file mode 100644
index 0000000..0408272
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/divasproc.c
@@ -0,0 +1,436 @@
+/* $Id: divasproc.c,v 1.19.4.3 2005/01/31 12:22:20 armin Exp $
+ *
+ * Low level driver for Eicon DIVA Server ISDN cards.
+ * /proc functions
+ *
+ * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
+ * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/list.h>
+#include <asm/uaccess.h>
+
+#include "platform.h"
+#include "debuglib.h"
+#undef ID_MASK
+#undef N_DATA
+#include "pc.h"
+#include "di_defs.h"
+#include "divasync.h"
+#include "di.h"
+#include "io.h"
+#include "xdi_msg.h"
+#include "xdi_adapter.h"
+#include "diva.h"
+#include "diva_pci.h"
+
+
+extern PISDN_ADAPTER IoAdapters[MAX_ADAPTER];
+extern void divas_get_version(char *);
+extern void diva_get_vserial_number(PISDN_ADAPTER IoAdapter, char *buf);
+
+/*********************************************************
+ ** Functions for /proc interface / File operations
+ *********************************************************/
+
+static char *divas_proc_name = "divas";
+static char *adapter_dir_name = "adapter";
+static char *info_proc_name = "info";
+static char *grp_opt_proc_name = "group_optimization";
+static char *d_l1_down_proc_name = "dynamic_l1_down";
+
+/*
+** "divas" entry
+*/
+
+extern struct proc_dir_entry *proc_net_eicon;
+static struct proc_dir_entry *divas_proc_entry = NULL;
+
+static ssize_t
+divas_read(struct file *file, char __user *buf, size_t count, loff_t * off)
+{
+ int len = 0;
+ int cadapter;
+ char tmpbuf[80];
+ char tmpser[16];
+
+ if (*off)
+ return 0;
+
+ divas_get_version(tmpbuf);
+ if (copy_to_user(buf + len, &tmpbuf, strlen(tmpbuf)))
+ return -EFAULT;
+ len += strlen(tmpbuf);
+
+ for (cadapter = 0; cadapter < MAX_ADAPTER; cadapter++) {
+ if (IoAdapters[cadapter]) {
+ diva_get_vserial_number(IoAdapters[cadapter],
+ tmpser);
+ sprintf(tmpbuf,
+ "%2d: %-30s Serial:%-10s IRQ:%2d\n",
+ cadapter + 1,
+ IoAdapters[cadapter]->Properties.Name,
+ tmpser,
+ IoAdapters[cadapter]->irq_info.irq_nr);
+ if ((strlen(tmpbuf) + len) > count)
+ break;
+ if (copy_to_user
+ (buf + len, &tmpbuf,
+ strlen(tmpbuf))) return -EFAULT;
+ len += strlen(tmpbuf);
+ }
+ }
+
+ *off += len;
+ return (len);
+}
+
+static ssize_t
+divas_write(struct file *file, const char __user *buf, size_t count, loff_t * off)
+{
+ return (-ENODEV);
+}
+
+static unsigned int divas_poll(struct file *file, poll_table * wait)
+{
+ return (POLLERR);
+}
+
+static int divas_open(struct inode *inode, struct file *file)
+{
+ return nonseekable_open(inode, file);
+}
+
+static int divas_close(struct inode *inode, struct file *file)
+{
+ return (0);
+}
+
+static const struct file_operations divas_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = divas_read,
+ .write = divas_write,
+ .poll = divas_poll,
+ .open = divas_open,
+ .release = divas_close
+};
+
+int create_divas_proc(void)
+{
+ divas_proc_entry = proc_create(divas_proc_name, S_IFREG | S_IRUGO,
+ proc_net_eicon, &divas_fops);
+ if (!divas_proc_entry)
+ return (0);
+
+ return (1);
+}
+
+void remove_divas_proc(void)
+{
+ if (divas_proc_entry) {
+ remove_proc_entry(divas_proc_name, proc_net_eicon);
+ divas_proc_entry = NULL;
+ }
+}
+
+/*
+** write group_optimization
+*/
+static int
+write_grp_opt(struct file *file, const char __user *buffer, unsigned long count,
+ void *data)
+{
+ diva_os_xdi_adapter_t *a = (diva_os_xdi_adapter_t *) data;
+ PISDN_ADAPTER IoAdapter = IoAdapters[a->controller - 1];
+
+ if ((count == 1) || (count == 2)) {
+ char c;
+ if (get_user(c, buffer))
+ return -EFAULT;
+ switch (c) {
+ case '0':
+ IoAdapter->capi_cfg.cfg_1 &=
+ ~DIVA_XDI_CAPI_CFG_1_GROUP_POPTIMIZATION_ON;
+ break;
+ case '1':
+ IoAdapter->capi_cfg.cfg_1 |=
+ DIVA_XDI_CAPI_CFG_1_GROUP_POPTIMIZATION_ON;
+ break;
+ default:
+ return (-EINVAL);
+ }
+ return (count);
+ }
+ return (-EINVAL);
+}
+
+/*
+** write dynamic_l1_down
+*/
+static int
+write_d_l1_down(struct file *file, const char __user *buffer, unsigned long count,
+ void *data)
+{
+ diva_os_xdi_adapter_t *a = (diva_os_xdi_adapter_t *) data;
+ PISDN_ADAPTER IoAdapter = IoAdapters[a->controller - 1];
+
+ if ((count == 1) || (count == 2)) {
+ char c;
+ if (get_user(c, buffer))
+ return -EFAULT;
+ switch (c) {
+ case '0':
+ IoAdapter->capi_cfg.cfg_1 &=
+ ~DIVA_XDI_CAPI_CFG_1_DYNAMIC_L1_ON;
+ break;
+ case '1':
+ IoAdapter->capi_cfg.cfg_1 |=
+ DIVA_XDI_CAPI_CFG_1_DYNAMIC_L1_ON;
+ break;
+ default:
+ return (-EINVAL);
+ }
+ return (count);
+ }
+ return (-EINVAL);
+}
+
+
+/*
+** read dynamic_l1_down
+*/
+static int
+read_d_l1_down(char *page, char **start, off_t off, int count, int *eof,
+ void *data)
+{
+ int len = 0;
+ diva_os_xdi_adapter_t *a = (diva_os_xdi_adapter_t *) data;
+ PISDN_ADAPTER IoAdapter = IoAdapters[a->controller - 1];
+
+ len += sprintf(page + len, "%s\n",
+ (IoAdapter->capi_cfg.
+ cfg_1 & DIVA_XDI_CAPI_CFG_1_DYNAMIC_L1_ON) ? "1" :
+ "0");
+
+ if (off + count >= len)
+ *eof = 1;
+ if (len < off)
+ return 0;
+ *start = page + off;
+ return ((count < len - off) ? count : len - off);
+}
+
+/*
+** read group_optimization
+*/
+static int
+read_grp_opt(char *page, char **start, off_t off, int count, int *eof,
+ void *data)
+{
+ int len = 0;
+ diva_os_xdi_adapter_t *a = (diva_os_xdi_adapter_t *) data;
+ PISDN_ADAPTER IoAdapter = IoAdapters[a->controller - 1];
+
+ len += sprintf(page + len, "%s\n",
+ (IoAdapter->capi_cfg.
+ cfg_1 & DIVA_XDI_CAPI_CFG_1_GROUP_POPTIMIZATION_ON)
+ ? "1" : "0");
+
+ if (off + count >= len)
+ *eof = 1;
+ if (len < off)
+ return 0;
+ *start = page + off;
+ return ((count < len - off) ? count : len - off);
+}
+
+/*
+** info write
+*/
+static int
+info_write(struct file *file, const char __user *buffer, unsigned long count,
+ void *data)
+{
+ diva_os_xdi_adapter_t *a = (diva_os_xdi_adapter_t *) data;
+ PISDN_ADAPTER IoAdapter = IoAdapters[a->controller - 1];
+ char c[4];
+
+ if (count <= 4)
+ return -EINVAL;
+
+ if (copy_from_user(c, buffer, 4))
+ return -EFAULT;
+
+ /* this is for test purposes only */
+ if (!memcmp(c, "trap", 4)) {
+ (*(IoAdapter->os_trap_nfy_Fnc)) (IoAdapter, IoAdapter->ANum);
+ return (count);
+ }
+ return (-EINVAL);
+}
+
+/*
+** info read
+*/
+static int
+info_read(char *page, char **start, off_t off, int count, int *eof,
+ void *data)
+{
+ int i = 0;
+ int len = 0;
+ char *p;
+ char tmpser[16];
+ diva_os_xdi_adapter_t *a = (diva_os_xdi_adapter_t *) data;
+ PISDN_ADAPTER IoAdapter = IoAdapters[a->controller - 1];
+
+ len +=
+ sprintf(page + len, "Name : %s\n",
+ IoAdapter->Properties.Name);
+ len += sprintf(page + len, "DSP state : %08x\n", a->dsp_mask);
+ len += sprintf(page + len, "Channels : %02d\n",
+ IoAdapter->Properties.Channels);
+ len += sprintf(page + len, "E. max/used : %03d/%03d\n",
+ IoAdapter->e_max, IoAdapter->e_count);
+ diva_get_vserial_number(IoAdapter, tmpser);
+ len += sprintf(page + len, "Serial : %s\n", tmpser);
+ len +=
+ sprintf(page + len, "IRQ : %d\n",
+ IoAdapter->irq_info.irq_nr);
+ len += sprintf(page + len, "CardIndex : %d\n", a->CardIndex);
+ len += sprintf(page + len, "CardOrdinal : %d\n", a->CardOrdinal);
+ len += sprintf(page + len, "Controller : %d\n", a->controller);
+ len += sprintf(page + len, "Bus-Type : %s\n",
+ (a->Bus ==
+ DIVAS_XDI_ADAPTER_BUS_ISA) ? "ISA" : "PCI");
+ len += sprintf(page + len, "Port-Name : %s\n", a->port_name);
+ if (a->Bus == DIVAS_XDI_ADAPTER_BUS_PCI) {
+ len +=
+ sprintf(page + len, "PCI-bus : %d\n",
+ a->resources.pci.bus);
+ len +=
+ sprintf(page + len, "PCI-func : %d\n",
+ a->resources.pci.func);
+ for (i = 0; i < 8; i++) {
+ if (a->resources.pci.bar[i]) {
+ len +=
+ sprintf(page + len,
+ "Mem / I/O %d : 0x%x / mapped : 0x%lx",
+ i, a->resources.pci.bar[i],
+ (unsigned long) a->resources.
+ pci.addr[i]);
+ if (a->resources.pci.length[i]) {
+ len +=
+ sprintf(page + len,
+ " / length : %d",
+ a->resources.pci.
+ length[i]);
+ }
+ len += sprintf(page + len, "\n");
+ }
+ }
+ }
+ if ((!a->xdi_adapter.port) &&
+ ((!a->xdi_adapter.ram) ||
+ (!a->xdi_adapter.reset)
+ || (!a->xdi_adapter.cfg))) {
+ if (!IoAdapter->irq_info.irq_nr) {
+ p = "slave";
+ } else {
+ p = "out of service";
+ }
+ } else if (a->xdi_adapter.trapped) {
+ p = "trapped";
+ } else if (a->xdi_adapter.Initialized) {
+ p = "active";
+ } else {
+ p = "ready";
+ }
+ len += sprintf(page + len, "State : %s\n", p);
+
+ if (off + count >= len)
+ *eof = 1;
+ if (len < off)
+ return 0;
+ *start = page + off;
+ return ((count < len - off) ? count : len - off);
+}
+
+/*
+** adapter proc init/de-init
+*/
+
+/* --------------------------------------------------------------------------
+ Create adapter directory and files in proc file system
+ -------------------------------------------------------------------------- */
+int create_adapter_proc(diva_os_xdi_adapter_t * a)
+{
+ struct proc_dir_entry *de, *pe;
+ char tmp[16];
+
+ sprintf(tmp, "%s%d", adapter_dir_name, a->controller);
+ if (!(de = proc_mkdir(tmp, proc_net_eicon)))
+ return (0);
+ a->proc_adapter_dir = (void *) de;
+
+ if (!(pe =
+ create_proc_entry(info_proc_name, S_IFREG | S_IRUGO | S_IWUSR, de)))
+ return (0);
+ a->proc_info = (void *) pe;
+ pe->write_proc = info_write;
+ pe->read_proc = info_read;
+ pe->data = a;
+
+ if ((pe = create_proc_entry(grp_opt_proc_name,
+ S_IFREG | S_IRUGO | S_IWUSR, de))) {
+ a->proc_grp_opt = (void *) pe;
+ pe->write_proc = write_grp_opt;
+ pe->read_proc = read_grp_opt;
+ pe->data = a;
+ }
+ if ((pe = create_proc_entry(d_l1_down_proc_name,
+ S_IFREG | S_IRUGO | S_IWUSR, de))) {
+ a->proc_d_l1_down = (void *) pe;
+ pe->write_proc = write_d_l1_down;
+ pe->read_proc = read_d_l1_down;
+ pe->data = a;
+ }
+
+ DBG_TRC(("proc entry %s created", tmp));
+
+ return (1);
+}
+
+/* --------------------------------------------------------------------------
+ Remove adapter directory and files in proc file system
+ -------------------------------------------------------------------------- */
+void remove_adapter_proc(diva_os_xdi_adapter_t * a)
+{
+ char tmp[16];
+
+ if (a->proc_adapter_dir) {
+ if (a->proc_d_l1_down) {
+ remove_proc_entry(d_l1_down_proc_name,
+ (struct proc_dir_entry *) a->proc_adapter_dir);
+ }
+ if (a->proc_grp_opt) {
+ remove_proc_entry(grp_opt_proc_name,
+ (struct proc_dir_entry *) a->proc_adapter_dir);
+ }
+ if (a->proc_info) {
+ remove_proc_entry(info_proc_name,
+ (struct proc_dir_entry *) a->proc_adapter_dir);
+ }
+ sprintf(tmp, "%s%d", adapter_dir_name, a->controller);
+ remove_proc_entry(tmp, proc_net_eicon);
+ DBG_TRC(("proc entry %s%d removed", adapter_dir_name,
+ a->controller));
+ }
+}
diff --git a/drivers/isdn/hardware/eicon/divasync.h b/drivers/isdn/hardware/eicon/divasync.h
new file mode 100644
index 0000000..85784a7
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/divasync.h
@@ -0,0 +1,489 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+#ifndef __DIVA_SYNC__H
+#define __DIVA_SYNC__H
+#define IDI_SYNC_REQ_REMOVE 0x00
+#define IDI_SYNC_REQ_GET_NAME 0x01
+#define IDI_SYNC_REQ_GET_SERIAL 0x02
+#define IDI_SYNC_REQ_SET_POSTCALL 0x03
+#define IDI_SYNC_REQ_GET_XLOG 0x04
+#define IDI_SYNC_REQ_GET_FEATURES 0x05
+#define IDI_SYNC_REQ_USB_REGISTER 0x06
+#define IDI_SYNC_REQ_USB_RELEASE 0x07
+#define IDI_SYNC_REQ_USB_ADD_DEVICE 0x08
+#define IDI_SYNC_REQ_USB_START_DEVICE 0x09
+#define IDI_SYNC_REQ_USB_STOP_DEVICE 0x0A
+#define IDI_SYNC_REQ_USB_REMOVE_DEVICE 0x0B
+#define IDI_SYNC_REQ_GET_CARDTYPE 0x0C
+#define IDI_SYNC_REQ_GET_DBG_XLOG 0x0D
+#define DIVA_USB
+#define DIVA_USB_REQ 0xAC
+#define DIVA_USB_TEST 0xAB
+#define DIVA_USB_ADD_ADAPTER 0xAC
+#define DIVA_USB_REMOVE_ADAPTER 0xAD
+#define IDI_SYNC_REQ_SERIAL_HOOK 0x80
+#define IDI_SYNC_REQ_XCHANGE_STATUS 0x81
+#define IDI_SYNC_REQ_USB_HOOK 0x82
+#define IDI_SYNC_REQ_PORTDRV_HOOK 0x83
+#define IDI_SYNC_REQ_SLI 0x84 /* SLI request from 3signal modem drivers */
+#define IDI_SYNC_REQ_RECONFIGURE 0x85
+#define IDI_SYNC_REQ_RESET 0x86
+#define IDI_SYNC_REQ_GET_85X_DEVICE_DATA 0x87
+#define IDI_SYNC_REQ_LOCK_85X 0x88
+#define IDI_SYNC_REQ_DIVA_85X_USB_DATA_EXCHANGE 0x99
+#define IDI_SYNC_REQ_DIPORT_EXCHANGE_REQ 0x98
+#define IDI_SYNC_REQ_GET_85X_EXT_PORT_TYPE 0xA0
+/******************************************************************************/
+#define IDI_SYNC_REQ_XDI_GET_EXTENDED_FEATURES 0x92
+/*
+ To receive XDI features:
+ 1. set 'buffer_length_in_bytes' to length of you buffer
+ 2. set 'features' to pointer to your buffer
+ 3. issue synchronous request to XDI
+ 4. Check that feature 'DIVA_XDI_EXTENDED_FEATURES_VALID' is present
+ after call. This feature does indicate that your request
+ was processed and XDI does support this synchronous request
+ 5. if on return bit 31 (0x80000000) in 'buffer_length_in_bytes' is
+ set then provided buffer was too small, and bits 30-0 does
+ contain necessary length of buffer.
+ in this case only features that do find place in the buffer
+ are indicated to caller
+*/
+typedef struct _diva_xdi_get_extended_xdi_features {
+ dword buffer_length_in_bytes;
+ byte *features;
+} diva_xdi_get_extended_xdi_features_t;
+/*
+ features[0]
+ */
+#define DIVA_XDI_EXTENDED_FEATURES_VALID 0x01
+#define DIVA_XDI_EXTENDED_FEATURE_CMA 0x02
+#define DIVA_XDI_EXTENDED_FEATURE_SDRAM_BAR 0x04
+#define DIVA_XDI_EXTENDED_FEATURE_CAPI_PRMS 0x08
+#define DIVA_XDI_EXTENDED_FEATURE_NO_CANCEL_RC 0x10
+#define DIVA_XDI_EXTENDED_FEATURE_RX_DMA 0x20
+#define DIVA_XDI_EXTENDED_FEATURE_MANAGEMENT_DMA 0x40
+#define DIVA_XDI_EXTENDED_FEATURE_WIDE_ID 0x80
+#define DIVA_XDI_EXTENDED_FEATURES_MAX_SZ 1
+/******************************************************************************/
+#define IDI_SYNC_REQ_XDI_GET_ADAPTER_SDRAM_BAR 0x93
+typedef struct _diva_xdi_get_adapter_sdram_bar {
+ dword bar;
+} diva_xdi_get_adapter_sdram_bar_t;
+/******************************************************************************/
+#define IDI_SYNC_REQ_XDI_GET_CAPI_PARAMS 0x94
+/*
+ CAPI Parameters will be written in the caller's buffer
+ */
+typedef struct _diva_xdi_get_capi_parameters {
+ dword structure_length;
+ byte flag_dynamic_l1_down;
+ byte group_optimization_enabled;
+} diva_xdi_get_capi_parameters_t;
+/******************************************************************************/
+#define IDI_SYNC_REQ_XDI_GET_LOGICAL_ADAPTER_NUMBER 0x95
+/*
+ Get logical adapter number, as assigned by XDI
+ 'controller' is starting with zero 'sub' controller number
+ in case of one adapter that supports multiple interfaces
+ 'controller' is zero for Master adapter (and adapter that supports
+ only one interface)
+ */
+typedef struct _diva_xdi_get_logical_adapter_number {
+ dword logical_adapter_number;
+ dword controller;
+ dword total_controllers;
+} diva_xdi_get_logical_adapter_number_s_t;
+/******************************************************************************/
+#define IDI_SYNC_REQ_UP1DM_OPERATION 0x96
+/******************************************************************************/
+#define IDI_SYNC_REQ_DMA_DESCRIPTOR_OPERATION 0x97
+#define IDI_SYNC_REQ_DMA_DESCRIPTOR_ALLOC 0x01
+#define IDI_SYNC_REQ_DMA_DESCRIPTOR_FREE 0x02
+typedef struct _diva_xdi_dma_descriptor_operation {
+ int operation;
+ int descriptor_number;
+ void* descriptor_address;
+ dword descriptor_magic;
+} diva_xdi_dma_descriptor_operation_t;
+/******************************************************************************/
+#define IDI_SYNC_REQ_DIDD_REGISTER_ADAPTER_NOTIFY 0x01
+#define IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER_NOTIFY 0x02
+#define IDI_SYNC_REQ_DIDD_ADD_ADAPTER 0x03
+#define IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER 0x04
+#define IDI_SYNC_REQ_DIDD_READ_ADAPTER_ARRAY 0x05
+#define IDI_SYNC_REQ_DIDD_GET_CFG_LIB_IFC 0x10
+typedef struct _diva_didd_adapter_notify {
+ dword handle; /* Notification handle */
+ void * callback;
+ void * context;
+} diva_didd_adapter_notify_t;
+typedef struct _diva_didd_add_adapter {
+ void * descriptor;
+} diva_didd_add_adapter_t;
+typedef struct _diva_didd_remove_adapter {
+ IDI_CALL p_request;
+} diva_didd_remove_adapter_t;
+typedef struct _diva_didd_read_adapter_array {
+ void * buffer;
+ dword length;
+} diva_didd_read_adapter_array_t;
+typedef struct _diva_didd_get_cfg_lib_ifc {
+ void* ifc;
+} diva_didd_get_cfg_lib_ifc_t;
+/******************************************************************************/
+#define IDI_SYNC_REQ_XDI_GET_STREAM 0x91
+#define DIVA_XDI_SYNCHRONOUS_SERVICE 0x01
+#define DIVA_XDI_DMA_SERVICE 0x02
+#define DIVA_XDI_AUTO_SERVICE 0x03
+#define DIVA_ISTREAM_COMPLETE_NOTIFY 0
+#define DIVA_ISTREAM_COMPLETE_READ 1
+#define DIVA_ISTREAM_COMPLETE_WRITE 2
+typedef struct _diva_xdi_stream_interface {
+ unsigned char Id; /* filled by XDI client */
+ unsigned char provided_service; /* filled by XDI */
+ unsigned char requested_service; /* filled by XDI Client */
+ void* xdi_context; /* filled by XDI */
+ void* client_context; /* filled by XDI client */
+ int (*write)(void* context,
+ int Id,
+ void* data,
+ int length,
+ int final,
+ byte usr1,
+ byte usr2);
+ int (*read)(void* context,
+ int Id,
+ void* data,
+ int max_length,
+ int* final,
+ byte* usr1,
+ byte* usr2);
+ int (*complete)(void* client_context,
+ int Id,
+ int what,
+ void* data,
+ int length,
+ int* final);
+} diva_xdi_stream_interface_t;
+/******************************************************************************/
+/*
+ * IDI_SYNC_REQ_SERIAL_HOOK - special interface for the DIVA Mobile card
+ */
+typedef struct
+{ unsigned char LineState; /* Modem line state (STATUS_R) */
+#define SERIAL_GSM_CELL 0x01 /* GSM or CELL cable attached */
+ unsigned char CardState; /* PCMCIA card state (0 = down) */
+ unsigned char IsdnState; /* ISDN layer 1 state (0 = down)*/
+ unsigned char HookState; /* current logical hook state */
+#define SERIAL_ON_HOOK 0x02 /* set in DIVA CTRL_R register */
+} SERIAL_STATE;
+typedef int ( * SERIAL_INT_CB) (void *Context) ;
+typedef int ( * SERIAL_DPC_CB) (void *Context) ;
+typedef unsigned char ( * SERIAL_I_SYNC) (void *Context) ;
+typedef struct
+{ /* 'Req' and 'Rc' must be at the same place as in the ENTITY struct */
+ unsigned char Req; /* request (must be always 0) */
+ unsigned char Rc; /* return code (is the request) */
+ unsigned char Function; /* private function code */
+#define SERIAL_HOOK_ATTACH 0x81
+#define SERIAL_HOOK_STATUS 0x82
+#define SERIAL_HOOK_I_SYNC 0x83
+#define SERIAL_HOOK_NOECHO 0x84
+#define SERIAL_HOOK_RING 0x85
+#define SERIAL_HOOK_DETACH 0x8f
+ unsigned char Flags; /* function refinements */
+ /* parameters passed by the ATTACH request */
+ SERIAL_INT_CB InterruptHandler; /* called on each interrupt */
+ SERIAL_DPC_CB DeferredHandler; /* called on hook state changes */
+ void *HandlerContext; /* context for both handlers */
+ /* return values for both the ATTACH and the STATUS request */
+ unsigned long IoBase; /* IO port assigned to UART */
+ SERIAL_STATE State;
+ /* parameters and return values for the I_SYNC function */
+ SERIAL_I_SYNC SyncFunction; /* to be called synchronized */
+ void *SyncContext; /* context for this function */
+ unsigned char SyncResult; /* return value of function */
+} SERIAL_HOOK;
+/*
+ * IDI_SYNC_REQ_XCHANGE_STATUS - exchange the status between IDI and WMP
+ * IDI_SYNC_REQ_RECONFIGURE - reconfiguration of IDI from WMP
+ */
+typedef struct
+{ /* 'Req' and 'Rc' must be at the same place as in the ENTITY struct */
+ unsigned char Req; /* request (must be always 0) */
+ unsigned char Rc; /* return code (is the request) */
+#define DRIVER_STATUS_BOOT 0xA1
+#define DRIVER_STATUS_INIT_DEV 0xA2
+#define DRIVER_STATUS_RUNNING 0xA3
+#define DRIVER_STATUS_SHUTDOWN 0xAF
+#define DRIVER_STATUS_TRAPPED 0xAE
+ unsigned char wmpStatus; /* exported by WMP */
+ unsigned char idiStatus; /* exported by IDI */
+ unsigned long wizProto ; /* from WMP registry to IDI */
+ /* the cardtype value is defined by cardtype.h */
+ unsigned long cardType ; /* from IDI registry to WMP */
+ unsigned long nt2 ; /* from IDI registry to WMP */
+ unsigned long permanent ; /* from IDI registry to WMP */
+ unsigned long stableL2 ; /* from IDI registry to WMP */
+ unsigned long tei ; /* from IDI registry to WMP */
+#define CRC4_MASK 0x00000003
+#define L1_TRISTATE_MASK 0x00000004
+#define WATCHDOG_MASK 0x00000008
+#define NO_ORDER_CHECK_MASK 0x00000010
+#define LOW_CHANNEL_MASK 0x00000020
+#define NO_HSCX30_MASK 0x00000040
+#define SET_BOARD 0x00001000
+#define SET_CRC4 0x00030000
+#define SET_L1_TRISTATE 0x00040000
+#define SET_WATCHDOG 0x00080000
+#define SET_NO_ORDER_CHECK 0x00100000
+#define SET_LOW_CHANNEL 0x00200000
+#define SET_NO_HSCX30 0x00400000
+#define SET_MODE 0x00800000
+#define SET_PROTO 0x02000000
+#define SET_CARDTYPE 0x04000000
+#define SET_NT2 0x08000000
+#define SET_PERMANENT 0x10000000
+#define SET_STABLEL2 0x20000000
+#define SET_TEI 0x40000000
+#define SET_NUMBERLEN 0x80000000
+ unsigned long Flag ; /* |31-Type-16|15-Mask-0| */
+ unsigned long NumberLen ; /* reconfiguration: union is empty */
+ union {
+ struct { /* possible reconfiguration, but ... ; SET_BOARD */
+ unsigned long SerialNumber ;
+ char *pCardname ; /* di_defs.h: BOARD_NAME_LENGTH */
+ } board ;
+ struct { /* reset: need resources */
+ void * pRawResources ;
+ void * pXlatResources ;
+ } res ;
+ struct { /* reconfiguration: wizProto == PROTTYPE_RBSCAS */
+#define GLARE_RESOLVE_MASK 0x00000001
+#define DID_MASK 0x00000002
+#define BEARER_CAP_MASK 0x0000000c
+#define SET_GLARE_RESOLVE 0x00010000
+#define SET_DID 0x00020000
+#define SET_BEARER_CAP 0x000c0000
+ unsigned long Flag ; /* |31-Type-16|15-VALUE-0| */
+ unsigned short DigitTimeout ;
+ unsigned short AnswerDelay ;
+ } rbs ;
+ struct { /* reconfiguration: wizProto == PROTTYPE_QSIG */
+#define CALL_REF_LENGTH1_MASK 0x00000001
+#define BRI_CHANNEL_ID_MASK 0x00000002
+#define SET_CALL_REF_LENGTH 0x00010000
+#define SET_BRI_CHANNEL_ID 0x00020000
+ unsigned long Flag ; /* |31-Type-16|15-VALUE-0| */
+ } qsig ;
+ struct { /* reconfiguration: NumberLen != 0 */
+#define SET_SPID1 0x00010000
+#define SET_NUMBER1 0x00020000
+#define SET_SUBADDRESS1 0x00040000
+#define SET_SPID2 0x00100000
+#define SET_NUMBER2 0x00200000
+#define SET_SUBADDRESS2 0x00400000
+#define MASK_SET 0xffff0000
+ unsigned long Flag ; /* |31-Type-16|15-Channel-0| */
+ unsigned char *pBuffer ; /* number value */
+ } isdnNo ;
+ }
+parms
+;
+} isdnProps ;
+/*
+ * IDI_SYNC_REQ_PORTDRV_HOOK - signal plug/unplug (Award Cardware only)
+ */
+typedef void ( * PORTDRV_HOOK_CB) (void *Context, int Plug) ;
+typedef struct
+{ /* 'Req' and 'Rc' must be at the same place as in the ENTITY struct */
+ unsigned char Req; /* request (must be always 0) */
+ unsigned char Rc; /* return code (is the request) */
+ unsigned char Function; /* private function code */
+ unsigned char Flags; /* function refinements */
+ PORTDRV_HOOK_CB Callback; /* to be called on plug/unplug */
+ void *Context; /* context for callback */
+ unsigned long Info; /* more info if needed */
+} PORTDRV_HOOK ;
+/* Codes for the 'Rc' element in structure below. */
+#define SLI_INSTALL (0xA1)
+#define SLI_UNINSTALL (0xA2)
+typedef int ( * SLIENTRYPOINT)(void* p3SignalAPI, void* pContext);
+typedef struct
+{ /* 'Req' and 'Rc' must be at the same place as in the ENTITY struct */
+ unsigned char Req; /* request (must be always 0) */
+ unsigned char Rc; /* return code (is the request) */
+ unsigned char Function; /* private function code */
+ unsigned char Flags; /* function refinements */
+ SLIENTRYPOINT Callback; /* to be called on plug/unplug */
+ void *Context; /* context for callback */
+ unsigned long Info; /* more info if needed */
+} SLIENTRYPOINT_REQ ;
+/******************************************************************************/
+/*
+ * Definitions for DIVA USB
+ */
+typedef int ( * USB_SEND_REQ) (unsigned char PipeIndex, unsigned char Type,void *Data, int sizeData);
+typedef int ( * USB_START_DEV) (void *Adapter, void *Ipac) ;
+/* called from WDM */
+typedef void ( * USB_RECV_NOTIFY) (void *Ipac, void *msg) ;
+typedef void ( * USB_XMIT_NOTIFY) (void *Ipac, unsigned char PipeIndex) ;
+/******************************************************************************/
+/*
+ * Parameter description for synchronous requests.
+ *
+ * Sorry, must repeat some parts of di_defs.h here because
+ * they are not defined for all operating environments
+ */
+typedef union
+{ ENTITY Entity;
+ struct
+ { /* 'Req' and 'Rc' are at the same place as in the ENTITY struct */
+ unsigned char Req; /* request (must be always 0) */
+ unsigned char Rc; /* return code (is the request) */
+ } Request;
+ struct
+ { unsigned char Req; /* request (must be always 0) */
+ unsigned char Rc; /* return code (0x01) */
+ unsigned char name[BOARD_NAME_LENGTH];
+ } GetName;
+ struct
+ { unsigned char Req; /* request (must be always 0) */
+ unsigned char Rc; /* return code (0x02) */
+ unsigned long serial; /* serial number */
+ } GetSerial;
+ struct
+ { unsigned char Req; /* request (must be always 0) */
+ unsigned char Rc; /* return code (0x02) */
+ unsigned long lineIdx;/* line, 0 if card has only one */
+ } GetLineIdx;
+ struct
+ { unsigned char Req; /* request (must be always 0) */
+ unsigned char Rc; /* return code (0x02) */
+ unsigned long cardtype;/* card type */
+ } GetCardType;
+ struct
+ { unsigned short command;/* command = 0x0300 */
+ unsigned short dummy; /* not used */
+ IDI_CALL callback;/* routine to call back */
+ ENTITY *contxt; /* ptr to entity to use */
+ } PostCall;
+ struct
+ { unsigned char Req; /* request (must be always 0) */
+ unsigned char Rc; /* return code (0x04) */
+ unsigned char pcm[1]; /* buffer (a pc_maint struct) */
+ } GetXlog;
+ struct
+ { unsigned char Req; /* request (must be always 0) */
+ unsigned char Rc; /* return code (0x05) */
+ unsigned short features;/* feature defines see below */
+ } GetFeatures;
+ SERIAL_HOOK SerialHook;
+/* Added for DIVA USB */
+ struct
+ { unsigned char Req;
+ unsigned char Rc;
+ USB_SEND_REQ UsbSendRequest; /* function in Diva Usb WDM driver in usb_os.c, */
+ /* called from usb_drv.c to send a message to our device */
+ /* eg UsbSendRequest (USB_PIPE_SIGNAL, USB_IPAC_START, 0, 0) ; */
+ USB_RECV_NOTIFY usb_recv; /* called from usb_os.c to pass a received message and ptr to IPAC */
+ /* on to usb_drv.c by a call to usb_recv(). */
+ USB_XMIT_NOTIFY usb_xmit; /* called from usb_os.c in DivaUSB.sys WDM to indicate a completed transmit */
+ /* to usb_drv.c by a call to usb_xmit(). */
+ USB_START_DEV UsbStartDevice; /* Start the USB Device, in usb_os.c */
+ IDI_CALL callback; /* routine to call back */
+ ENTITY *contxt; /* ptr to entity to use */
+ void ** ipac_ptr; /* pointer to struct IPAC in VxD */
+ } Usb_Msg_old;
+/* message used by WDM and VXD to pass pointers of function and IPAC* */
+ struct
+ { unsigned char Req;
+ unsigned char Rc;
+ USB_SEND_REQ pUsbSendRequest;/* function in Diva Usb WDM driver in usb_os.c, */
+ /* called from usb_drv.c to send a message to our device */
+ /* eg UsbSendRequest (USB_PIPE_SIGNAL, USB_IPAC_START, 0, 0) ; */
+ USB_RECV_NOTIFY p_usb_recv; /* called from usb_os.c to pass a received message and ptr to IPAC */
+ /* on to usb_drv.c by a call to usb_recv(). */
+ USB_XMIT_NOTIFY p_usb_xmit; /* called from usb_os.c in DivaUSB.sys WDM to indicate a completed transmit */
+ /* to usb_drv.c by a call to usb_xmit().*/
+ void *ipac_ptr; /* &Diva.ipac pointer to struct IPAC in VxD */
+ } Usb_Msg;
+ PORTDRV_HOOK PortdrvHook;
+ SLIENTRYPOINT_REQ sliEntryPointReq;
+ struct {
+ unsigned char Req;
+ unsigned char Rc;
+ diva_xdi_stream_interface_t info;
+ } xdi_stream_info;
+ struct {
+ unsigned char Req;
+ unsigned char Rc;
+ diva_xdi_get_extended_xdi_features_t info;
+ } xdi_extended_features;
+ struct {
+ unsigned char Req;
+ unsigned char Rc;
+ diva_xdi_get_adapter_sdram_bar_t info;
+ } xdi_sdram_bar;
+ struct {
+ unsigned char Req;
+ unsigned char Rc;
+ diva_xdi_get_capi_parameters_t info;
+ } xdi_capi_prms;
+ struct {
+ ENTITY e;
+ diva_didd_adapter_notify_t info;
+ } didd_notify;
+ struct {
+ ENTITY e;
+ diva_didd_add_adapter_t info;
+ } didd_add_adapter;
+ struct {
+ ENTITY e;
+ diva_didd_remove_adapter_t info;
+ } didd_remove_adapter;
+ struct {
+ ENTITY e;
+ diva_didd_read_adapter_array_t info;
+ } didd_read_adapter_array;
+ struct {
+ ENTITY e;
+ diva_didd_get_cfg_lib_ifc_t info;
+ } didd_get_cfg_lib_ifc;
+ struct {
+ unsigned char Req;
+ unsigned char Rc;
+ diva_xdi_get_logical_adapter_number_s_t info;
+ } xdi_logical_adapter_number;
+ struct {
+ unsigned char Req;
+ unsigned char Rc;
+ diva_xdi_dma_descriptor_operation_t info;
+ } xdi_dma_descriptor_operation;
+} IDI_SYNC_REQ;
+/******************************************************************************/
+#endif /* __DIVA_SYNC__H */
diff --git a/drivers/isdn/hardware/eicon/dqueue.c b/drivers/isdn/hardware/eicon/dqueue.c
new file mode 100644
index 0000000..9822582
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/dqueue.c
@@ -0,0 +1,110 @@
+/* $Id: dqueue.c,v 1.5 2003/04/12 21:40:49 schindler Exp $
+ *
+ * Driver for Eicon DIVA Server ISDN cards.
+ * User Mode IDI Interface
+ *
+ * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
+ * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include "platform.h"
+#include "dqueue.h"
+
+int
+diva_data_q_init(diva_um_idi_data_queue_t * q,
+ int max_length, int max_segments)
+{
+ int i;
+
+ q->max_length = max_length;
+ q->segments = max_segments;
+
+ for (i = 0; i < q->segments; i++) {
+ q->data[i] = NULL;
+ q->length[i] = 0;
+ }
+ q->read = q->write = q->count = q->segment_pending = 0;
+
+ for (i = 0; i < q->segments; i++) {
+ if (!(q->data[i] = diva_os_malloc(0, q->max_length))) {
+ diva_data_q_finit(q);
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+int diva_data_q_finit(diva_um_idi_data_queue_t * q)
+{
+ int i;
+
+ for (i = 0; i < q->segments; i++) {
+ if (q->data[i]) {
+ diva_os_free(0, q->data[i]);
+ }
+ q->data[i] = NULL;
+ q->length[i] = 0;
+ }
+ q->read = q->write = q->count = q->segment_pending = 0;
+
+ return (0);
+}
+
+int diva_data_q_get_max_length(const diva_um_idi_data_queue_t * q)
+{
+ return (q->max_length);
+}
+
+void *diva_data_q_get_segment4write(diva_um_idi_data_queue_t * q)
+{
+ if ((!q->segment_pending) && (q->count < q->segments)) {
+ q->segment_pending = 1;
+ return (q->data[q->write]);
+ }
+
+ return NULL;
+}
+
+void
+diva_data_q_ack_segment4write(diva_um_idi_data_queue_t * q, int length)
+{
+ if (q->segment_pending) {
+ q->length[q->write] = length;
+ q->count++;
+ q->write++;
+ if (q->write >= q->segments) {
+ q->write = 0;
+ }
+ q->segment_pending = 0;
+ }
+}
+
+const void *diva_data_q_get_segment4read(const diva_um_idi_data_queue_t *
+ q)
+{
+ if (q->count) {
+ return (q->data[q->read]);
+ }
+ return NULL;
+}
+
+int diva_data_q_get_segment_length(const diva_um_idi_data_queue_t * q)
+{
+ return (q->length[q->read]);
+}
+
+void diva_data_q_ack_segment4read(diva_um_idi_data_queue_t * q)
+{
+ if (q->count) {
+ q->length[q->read] = 0;
+ q->count--;
+ q->read++;
+ if (q->read >= q->segments) {
+ q->read = 0;
+ }
+ }
+}
diff --git a/drivers/isdn/hardware/eicon/dqueue.h b/drivers/isdn/hardware/eicon/dqueue.h
new file mode 100644
index 0000000..72d21c9
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/dqueue.h
@@ -0,0 +1,31 @@
+/* $Id: dqueue.h,v 1.1.2.2 2001/02/08 12:25:43 armin Exp $ */
+
+#ifndef _DIVA_USER_MODE_IDI_DATA_QUEUE_H__
+#define _DIVA_USER_MODE_IDI_DATA_QUEUE_H__
+
+#define DIVA_UM_IDI_MAX_MSGS 64
+
+typedef struct _diva_um_idi_data_queue {
+ int segments;
+ int max_length;
+ int read;
+ int write;
+ int count;
+ int segment_pending;
+ void *data[DIVA_UM_IDI_MAX_MSGS];
+ int length[DIVA_UM_IDI_MAX_MSGS];
+} diva_um_idi_data_queue_t;
+
+int diva_data_q_init(diva_um_idi_data_queue_t * q,
+ int max_length, int max_segments);
+int diva_data_q_finit(diva_um_idi_data_queue_t * q);
+int diva_data_q_get_max_length(const diva_um_idi_data_queue_t * q);
+void *diva_data_q_get_segment4write(diva_um_idi_data_queue_t * q);
+void diva_data_q_ack_segment4write(diva_um_idi_data_queue_t * q,
+ int length);
+const void *diva_data_q_get_segment4read(const diva_um_idi_data_queue_t *
+ q);
+int diva_data_q_get_segment_length(const diva_um_idi_data_queue_t * q);
+void diva_data_q_ack_segment4read(diva_um_idi_data_queue_t * q);
+
+#endif
diff --git a/drivers/isdn/hardware/eicon/dsp_defs.h b/drivers/isdn/hardware/eicon/dsp_defs.h
new file mode 100644
index 0000000..fec1e38
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/dsp_defs.h
@@ -0,0 +1,301 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+#ifndef DSP_DEFS_H_
+#define DSP_DEFS_H_
+#include "dspdids.h"
+/*---------------------------------------------------------------------------*/
+#define dsp_download_reserve_space(fp,length)
+/*****************************************************************************/
+/*
+ * OS file access abstraction layer
+ *
+ * I/O functions returns -1 on error, 0 on EOF
+ */
+struct _OsFileHandle_;
+typedef long ( * OsFileIo) (struct _OsFileHandle_ *handle,
+ void *buffer,
+ long size) ;
+typedef long ( * OsFileSeek)(struct _OsFileHandle_ *handle,
+ long position,
+ int mode) ;
+typedef long ( * OsCardLoad)(struct _OsFileHandle_ *handle,
+ long length,
+ void * *addr) ;
+typedef struct _OsFileHandle_
+{ void *sysFileDesc ;
+ unsigned long sysFileSize ;
+ OsFileIo sysFileRead ;
+ OsFileSeek sysFileSeek ;
+ void *sysLoadDesc ;
+ OsCardLoad sysCardLoad ;
+} OsFileHandle ;
+extern OsFileHandle *OsOpenFile (char *path_name) ;
+extern void OsCloseFile (OsFileHandle *fp) ;
+/*****************************************************************************/
+#define DSP_TELINDUS_FILE "dspdload.bin"
+/* special DSP file for BRI cards for Qsig and CornetN because of missing memory */
+#define DSP_QSIG_TELINDUS_FILE "dspdqsig.bin"
+#define DSP_MDM_TELINDUS_FILE "dspdvmdm.bin"
+#define DSP_FAX_TELINDUS_FILE "dspdvfax.bin"
+#define DSP_DIRECTORY_ENTRIES 64
+#define DSP_MEMORY_TYPE_EXTERNAL_DM 0
+#define DSP_MEMORY_TYPE_EXTERNAL_PM 1
+#define DSP_MEMORY_TYPE_INTERNAL_DM 2
+#define DSP_MEMORY_TYPE_INTERNAL_PM 3
+#define DSP_DOWNLOAD_FLAG_BOOTABLE 0x0001
+#define DSP_DOWNLOAD_FLAG_2181 0x0002
+#define DSP_DOWNLOAD_FLAG_TIMECRITICAL 0x0004
+#define DSP_DOWNLOAD_FLAG_COMPAND 0x0008
+#define DSP_MEMORY_BLOCK_COUNT 16
+#define DSP_SEGMENT_PM_FLAG 0x0001
+#define DSP_SEGMENT_SHARED_FLAG 0x0002
+#define DSP_SEGMENT_EXTERNAL_DM DSP_MEMORY_TYPE_EXTERNAL_DM
+#define DSP_SEGMENT_EXTERNAL_PM DSP_MEMORY_TYPE_EXTERNAL_PM
+#define DSP_SEGMENT_INTERNAL_DM DSP_MEMORY_TYPE_INTERNAL_DM
+#define DSP_SEGMENT_INTERNAL_PM DSP_MEMORY_TYPE_INTERNAL_PM
+#define DSP_SEGMENT_FIRST_RELOCATABLE 4
+#define DSP_DATA_BLOCK_PM_FLAG 0x0001
+#define DSP_DATA_BLOCK_DWORD_FLAG 0x0002
+#define DSP_DATA_BLOCK_RESOLVE_FLAG 0x0004
+#define DSP_RELOC_NONE 0x00
+#define DSP_RELOC_SEGMENT_MASK 0x3f
+#define DSP_RELOC_TYPE_MASK 0xc0
+#define DSP_RELOC_TYPE_0 0x00 /* relocation of address in DM word / high part of PM word */
+#define DSP_RELOC_TYPE_1 0x40 /* relocation of address in low part of PM data word */
+#define DSP_RELOC_TYPE_2 0x80 /* relocation of address in standard command */
+#define DSP_RELOC_TYPE_3 0xc0 /* relocation of address in call/jump on flag in */
+#define DSP_COMBIFILE_FORMAT_IDENTIFICATION_SIZE 48
+#define DSP_COMBIFILE_FORMAT_VERSION_BCD 0x0100
+#define DSP_FILE_FORMAT_IDENTIFICATION_SIZE 48
+#define DSP_FILE_FORMAT_VERSION_BCD 0x0100
+typedef struct tag_dsp_combifile_header
+{
+ char format_identification[DSP_COMBIFILE_FORMAT_IDENTIFICATION_SIZE];
+ word format_version_bcd;
+ word header_size;
+ word combifile_description_size;
+ word directory_entries;
+ word directory_size;
+ word download_count;
+ word usage_mask_size;
+} t_dsp_combifile_header;
+typedef struct tag_dsp_combifile_directory_entry
+{
+ word card_type_number;
+ word file_set_number;
+} t_dsp_combifile_directory_entry;
+typedef struct tag_dsp_file_header
+{
+ char format_identification[DSP_FILE_FORMAT_IDENTIFICATION_SIZE];
+ word format_version_bcd;
+ word download_id;
+ word download_flags;
+ word required_processing_power;
+ word interface_channel_count;
+ word header_size;
+ word download_description_size;
+ word memory_block_table_size;
+ word memory_block_count;
+ word segment_table_size;
+ word segment_count;
+ word symbol_table_size;
+ word symbol_count;
+ word total_data_size_dm;
+ word data_block_count_dm;
+ word total_data_size_pm;
+ word data_block_count_pm;
+} t_dsp_file_header;
+typedef struct tag_dsp_memory_block_desc
+{
+ word alias_memory_block;
+ word memory_type;
+ word address;
+ word size; /* DSP words */
+} t_dsp_memory_block_desc;
+typedef struct tag_dsp_segment_desc
+{
+ word memory_block;
+ word attributes;
+ word base;
+ word size;
+ word alignment; /* ==0 -> no other legal start address than base */
+} t_dsp_segment_desc;
+typedef struct tag_dsp_symbol_desc
+{
+ word symbol_id;
+ word segment;
+ word offset;
+ word size; /* DSP words */
+} t_dsp_symbol_desc;
+typedef struct tag_dsp_data_block_header
+{
+ word attributes;
+ word segment;
+ word offset;
+ word size; /* DSP words */
+} t_dsp_data_block_header;
+typedef struct tag_dsp_download_desc
+{
+ word download_id;
+ word download_flags;
+ word required_processing_power;
+ word interface_channel_count;
+ word excess_header_size;
+ word memory_block_count;
+ word segment_count;
+ word symbol_count;
+ word data_block_count_dm;
+ word data_block_count_pm;
+ byte * p_excess_header_data;
+ char * p_download_description;
+ t_dsp_memory_block_desc *p_memory_block_table;
+ t_dsp_segment_desc *p_segment_table;
+ t_dsp_symbol_desc *p_symbol_table;
+ word * p_data_blocks_dm;
+ word * p_data_blocks_pm;
+} t_dsp_desc;
+typedef struct tag_dsp_portable_download_desc /* be sure to keep native alignment for MAESTRA's */
+{
+ word download_id;
+ word download_flags;
+ word required_processing_power;
+ word interface_channel_count;
+ word excess_header_size;
+ word memory_block_count;
+ word segment_count;
+ word symbol_count;
+ word data_block_count_dm;
+ word data_block_count_pm;
+ dword p_excess_header_data;
+ dword p_download_description;
+ dword p_memory_block_table;
+ dword p_segment_table;
+ dword p_symbol_table;
+ dword p_data_blocks_dm;
+ dword p_data_blocks_pm;
+} t_dsp_portable_desc;
+#define DSP_DOWNLOAD_INDEX_KERNEL 0
+#define DSP30TX_DOWNLOAD_INDEX_KERNEL 1
+#define DSP30RX_DOWNLOAD_INDEX_KERNEL 2
+#define DSP_MAX_DOWNLOAD_COUNT 64
+#define DSP_DOWNLOAD_MAX_SEGMENTS 16
+#define DSP_UDATA_REQUEST_RECONFIGURE 0
+/*
+parameters:
+ <word> reconfigure delay (in 8kHz samples)
+ <word> reconfigure code
+ <byte> reconfigure hdlc preamble flags
+*/
+#define DSP_RECONFIGURE_TX_FLAG 0x8000
+#define DSP_RECONFIGURE_SHORT_TRAIN_FLAG 0x4000
+#define DSP_RECONFIGURE_ECHO_PROTECT_FLAG 0x2000
+#define DSP_RECONFIGURE_HDLC_FLAG 0x1000
+#define DSP_RECONFIGURE_SYNC_FLAG 0x0800
+#define DSP_RECONFIGURE_PROTOCOL_MASK 0x00ff
+#define DSP_RECONFIGURE_IDLE 0
+#define DSP_RECONFIGURE_V25 1
+#define DSP_RECONFIGURE_V21_CH2 2
+#define DSP_RECONFIGURE_V27_2400 3
+#define DSP_RECONFIGURE_V27_4800 4
+#define DSP_RECONFIGURE_V29_7200 5
+#define DSP_RECONFIGURE_V29_9600 6
+#define DSP_RECONFIGURE_V33_12000 7
+#define DSP_RECONFIGURE_V33_14400 8
+#define DSP_RECONFIGURE_V17_7200 9
+#define DSP_RECONFIGURE_V17_9600 10
+#define DSP_RECONFIGURE_V17_12000 11
+#define DSP_RECONFIGURE_V17_14400 12
+/*
+data indications if transparent framer
+ <byte> data 0
+ <byte> data 1
+ ...
+data indications if HDLC framer
+ <byte> data 0
+ <byte> data 1
+ ...
+ <byte> CRC 0
+ <byte> CRC 1
+ <byte> preamble flags
+*/
+#define DSP_UDATA_INDICATION_SYNC 0
+/*
+returns:
+ <word> time of sync (sampled from counter at 8kHz)
+*/
+#define DSP_UDATA_INDICATION_DCD_OFF 1
+/*
+returns:
+ <word> time of DCD off (sampled from counter at 8kHz)
+*/
+#define DSP_UDATA_INDICATION_DCD_ON 2
+/*
+returns:
+ <word> time of DCD on (sampled from counter at 8kHz)
+ <byte> connected norm
+ <word> connected options
+ <dword> connected speed (bit/s)
+*/
+#define DSP_UDATA_INDICATION_CTS_OFF 3
+/*
+returns:
+ <word> time of CTS off (sampled from counter at 8kHz)
+*/
+#define DSP_UDATA_INDICATION_CTS_ON 4
+/*
+returns:
+ <word> time of CTS on (sampled from counter at 8kHz)
+ <byte> connected norm
+ <word> connected options
+ <dword> connected speed (bit/s)
+*/
+#define DSP_CONNECTED_NORM_UNSPECIFIED 0
+#define DSP_CONNECTED_NORM_V21 1
+#define DSP_CONNECTED_NORM_V23 2
+#define DSP_CONNECTED_NORM_V22 3
+#define DSP_CONNECTED_NORM_V22_BIS 4
+#define DSP_CONNECTED_NORM_V32_BIS 5
+#define DSP_CONNECTED_NORM_V34 6
+#define DSP_CONNECTED_NORM_V8 7
+#define DSP_CONNECTED_NORM_BELL_212A 8
+#define DSP_CONNECTED_NORM_BELL_103 9
+#define DSP_CONNECTED_NORM_V29_LEASED_LINE 10
+#define DSP_CONNECTED_NORM_V33_LEASED_LINE 11
+#define DSP_CONNECTED_NORM_TFAST 12
+#define DSP_CONNECTED_NORM_V21_CH2 13
+#define DSP_CONNECTED_NORM_V27_TER 14
+#define DSP_CONNECTED_NORM_V29 15
+#define DSP_CONNECTED_NORM_V33 16
+#define DSP_CONNECTED_NORM_V17 17
+#define DSP_CONNECTED_OPTION_TRELLIS 0x0001
+/*---------------------------------------------------------------------------*/
+extern char *dsp_read_file (OsFileHandle *fp,
+ word card_type_number,
+ word *p_dsp_download_count,
+ t_dsp_desc *p_dsp_download_table,
+ t_dsp_portable_desc *p_dsp_portable_download_table) ;
+/*---------------------------------------------------------------------------*/
+#endif /* DSP_DEFS_H_ */
diff --git a/drivers/isdn/hardware/eicon/dsp_tst.h b/drivers/isdn/hardware/eicon/dsp_tst.h
new file mode 100644
index 0000000..a6021e5
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/dsp_tst.h
@@ -0,0 +1,47 @@
+/* $Id: dsp_tst.h,v 1.1.2.2 2001/02/08 12:25:43 armin Exp $ */
+
+#ifndef __DIVA_PRI_HOST_TEST_DSPS_H__
+#define __DIVA_PRI_HOST_TEST_DSPS_H__
+
+/*
+ DSP registers on maestra pri
+ */
+#define DSP1_PORT (0x00)
+#define DSP2_PORT (0x8)
+#define DSP3_PORT (0x800)
+#define DSP4_PORT (0x808)
+#define DSP5_PORT (0x810)
+#define DSP6_PORT (0x818)
+#define DSP7_PORT (0x820)
+#define DSP8_PORT (0x828)
+#define DSP9_PORT (0x830)
+#define DSP10_PORT (0x840)
+#define DSP11_PORT (0x848)
+#define DSP12_PORT (0x850)
+#define DSP13_PORT (0x858)
+#define DSP14_PORT (0x860)
+#define DSP15_PORT (0x868)
+#define DSP16_PORT (0x870)
+#define DSP17_PORT (0x1000)
+#define DSP18_PORT (0x1008)
+#define DSP19_PORT (0x1010)
+#define DSP20_PORT (0x1018)
+#define DSP21_PORT (0x1020)
+#define DSP22_PORT (0x1028)
+#define DSP23_PORT (0x1030)
+#define DSP24_PORT (0x1040)
+#define DSP25_PORT (0x1048)
+#define DSP26_PORT (0x1050)
+#define DSP27_PORT (0x1058)
+#define DSP28_PORT (0x1060)
+#define DSP29_PORT (0x1068)
+#define DSP30_PORT (0x1070)
+#define DSP_ADR_OFFS 0x80
+
+/*------------------------------------------------------------------
+ Dsp related definitions
+ ------------------------------------------------------------------ */
+#define DSP_SIGNATURE_PROBE_WORD 0x5a5a
+#define dsp_make_address_ex(pm,address) ((word)((pm) ? (address) : (address) + 0x4000))
+
+#endif
diff --git a/drivers/isdn/hardware/eicon/dspdids.h b/drivers/isdn/hardware/eicon/dspdids.h
new file mode 100644
index 0000000..ebe131a
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/dspdids.h
@@ -0,0 +1,75 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+#ifndef DSPDIDS_H_
+#define DSPDIDS_H_
+/*---------------------------------------------------------------------------*/
+#define DSP_DID_INVALID 0
+#define DSP_DID_DIVA 1
+#define DSP_DID_DIVA_PRO 2
+#define DSP_DID_DIVA_PRO_20 3
+#define DSP_DID_DIVA_PRO_PCCARD 4
+#define DSP_DID_DIVA_SERVER_BRI_1M 5
+#define DSP_DID_DIVA_SERVER_BRI_2M 6
+#define DSP_DID_DIVA_SERVER_PRI_2M_TX 7
+#define DSP_DID_DIVA_SERVER_PRI_2M_RX 8
+#define DSP_DID_DIVA_SERVER_PRI_30M 9
+#define DSP_DID_TASK_HSCX 100
+#define DSP_DID_TASK_HSCX_PRI_2M_TX 101
+#define DSP_DID_TASK_HSCX_PRI_2M_RX 102
+#define DSP_DID_TASK_V110KRNL 200
+#define DSP_DID_OVERLAY_V1100 201
+#define DSP_DID_OVERLAY_V1101 202
+#define DSP_DID_OVERLAY_V1102 203
+#define DSP_DID_OVERLAY_V1103 204
+#define DSP_DID_OVERLAY_V1104 205
+#define DSP_DID_OVERLAY_V1105 206
+#define DSP_DID_OVERLAY_V1106 207
+#define DSP_DID_OVERLAY_V1107 208
+#define DSP_DID_OVERLAY_V1108 209
+#define DSP_DID_OVERLAY_V1109 210
+#define DSP_DID_TASK_V110_PRI_2M_TX 220
+#define DSP_DID_TASK_V110_PRI_2M_RX 221
+#define DSP_DID_TASK_MODEM 300
+#define DSP_DID_TASK_FAX05 400
+#define DSP_DID_TASK_VOICE 500
+#define DSP_DID_TASK_TIKRNL81 600
+#define DSP_DID_OVERLAY_DIAL 601
+#define DSP_DID_OVERLAY_V22 602
+#define DSP_DID_OVERLAY_V32 603
+#define DSP_DID_OVERLAY_FSK 604
+#define DSP_DID_OVERLAY_FAX 605
+#define DSP_DID_OVERLAY_VXX 606
+#define DSP_DID_OVERLAY_V8 607
+#define DSP_DID_OVERLAY_INFO 608
+#define DSP_DID_OVERLAY_V34 609
+#define DSP_DID_OVERLAY_DFX 610
+#define DSP_DID_PARTIAL_OVERLAY_DIAL 611
+#define DSP_DID_PARTIAL_OVERLAY_FSK 612
+#define DSP_DID_PARTIAL_OVERLAY_FAX 613
+#define DSP_DID_TASK_TIKRNL05 700
+/*---------------------------------------------------------------------------*/
+#endif
+/*---------------------------------------------------------------------------*/
diff --git a/drivers/isdn/hardware/eicon/dsrv4bri.h b/drivers/isdn/hardware/eicon/dsrv4bri.h
new file mode 100644
index 0000000..732d22d
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/dsrv4bri.h
@@ -0,0 +1,40 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+#ifndef __DIVA_XDI_DSRV_4_BRI_INC__
+#define __DIVA_XDI_DSRV_4_BRI_INC__
+/*
+ * Some special registers in the PLX 9054
+ */
+#define PLX9054_P2LDBELL 0x60
+#define PLX9054_L2PDBELL 0x64
+#define PLX9054_INTCSR 0x69
+#define PLX9054_INT_ENABLE 0x09
+#define PLX9054_SOFT_RESET 0x4000
+#define PLX9054_RELOAD_EEPROM 0x2000
+#define DIVA_4BRI_REVISION(__x__) (((__x__)->cardType == CARDTYPE_DIVASRV_Q_8M_V2_PCI) || ((__x__)->cardType == CARDTYPE_DIVASRV_VOICE_Q_8M_V2_PCI) || ((__x__)->cardType == CARDTYPE_DIVASRV_B_2M_V2_PCI) || ((__x__)->cardType == CARDTYPE_DIVASRV_B_2F_PCI) || ((__x__)->cardType == CARDTYPE_DIVASRV_VOICE_B_2M_V2_PCI))
+void diva_os_set_qBri_functions (PISDN_ADAPTER IoAdapter);
+void diva_os_set_qBri2_functions (PISDN_ADAPTER IoAdapter);
+#endif
diff --git a/drivers/isdn/hardware/eicon/dsrv_bri.h b/drivers/isdn/hardware/eicon/dsrv_bri.h
new file mode 100644
index 0000000..f38ebbe
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/dsrv_bri.h
@@ -0,0 +1,37 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+#ifndef __DIVA_XDI_DSRV_BRI_INC__
+#define __DIVA_XDI_DSRV_BRI_INC__
+/*
+ Functions exported from os dependent part of
+ BRI card configuration and used in
+ OS independed part
+ */
+/*
+ Prepare OS dependent part of BRI functions
+ */
+void diva_os_prepare_maestra_functions (PISDN_ADAPTER IoAdapter);
+#endif
diff --git a/drivers/isdn/hardware/eicon/dsrv_pri.h b/drivers/isdn/hardware/eicon/dsrv_pri.h
new file mode 100644
index 0000000..8611826
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/dsrv_pri.h
@@ -0,0 +1,38 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+#ifndef __DIVA_XDI_DSRV_PRI_INC__
+#define __DIVA_XDI_DSRV_PRI_INC__
+/*
+ Functions exported from os dependent part of
+ PRI card configuration and used in
+ OS independed part
+ */
+/*
+ Prepare OS dependent part of PRI/PRI Rev.2 functions
+ */
+void diva_os_prepare_pri_functions (PISDN_ADAPTER IoAdapter);
+void diva_os_prepare_pri2_functions (PISDN_ADAPTER IoAdapter);
+#endif
diff --git a/drivers/isdn/hardware/eicon/entity.h b/drivers/isdn/hardware/eicon/entity.h
new file mode 100644
index 0000000..16252cf
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/entity.h
@@ -0,0 +1,28 @@
+/* $Id: entity.h,v 1.4 2004/03/21 17:26:01 armin Exp $ */
+
+#ifndef __DIVAS_USER_MODE_IDI_ENTITY__
+#define __DIVAS_USER_MODE_IDI_ENTITY__
+
+#define DIVA_UM_IDI_RC_PENDING 0x00000001
+#define DIVA_UM_IDI_REMOVE_PENDING 0x00000002
+#define DIVA_UM_IDI_TX_FLOW_CONTROL 0x00000004
+#define DIVA_UM_IDI_REMOVED 0x00000008
+#define DIVA_UM_IDI_ASSIGN_PENDING 0x00000010
+
+typedef struct _divas_um_idi_entity {
+ struct list_head link;
+ diva_um_idi_adapter_t* adapter; /* Back to adapter */
+ ENTITY e;
+ void* os_ref;
+ dword status;
+ void* os_context;
+ int rc_count;
+ diva_um_idi_data_queue_t data; /* definad by user 1 ... MAX */
+ diva_um_idi_data_queue_t rc; /* two entries */
+ BUFFERS XData;
+ BUFFERS RData;
+ byte buffer[2048+512];
+} divas_um_idi_entity_t;
+
+
+#endif
diff --git a/drivers/isdn/hardware/eicon/helpers.h b/drivers/isdn/hardware/eicon/helpers.h
new file mode 100644
index 0000000..b212311
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/helpers.h
@@ -0,0 +1,51 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+#ifndef __DIVA_XDI_CARD_CONFIG_HELPERS_INC__
+#define __DIVA_XDI_CARD_CONFIG_HELPERS_INC__
+dword diva_get_protocol_file_features (byte* File,
+ int offset,
+ char *IdStringBuffer,
+ dword IdBufferSize);
+void diva_configure_protocol (PISDN_ADAPTER IoAdapter);
+/*
+ Low level file access system abstraction
+ */
+/* -------------------------------------------------------------------------
+ Access to single file
+ Return pointer to the image of the requested file,
+ write image length to 'FileLength'
+ ------------------------------------------------------------------------- */
+void *xdiLoadFile (char *FileName, dword *FileLength, unsigned long MaxLoadSize) ;
+/* -------------------------------------------------------------------------
+ Dependent on the protocol settings does read return pointer
+ to the image of appropriate protocol file
+ ------------------------------------------------------------------------- */
+void *xdiLoadArchive (PISDN_ADAPTER IoAdapter, dword *FileLength, unsigned long MaxLoadSize) ;
+/* --------------------------------------------------------------------------
+ Free all system resources accessed by xdiLoadFile and xdiLoadArchive
+ -------------------------------------------------------------------------- */
+void xdiFreeFile (void* handle);
+#endif
diff --git a/drivers/isdn/hardware/eicon/idifunc.c b/drivers/isdn/hardware/eicon/idifunc.c
new file mode 100644
index 0000000..db87d51
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/idifunc.c
@@ -0,0 +1,268 @@
+/* $Id: idifunc.c,v 1.14.4.4 2004/08/28 20:03:53 armin Exp $
+ *
+ * Driver for Eicon DIVA Server ISDN cards.
+ * User Mode IDI Interface
+ *
+ * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
+ * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include "platform.h"
+#include "di_defs.h"
+#include "divasync.h"
+#include "um_xdi.h"
+#include "um_idi.h"
+
+#define DBG_MINIMUM (DL_LOG + DL_FTL + DL_ERR)
+#define DBG_DEFAULT (DBG_MINIMUM + DL_XLOG + DL_REG)
+
+extern char *DRIVERRELEASE_IDI;
+
+extern void DIVA_DIDD_Read(void *, int);
+extern int diva_user_mode_idi_create_adapter(const DESCRIPTOR *, int);
+extern void diva_user_mode_idi_remove_adapter(int);
+
+static dword notify_handle;
+static DESCRIPTOR DAdapter;
+static DESCRIPTOR MAdapter;
+
+static void no_printf(unsigned char *x, ...)
+{
+ /* dummy debug function */
+}
+
+#include "debuglib.c"
+
+/*
+ * stop debug
+ */
+static void stop_dbg(void)
+{
+ DbgDeregister();
+ memset(&MAdapter, 0, sizeof(MAdapter));
+ dprintf = no_printf;
+}
+
+typedef struct _udiva_card {
+ struct list_head list;
+ int Id;
+ DESCRIPTOR d;
+} udiva_card;
+
+static LIST_HEAD(cards);
+static diva_os_spin_lock_t ll_lock;
+
+/*
+ * find card in list
+ */
+static udiva_card *find_card_in_list(DESCRIPTOR * d)
+{
+ udiva_card *card;
+ struct list_head *tmp;
+ diva_os_spin_lock_magic_t old_irql;
+
+ diva_os_enter_spin_lock(&ll_lock, &old_irql, "find card");
+ list_for_each(tmp, &cards) {
+ card = list_entry(tmp, udiva_card, list);
+ if (card->d.request == d->request) {
+ diva_os_leave_spin_lock(&ll_lock, &old_irql,
+ "find card");
+ return (card);
+ }
+ }
+ diva_os_leave_spin_lock(&ll_lock, &old_irql, "find card");
+ return ((udiva_card *) NULL);
+}
+
+/*
+ * new card
+ */
+static void um_new_card(DESCRIPTOR * d)
+{
+ int adapter_nr = 0;
+ udiva_card *card = NULL;
+ IDI_SYNC_REQ sync_req;
+ diva_os_spin_lock_magic_t old_irql;
+
+ if (!(card = diva_os_malloc(0, sizeof(udiva_card)))) {
+ DBG_ERR(("cannot get buffer for card"));
+ return;
+ }
+ memcpy(&card->d, d, sizeof(DESCRIPTOR));
+ sync_req.xdi_logical_adapter_number.Req = 0;
+ sync_req.xdi_logical_adapter_number.Rc =
+ IDI_SYNC_REQ_XDI_GET_LOGICAL_ADAPTER_NUMBER;
+ card->d.request((ENTITY *) & sync_req);
+ adapter_nr =
+ sync_req.xdi_logical_adapter_number.info.logical_adapter_number;
+ card->Id = adapter_nr;
+ if (!(diva_user_mode_idi_create_adapter(d, adapter_nr))) {
+ diva_os_enter_spin_lock(&ll_lock, &old_irql, "add card");
+ list_add_tail(&card->list, &cards);
+ diva_os_leave_spin_lock(&ll_lock, &old_irql, "add card");
+ } else {
+ DBG_ERR(("could not create user mode idi card %d",
+ adapter_nr));
+ diva_os_free(0, card);
+ }
+}
+
+/*
+ * remove card
+ */
+static void um_remove_card(DESCRIPTOR * d)
+{
+ diva_os_spin_lock_magic_t old_irql;
+ udiva_card *card = NULL;
+
+ if (!(card = find_card_in_list(d))) {
+ DBG_ERR(("cannot find card to remove"));
+ return;
+ }
+ diva_user_mode_idi_remove_adapter(card->Id);
+ diva_os_enter_spin_lock(&ll_lock, &old_irql, "remove card");
+ list_del(&card->list);
+ diva_os_leave_spin_lock(&ll_lock, &old_irql, "remove card");
+ DBG_LOG(("idi proc entry removed for card %d", card->Id));
+ diva_os_free(0, card);
+}
+
+/*
+ * remove all adapter
+ */
+static void DIVA_EXIT_FUNCTION remove_all_idi_proc(void)
+{
+ udiva_card *card;
+ diva_os_spin_lock_magic_t old_irql;
+
+rescan:
+ diva_os_enter_spin_lock(&ll_lock, &old_irql, "remove all");
+ if (!list_empty(&cards)) {
+ card = list_entry(cards.next, udiva_card, list);
+ list_del(&card->list);
+ diva_os_leave_spin_lock(&ll_lock, &old_irql, "remove all");
+ diva_user_mode_idi_remove_adapter(card->Id);
+ diva_os_free(0, card);
+ goto rescan;
+ }
+ diva_os_leave_spin_lock(&ll_lock, &old_irql, "remove all");
+}
+
+/*
+ * DIDD notify callback
+ */
+static void *didd_callback(void *context, DESCRIPTOR * adapter,
+ int removal)
+{
+ if (adapter->type == IDI_DADAPTER) {
+ DBG_ERR(("Notification about IDI_DADAPTER change ! Oops."));
+ return (NULL);
+ } else if (adapter->type == IDI_DIMAINT) {
+ if (removal) {
+ stop_dbg();
+ } else {
+ memcpy(&MAdapter, adapter, sizeof(MAdapter));
+ dprintf = (DIVA_DI_PRINTF) MAdapter.request;
+ DbgRegister("User IDI", DRIVERRELEASE_IDI, DBG_DEFAULT);
+ }
+ } else if ((adapter->type > 0) && (adapter->type < 16)) { /* IDI Adapter */
+ if (removal) {
+ um_remove_card(adapter);
+ } else {
+ um_new_card(adapter);
+ }
+ }
+ return (NULL);
+}
+
+/*
+ * connect DIDD
+ */
+static int DIVA_INIT_FUNCTION connect_didd(void)
+{
+ int x = 0;
+ int dadapter = 0;
+ IDI_SYNC_REQ req;
+ DESCRIPTOR DIDD_Table[MAX_DESCRIPTORS];
+
+ DIVA_DIDD_Read(DIDD_Table, sizeof(DIDD_Table));
+
+ for (x = 0; x < MAX_DESCRIPTORS; x++) {
+ if (DIDD_Table[x].type == IDI_DADAPTER) { /* DADAPTER found */
+ dadapter = 1;
+ memcpy(&DAdapter, &DIDD_Table[x], sizeof(DAdapter));
+ req.didd_notify.e.Req = 0;
+ req.didd_notify.e.Rc =
+ IDI_SYNC_REQ_DIDD_REGISTER_ADAPTER_NOTIFY;
+ req.didd_notify.info.callback = (void *)didd_callback;
+ req.didd_notify.info.context = NULL;
+ DAdapter.request((ENTITY *) & req);
+ if (req.didd_notify.e.Rc != 0xff) {
+ stop_dbg();
+ return (0);
+ }
+ notify_handle = req.didd_notify.info.handle;
+ } else if (DIDD_Table[x].type == IDI_DIMAINT) { /* MAINT found */
+ memcpy(&MAdapter, &DIDD_Table[x], sizeof(DAdapter));
+ dprintf = (DIVA_DI_PRINTF) MAdapter.request;
+ DbgRegister("User IDI", DRIVERRELEASE_IDI, DBG_DEFAULT);
+ } else if ((DIDD_Table[x].type > 0)
+ && (DIDD_Table[x].type < 16)) { /* IDI Adapter found */
+ um_new_card(&DIDD_Table[x]);
+ }
+ }
+
+ if (!dadapter) {
+ stop_dbg();
+ }
+
+ return (dadapter);
+}
+
+/*
+ * Disconnect from DIDD
+ */
+static void DIVA_EXIT_FUNCTION disconnect_didd(void)
+{
+ IDI_SYNC_REQ req;
+
+ stop_dbg();
+
+ req.didd_notify.e.Req = 0;
+ req.didd_notify.e.Rc = IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER_NOTIFY;
+ req.didd_notify.info.handle = notify_handle;
+ DAdapter.request((ENTITY *) & req);
+}
+
+/*
+ * init
+ */
+int DIVA_INIT_FUNCTION idifunc_init(void)
+{
+ diva_os_initialize_spin_lock(&ll_lock, "idifunc");
+
+ if (diva_user_mode_idi_init()) {
+ DBG_ERR(("init: init failed."));
+ return (0);
+ }
+
+ if (!connect_didd()) {
+ diva_user_mode_idi_finit();
+ DBG_ERR(("init: failed to connect to DIDD."));
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * finit
+ */
+void DIVA_EXIT_FUNCTION idifunc_finit(void)
+{
+ diva_user_mode_idi_finit();
+ disconnect_didd();
+ remove_all_idi_proc();
+}
diff --git a/drivers/isdn/hardware/eicon/io.c b/drivers/isdn/hardware/eicon/io.c
new file mode 100644
index 0000000..6fd9b00
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/io.c
@@ -0,0 +1,852 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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 "platform.h"
+#include "di_defs.h"
+#include "pc.h"
+#include "pr_pc.h"
+#include "divasync.h"
+#define MIPS_SCOM
+#include "pkmaint.h" /* pc_main.h, packed in os-dependent fashion */
+#include "di.h"
+#include "mi_pc.h"
+#include "io.h"
+extern ADAPTER * adapter[MAX_ADAPTER];
+extern PISDN_ADAPTER IoAdapters[MAX_ADAPTER];
+void request (PISDN_ADAPTER, ENTITY *);
+static void pcm_req (PISDN_ADAPTER, ENTITY *);
+/* --------------------------------------------------------------------------
+ local functions
+ -------------------------------------------------------------------------- */
+#define ReqFunc(N) \
+static void Request##N(ENTITY *e) \
+{ if ( IoAdapters[N] ) (* IoAdapters[N]->DIRequest)(IoAdapters[N], e) ; }
+ReqFunc(0)
+ReqFunc(1)
+ReqFunc(2)
+ReqFunc(3)
+ReqFunc(4)
+ReqFunc(5)
+ReqFunc(6)
+ReqFunc(7)
+ReqFunc(8)
+ReqFunc(9)
+ReqFunc(10)
+ReqFunc(11)
+ReqFunc(12)
+ReqFunc(13)
+ReqFunc(14)
+ReqFunc(15)
+IDI_CALL Requests[MAX_ADAPTER] =
+{ &Request0, &Request1, &Request2, &Request3,
+ &Request4, &Request5, &Request6, &Request7,
+ &Request8, &Request9, &Request10, &Request11,
+ &Request12, &Request13, &Request14, &Request15
+};
+/*****************************************************************************/
+/*
+ This array should indicate all new services, that this version of XDI
+ is able to provide to his clients
+ */
+static byte extended_xdi_features[DIVA_XDI_EXTENDED_FEATURES_MAX_SZ+1] = {
+ (DIVA_XDI_EXTENDED_FEATURES_VALID |
+ DIVA_XDI_EXTENDED_FEATURE_SDRAM_BAR |
+ DIVA_XDI_EXTENDED_FEATURE_CAPI_PRMS |
+#if defined(DIVA_IDI_RX_DMA)
+ DIVA_XDI_EXTENDED_FEATURE_CMA |
+ DIVA_XDI_EXTENDED_FEATURE_RX_DMA |
+ DIVA_XDI_EXTENDED_FEATURE_MANAGEMENT_DMA |
+#endif
+ DIVA_XDI_EXTENDED_FEATURE_NO_CANCEL_RC),
+ 0
+};
+/*****************************************************************************/
+void
+dump_xlog_buffer (PISDN_ADAPTER IoAdapter, Xdesc *xlogDesc)
+{
+ dword logLen ;
+ word *Xlog = xlogDesc->buf ;
+ word logCnt = xlogDesc->cnt ;
+ word logOut = xlogDesc->out / sizeof(*Xlog) ;
+ DBG_FTL(("%s: ************* XLOG recovery (%d) *************",
+ &IoAdapter->Name[0], (int)logCnt))
+ DBG_FTL(("Microcode: %s", &IoAdapter->ProtocolIdString[0]))
+ for ( ; logCnt > 0 ; --logCnt )
+ {
+ if ( !GET_WORD(&Xlog[logOut]) )
+ {
+ if ( --logCnt == 0 )
+ break ;
+ logOut = 0 ;
+ }
+ if ( GET_WORD(&Xlog[logOut]) <= (logOut * sizeof(*Xlog)) )
+ {
+ if ( logCnt > 2 )
+ {
+ DBG_FTL(("Possibly corrupted XLOG: %d entries left",
+ (int)logCnt))
+ }
+ break ;
+ }
+ logLen = (dword)(GET_WORD(&Xlog[logOut]) - (logOut * sizeof(*Xlog))) ;
+ DBG_FTL_MXLOG(( (char *)&Xlog[logOut + 1], (dword)(logLen - 2) ))
+ logOut = (GET_WORD(&Xlog[logOut]) + 1) / sizeof(*Xlog) ;
+ }
+ DBG_FTL(("%s: ***************** end of XLOG *****************",
+ &IoAdapter->Name[0]))
+}
+/*****************************************************************************/
+#if defined(XDI_USE_XLOG)
+static char *(ExceptionCauseTable[]) =
+{
+ "Interrupt",
+ "TLB mod /IBOUND",
+ "TLB load /DBOUND",
+ "TLB store",
+ "Address error load",
+ "Address error store",
+ "Instruction load bus error",
+ "Data load/store bus error",
+ "Syscall",
+ "Breakpoint",
+ "Reverd instruction",
+ "Coprocessor unusable",
+ "Overflow",
+ "TRAP",
+ "VCEI",
+ "Floating Point Exception",
+ "CP2",
+ "Reserved 17",
+ "Reserved 18",
+ "Reserved 19",
+ "Reserved 20",
+ "Reserved 21",
+ "Reserved 22",
+ "WATCH",
+ "Reserved 24",
+ "Reserved 25",
+ "Reserved 26",
+ "Reserved 27",
+ "Reserved 28",
+ "Reserved 29",
+ "Reserved 30",
+ "VCED"
+} ;
+#endif
+void
+dump_trap_frame (PISDN_ADAPTER IoAdapter, byte __iomem *exceptionFrame)
+{
+ MP_XCPTC __iomem *xcept = (MP_XCPTC __iomem *)exceptionFrame ;
+ dword __iomem *regs;
+ regs = &xcept->regs[0] ;
+ DBG_FTL(("%s: ***************** CPU TRAPPED *****************",
+ &IoAdapter->Name[0]))
+ DBG_FTL(("Microcode: %s", &IoAdapter->ProtocolIdString[0]))
+ DBG_FTL(("Cause: %s",
+ ExceptionCauseTable[(READ_DWORD(&xcept->cr) & 0x0000007c) >> 2]))
+ DBG_FTL(("sr 0x%08x cr 0x%08x epc 0x%08x vaddr 0x%08x",
+ READ_DWORD(&xcept->sr), READ_DWORD(&xcept->cr),
+ READ_DWORD(&xcept->epc), READ_DWORD(&xcept->vaddr)))
+ DBG_FTL(("zero 0x%08x at 0x%08x v0 0x%08x v1 0x%08x",
+ READ_DWORD(&regs[ 0]), READ_DWORD(&regs[ 1]),
+ READ_DWORD(&regs[ 2]), READ_DWORD(&regs[ 3])))
+ DBG_FTL(("a0 0x%08x a1 0x%08x a2 0x%08x a3 0x%08x",
+ READ_DWORD(&regs[ 4]), READ_DWORD(&regs[ 5]),
+ READ_DWORD(&regs[ 6]), READ_DWORD(&regs[ 7])))
+ DBG_FTL(("t0 0x%08x t1 0x%08x t2 0x%08x t3 0x%08x",
+ READ_DWORD(&regs[ 8]), READ_DWORD(&regs[ 9]),
+ READ_DWORD(&regs[10]), READ_DWORD(&regs[11])))
+ DBG_FTL(("t4 0x%08x t5 0x%08x t6 0x%08x t7 0x%08x",
+ READ_DWORD(&regs[12]), READ_DWORD(&regs[13]),
+ READ_DWORD(&regs[14]), READ_DWORD(&regs[15])))
+ DBG_FTL(("s0 0x%08x s1 0x%08x s2 0x%08x s3 0x%08x",
+ READ_DWORD(&regs[16]), READ_DWORD(&regs[17]),
+ READ_DWORD(&regs[18]), READ_DWORD(&regs[19])))
+ DBG_FTL(("s4 0x%08x s5 0x%08x s6 0x%08x s7 0x%08x",
+ READ_DWORD(&regs[20]), READ_DWORD(&regs[21]),
+ READ_DWORD(&regs[22]), READ_DWORD(&regs[23])))
+ DBG_FTL(("t8 0x%08x t9 0x%08x k0 0x%08x k1 0x%08x",
+ READ_DWORD(&regs[24]), READ_DWORD(&regs[25]),
+ READ_DWORD(&regs[26]), READ_DWORD(&regs[27])))
+ DBG_FTL(("gp 0x%08x sp 0x%08x s8 0x%08x ra 0x%08x",
+ READ_DWORD(&regs[28]), READ_DWORD(&regs[29]),
+ READ_DWORD(&regs[30]), READ_DWORD(&regs[31])))
+ DBG_FTL(("md 0x%08x|%08x resvd 0x%08x class 0x%08x",
+ READ_DWORD(&xcept->mdhi), READ_DWORD(&xcept->mdlo),
+ READ_DWORD(&xcept->reseverd), READ_DWORD(&xcept->xclass)))
+}
+/* --------------------------------------------------------------------------
+ Real XDI Request function
+ -------------------------------------------------------------------------- */
+void request(PISDN_ADAPTER IoAdapter, ENTITY * e)
+{
+ byte i;
+ diva_os_spin_lock_magic_t irql;
+/*
+ * if the Req field in the entity structure is 0,
+ * we treat this request as a special function call
+ */
+ if ( !e->Req )
+ {
+ IDI_SYNC_REQ *syncReq = (IDI_SYNC_REQ *)e ;
+ switch (e->Rc)
+ {
+#if defined(DIVA_IDI_RX_DMA)
+ case IDI_SYNC_REQ_DMA_DESCRIPTOR_OPERATION: {
+ diva_xdi_dma_descriptor_operation_t* pI = \
+ &syncReq->xdi_dma_descriptor_operation.info;
+ if (!IoAdapter->dma_map) {
+ pI->operation = -1;
+ pI->descriptor_number = -1;
+ return;
+ }
+ diva_os_enter_spin_lock (&IoAdapter->data_spin_lock, &irql, "dma_op");
+ if (pI->operation == IDI_SYNC_REQ_DMA_DESCRIPTOR_ALLOC) {
+ pI->descriptor_number = diva_alloc_dma_map_entry (\
+ (struct _diva_dma_map_entry*)IoAdapter->dma_map);
+ if (pI->descriptor_number >= 0) {
+ dword dma_magic;
+ void* local_addr;
+ diva_get_dma_map_entry (\
+ (struct _diva_dma_map_entry*)IoAdapter->dma_map,
+ pI->descriptor_number,
+ &local_addr, &dma_magic);
+ pI->descriptor_address = local_addr;
+ pI->descriptor_magic = dma_magic;
+ pI->operation = 0;
+ } else {
+ pI->operation = -1;
+ }
+ } else if ((pI->operation == IDI_SYNC_REQ_DMA_DESCRIPTOR_FREE) &&
+ (pI->descriptor_number >= 0)) {
+ diva_free_dma_map_entry((struct _diva_dma_map_entry*)IoAdapter->dma_map,
+ pI->descriptor_number);
+ pI->descriptor_number = -1;
+ pI->operation = 0;
+ } else {
+ pI->descriptor_number = -1;
+ pI->operation = -1;
+ }
+ diva_os_leave_spin_lock (&IoAdapter->data_spin_lock, &irql, "dma_op");
+ } return;
+#endif
+ case IDI_SYNC_REQ_XDI_GET_LOGICAL_ADAPTER_NUMBER: {
+ diva_xdi_get_logical_adapter_number_s_t *pI = \
+ &syncReq->xdi_logical_adapter_number.info;
+ pI->logical_adapter_number = IoAdapter->ANum;
+ pI->controller = IoAdapter->ControllerNumber;
+ pI->total_controllers = IoAdapter->Properties.Adapters;
+ } return;
+ case IDI_SYNC_REQ_XDI_GET_CAPI_PARAMS: {
+ diva_xdi_get_capi_parameters_t prms, *pI = &syncReq->xdi_capi_prms.info;
+ memset (&prms, 0x00, sizeof(prms));
+ prms.structure_length = min_t(size_t, sizeof(prms), pI->structure_length);
+ memset (pI, 0x00, pI->structure_length);
+ prms.flag_dynamic_l1_down = (IoAdapter->capi_cfg.cfg_1 & \
+ DIVA_XDI_CAPI_CFG_1_DYNAMIC_L1_ON) ? 1 : 0;
+ prms.group_optimization_enabled = (IoAdapter->capi_cfg.cfg_1 & \
+ DIVA_XDI_CAPI_CFG_1_GROUP_POPTIMIZATION_ON) ? 1 : 0;
+ memcpy (pI, &prms, prms.structure_length);
+ } return;
+ case IDI_SYNC_REQ_XDI_GET_ADAPTER_SDRAM_BAR:
+ syncReq->xdi_sdram_bar.info.bar = IoAdapter->sdram_bar;
+ return;
+ case IDI_SYNC_REQ_XDI_GET_EXTENDED_FEATURES: {
+ dword i;
+ diva_xdi_get_extended_xdi_features_t* pI =\
+ &syncReq->xdi_extended_features.info;
+ pI->buffer_length_in_bytes &= ~0x80000000;
+ if (pI->buffer_length_in_bytes && pI->features) {
+ memset (pI->features, 0x00, pI->buffer_length_in_bytes);
+ }
+ for (i = 0; ((pI->features) && (i < pI->buffer_length_in_bytes) &&
+ (i < DIVA_XDI_EXTENDED_FEATURES_MAX_SZ)); i++) {
+ pI->features[i] = extended_xdi_features[i];
+ }
+ if ((pI->buffer_length_in_bytes < DIVA_XDI_EXTENDED_FEATURES_MAX_SZ) ||
+ (!pI->features)) {
+ pI->buffer_length_in_bytes =\
+ (0x80000000 | DIVA_XDI_EXTENDED_FEATURES_MAX_SZ);
+ }
+ } return;
+ case IDI_SYNC_REQ_XDI_GET_STREAM:
+ if (IoAdapter) {
+ diva_xdi_provide_istream_info (&IoAdapter->a,
+ &syncReq->xdi_stream_info.info);
+ } else {
+ syncReq->xdi_stream_info.info.provided_service = 0;
+ }
+ return;
+ case IDI_SYNC_REQ_GET_NAME:
+ if ( IoAdapter )
+ {
+ strcpy (&syncReq->GetName.name[0], IoAdapter->Name) ;
+ DBG_TRC(("xdi: Adapter %d / Name '%s'",
+ IoAdapter->ANum, IoAdapter->Name))
+ return ;
+ }
+ syncReq->GetName.name[0] = '\0' ;
+ break ;
+ case IDI_SYNC_REQ_GET_SERIAL:
+ if ( IoAdapter )
+ {
+ syncReq->GetSerial.serial = IoAdapter->serialNo ;
+ DBG_TRC(("xdi: Adapter %d / SerialNo %ld",
+ IoAdapter->ANum, IoAdapter->serialNo))
+ return ;
+ }
+ syncReq->GetSerial.serial = 0 ;
+ break ;
+ case IDI_SYNC_REQ_GET_CARDTYPE:
+ if ( IoAdapter )
+ {
+ syncReq->GetCardType.cardtype = IoAdapter->cardType ;
+ DBG_TRC(("xdi: Adapter %d / CardType %ld",
+ IoAdapter->ANum, IoAdapter->cardType))
+ return ;
+ }
+ syncReq->GetCardType.cardtype = 0 ;
+ break ;
+ case IDI_SYNC_REQ_GET_XLOG:
+ if ( IoAdapter )
+ {
+ pcm_req (IoAdapter, e) ;
+ return ;
+ }
+ e->Ind = 0 ;
+ break ;
+ case IDI_SYNC_REQ_GET_DBG_XLOG:
+ if ( IoAdapter )
+ {
+ pcm_req (IoAdapter, e) ;
+ return ;
+ }
+ e->Ind = 0 ;
+ break ;
+ case IDI_SYNC_REQ_GET_FEATURES:
+ if ( IoAdapter )
+ {
+ syncReq->GetFeatures.features =
+ (unsigned short)IoAdapter->features ;
+ return ;
+ }
+ syncReq->GetFeatures.features = 0 ;
+ break ;
+ case IDI_SYNC_REQ_PORTDRV_HOOK:
+ if ( IoAdapter )
+ {
+ DBG_TRC(("Xdi:IDI_SYNC_REQ_PORTDRV_HOOK - ignored"))
+ return ;
+ }
+ break;
+ }
+ if ( IoAdapter )
+ {
+ return ;
+ }
+ }
+ DBG_TRC(("xdi: Id 0x%x / Req 0x%x / Rc 0x%x", e->Id, e->Req, e->Rc))
+ if ( !IoAdapter )
+ {
+ DBG_FTL(("xdi: uninitialized Adapter used - ignore request"))
+ return ;
+ }
+ diva_os_enter_spin_lock (&IoAdapter->data_spin_lock, &irql, "data_req");
+/*
+ * assign an entity
+ */
+ if ( !(e->Id &0x1f) )
+ {
+ if ( IoAdapter->e_count >= IoAdapter->e_max )
+ {
+ DBG_FTL(("xdi: all Ids in use (max=%d) --> Req ignored",
+ IoAdapter->e_max))
+ diva_os_leave_spin_lock (&IoAdapter->data_spin_lock, &irql, "data_req");
+ return ;
+ }
+/*
+ * find a new free id
+ */
+ for ( i = 1 ; IoAdapter->e_tbl[i].e ; ++i ) ;
+ IoAdapter->e_tbl[i].e = e ;
+ IoAdapter->e_count++ ;
+ e->No = (byte)i ;
+ e->More = 0 ;
+ e->RCurrent = 0xff ;
+ }
+ else
+ {
+ i = e->No ;
+ }
+/*
+ * if the entity is still busy, ignore the request call
+ */
+ if ( e->More & XBUSY )
+ {
+ DBG_FTL(("xdi: Id 0x%x busy --> Req 0x%x ignored", e->Id, e->Req))
+ if ( !IoAdapter->trapped && IoAdapter->trapFnc )
+ {
+ IoAdapter->trapFnc (IoAdapter) ;
+ /*
+ Firs trap, also notify user if supported
+ */
+ if (IoAdapter->trapped && IoAdapter->os_trap_nfy_Fnc) {
+ (*(IoAdapter->os_trap_nfy_Fnc))(IoAdapter, IoAdapter->ANum);
+ }
+ }
+ diva_os_leave_spin_lock (&IoAdapter->data_spin_lock, &irql, "data_req");
+ return ;
+ }
+/*
+ * initialize transmit status variables
+ */
+ e->More |= XBUSY ;
+ e->More &= ~XMOREF ;
+ e->XCurrent = 0 ;
+ e->XOffset = 0 ;
+/*
+ * queue this entity in the adapter request queue
+ */
+ IoAdapter->e_tbl[i].next = 0 ;
+ if ( IoAdapter->head )
+ {
+ IoAdapter->e_tbl[IoAdapter->tail].next = i ;
+ IoAdapter->tail = i ;
+ }
+ else
+ {
+ IoAdapter->head = i ;
+ IoAdapter->tail = i ;
+ }
+/*
+ * queue the DPC to process the request
+ */
+ diva_os_schedule_soft_isr (&IoAdapter->req_soft_isr);
+ diva_os_leave_spin_lock (&IoAdapter->data_spin_lock, &irql, "data_req");
+}
+/* ---------------------------------------------------------------------
+ Main DPC routine
+ --------------------------------------------------------------------- */
+void DIDpcRoutine (struct _diva_os_soft_isr* psoft_isr, void* Context) {
+ PISDN_ADAPTER IoAdapter = (PISDN_ADAPTER)Context ;
+ ADAPTER* a = &IoAdapter->a ;
+ diva_os_atomic_t* pin_dpc = &IoAdapter->in_dpc;
+ if (diva_os_atomic_increment (pin_dpc) == 1) {
+ do {
+ if ( IoAdapter->tst_irq (a) )
+ {
+ if ( !IoAdapter->Unavailable )
+ IoAdapter->dpc (a) ;
+ IoAdapter->clr_irq (a) ;
+ }
+ IoAdapter->out (a) ;
+ } while (diva_os_atomic_decrement (pin_dpc) > 0);
+ /* ----------------------------------------------------------------
+ Look for XLOG request (cards with indirect addressing)
+ ---------------------------------------------------------------- */
+ if (IoAdapter->pcm_pending) {
+ struct pc_maint *pcm;
+ diva_os_spin_lock_magic_t OldIrql ;
+ diva_os_enter_spin_lock (&IoAdapter->data_spin_lock,
+ &OldIrql,
+ "data_dpc");
+ pcm = (struct pc_maint *)IoAdapter->pcm_data;
+ switch (IoAdapter->pcm_pending) {
+ case 1: /* ask card for XLOG */
+ a->ram_out (a, &IoAdapter->pcm->rc, 0) ;
+ a->ram_out (a, &IoAdapter->pcm->req, pcm->req) ;
+ IoAdapter->pcm_pending = 2;
+ break;
+ case 2: /* Try to get XLOG from the card */
+ if ((int)(a->ram_in (a, &IoAdapter->pcm->rc))) {
+ a->ram_in_buffer (a, IoAdapter->pcm, pcm, sizeof(*pcm)) ;
+ IoAdapter->pcm_pending = 3;
+ }
+ break;
+ case 3: /* let XDI recovery XLOG */
+ break;
+ }
+ diva_os_leave_spin_lock (&IoAdapter->data_spin_lock,
+ &OldIrql,
+ "data_dpc");
+ }
+ /* ---------------------------------------------------------------- */
+ }
+}
+/* --------------------------------------------------------------------------
+ XLOG interface
+ -------------------------------------------------------------------------- */
+static void
+pcm_req (PISDN_ADAPTER IoAdapter, ENTITY *e)
+{
+ diva_os_spin_lock_magic_t OldIrql ;
+ int i, rc ;
+ ADAPTER *a = &IoAdapter->a ;
+ struct pc_maint *pcm = (struct pc_maint *)&e->Ind ;
+/*
+ * special handling of I/O based card interface
+ * the memory access isn't an atomic operation !
+ */
+ if ( IoAdapter->Properties.Card == CARD_MAE )
+ {
+ diva_os_enter_spin_lock (&IoAdapter->data_spin_lock,
+ &OldIrql,
+ "data_pcm_1");
+ IoAdapter->pcm_data = (void *)pcm;
+ IoAdapter->pcm_pending = 1;
+ diva_os_schedule_soft_isr (&IoAdapter->req_soft_isr);
+ diva_os_leave_spin_lock (&IoAdapter->data_spin_lock,
+ &OldIrql,
+ "data_pcm_1");
+ for ( rc = 0, i = (IoAdapter->trapped ? 3000 : 250) ; !rc && (i > 0) ; --i )
+ {
+ diva_os_sleep (1) ;
+ if (IoAdapter->pcm_pending == 3) {
+ diva_os_enter_spin_lock (&IoAdapter->data_spin_lock,
+ &OldIrql,
+ "data_pcm_3");
+ IoAdapter->pcm_pending = 0;
+ IoAdapter->pcm_data = NULL ;
+ diva_os_leave_spin_lock (&IoAdapter->data_spin_lock,
+ &OldIrql,
+ "data_pcm_3");
+ return ;
+ }
+ diva_os_enter_spin_lock (&IoAdapter->data_spin_lock,
+ &OldIrql,
+ "data_pcm_2");
+ diva_os_schedule_soft_isr (&IoAdapter->req_soft_isr);
+ diva_os_leave_spin_lock (&IoAdapter->data_spin_lock,
+ &OldIrql,
+ "data_pcm_2");
+ }
+ diva_os_enter_spin_lock (&IoAdapter->data_spin_lock,
+ &OldIrql,
+ "data_pcm_4");
+ IoAdapter->pcm_pending = 0;
+ IoAdapter->pcm_data = NULL ;
+ diva_os_leave_spin_lock (&IoAdapter->data_spin_lock,
+ &OldIrql,
+ "data_pcm_4");
+ goto Trapped ;
+ }
+/*
+ * memory based shared ram is accessible from different
+ * processors without disturbing concurrent processes.
+ */
+ a->ram_out (a, &IoAdapter->pcm->rc, 0) ;
+ a->ram_out (a, &IoAdapter->pcm->req, pcm->req) ;
+ for ( i = (IoAdapter->trapped ? 3000 : 250) ; --i > 0 ; )
+ {
+ diva_os_sleep (1) ;
+ rc = (int)(a->ram_in (a, &IoAdapter->pcm->rc)) ;
+ if ( rc )
+ {
+ a->ram_in_buffer (a, IoAdapter->pcm, pcm, sizeof(*pcm)) ;
+ return ;
+ }
+ }
+Trapped:
+ if ( IoAdapter->trapFnc )
+ {
+ int trapped = IoAdapter->trapped;
+ IoAdapter->trapFnc (IoAdapter) ;
+ /*
+ Firs trap, also notify user if supported
+ */
+ if (!trapped && IoAdapter->trapped && IoAdapter->os_trap_nfy_Fnc) {
+ (*(IoAdapter->os_trap_nfy_Fnc))(IoAdapter, IoAdapter->ANum);
+ }
+ }
+}
+/*------------------------------------------------------------------*/
+/* ram access functions for memory mapped cards */
+/*------------------------------------------------------------------*/
+byte mem_in (ADAPTER *a, void *addr)
+{
+ byte val;
+ volatile byte __iomem *Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io);
+ val = READ_BYTE(Base + (unsigned long)addr);
+ DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base);
+ return (val);
+}
+word mem_inw (ADAPTER *a, void *addr)
+{
+ word val;
+ volatile byte __iomem *Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io);
+ val = READ_WORD((Base + (unsigned long)addr));
+ DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base);
+ return (val);
+}
+void mem_in_dw (ADAPTER *a, void *addr, dword* data, int dwords)
+{
+ volatile byte __iomem * Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io);
+ while (dwords--) {
+ *data++ = READ_DWORD((Base + (unsigned long)addr));
+ addr+=4;
+ }
+ DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base);
+}
+void mem_in_buffer (ADAPTER *a, void *addr, void *buffer, word length)
+{
+ volatile byte __iomem *Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io);
+ memcpy_fromio(buffer, (Base + (unsigned long)addr), length);
+ DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base);
+}
+void mem_look_ahead (ADAPTER *a, PBUFFER *RBuffer, ENTITY *e)
+{
+ PISDN_ADAPTER IoAdapter = (PISDN_ADAPTER)a->io ;
+ IoAdapter->RBuffer.length = mem_inw (a, &RBuffer->length) ;
+ mem_in_buffer (a, RBuffer->P, IoAdapter->RBuffer.P,
+ IoAdapter->RBuffer.length) ;
+ e->RBuffer = (DBUFFER *)&IoAdapter->RBuffer ;
+}
+void mem_out (ADAPTER *a, void *addr, byte data)
+{
+ volatile byte __iomem *Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io);
+ WRITE_BYTE(Base + (unsigned long)addr, data);
+ DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base);
+}
+void mem_outw (ADAPTER *a, void *addr, word data)
+{
+ volatile byte __iomem * Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io);
+ WRITE_WORD((Base + (unsigned long)addr), data);
+ DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base);
+}
+void mem_out_dw (ADAPTER *a, void *addr, const dword* data, int dwords)
+{
+ volatile byte __iomem * Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io);
+ while (dwords--) {
+ WRITE_DWORD((Base + (unsigned long)addr), *data);
+ addr+=4;
+ data++;
+ }
+ DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base);
+}
+void mem_out_buffer (ADAPTER *a, void *addr, void *buffer, word length)
+{
+ volatile byte __iomem * Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io);
+ memcpy_toio((Base + (unsigned long)addr), buffer, length) ;
+ DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base);
+}
+void mem_inc (ADAPTER *a, void *addr)
+{
+ volatile byte __iomem *Base = DIVA_OS_MEM_ATTACH_RAM((PISDN_ADAPTER)a->io);
+ byte x = READ_BYTE(Base + (unsigned long)addr);
+ WRITE_BYTE(Base + (unsigned long)addr, x + 1);
+ DIVA_OS_MEM_DETACH_RAM((PISDN_ADAPTER)a->io, Base);
+}
+/*------------------------------------------------------------------*/
+/* ram access functions for io-mapped cards */
+/*------------------------------------------------------------------*/
+byte io_in(ADAPTER * a, void * adr)
+{
+ byte val;
+ byte __iomem *Port = DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io);
+ outppw(Port + 4, (word)(unsigned long)adr);
+ val = inpp(Port);
+ DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port);
+ return(val);
+}
+word io_inw(ADAPTER * a, void * adr)
+{
+ word val;
+ byte __iomem *Port = DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io);
+ outppw(Port + 4, (word)(unsigned long)adr);
+ val = inppw(Port);
+ DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port);
+ return(val);
+}
+void io_in_buffer(ADAPTER * a, void * adr, void * buffer, word len)
+{
+ byte __iomem *Port = DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io);
+ byte* P = (byte*)buffer;
+ if ((long)adr & 1) {
+ outppw(Port+4, (word)(unsigned long)adr);
+ *P = inpp(Port);
+ P++;
+ adr = ((byte *) adr) + 1;
+ len--;
+ if (!len) {
+ DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port);
+ return;
+ }
+ }
+ outppw(Port+4, (word)(unsigned long)adr);
+ inppw_buffer (Port, P, len+1);
+ DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port);
+}
+void io_look_ahead(ADAPTER * a, PBUFFER * RBuffer, ENTITY * e)
+{
+ byte __iomem *Port = DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io);
+ outppw(Port+4, (word)(unsigned long)RBuffer);
+ ((PISDN_ADAPTER)a->io)->RBuffer.length = inppw(Port);
+ inppw_buffer (Port, ((PISDN_ADAPTER)a->io)->RBuffer.P, ((PISDN_ADAPTER)a->io)->RBuffer.length + 1);
+ e->RBuffer = (DBUFFER *) &(((PISDN_ADAPTER)a->io)->RBuffer);
+ DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port);
+}
+void io_out(ADAPTER * a, void * adr, byte data)
+{
+ byte __iomem *Port = DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io);
+ outppw(Port+4, (word)(unsigned long)adr);
+ outpp(Port, data);
+ DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port);
+}
+void io_outw(ADAPTER * a, void * adr, word data)
+{
+ byte __iomem *Port = DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io);
+ outppw(Port+4, (word)(unsigned long)adr);
+ outppw(Port, data);
+ DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port);
+}
+void io_out_buffer(ADAPTER * a, void * adr, void * buffer, word len)
+{
+ byte __iomem *Port = DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io);
+ byte* P = (byte*)buffer;
+ if ((long)adr & 1) {
+ outppw(Port+4, (word)(unsigned long)adr);
+ outpp(Port, *P);
+ P++;
+ adr = ((byte *) adr) + 1;
+ len--;
+ if (!len) {
+ DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port);
+ return;
+ }
+ }
+ outppw(Port+4, (word)(unsigned long)adr);
+ outppw_buffer (Port, P, len+1);
+ DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port);
+}
+void io_inc(ADAPTER * a, void * adr)
+{
+ byte x;
+ byte __iomem *Port = DIVA_OS_MEM_ATTACH_PORT((PISDN_ADAPTER)a->io);
+ outppw(Port+4, (word)(unsigned long)adr);
+ x = inpp(Port);
+ outppw(Port+4, (word)(unsigned long)adr);
+ outpp(Port, x+1);
+ DIVA_OS_MEM_DETACH_PORT((PISDN_ADAPTER)a->io, Port);
+}
+/*------------------------------------------------------------------*/
+/* OS specific functions related to queuing of entities */
+/*------------------------------------------------------------------*/
+void free_entity(ADAPTER * a, byte e_no)
+{
+ PISDN_ADAPTER IoAdapter;
+ diva_os_spin_lock_magic_t irql;
+ IoAdapter = (PISDN_ADAPTER) a->io;
+ diva_os_enter_spin_lock (&IoAdapter->data_spin_lock, &irql, "data_free");
+ IoAdapter->e_tbl[e_no].e = NULL;
+ IoAdapter->e_count--;
+ diva_os_leave_spin_lock (&IoAdapter->data_spin_lock, &irql, "data_free");
+}
+void assign_queue(ADAPTER * a, byte e_no, word ref)
+{
+ PISDN_ADAPTER IoAdapter;
+ diva_os_spin_lock_magic_t irql;
+ IoAdapter = (PISDN_ADAPTER) a->io;
+ diva_os_enter_spin_lock (&IoAdapter->data_spin_lock, &irql, "data_assign");
+ IoAdapter->e_tbl[e_no].assign_ref = ref;
+ IoAdapter->e_tbl[e_no].next = (byte)IoAdapter->assign;
+ IoAdapter->assign = e_no;
+ diva_os_leave_spin_lock (&IoAdapter->data_spin_lock, &irql, "data_assign");
+}
+byte get_assign(ADAPTER * a, word ref)
+{
+ PISDN_ADAPTER IoAdapter;
+ diva_os_spin_lock_magic_t irql;
+ byte e_no;
+ IoAdapter = (PISDN_ADAPTER) a->io;
+ diva_os_enter_spin_lock (&IoAdapter->data_spin_lock,
+ &irql,
+ "data_assign_get");
+ for(e_no = (byte)IoAdapter->assign;
+ e_no && IoAdapter->e_tbl[e_no].assign_ref!=ref;
+ e_no = IoAdapter->e_tbl[e_no].next);
+ diva_os_leave_spin_lock (&IoAdapter->data_spin_lock,
+ &irql,
+ "data_assign_get");
+ return e_no;
+}
+void req_queue(ADAPTER * a, byte e_no)
+{
+ PISDN_ADAPTER IoAdapter;
+ diva_os_spin_lock_magic_t irql;
+ IoAdapter = (PISDN_ADAPTER) a->io;
+ diva_os_enter_spin_lock (&IoAdapter->data_spin_lock, &irql, "data_req_q");
+ IoAdapter->e_tbl[e_no].next = 0;
+ if(IoAdapter->head) {
+ IoAdapter->e_tbl[IoAdapter->tail].next = e_no;
+ IoAdapter->tail = e_no;
+ }
+ else {
+ IoAdapter->head = e_no;
+ IoAdapter->tail = e_no;
+ }
+ diva_os_leave_spin_lock (&IoAdapter->data_spin_lock, &irql, "data_req_q");
+}
+byte look_req(ADAPTER * a)
+{
+ PISDN_ADAPTER IoAdapter;
+ IoAdapter = (PISDN_ADAPTER) a->io;
+ return ((byte)IoAdapter->head) ;
+}
+void next_req(ADAPTER * a)
+{
+ PISDN_ADAPTER IoAdapter;
+ diva_os_spin_lock_magic_t irql;
+ IoAdapter = (PISDN_ADAPTER) a->io;
+ diva_os_enter_spin_lock (&IoAdapter->data_spin_lock, &irql, "data_req_next");
+ IoAdapter->head = IoAdapter->e_tbl[IoAdapter->head].next;
+ if(!IoAdapter->head) IoAdapter->tail = 0;
+ diva_os_leave_spin_lock (&IoAdapter->data_spin_lock, &irql, "data_req_next");
+}
+/*------------------------------------------------------------------*/
+/* memory map functions */
+/*------------------------------------------------------------------*/
+ENTITY * entity_ptr(ADAPTER * a, byte e_no)
+{
+ PISDN_ADAPTER IoAdapter;
+ IoAdapter = (PISDN_ADAPTER) a->io;
+ return (IoAdapter->e_tbl[e_no].e);
+}
+void * PTR_X(ADAPTER * a, ENTITY * e)
+{
+ return ((void *) e->X);
+}
+void * PTR_R(ADAPTER * a, ENTITY * e)
+{
+ return ((void *) e->R);
+}
+void * PTR_P(ADAPTER * a, ENTITY * e, void * P)
+{
+ return P;
+}
+void CALLBACK(ADAPTER * a, ENTITY * e)
+{
+ if ( e && e->callback )
+ e->callback (e) ;
+}
diff --git a/drivers/isdn/hardware/eicon/io.h b/drivers/isdn/hardware/eicon/io.h
new file mode 100644
index 0000000..0c6c650
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/io.h
@@ -0,0 +1,308 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+#ifndef __DIVA_XDI_COMMON_IO_H_INC__ /* { */
+#define __DIVA_XDI_COMMON_IO_H_INC__
+/*
+ maximum = 16 adapters
+ */
+#define DI_MAX_LINKS MAX_ADAPTER
+#define ISDN_MAX_NUM_LEN 60
+/* --------------------------------------------------------------------------
+ structure for quadro card management (obsolete for
+ systems that do provide per card load event)
+ -------------------------------------------------------------------------- */
+typedef struct {
+ dword Num ;
+ DEVICE_NAME DeviceName[4] ;
+ PISDN_ADAPTER QuadroAdapter[4] ;
+} ADAPTER_LIST_ENTRY, *PADAPTER_LIST_ENTRY ;
+/* --------------------------------------------------------------------------
+ Special OS memory support structures
+ -------------------------------------------------------------------------- */
+#define MAX_MAPPED_ENTRIES 8
+typedef struct {
+ void * Address;
+ dword Length;
+} ADAPTER_MEMORY ;
+/* --------------------------------------------------------------------------
+ Configuration of XDI clients carried by XDI
+ -------------------------------------------------------------------------- */
+#define DIVA_XDI_CAPI_CFG_1_DYNAMIC_L1_ON 0x01
+#define DIVA_XDI_CAPI_CFG_1_GROUP_POPTIMIZATION_ON 0x02
+typedef struct _diva_xdi_capi_cfg {
+ byte cfg_1;
+} diva_xdi_capi_cfg_t;
+/* --------------------------------------------------------------------------
+ Main data structure kept per adapter
+ -------------------------------------------------------------------------- */
+struct _ISDN_ADAPTER {
+ void (* DIRequest)(PISDN_ADAPTER, ENTITY *) ;
+ int State ; /* from NT4 1.srv, a good idea, but a poor achievment */
+ int Initialized ;
+ int RegisteredWithDidd ;
+ int Unavailable ; /* callback function possible? */
+ int ResourcesClaimed ;
+ int PnpBiosConfigUsed ;
+ dword Logging ;
+ dword features ;
+ char ProtocolIdString[80] ;
+ /*
+ remember mapped memory areas
+ */
+ ADAPTER_MEMORY MappedMemory[MAX_MAPPED_ENTRIES] ;
+ CARD_PROPERTIES Properties ;
+ dword cardType ;
+ dword protocol_id ; /* configured protocol identifier */
+ char protocol_name[8] ; /* readable name of protocol */
+ dword BusType ;
+ dword BusNumber ;
+ dword slotNumber ;
+ dword slotId ;
+ dword ControllerNumber ; /* for QUADRO cards only */
+ PISDN_ADAPTER MultiMaster ; /* for 4-BRI card only - use MultiMaster or QuadroList */
+ PADAPTER_LIST_ENTRY QuadroList ; /* for QUADRO card only */
+ PDEVICE_OBJECT DeviceObject ;
+ dword DeviceId ;
+ diva_os_adapter_irq_info_t irq_info;
+ dword volatile IrqCount ;
+ int trapped ;
+ dword DspCodeBaseAddr ;
+ dword MaxDspCodeSize ;
+ dword downloadAddr ;
+ dword DspCodeBaseAddrTable[4] ; /* add. for MultiMaster */
+ dword MaxDspCodeSizeTable[4] ; /* add. for MultiMaster */
+ dword downloadAddrTable[4] ; /* add. for MultiMaster */
+ dword MemoryBase ;
+ dword MemorySize ;
+ byte __iomem *Address ;
+ byte __iomem *Config ;
+ byte __iomem *Control ;
+ byte __iomem *reset ;
+ byte __iomem *port ;
+ byte __iomem *ram ;
+ byte __iomem *cfg ;
+ byte __iomem *prom ;
+ byte __iomem *ctlReg ;
+ struct pc_maint *pcm ;
+ diva_os_dependent_devica_name_t os_name;
+ byte Name[32] ;
+ dword serialNo ;
+ dword ANum ;
+ dword ArchiveType ; /* ARCHIVE_TYPE_NONE ..._SINGLE ..._USGEN ..._MULTI */
+ char *ProtocolSuffix ; /* internal protocolfile table */
+ char Archive[32] ;
+ char Protocol[32] ;
+ char AddDownload[32] ; /* Dsp- or other additional download files */
+ char Oad1[ISDN_MAX_NUM_LEN] ;
+ char Osa1[ISDN_MAX_NUM_LEN] ;
+ char Oad2[ISDN_MAX_NUM_LEN] ;
+ char Osa2[ISDN_MAX_NUM_LEN] ;
+ char Spid1[ISDN_MAX_NUM_LEN] ;
+ char Spid2[ISDN_MAX_NUM_LEN] ;
+ byte nosig ;
+ byte BriLayer2LinkCount ; /* amount of TEI's that adapter will support in P2MP mode */
+ dword Channels ;
+ dword tei ;
+ dword nt2 ;
+ dword TerminalCount ;
+ dword WatchDog ;
+ dword Permanent ;
+ dword BChMask ; /* B channel mask for unchannelized modes */
+ dword StableL2 ;
+ dword DidLen ;
+ dword NoOrderCheck ;
+ dword ForceLaw; /* VoiceCoding - default:0, a-law: 1, my-law: 2 */
+ dword SigFlags ;
+ dword LowChannel ;
+ dword NoHscx30 ;
+ dword ProtVersion ;
+ dword crc4 ;
+ dword L1TristateOrQsig ; /* enable Layer 1 Tristate (bit 2)Or Qsig params (bit 0,1)*/
+ dword InitialDspInfo ;
+ dword ModemGuardTone ;
+ dword ModemMinSpeed ;
+ dword ModemMaxSpeed ;
+ dword ModemOptions ;
+ dword ModemOptions2 ;
+ dword ModemNegotiationMode ;
+ dword ModemModulationsMask ;
+ dword ModemTransmitLevel ;
+ dword FaxOptions ;
+ dword FaxMaxSpeed ;
+ dword Part68LevelLimiter ;
+ dword UsEktsNumCallApp ;
+ byte UsEktsFeatAddConf ;
+ byte UsEktsFeatRemoveConf ;
+ byte UsEktsFeatCallTransfer ;
+ byte UsEktsFeatMsgWaiting ;
+ byte QsigDialect;
+ byte ForceVoiceMailAlert;
+ byte DisableAutoSpid;
+ byte ModemCarrierWaitTimeSec;
+ byte ModemCarrierLossWaitTimeTenthSec;
+ byte PiafsLinkTurnaroundInFrames;
+ byte DiscAfterProgress;
+ byte AniDniLimiter[3];
+ byte TxAttenuation; /* PRI/E1 only: attenuate TX signal */
+ word QsigFeatures;
+ dword GenerateRingtone ;
+ dword SupplementaryServicesFeatures;
+ dword R2Dialect;
+ dword R2CasOptions;
+ dword FaxV34Options;
+ dword DisabledDspMask;
+ dword AdapterTestMask;
+ dword DspImageLength;
+ word AlertToIn20mSecTicks;
+ word ModemEyeSetup;
+ byte R2CtryLength;
+ byte CCBSRelTimer;
+ byte *PcCfgBufferFile;/* flexible parameter via file */
+ byte *PcCfgBuffer ; /* flexible parameter via multistring */
+ diva_os_dump_file_t dump_file; /* dump memory to file at lowest irq level */
+ diva_os_board_trace_t board_trace ; /* traces from the board */
+ diva_os_spin_lock_t isr_spin_lock;
+ diva_os_spin_lock_t data_spin_lock;
+ diva_os_soft_isr_t req_soft_isr;
+ diva_os_soft_isr_t isr_soft_isr;
+ diva_os_atomic_t in_dpc;
+ PBUFFER RBuffer; /* Copy of receive lookahead buffer */
+ word e_max;
+ word e_count;
+ E_INFO *e_tbl;
+ word assign; /* list of pending ASSIGNs */
+ word head; /* head of request queue */
+ word tail; /* tail of request queue */
+ ADAPTER a ; /* not a separate structure */
+ void (* out)(ADAPTER * a) ;
+ byte (* dpc)(ADAPTER * a) ;
+ byte (* tst_irq)(ADAPTER * a) ;
+ void (* clr_irq)(ADAPTER * a) ;
+ int (* load)(PISDN_ADAPTER) ;
+ int (* mapmem)(PISDN_ADAPTER) ;
+ int (* chkIrq)(PISDN_ADAPTER) ;
+ void (* disIrq)(PISDN_ADAPTER) ;
+ void (* start)(PISDN_ADAPTER) ;
+ void (* stop)(PISDN_ADAPTER) ;
+ void (* rstFnc)(PISDN_ADAPTER) ;
+ void (* trapFnc)(PISDN_ADAPTER) ;
+ dword (* DetectDsps)(PISDN_ADAPTER) ;
+ void (* os_trap_nfy_Fnc)(PISDN_ADAPTER, dword) ;
+ diva_os_isr_callback_t diva_isr_handler;
+ dword sdram_bar; /* must be 32 bit */
+ dword fpga_features;
+ volatile int pcm_pending;
+ volatile void * pcm_data;
+ diva_xdi_capi_cfg_t capi_cfg;
+ dword tasks;
+ void *dma_map;
+ int (*DivaAdapterTestProc)(PISDN_ADAPTER);
+ void *AdapterTestMemoryStart;
+ dword AdapterTestMemoryLength;
+ const byte* cfg_lib_memory_init;
+ dword cfg_lib_memory_init_length;
+};
+/* ---------------------------------------------------------------------
+ Entity table
+ --------------------------------------------------------------------- */
+struct e_info_s {
+ ENTITY * e;
+ byte next; /* chaining index */
+ word assign_ref; /* assign reference */
+};
+/* ---------------------------------------------------------------------
+ S-cards shared ram structure for loading
+ --------------------------------------------------------------------- */
+struct s_load {
+ byte ctrl;
+ byte card;
+ byte msize;
+ byte fill0;
+ word ebit;
+ word elocl;
+ word eloch;
+ byte reserved[20];
+ word signature;
+ byte fill[224];
+ byte b[256];
+};
+#define PR_RAM ((struct pr_ram *)0)
+#define RAM ((struct dual *)0)
+/* ---------------------------------------------------------------------
+ platform specific conversions
+ --------------------------------------------------------------------- */
+extern void * PTR_P(ADAPTER * a, ENTITY * e, void * P);
+extern void * PTR_X(ADAPTER * a, ENTITY * e);
+extern void * PTR_R(ADAPTER * a, ENTITY * e);
+extern void CALLBACK(ADAPTER * a, ENTITY * e);
+extern void set_ram(void * * adr_ptr);
+/* ---------------------------------------------------------------------
+ ram access functions for io mapped cards
+ --------------------------------------------------------------------- */
+byte io_in(ADAPTER * a, void * adr);
+word io_inw(ADAPTER * a, void * adr);
+void io_in_buffer(ADAPTER * a, void * adr, void * P, word length);
+void io_look_ahead(ADAPTER * a, PBUFFER * RBuffer, ENTITY * e);
+void io_out(ADAPTER * a, void * adr, byte data);
+void io_outw(ADAPTER * a, void * adr, word data);
+void io_out_buffer(ADAPTER * a, void * adr, void * P, word length);
+void io_inc(ADAPTER * a, void * adr);
+void bri_in_buffer (PISDN_ADAPTER IoAdapter, dword Pos,
+ void *Buf, dword Len);
+int bri_out_buffer (PISDN_ADAPTER IoAdapter, dword Pos,
+ void *Buf, dword Len, int Verify);
+/* ---------------------------------------------------------------------
+ ram access functions for memory mapped cards
+ --------------------------------------------------------------------- */
+byte mem_in(ADAPTER * a, void * adr);
+word mem_inw(ADAPTER * a, void * adr);
+void mem_in_buffer(ADAPTER * a, void * adr, void * P, word length);
+void mem_look_ahead(ADAPTER * a, PBUFFER * RBuffer, ENTITY * e);
+void mem_out(ADAPTER * a, void * adr, byte data);
+void mem_outw(ADAPTER * a, void * adr, word data);
+void mem_out_buffer(ADAPTER * a, void * adr, void * P, word length);
+void mem_inc(ADAPTER * a, void * adr);
+void mem_in_dw (ADAPTER *a, void *addr, dword* data, int dwords);
+void mem_out_dw (ADAPTER *a, void *addr, const dword* data, int dwords);
+/* ---------------------------------------------------------------------
+ functions exported by io.c
+ --------------------------------------------------------------------- */
+extern IDI_CALL Requests[MAX_ADAPTER] ;
+extern void DIDpcRoutine (struct _diva_os_soft_isr* psoft_isr,
+ void* context);
+extern void request (PISDN_ADAPTER, ENTITY *) ;
+/* ---------------------------------------------------------------------
+ trapFn helpers, used to recover debug trace from dead card
+ --------------------------------------------------------------------- */
+typedef struct {
+ word *buf ;
+ word cnt ;
+ word out ;
+} Xdesc ;
+extern void dump_trap_frame (PISDN_ADAPTER IoAdapter, byte __iomem *exception) ;
+extern void dump_xlog_buffer (PISDN_ADAPTER IoAdapter, Xdesc *xlogDesc) ;
+/* --------------------------------------------------------------------- */
+#endif /* } __DIVA_XDI_COMMON_IO_H_INC__ */
diff --git a/drivers/isdn/hardware/eicon/istream.c b/drivers/isdn/hardware/eicon/istream.c
new file mode 100644
index 0000000..18f8798
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/istream.c
@@ -0,0 +1,226 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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 "platform.h"
+#if defined(DIVA_ISTREAM) /* { */
+#include "pc.h"
+#include "pr_pc.h"
+#include "di_defs.h"
+#include "divasync.h"
+#include "di.h"
+#if !defined USE_EXTENDED_DEBUGS
+ #include "dimaint.h"
+#else
+ #define dprintf
+#endif
+#include "dfifo.h"
+int diva_istream_write (void* context,
+ int Id,
+ void* data,
+ int length,
+ int final,
+ byte usr1,
+ byte usr2);
+int diva_istream_read (void* context,
+ int Id,
+ void* data,
+ int max_length,
+ int* final,
+ byte* usr1,
+ byte* usr2);
+/* -------------------------------------------------------------------
+ Does provide iStream interface to the client
+ ------------------------------------------------------------------- */
+void diva_xdi_provide_istream_info (ADAPTER* a,
+ diva_xdi_stream_interface_t* pi) {
+ pi->provided_service = 0;
+}
+/* ------------------------------------------------------------------
+ Does write the data from caller's buffer to the card's
+ stream interface.
+ If synchronous service was requested, then function
+ does return amount of data written to stream.
+ 'final' does indicate that pice of data to be written is
+ final part of frame (necessary only by structured datatransfer)
+ return 0 if zero lengh packet was written
+ return -1 if stream is full
+ ------------------------------------------------------------------ */
+int diva_istream_write (void* context,
+ int Id,
+ void* data,
+ int length,
+ int final,
+ byte usr1,
+ byte usr2) {
+ ADAPTER* a = (ADAPTER*)context;
+ int written = 0, to_write = -1;
+ char tmp[4];
+ byte* data_ptr = (byte*)data;
+ for (;;) {
+ a->ram_in_dw (a,
+#ifdef PLATFORM_GT_32BIT
+ ULongToPtr(a->tx_stream[Id] + a->tx_pos[Id]),
+#else
+ (void*)(a->tx_stream[Id] + a->tx_pos[Id]),
+#endif
+ (dword*)&tmp[0],
+ 1);
+ if (tmp[0] & DIVA_DFIFO_READY) { /* No free blocks more */
+ if (to_write < 0)
+ return (-1); /* was not able to write */
+ break; /* only part of message was written */
+ }
+ to_write = min(length, DIVA_DFIFO_DATA_SZ);
+ if (to_write) {
+ a->ram_out_buffer (a,
+#ifdef PLATFORM_GT_32BIT
+ ULongToPtr(a->tx_stream[Id] + a->tx_pos[Id]+4),
+#else
+ (void*)(a->tx_stream[Id] + a->tx_pos[Id] + 4),
+#endif
+ data_ptr,
+ (word)to_write);
+ length -= to_write;
+ written += to_write;
+ data_ptr += to_write;
+ }
+ tmp[1] = (char)to_write;
+ tmp[0] = (tmp[0] & DIVA_DFIFO_WRAP) |
+ DIVA_DFIFO_READY |
+ ((!length && final) ? DIVA_DFIFO_LAST : 0);
+ if (tmp[0] & DIVA_DFIFO_LAST) {
+ tmp[2] = usr1;
+ tmp[3] = usr2;
+ }
+ a->ram_out_dw (a,
+#ifdef PLATFORM_GT_32BIT
+ ULongToPtr(a->tx_stream[Id] + a->tx_pos[Id]),
+#else
+ (void*)(a->tx_stream[Id] + a->tx_pos[Id]),
+#endif
+ (dword*)&tmp[0],
+ 1);
+ if (tmp[0] & DIVA_DFIFO_WRAP) {
+ a->tx_pos[Id] = 0;
+ } else {
+ a->tx_pos[Id] += DIVA_DFIFO_STEP;
+ }
+ if (!length) {
+ break;
+ }
+ }
+ return (written);
+}
+/* -------------------------------------------------------------------
+ In case of SYNCRONOUS service:
+ Does write data from stream in caller's buffer.
+ Does return amount of data written to buffer
+ Final flag is set on return if last part of structured frame
+ was received
+ return 0 if zero packet was received
+ return -1 if stream is empty
+ return -2 if read buffer does not profide sufficient space
+ to accommodate entire segment
+ max_length should be at least 68 bytes
+ ------------------------------------------------------------------- */
+int diva_istream_read (void* context,
+ int Id,
+ void* data,
+ int max_length,
+ int* final,
+ byte* usr1,
+ byte* usr2) {
+ ADAPTER* a = (ADAPTER*)context;
+ int read = 0, to_read = -1;
+ char tmp[4];
+ byte* data_ptr = (byte*)data;
+ *final = 0;
+ for (;;) {
+ a->ram_in_dw (a,
+#ifdef PLATFORM_GT_32BIT
+ ULongToPtr(a->rx_stream[Id] + a->rx_pos[Id]),
+#else
+ (void*)(a->rx_stream[Id] + a->rx_pos[Id]),
+#endif
+ (dword*)&tmp[0],
+ 1);
+ if (tmp[1] > max_length) {
+ if (to_read < 0)
+ return (-2); /* was not able to read */
+ break;
+ }
+ if (!(tmp[0] & DIVA_DFIFO_READY)) {
+ if (to_read < 0)
+ return (-1); /* was not able to read */
+ break;
+ }
+ to_read = min(max_length, (int)tmp[1]);
+ if (to_read) {
+ a->ram_in_buffer(a,
+#ifdef PLATFORM_GT_32BIT
+ ULongToPtr(a->rx_stream[Id] + a->rx_pos[Id] + 4),
+#else
+ (void*)(a->rx_stream[Id] + a->rx_pos[Id] + 4),
+#endif
+ data_ptr,
+ (word)to_read);
+ max_length -= to_read;
+ read += to_read;
+ data_ptr += to_read;
+ }
+ if (tmp[0] & DIVA_DFIFO_LAST) {
+ *final = 1;
+ }
+ tmp[0] &= DIVA_DFIFO_WRAP;
+ a->ram_out_dw(a,
+#ifdef PLATFORM_GT_32BIT
+ ULongToPtr(a->rx_stream[Id] + a->rx_pos[Id]),
+#else
+ (void*)(a->rx_stream[Id] + a->rx_pos[Id]),
+#endif
+ (dword*)&tmp[0],
+ 1);
+ if (tmp[0] & DIVA_DFIFO_WRAP) {
+ a->rx_pos[Id] = 0;
+ } else {
+ a->rx_pos[Id] += DIVA_DFIFO_STEP;
+ }
+ if (*final) {
+ if (usr1)
+ *usr1 = tmp[2];
+ if (usr2)
+ *usr2 = tmp[3];
+ break;
+ }
+ }
+ return (read);
+}
+/* ---------------------------------------------------------------------
+ Does check if one of streams had caused interrupt and does
+ wake up corresponding application
+ --------------------------------------------------------------------- */
+void pr_stream (ADAPTER * a) {
+}
+#endif /* } */
diff --git a/drivers/isdn/hardware/eicon/kst_ifc.h b/drivers/isdn/hardware/eicon/kst_ifc.h
new file mode 100644
index 0000000..203189a
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/kst_ifc.h
@@ -0,0 +1,336 @@
+/*
+ *
+ Copyright (c) Eicon Networks, 2000.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 1.9
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+#ifndef __DIVA_EICON_TRACE_API__
+#define __DIVA_EICON_TRACE_API__
+
+#define DIVA_TRACE_LINE_TYPE_LEN 64
+#define DIVA_TRACE_IE_LEN 64
+#define DIVA_TRACE_FAX_PRMS_LEN 128
+
+typedef struct _diva_trace_ie {
+ byte length;
+ byte data[DIVA_TRACE_IE_LEN];
+} diva_trace_ie_t;
+
+/*
+ Structure used to represent "State\\BX\\Modem" directory
+ to user.
+ */
+typedef struct _diva_trace_modem_state {
+ dword ChannelNumber;
+
+ dword Event;
+
+ dword Norm;
+
+ dword Options; /* Options received from Application */
+
+ dword TxSpeed;
+ dword RxSpeed;
+
+ dword RoundtripMsec;
+
+ dword SymbolRate;
+
+ int RxLeveldBm;
+ int EchoLeveldBm;
+
+ dword SNRdb;
+ dword MAE;
+
+ dword LocalRetrains;
+ dword RemoteRetrains;
+ dword LocalResyncs;
+ dword RemoteResyncs;
+
+ dword DiscReason;
+
+} diva_trace_modem_state_t;
+
+/*
+ Representation of "State\\BX\\FAX" directory
+ */
+typedef struct _diva_trace_fax_state {
+ dword ChannelNumber;
+ dword Event;
+ dword Page_Counter;
+ dword Features;
+ char Station_ID[DIVA_TRACE_FAX_PRMS_LEN];
+ char Subaddress[DIVA_TRACE_FAX_PRMS_LEN];
+ char Password[DIVA_TRACE_FAX_PRMS_LEN];
+ dword Speed;
+ dword Resolution;
+ dword Paper_Width;
+ dword Paper_Length;
+ dword Scanline_Time;
+ dword Disc_Reason;
+ dword dummy;
+} diva_trace_fax_state_t;
+
+/*
+ Structure used to represent Interface State in the abstract
+ and interface/D-channel protocol independent form.
+ */
+typedef struct _diva_trace_interface_state {
+ char Layer1[DIVA_TRACE_LINE_TYPE_LEN];
+ char Layer2[DIVA_TRACE_LINE_TYPE_LEN];
+} diva_trace_interface_state_t;
+
+typedef struct _diva_incoming_call_statistics {
+ dword Calls;
+ dword Connected;
+ dword User_Busy;
+ dword Call_Rejected;
+ dword Wrong_Number;
+ dword Incompatible_Dst;
+ dword Out_of_Order;
+ dword Ignored;
+} diva_incoming_call_statistics_t;
+
+typedef struct _diva_outgoing_call_statistics {
+ dword Calls;
+ dword Connected;
+ dword User_Busy;
+ dword No_Answer;
+ dword Wrong_Number;
+ dword Call_Rejected;
+ dword Other_Failures;
+} diva_outgoing_call_statistics_t;
+
+typedef struct _diva_modem_call_statistics {
+ dword Disc_Normal;
+ dword Disc_Unspecified;
+ dword Disc_Busy_Tone;
+ dword Disc_Congestion;
+ dword Disc_Carr_Wait;
+ dword Disc_Trn_Timeout;
+ dword Disc_Incompat;
+ dword Disc_Frame_Rej;
+ dword Disc_V42bis;
+} diva_modem_call_statistics_t;
+
+typedef struct _diva_fax_call_statistics {
+ dword Disc_Normal;
+ dword Disc_Not_Ident;
+ dword Disc_No_Response;
+ dword Disc_Retries;
+ dword Disc_Unexp_Msg;
+ dword Disc_No_Polling;
+ dword Disc_Training;
+ dword Disc_Unexpected;
+ dword Disc_Application;
+ dword Disc_Incompat;
+ dword Disc_No_Command;
+ dword Disc_Long_Msg;
+ dword Disc_Supervisor;
+ dword Disc_SUB_SEP_PWD;
+ dword Disc_Invalid_Msg;
+ dword Disc_Page_Coding;
+ dword Disc_App_Timeout;
+ dword Disc_Unspecified;
+} diva_fax_call_statistics_t;
+
+typedef struct _diva_prot_statistics {
+ dword X_Frames;
+ dword X_Bytes;
+ dword X_Errors;
+ dword R_Frames;
+ dword R_Bytes;
+ dword R_Errors;
+} diva_prot_statistics_t;
+
+typedef struct _diva_ifc_statistics {
+ diva_incoming_call_statistics_t inc;
+ diva_outgoing_call_statistics_t outg;
+ diva_modem_call_statistics_t mdm;
+ diva_fax_call_statistics_t fax;
+ diva_prot_statistics_t b1;
+ diva_prot_statistics_t b2;
+ diva_prot_statistics_t d1;
+ diva_prot_statistics_t d2;
+} diva_ifc_statistics_t;
+
+/*
+ Structure used to represent "State\\BX" directory
+ to user.
+ */
+typedef struct _diva_trace_line_state {
+ dword ChannelNumber;
+
+ char Line[DIVA_TRACE_LINE_TYPE_LEN];
+
+ char Framing[DIVA_TRACE_LINE_TYPE_LEN];
+
+ char Layer2[DIVA_TRACE_LINE_TYPE_LEN];
+ char Layer3[DIVA_TRACE_LINE_TYPE_LEN];
+
+ char RemoteAddress[DIVA_TRACE_LINE_TYPE_LEN];
+ char RemoteSubAddress[DIVA_TRACE_LINE_TYPE_LEN];
+
+ char LocalAddress[DIVA_TRACE_LINE_TYPE_LEN];
+ char LocalSubAddress[DIVA_TRACE_LINE_TYPE_LEN];
+
+ diva_trace_ie_t call_BC;
+ diva_trace_ie_t call_HLC;
+ diva_trace_ie_t call_LLC;
+
+ dword Charges;
+
+ dword CallReference;
+
+ dword LastDisconnecCause;
+
+ char UserID[DIVA_TRACE_LINE_TYPE_LEN];
+
+ diva_trace_modem_state_t modem;
+ diva_trace_fax_state_t fax;
+
+ diva_trace_interface_state_t* pInterface;
+
+ diva_ifc_statistics_t* pInterfaceStat;
+
+} diva_trace_line_state_t;
+
+#define DIVA_SUPER_TRACE_NOTIFY_LINE_CHANGE ('l')
+#define DIVA_SUPER_TRACE_NOTIFY_MODEM_CHANGE ('m')
+#define DIVA_SUPER_TRACE_NOTIFY_FAX_CHANGE ('f')
+#define DIVA_SUPER_TRACE_INTERFACE_CHANGE ('i')
+#define DIVA_SUPER_TRACE_NOTIFY_STAT_CHANGE ('s')
+#define DIVA_SUPER_TRACE_NOTIFY_MDM_STAT_CHANGE ('M')
+#define DIVA_SUPER_TRACE_NOTIFY_FAX_STAT_CHANGE ('F')
+
+struct _diva_strace_library_interface;
+typedef void (*diva_trace_channel_state_change_proc_t)(void* user_context,
+ struct _diva_strace_library_interface* hLib,
+ int Adapter,
+ diva_trace_line_state_t* channel, int notify_subject);
+typedef void (*diva_trace_channel_trace_proc_t)(void* user_context,
+ struct _diva_strace_library_interface* hLib,
+ int Adapter, void* xlog_buffer, int length);
+typedef void (*diva_trace_error_proc_t)(void* user_context,
+ struct _diva_strace_library_interface* hLib,
+ int Adapter,
+ int error, const char* file, int line);
+
+/*
+ This structure creates interface from user to library
+ */
+typedef struct _diva_trace_library_user_interface {
+ void* user_context;
+ diva_trace_channel_state_change_proc_t notify_proc;
+ diva_trace_channel_trace_proc_t trace_proc;
+ diva_trace_error_proc_t error_notify_proc;
+} diva_trace_library_user_interface_t;
+
+/*
+ Interface from Library to User
+ */
+typedef int (*DivaSTraceLibraryStart_proc_t)(void* hLib);
+typedef int (*DivaSTraceLibraryFinit_proc_t)(void* hLib);
+typedef int (*DivaSTraceMessageInput_proc_t)(void* hLib);
+typedef void* (*DivaSTraceGetHandle_proc_t)(void* hLib);
+
+/*
+ Turn Audio Tap trace on/off
+ Channel should be in the range 1 ... Number of Channels
+ */
+typedef int (*DivaSTraceSetAudioTap_proc_t)(void* hLib, int Channel, int on);
+
+/*
+ Turn B-channel trace on/off
+ Channel should be in the range 1 ... Number of Channels
+ */
+typedef int (*DivaSTraceSetBChannel_proc_t)(void* hLib, int Channel, int on);
+
+/*
+ Turn D-channel (Layer1/Layer2/Layer3) trace on/off
+ Layer1 - All D-channel frames received/sent over the interface
+ inclusive Layer 2 headers, Layer 2 frames and TEI management frames
+ Layer2 - Events from LAPD protocol instance with SAPI of signalling protocol
+ Layer3 - All D-channel frames addressed to assigned to the card TEI and
+ SAPI of signalling protocol, and signalling protocol events.
+ */
+typedef int (*DivaSTraceSetDChannel_proc_t)(void* hLib, int on);
+
+/*
+ Get overall card statistics
+ */
+typedef int (*DivaSTraceGetOutgoingCallStatistics_proc_t)(void* hLib);
+typedef int (*DivaSTraceGetIncomingCallStatistics_proc_t)(void* hLib);
+typedef int (*DivaSTraceGetModemStatistics_proc_t)(void* hLib);
+typedef int (*DivaSTraceGetFaxStatistics_proc_t)(void* hLib);
+typedef int (*DivaSTraceGetBLayer1Statistics_proc_t)(void* hLib);
+typedef int (*DivaSTraceGetBLayer2Statistics_proc_t)(void* hLib);
+typedef int (*DivaSTraceGetDLayer1Statistics_proc_t)(void* hLib);
+typedef int (*DivaSTraceGetDLayer2Statistics_proc_t)(void* hLib);
+
+/*
+ Call control
+ */
+typedef int (*DivaSTraceClearCall_proc_t)(void* hLib, int Channel);
+
+typedef struct _diva_strace_library_interface {
+ void* hLib;
+ DivaSTraceLibraryStart_proc_t DivaSTraceLibraryStart;
+ DivaSTraceLibraryStart_proc_t DivaSTraceLibraryStop;
+ DivaSTraceLibraryFinit_proc_t DivaSTraceLibraryFinit;
+ DivaSTraceMessageInput_proc_t DivaSTraceMessageInput;
+ DivaSTraceGetHandle_proc_t DivaSTraceGetHandle;
+ DivaSTraceSetAudioTap_proc_t DivaSTraceSetAudioTap;
+ DivaSTraceSetBChannel_proc_t DivaSTraceSetBChannel;
+ DivaSTraceSetDChannel_proc_t DivaSTraceSetDChannel;
+ DivaSTraceSetDChannel_proc_t DivaSTraceSetInfo;
+ DivaSTraceGetOutgoingCallStatistics_proc_t \
+ DivaSTraceGetOutgoingCallStatistics;
+ DivaSTraceGetIncomingCallStatistics_proc_t \
+ DivaSTraceGetIncomingCallStatistics;
+ DivaSTraceGetModemStatistics_proc_t \
+ DivaSTraceGetModemStatistics;
+ DivaSTraceGetFaxStatistics_proc_t \
+ DivaSTraceGetFaxStatistics;
+ DivaSTraceGetBLayer1Statistics_proc_t \
+ DivaSTraceGetBLayer1Statistics;
+ DivaSTraceGetBLayer2Statistics_proc_t \
+ DivaSTraceGetBLayer2Statistics;
+ DivaSTraceGetDLayer1Statistics_proc_t \
+ DivaSTraceGetDLayer1Statistics;
+ DivaSTraceGetDLayer2Statistics_proc_t \
+ DivaSTraceGetDLayer2Statistics;
+ DivaSTraceClearCall_proc_t DivaSTraceClearCall;
+} diva_strace_library_interface_t;
+
+/*
+ Create and return Library interface
+ */
+diva_strace_library_interface_t* DivaSTraceLibraryCreateInstance (int Adapter,
+ const diva_trace_library_user_interface_t* user_proc,
+ byte* pmem);
+dword DivaSTraceGetMemotyRequirement (int channels);
+
+#define DIVA_MAX_ADAPTERS 64
+#define DIVA_MAX_LINES 32
+
+#endif
+
diff --git a/drivers/isdn/hardware/eicon/maintidi.c b/drivers/isdn/hardware/eicon/maintidi.c
new file mode 100644
index 0000000..23960cb
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/maintidi.c
@@ -0,0 +1,2194 @@
+/*
+ *
+ Copyright (c) Eicon Networks, 2000.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 1.9
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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 "platform.h"
+#include "kst_ifc.h"
+#include "di_defs.h"
+#include "maintidi.h"
+#include "pc.h"
+#include "man_defs.h"
+
+
+extern void diva_mnt_internal_dprintf (dword drv_id, dword type, char* p, ...);
+
+#define MODEM_PARSE_ENTRIES 16 /* amount of variables of interest */
+#define FAX_PARSE_ENTRIES 12 /* amount of variables of interest */
+#define LINE_PARSE_ENTRIES 15 /* amount of variables of interest */
+#define STAT_PARSE_ENTRIES 70 /* amount of variables of interest */
+
+/*
+ LOCAL FUNCTIONS
+ */
+static int DivaSTraceLibraryStart (void* hLib);
+static int DivaSTraceLibraryStop (void* hLib);
+static int SuperTraceLibraryFinit (void* hLib);
+static void* SuperTraceGetHandle (void* hLib);
+static int SuperTraceMessageInput (void* hLib);
+static int SuperTraceSetAudioTap (void* hLib, int Channel, int on);
+static int SuperTraceSetBChannel (void* hLib, int Channel, int on);
+static int SuperTraceSetDChannel (void* hLib, int on);
+static int SuperTraceSetInfo (void* hLib, int on);
+static int SuperTraceClearCall (void* hLib, int Channel);
+static int SuperTraceGetOutgoingCallStatistics (void* hLib);
+static int SuperTraceGetIncomingCallStatistics (void* hLib);
+static int SuperTraceGetModemStatistics (void* hLib);
+static int SuperTraceGetFaxStatistics (void* hLib);
+static int SuperTraceGetBLayer1Statistics (void* hLib);
+static int SuperTraceGetBLayer2Statistics (void* hLib);
+static int SuperTraceGetDLayer1Statistics (void* hLib);
+static int SuperTraceGetDLayer2Statistics (void* hLib);
+
+/*
+ LOCAL FUNCTIONS
+ */
+static int ScheduleNextTraceRequest (diva_strace_context_t* pLib);
+static int process_idi_event (diva_strace_context_t* pLib,
+ diva_man_var_header_t* pVar);
+static int process_idi_info (diva_strace_context_t* pLib,
+ diva_man_var_header_t* pVar);
+static int diva_modem_event (diva_strace_context_t* pLib, int Channel);
+static int diva_fax_event (diva_strace_context_t* pLib, int Channel);
+static int diva_line_event (diva_strace_context_t* pLib, int Channel);
+static int diva_modem_info (diva_strace_context_t* pLib,
+ int Channel,
+ diva_man_var_header_t* pVar);
+static int diva_fax_info (diva_strace_context_t* pLib,
+ int Channel,
+ diva_man_var_header_t* pVar);
+static int diva_line_info (diva_strace_context_t* pLib,
+ int Channel,
+ diva_man_var_header_t* pVar);
+static int diva_ifc_statistics (diva_strace_context_t* pLib,
+ diva_man_var_header_t* pVar);
+static diva_man_var_header_t* get_next_var (diva_man_var_header_t* pVar);
+static diva_man_var_header_t* find_var (diva_man_var_header_t* pVar,
+ const char* name);
+static int diva_strace_read_int (diva_man_var_header_t* pVar, int* var);
+static int diva_strace_read_uint (diva_man_var_header_t* pVar, dword* var);
+static int diva_strace_read_asz (diva_man_var_header_t* pVar, char* var);
+static int diva_strace_read_asc (diva_man_var_header_t* pVar, char* var);
+static int diva_strace_read_ie (diva_man_var_header_t* pVar,
+ diva_trace_ie_t* var);
+static void diva_create_parse_table (diva_strace_context_t* pLib);
+static void diva_trace_error (diva_strace_context_t* pLib,
+ int error, const char* file, int line);
+static void diva_trace_notify_user (diva_strace_context_t* pLib,
+ int Channel,
+ int notify_subject);
+static int diva_trace_read_variable (diva_man_var_header_t* pVar,
+ void* variable);
+
+/*
+ Initialize the library and return context
+ of the created trace object that will represent
+ the IDI adapter.
+ Return 0 on error.
+ */
+diva_strace_library_interface_t* DivaSTraceLibraryCreateInstance (int Adapter,
+ const diva_trace_library_user_interface_t* user_proc,
+ byte* pmem) {
+ diva_strace_context_t* pLib = (diva_strace_context_t*)pmem;
+ int i;
+
+ if (!pLib) {
+ return NULL;
+ }
+
+ pmem += sizeof(*pLib);
+ memset(pLib, 0x00, sizeof(*pLib));
+
+ pLib->Adapter = Adapter;
+
+ /*
+ Set up Library Interface
+ */
+ pLib->instance.hLib = pLib;
+ pLib->instance.DivaSTraceLibraryStart = DivaSTraceLibraryStart;
+ pLib->instance.DivaSTraceLibraryStop = DivaSTraceLibraryStop;
+ pLib->instance.DivaSTraceLibraryFinit = SuperTraceLibraryFinit;
+ pLib->instance.DivaSTraceMessageInput = SuperTraceMessageInput;
+ pLib->instance.DivaSTraceGetHandle = SuperTraceGetHandle;
+ pLib->instance.DivaSTraceSetAudioTap = SuperTraceSetAudioTap;
+ pLib->instance.DivaSTraceSetBChannel = SuperTraceSetBChannel;
+ pLib->instance.DivaSTraceSetDChannel = SuperTraceSetDChannel;
+ pLib->instance.DivaSTraceSetInfo = SuperTraceSetInfo;
+ pLib->instance.DivaSTraceGetOutgoingCallStatistics = \
+ SuperTraceGetOutgoingCallStatistics;
+ pLib->instance.DivaSTraceGetIncomingCallStatistics = \
+ SuperTraceGetIncomingCallStatistics;
+ pLib->instance.DivaSTraceGetModemStatistics = \
+ SuperTraceGetModemStatistics;
+ pLib->instance.DivaSTraceGetFaxStatistics = \
+ SuperTraceGetFaxStatistics;
+ pLib->instance.DivaSTraceGetBLayer1Statistics = \
+ SuperTraceGetBLayer1Statistics;
+ pLib->instance.DivaSTraceGetBLayer2Statistics = \
+ SuperTraceGetBLayer2Statistics;
+ pLib->instance.DivaSTraceGetDLayer1Statistics = \
+ SuperTraceGetDLayer1Statistics;
+ pLib->instance.DivaSTraceGetDLayer2Statistics = \
+ SuperTraceGetDLayer2Statistics;
+ pLib->instance.DivaSTraceClearCall = SuperTraceClearCall;
+
+
+ if (user_proc) {
+ pLib->user_proc_table.user_context = user_proc->user_context;
+ pLib->user_proc_table.notify_proc = user_proc->notify_proc;
+ pLib->user_proc_table.trace_proc = user_proc->trace_proc;
+ pLib->user_proc_table.error_notify_proc = user_proc->error_notify_proc;
+ }
+
+ if (!(pLib->hAdapter = SuperTraceOpenAdapter (Adapter))) {
+ diva_mnt_internal_dprintf (0, DLI_ERR, "Can not open XDI adapter");
+ return NULL;
+ }
+ pLib->Channels = SuperTraceGetNumberOfChannels (pLib->hAdapter);
+
+ /*
+ Calculate amount of parte table entites necessary to translate
+ information from all events of onterest
+ */
+ pLib->parse_entries = (MODEM_PARSE_ENTRIES + FAX_PARSE_ENTRIES + \
+ STAT_PARSE_ENTRIES + \
+ LINE_PARSE_ENTRIES + 1) * pLib->Channels;
+ pLib->parse_table = (diva_strace_path2action_t*)pmem;
+
+ for (i = 0; i < 30; i++) {
+ pLib->lines[i].pInterface = &pLib->Interface;
+ pLib->lines[i].pInterfaceStat = &pLib->InterfaceStat;
+ }
+
+ pLib->e.R = &pLib->RData;
+
+ pLib->req_busy = 1;
+ pLib->rc_ok = ASSIGN_OK;
+
+ diva_create_parse_table (pLib);
+
+ return ((diva_strace_library_interface_t*)pLib);
+}
+
+static int DivaSTraceLibraryStart (void* hLib) {
+ diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+
+ return (SuperTraceASSIGN (pLib->hAdapter, pLib->buffer));
+}
+
+/*
+ Return (-1) on error
+ Return (0) if was initiated or pending
+ Return (1) if removal is complete
+ */
+static int DivaSTraceLibraryStop (void* hLib) {
+ diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+
+ if (!pLib->e.Id) { /* Was never started/assigned */
+ return (1);
+ }
+
+ switch (pLib->removal_state) {
+ case 0:
+ pLib->removal_state = 1;
+ ScheduleNextTraceRequest(pLib);
+ break;
+
+ case 3:
+ return (1);
+ }
+
+ return (0);
+}
+
+static int SuperTraceLibraryFinit (void* hLib) {
+ diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+ if (pLib) {
+ if (pLib->hAdapter) {
+ SuperTraceCloseAdapter (pLib->hAdapter);
+ }
+ return (0);
+ }
+ return (-1);
+}
+
+static void* SuperTraceGetHandle (void* hLib) {
+ diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+
+ return (&pLib->e);
+}
+
+/*
+ After library handle object is gone in signaled state
+ this function should be called and will pick up incoming
+ IDI messages (return codes and indications).
+ */
+static int SuperTraceMessageInput (void* hLib) {
+ diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+ int ret = 0;
+ byte Rc, Ind;
+
+ if (pLib->e.complete == 255) {
+ /*
+ Process return code
+ */
+ pLib->req_busy = 0;
+ Rc = pLib->e.Rc;
+ pLib->e.Rc = 0;
+
+ if (pLib->removal_state == 2) {
+ pLib->removal_state = 3;
+ return (0);
+ }
+
+ if (Rc != pLib->rc_ok) {
+ int ignore = 0;
+ /*
+ Auto-detect amount of events/channels and features
+ */
+ if (pLib->general_b_ch_event == 1) {
+ pLib->general_b_ch_event = 2;
+ ignore = 1;
+ } else if (pLib->general_fax_event == 1) {
+ pLib->general_fax_event = 2;
+ ignore = 1;
+ } else if (pLib->general_mdm_event == 1) {
+ pLib->general_mdm_event = 2;
+ ignore = 1;
+ } else if ((pLib->ChannelsTraceActive < pLib->Channels) && pLib->ChannelsTraceActive) {
+ pLib->ChannelsTraceActive = pLib->Channels;
+ ignore = 1;
+ } else if (pLib->ModemTraceActive < pLib->Channels) {
+ pLib->ModemTraceActive = pLib->Channels;
+ ignore = 1;
+ } else if (pLib->FaxTraceActive < pLib->Channels) {
+ pLib->FaxTraceActive = pLib->Channels;
+ ignore = 1;
+ } else if (pLib->audio_trace_init == 2) {
+ ignore = 1;
+ pLib->audio_trace_init = 1;
+ } else if (pLib->eye_pattern_pending) {
+ pLib->eye_pattern_pending = 0;
+ ignore = 1;
+ } else if (pLib->audio_tap_pending) {
+ pLib->audio_tap_pending = 0;
+ ignore = 1;
+ }
+
+ if (!ignore) {
+ return (-1); /* request failed */
+ }
+ } else {
+ if (pLib->general_b_ch_event == 1) {
+ pLib->ChannelsTraceActive = pLib->Channels;
+ pLib->general_b_ch_event = 2;
+ } else if (pLib->general_fax_event == 1) {
+ pLib->general_fax_event = 2;
+ pLib->FaxTraceActive = pLib->Channels;
+ } else if (pLib->general_mdm_event == 1) {
+ pLib->general_mdm_event = 2;
+ pLib->ModemTraceActive = pLib->Channels;
+ }
+ }
+ if (pLib->audio_trace_init == 2) {
+ pLib->audio_trace_init = 1;
+ }
+ pLib->rc_ok = 0xff; /* default OK after assign was done */
+ if ((ret = ScheduleNextTraceRequest(pLib))) {
+ return (-1);
+ }
+ } else {
+ /*
+ Process indication
+ Always 'RNR' indication if return code is pending
+ */
+ Ind = pLib->e.Ind;
+ pLib->e.Ind = 0;
+ if (pLib->removal_state) {
+ pLib->e.RNum = 0;
+ pLib->e.RNR = 2;
+ } else if (pLib->req_busy) {
+ pLib->e.RNum = 0;
+ pLib->e.RNR = 1;
+ } else {
+ if (pLib->e.complete != 0x02) {
+ /*
+ Look-ahead call, set up buffers
+ */
+ pLib->e.RNum = 1;
+ pLib->e.R->P = (byte*)&pLib->buffer[0];
+ pLib->e.R->PLength = (word)(sizeof(pLib->buffer) - 1);
+
+ } else {
+ /*
+ Indication reception complete, process it now
+ */
+ byte* p = (byte*)&pLib->buffer[0];
+ pLib->buffer[pLib->e.R->PLength] = 0; /* terminate I.E. with zero */
+
+ switch (Ind) {
+ case MAN_COMBI_IND: {
+ int total_length = pLib->e.R->PLength;
+ word this_ind_length;
+
+ while (total_length > 3 && *p) {
+ Ind = *p++;
+ this_ind_length = (word)p[0] | ((word)p[1] << 8);
+ p += 2;
+
+ switch (Ind) {
+ case MAN_INFO_IND:
+ if (process_idi_info (pLib, (diva_man_var_header_t*)p)) {
+ return (-1);
+ }
+ break;
+ case MAN_EVENT_IND:
+ if (process_idi_event (pLib, (diva_man_var_header_t*)p)) {
+ return (-1);
+ }
+ break;
+ case MAN_TRACE_IND:
+ if (pLib->trace_on == 1) {
+ /*
+ Ignore first trace event that is result of
+ EVENT_ON operation
+ */
+ pLib->trace_on++;
+ } else {
+ /*
+ Delivery XLOG buffer to application
+ */
+ if (pLib->user_proc_table.trace_proc) {
+ (*(pLib->user_proc_table.trace_proc))(pLib->user_proc_table.user_context,
+ &pLib->instance, pLib->Adapter,
+ p, this_ind_length);
+ }
+ }
+ break;
+ default:
+ diva_mnt_internal_dprintf (0, DLI_ERR, "Unknon IDI Ind (DMA mode): %02x", Ind);
+ }
+ p += (this_ind_length+1);
+ total_length -= (4 + this_ind_length);
+ }
+ } break;
+ case MAN_INFO_IND:
+ if (process_idi_info (pLib, (diva_man_var_header_t*)p)) {
+ return (-1);
+ }
+ break;
+ case MAN_EVENT_IND:
+ if (process_idi_event (pLib, (diva_man_var_header_t*)p)) {
+ return (-1);
+ }
+ break;
+ case MAN_TRACE_IND:
+ if (pLib->trace_on == 1) {
+ /*
+ Ignore first trace event that is result of
+ EVENT_ON operation
+ */
+ pLib->trace_on++;
+ } else {
+ /*
+ Delivery XLOG buffer to application
+ */
+ if (pLib->user_proc_table.trace_proc) {
+ (*(pLib->user_proc_table.trace_proc))(pLib->user_proc_table.user_context,
+ &pLib->instance, pLib->Adapter,
+ p, pLib->e.R->PLength);
+ }
+ }
+ break;
+ default:
+ diva_mnt_internal_dprintf (0, DLI_ERR, "Unknon IDI Ind: %02x", Ind);
+ }
+ }
+ }
+ }
+
+ if ((ret = ScheduleNextTraceRequest(pLib))) {
+ return (-1);
+ }
+
+ return (ret);
+}
+
+/*
+ Internal state machine responsible for scheduling of requests
+ */
+static int ScheduleNextTraceRequest (diva_strace_context_t* pLib) {
+ char name[64];
+ int ret = 0;
+ int i;
+
+ if (pLib->req_busy) {
+ return (0);
+ }
+
+ if (pLib->removal_state == 1) {
+ if (SuperTraceREMOVE (pLib->hAdapter)) {
+ pLib->removal_state = 3;
+ } else {
+ pLib->req_busy = 1;
+ pLib->removal_state = 2;
+ }
+ return (0);
+ }
+
+ if (pLib->removal_state) {
+ return (0);
+ }
+
+ if (!pLib->general_b_ch_event) {
+ if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, "State\\B Event", pLib->buffer))) {
+ return (-1);
+ }
+ pLib->general_b_ch_event = 1;
+ pLib->req_busy = 1;
+ return (0);
+ }
+
+ if (!pLib->general_fax_event) {
+ if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, "State\\FAX Event", pLib->buffer))) {
+ return (-1);
+ }
+ pLib->general_fax_event = 1;
+ pLib->req_busy = 1;
+ return (0);
+ }
+
+ if (!pLib->general_mdm_event) {
+ if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, "State\\Modem Event", pLib->buffer))) {
+ return (-1);
+ }
+ pLib->general_mdm_event = 1;
+ pLib->req_busy = 1;
+ return (0);
+ }
+
+ if (pLib->ChannelsTraceActive < pLib->Channels) {
+ pLib->ChannelsTraceActive++;
+ sprintf (name, "State\\B%d\\Line", pLib->ChannelsTraceActive);
+ if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, name, pLib->buffer))) {
+ pLib->ChannelsTraceActive--;
+ return (-1);
+ }
+ pLib->req_busy = 1;
+ return (0);
+ }
+
+ if (pLib->ModemTraceActive < pLib->Channels) {
+ pLib->ModemTraceActive++;
+ sprintf (name, "State\\B%d\\Modem\\Event", pLib->ModemTraceActive);
+ if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, name, pLib->buffer))) {
+ pLib->ModemTraceActive--;
+ return (-1);
+ }
+ pLib->req_busy = 1;
+ return (0);
+ }
+
+ if (pLib->FaxTraceActive < pLib->Channels) {
+ pLib->FaxTraceActive++;
+ sprintf (name, "State\\B%d\\FAX\\Event", pLib->FaxTraceActive);
+ if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, name, pLib->buffer))) {
+ pLib->FaxTraceActive--;
+ return (-1);
+ }
+ pLib->req_busy = 1;
+ return (0);
+ }
+
+ if (!pLib->trace_mask_init) {
+ word tmp = 0x0000;
+ if (SuperTraceWriteVar (pLib->hAdapter,
+ pLib->buffer,
+ "Trace\\Event Enable",
+ &tmp,
+ 0x87, /* MI_BITFLD */
+ sizeof(tmp))) {
+ return (-1);
+ }
+ pLib->trace_mask_init = 1;
+ pLib->req_busy = 1;
+ return (0);
+ }
+
+ if (!pLib->audio_trace_init) {
+ dword tmp = 0x00000000;
+ if (SuperTraceWriteVar (pLib->hAdapter,
+ pLib->buffer,
+ "Trace\\AudioCh# Enable",
+ &tmp,
+ 0x87, /* MI_BITFLD */
+ sizeof(tmp))) {
+ return (-1);
+ }
+ pLib->audio_trace_init = 2;
+ pLib->req_busy = 1;
+ return (0);
+ }
+
+ if (!pLib->bchannel_init) {
+ dword tmp = 0x00000000;
+ if (SuperTraceWriteVar (pLib->hAdapter,
+ pLib->buffer,
+ "Trace\\B-Ch# Enable",
+ &tmp,
+ 0x87, /* MI_BITFLD */
+ sizeof(tmp))) {
+ return (-1);
+ }
+ pLib->bchannel_init = 1;
+ pLib->req_busy = 1;
+ return (0);
+ }
+
+ if (!pLib->trace_length_init) {
+ word tmp = 30;
+ if (SuperTraceWriteVar (pLib->hAdapter,
+ pLib->buffer,
+ "Trace\\Max Log Length",
+ &tmp,
+ 0x82, /* MI_UINT */
+ sizeof(tmp))) {
+ return (-1);
+ }
+ pLib->trace_length_init = 1;
+ pLib->req_busy = 1;
+ return (0);
+ }
+
+ if (!pLib->trace_on) {
+ if (SuperTraceTraceOnRequest (pLib->hAdapter,
+ "Trace\\Log Buffer",
+ pLib->buffer)) {
+ return (-1);
+ }
+ pLib->trace_on = 1;
+ pLib->req_busy = 1;
+ return (0);
+ }
+
+ if (pLib->trace_event_mask != pLib->current_trace_event_mask) {
+ if (SuperTraceWriteVar (pLib->hAdapter,
+ pLib->buffer,
+ "Trace\\Event Enable",
+ &pLib->trace_event_mask,
+ 0x87, /* MI_BITFLD */
+ sizeof(pLib->trace_event_mask))) {
+ return (-1);
+ }
+ pLib->current_trace_event_mask = pLib->trace_event_mask;
+ pLib->req_busy = 1;
+ return (0);
+ }
+
+ if ((pLib->audio_tap_pending >= 0) && (pLib->audio_tap_mask != pLib->current_audio_tap_mask)) {
+ if (SuperTraceWriteVar (pLib->hAdapter,
+ pLib->buffer,
+ "Trace\\AudioCh# Enable",
+ &pLib->audio_tap_mask,
+ 0x87, /* MI_BITFLD */
+ sizeof(pLib->audio_tap_mask))) {
+ return (-1);
+ }
+ pLib->current_audio_tap_mask = pLib->audio_tap_mask;
+ pLib->audio_tap_pending = 1;
+ pLib->req_busy = 1;
+ return (0);
+ }
+
+ if ((pLib->eye_pattern_pending >= 0) && (pLib->audio_tap_mask != pLib->current_eye_pattern_mask)) {
+ if (SuperTraceWriteVar (pLib->hAdapter,
+ pLib->buffer,
+ "Trace\\EyeCh# Enable",
+ &pLib->audio_tap_mask,
+ 0x87, /* MI_BITFLD */
+ sizeof(pLib->audio_tap_mask))) {
+ return (-1);
+ }
+ pLib->current_eye_pattern_mask = pLib->audio_tap_mask;
+ pLib->eye_pattern_pending = 1;
+ pLib->req_busy = 1;
+ return (0);
+ }
+
+ if (pLib->bchannel_trace_mask != pLib->current_bchannel_trace_mask) {
+ if (SuperTraceWriteVar (pLib->hAdapter,
+ pLib->buffer,
+ "Trace\\B-Ch# Enable",
+ &pLib->bchannel_trace_mask,
+ 0x87, /* MI_BITFLD */
+ sizeof(pLib->bchannel_trace_mask))) {
+ return (-1);
+ }
+ pLib->current_bchannel_trace_mask = pLib->bchannel_trace_mask;
+ pLib->req_busy = 1;
+ return (0);
+ }
+
+ if (!pLib->trace_events_down) {
+ if (SuperTraceTraceOnRequest (pLib->hAdapter,
+ "Events Down",
+ pLib->buffer)) {
+ return (-1);
+ }
+ pLib->trace_events_down = 1;
+ pLib->req_busy = 1;
+ return (0);
+ }
+
+ if (!pLib->l1_trace) {
+ if (SuperTraceTraceOnRequest (pLib->hAdapter,
+ "State\\Layer1",
+ pLib->buffer)) {
+ return (-1);
+ }
+ pLib->l1_trace = 1;
+ pLib->req_busy = 1;
+ return (0);
+ }
+
+ if (!pLib->l2_trace) {
+ if (SuperTraceTraceOnRequest (pLib->hAdapter,
+ "State\\Layer2 No1",
+ pLib->buffer)) {
+ return (-1);
+ }
+ pLib->l2_trace = 1;
+ pLib->req_busy = 1;
+ return (0);
+ }
+
+ for (i = 0; i < 30; i++) {
+ if (pLib->pending_line_status & (1L << i)) {
+ sprintf (name, "State\\B%d", i+1);
+ if (SuperTraceReadRequest (pLib->hAdapter, name, pLib->buffer)) {
+ return (-1);
+ }
+ pLib->pending_line_status &= ~(1L << i);
+ pLib->req_busy = 1;
+ return (0);
+ }
+ if (pLib->pending_modem_status & (1L << i)) {
+ sprintf (name, "State\\B%d\\Modem", i+1);
+ if (SuperTraceReadRequest (pLib->hAdapter, name, pLib->buffer)) {
+ return (-1);
+ }
+ pLib->pending_modem_status &= ~(1L << i);
+ pLib->req_busy = 1;
+ return (0);
+ }
+ if (pLib->pending_fax_status & (1L << i)) {
+ sprintf (name, "State\\B%d\\FAX", i+1);
+ if (SuperTraceReadRequest (pLib->hAdapter, name, pLib->buffer)) {
+ return (-1);
+ }
+ pLib->pending_fax_status &= ~(1L << i);
+ pLib->req_busy = 1;
+ return (0);
+ }
+ if (pLib->clear_call_command & (1L << i)) {
+ sprintf (name, "State\\B%d\\Clear Call", i+1);
+ if (SuperTraceExecuteRequest (pLib->hAdapter, name, pLib->buffer)) {
+ return (-1);
+ }
+ pLib->clear_call_command &= ~(1L << i);
+ pLib->req_busy = 1;
+ return (0);
+ }
+ }
+
+ if (pLib->outgoing_ifc_stats) {
+ if (SuperTraceReadRequest (pLib->hAdapter,
+ "Statistics\\Outgoing Calls",
+ pLib->buffer)) {
+ return (-1);
+ }
+ pLib->outgoing_ifc_stats = 0;
+ pLib->req_busy = 1;
+ return (0);
+ }
+
+ if (pLib->incoming_ifc_stats) {
+ if (SuperTraceReadRequest (pLib->hAdapter,
+ "Statistics\\Incoming Calls",
+ pLib->buffer)) {
+ return (-1);
+ }
+ pLib->incoming_ifc_stats = 0;
+ pLib->req_busy = 1;
+ return (0);
+ }
+
+ if (pLib->modem_ifc_stats) {
+ if (SuperTraceReadRequest (pLib->hAdapter,
+ "Statistics\\Modem",
+ pLib->buffer)) {
+ return (-1);
+ }
+ pLib->modem_ifc_stats = 0;
+ pLib->req_busy = 1;
+ return (0);
+ }
+
+ if (pLib->fax_ifc_stats) {
+ if (SuperTraceReadRequest (pLib->hAdapter,
+ "Statistics\\FAX",
+ pLib->buffer)) {
+ return (-1);
+ }
+ pLib->fax_ifc_stats = 0;
+ pLib->req_busy = 1;
+ return (0);
+ }
+
+ if (pLib->b1_ifc_stats) {
+ if (SuperTraceReadRequest (pLib->hAdapter,
+ "Statistics\\B-Layer1",
+ pLib->buffer)) {
+ return (-1);
+ }
+ pLib->b1_ifc_stats = 0;
+ pLib->req_busy = 1;
+ return (0);
+ }
+
+ if (pLib->b2_ifc_stats) {
+ if (SuperTraceReadRequest (pLib->hAdapter,
+ "Statistics\\B-Layer2",
+ pLib->buffer)) {
+ return (-1);
+ }
+ pLib->b2_ifc_stats = 0;
+ pLib->req_busy = 1;
+ return (0);
+ }
+
+ if (pLib->d1_ifc_stats) {
+ if (SuperTraceReadRequest (pLib->hAdapter,
+ "Statistics\\D-Layer1",
+ pLib->buffer)) {
+ return (-1);
+ }
+ pLib->d1_ifc_stats = 0;
+ pLib->req_busy = 1;
+ return (0);
+ }
+
+ if (pLib->d2_ifc_stats) {
+ if (SuperTraceReadRequest (pLib->hAdapter,
+ "Statistics\\D-Layer2",
+ pLib->buffer)) {
+ return (-1);
+ }
+ pLib->d2_ifc_stats = 0;
+ pLib->req_busy = 1;
+ return (0);
+ }
+
+ if (!pLib->IncomingCallsCallsActive) {
+ pLib->IncomingCallsCallsActive = 1;
+ sprintf (name, "%s", "Statistics\\Incoming Calls\\Calls");
+ if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, name, pLib->buffer))) {
+ pLib->IncomingCallsCallsActive = 0;
+ return (-1);
+ }
+ pLib->req_busy = 1;
+ return (0);
+ }
+ if (!pLib->IncomingCallsConnectedActive) {
+ pLib->IncomingCallsConnectedActive = 1;
+ sprintf (name, "%s", "Statistics\\Incoming Calls\\Connected");
+ if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, name, pLib->buffer))) {
+ pLib->IncomingCallsConnectedActive = 0;
+ return (-1);
+ }
+ pLib->req_busy = 1;
+ return (0);
+ }
+ if (!pLib->OutgoingCallsCallsActive) {
+ pLib->OutgoingCallsCallsActive = 1;
+ sprintf (name, "%s", "Statistics\\Outgoing Calls\\Calls");
+ if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, name, pLib->buffer))) {
+ pLib->OutgoingCallsCallsActive = 0;
+ return (-1);
+ }
+ pLib->req_busy = 1;
+ return (0);
+ }
+ if (!pLib->OutgoingCallsConnectedActive) {
+ pLib->OutgoingCallsConnectedActive = 1;
+ sprintf (name, "%s", "Statistics\\Outgoing Calls\\Connected");
+ if ((ret = SuperTraceTraceOnRequest(pLib->hAdapter, name, pLib->buffer))) {
+ pLib->OutgoingCallsConnectedActive = 0;
+ return (-1);
+ }
+ pLib->req_busy = 1;
+ return (0);
+ }
+
+ return (0);
+}
+
+static int process_idi_event (diva_strace_context_t* pLib,
+ diva_man_var_header_t* pVar) {
+ const char* path = (char*)&pVar->path_length+1;
+ char name[64];
+ int i;
+
+ if (!strncmp("State\\B Event", path, pVar->path_length)) {
+ dword ch_id;
+ if (!diva_trace_read_variable (pVar, &ch_id)) {
+ if (!pLib->line_init_event && !pLib->pending_line_status) {
+ for (i = 1; i <= pLib->Channels; i++) {
+ diva_line_event(pLib, i);
+ }
+ return (0);
+ } else if (ch_id && ch_id <= pLib->Channels) {
+ return (diva_line_event(pLib, (int)ch_id));
+ }
+ return (0);
+ }
+ return (-1);
+ }
+
+ if (!strncmp("State\\FAX Event", path, pVar->path_length)) {
+ dword ch_id;
+ if (!diva_trace_read_variable (pVar, &ch_id)) {
+ if (!pLib->pending_fax_status && !pLib->fax_init_event) {
+ for (i = 1; i <= pLib->Channels; i++) {
+ diva_fax_event(pLib, i);
+ }
+ return (0);
+ } else if (ch_id && ch_id <= pLib->Channels) {
+ return (diva_fax_event(pLib, (int)ch_id));
+ }
+ return (0);
+ }
+ return (-1);
+ }
+
+ if (!strncmp("State\\Modem Event", path, pVar->path_length)) {
+ dword ch_id;
+ if (!diva_trace_read_variable (pVar, &ch_id)) {
+ if (!pLib->pending_modem_status && !pLib->modem_init_event) {
+ for (i = 1; i <= pLib->Channels; i++) {
+ diva_modem_event(pLib, i);
+ }
+ return (0);
+ } else if (ch_id && ch_id <= pLib->Channels) {
+ return (diva_modem_event(pLib, (int)ch_id));
+ }
+ return (0);
+ }
+ return (-1);
+ }
+
+ /*
+ First look for Line Event
+ */
+ for (i = 1; i <= pLib->Channels; i++) {
+ sprintf (name, "State\\B%d\\Line", i);
+ if (find_var (pVar, name)) {
+ return (diva_line_event(pLib, i));
+ }
+ }
+
+ /*
+ Look for Moden Progress Event
+ */
+ for (i = 1; i <= pLib->Channels; i++) {
+ sprintf (name, "State\\B%d\\Modem\\Event", i);
+ if (find_var (pVar, name)) {
+ return (diva_modem_event (pLib, i));
+ }
+ }
+
+ /*
+ Look for Fax Event
+ */
+ for (i = 1; i <= pLib->Channels; i++) {
+ sprintf (name, "State\\B%d\\FAX\\Event", i);
+ if (find_var (pVar, name)) {
+ return (diva_fax_event (pLib, i));
+ }
+ }
+
+ /*
+ Notification about loss of events
+ */
+ if (!strncmp("Events Down", path, pVar->path_length)) {
+ if (pLib->trace_events_down == 1) {
+ pLib->trace_events_down = 2;
+ } else {
+ diva_trace_error (pLib, 1, "Events Down", 0);
+ }
+ return (0);
+ }
+
+ if (!strncmp("State\\Layer1", path, pVar->path_length)) {
+ diva_strace_read_asz (pVar, &pLib->lines[0].pInterface->Layer1[0]);
+ if (pLib->l1_trace == 1) {
+ pLib->l1_trace = 2;
+ } else {
+ diva_trace_notify_user (pLib, 0, DIVA_SUPER_TRACE_INTERFACE_CHANGE);
+ }
+ return (0);
+ }
+ if (!strncmp("State\\Layer2 No1", path, pVar->path_length)) {
+ char* tmp = &pLib->lines[0].pInterface->Layer2[0];
+ dword l2_state;
+ diva_strace_read_uint (pVar, &l2_state);
+
+ switch (l2_state) {
+ case 0:
+ strcpy (tmp, "Idle");
+ break;
+ case 1:
+ strcpy (tmp, "Layer2 UP");
+ break;
+ case 2:
+ strcpy (tmp, "Layer2 Disconnecting");
+ break;
+ case 3:
+ strcpy (tmp, "Layer2 Connecting");
+ break;
+ case 4:
+ strcpy (tmp, "SPID Initializing");
+ break;
+ case 5:
+ strcpy (tmp, "SPID Initialised");
+ break;
+ case 6:
+ strcpy (tmp, "Layer2 Connecting");
+ break;
+
+ case 7:
+ strcpy (tmp, "Auto SPID Stopped");
+ break;
+
+ case 8:
+ strcpy (tmp, "Auto SPID Idle");
+ break;
+
+ case 9:
+ strcpy (tmp, "Auto SPID Requested");
+ break;
+
+ case 10:
+ strcpy (tmp, "Auto SPID Delivery");
+ break;
+
+ case 11:
+ strcpy (tmp, "Auto SPID Complete");
+ break;
+
+ default:
+ sprintf (tmp, "U:%d", (int)l2_state);
+ }
+ if (pLib->l2_trace == 1) {
+ pLib->l2_trace = 2;
+ } else {
+ diva_trace_notify_user (pLib, 0, DIVA_SUPER_TRACE_INTERFACE_CHANGE);
+ }
+ return (0);
+ }
+
+ if (!strncmp("Statistics\\Incoming Calls\\Calls", path, pVar->path_length) ||
+ !strncmp("Statistics\\Incoming Calls\\Connected", path, pVar->path_length)) {
+ return (SuperTraceGetIncomingCallStatistics (pLib));
+ }
+
+ if (!strncmp("Statistics\\Outgoing Calls\\Calls", path, pVar->path_length) ||
+ !strncmp("Statistics\\Outgoing Calls\\Connected", path, pVar->path_length)) {
+ return (SuperTraceGetOutgoingCallStatistics (pLib));
+ }
+
+ return (-1);
+}
+
+static int diva_line_event (diva_strace_context_t* pLib, int Channel) {
+ pLib->pending_line_status |= (1L << (Channel-1));
+ return (0);
+}
+
+static int diva_modem_event (diva_strace_context_t* pLib, int Channel) {
+ pLib->pending_modem_status |= (1L << (Channel-1));
+ return (0);
+}
+
+static int diva_fax_event (diva_strace_context_t* pLib, int Channel) {
+ pLib->pending_fax_status |= (1L << (Channel-1));
+ return (0);
+}
+
+/*
+ Process INFO indications that arrive from the card
+ Uses path of first I.E. to detect the source of the
+ infication
+ */
+static int process_idi_info (diva_strace_context_t* pLib,
+ diva_man_var_header_t* pVar) {
+ const char* path = (char*)&pVar->path_length+1;
+ char name[64];
+ int i, len;
+
+ /*
+ First look for Modem Status Info
+ */
+ for (i = pLib->Channels; i > 0; i--) {
+ len = sprintf (name, "State\\B%d\\Modem", i);
+ if (!strncmp(name, path, len)) {
+ return (diva_modem_info (pLib, i, pVar));
+ }
+ }
+
+ /*
+ Look for Fax Status Info
+ */
+ for (i = pLib->Channels; i > 0; i--) {
+ len = sprintf (name, "State\\B%d\\FAX", i);
+ if (!strncmp(name, path, len)) {
+ return (diva_fax_info (pLib, i, pVar));
+ }
+ }
+
+ /*
+ Look for Line Status Info
+ */
+ for (i = pLib->Channels; i > 0; i--) {
+ len = sprintf (name, "State\\B%d", i);
+ if (!strncmp(name, path, len)) {
+ return (diva_line_info (pLib, i, pVar));
+ }
+ }
+
+ if (!diva_ifc_statistics (pLib, pVar)) {
+ return (0);
+ }
+
+ return (-1);
+}
+
+/*
+ MODEM INSTANCE STATE UPDATE
+
+ Update Modem Status Information and issue notification to user,
+ that will inform about change in the state of modem instance, that is
+ associuated with this channel
+ */
+static int diva_modem_info (diva_strace_context_t* pLib,
+ int Channel,
+ diva_man_var_header_t* pVar) {
+ diva_man_var_header_t* cur;
+ int i, nr = Channel - 1;
+
+ for (i = pLib->modem_parse_entry_first[nr];
+ i <= pLib->modem_parse_entry_last[nr]; i++) {
+ if ((cur = find_var (pVar, pLib->parse_table[i].path))) {
+ if (diva_trace_read_variable (cur, pLib->parse_table[i].variable)) {
+ diva_trace_error (pLib, -3 , __FILE__, __LINE__);
+ return (-1);
+ }
+ } else {
+ diva_trace_error (pLib, -2 , __FILE__, __LINE__);
+ return (-1);
+ }
+ }
+
+ /*
+ We do not use first event to notify user - this is the event that is
+ generated as result of EVENT ON operation and is used only to initialize
+ internal variables of application
+ */
+ if (pLib->modem_init_event & (1L << nr)) {
+ diva_trace_notify_user (pLib, nr, DIVA_SUPER_TRACE_NOTIFY_MODEM_CHANGE);
+ } else {
+ pLib->modem_init_event |= (1L << nr);
+ }
+
+ return (0);
+}
+
+static int diva_fax_info (diva_strace_context_t* pLib,
+ int Channel,
+ diva_man_var_header_t* pVar) {
+ diva_man_var_header_t* cur;
+ int i, nr = Channel - 1;
+
+ for (i = pLib->fax_parse_entry_first[nr];
+ i <= pLib->fax_parse_entry_last[nr]; i++) {
+ if ((cur = find_var (pVar, pLib->parse_table[i].path))) {
+ if (diva_trace_read_variable (cur, pLib->parse_table[i].variable)) {
+ diva_trace_error (pLib, -3 , __FILE__, __LINE__);
+ return (-1);
+ }
+ } else {
+ diva_trace_error (pLib, -2 , __FILE__, __LINE__);
+ return (-1);
+ }
+ }
+
+ /*
+ We do not use first event to notify user - this is the event that is
+ generated as result of EVENT ON operation and is used only to initialize
+ internal variables of application
+ */
+ if (pLib->fax_init_event & (1L << nr)) {
+ diva_trace_notify_user (pLib, nr, DIVA_SUPER_TRACE_NOTIFY_FAX_CHANGE);
+ } else {
+ pLib->fax_init_event |= (1L << nr);
+ }
+
+ return (0);
+}
+
+/*
+ LINE STATE UPDATE
+ Update Line Status Information and issue notification to user,
+ that will inform about change in the line state.
+ */
+static int diva_line_info (diva_strace_context_t* pLib,
+ int Channel,
+ diva_man_var_header_t* pVar) {
+ diva_man_var_header_t* cur;
+ int i, nr = Channel - 1;
+
+ for (i = pLib->line_parse_entry_first[nr];
+ i <= pLib->line_parse_entry_last[nr]; i++) {
+ if ((cur = find_var (pVar, pLib->parse_table[i].path))) {
+ if (diva_trace_read_variable (cur, pLib->parse_table[i].variable)) {
+ diva_trace_error (pLib, -3 , __FILE__, __LINE__);
+ return (-1);
+ }
+ } else {
+ diva_trace_error (pLib, -2 , __FILE__, __LINE__);
+ return (-1);
+ }
+ }
+
+ /*
+ We do not use first event to notify user - this is the event that is
+ generated as result of EVENT ON operation and is used only to initialize
+ internal variables of application
+
+ Exception is is if the line is "online". In this case we have to notify
+ user about this confition.
+ */
+ if (pLib->line_init_event & (1L << nr)) {
+ diva_trace_notify_user (pLib, nr, DIVA_SUPER_TRACE_NOTIFY_LINE_CHANGE);
+ } else {
+ pLib->line_init_event |= (1L << nr);
+ if (strcmp (&pLib->lines[nr].Line[0], "Idle")) {
+ diva_trace_notify_user (pLib, nr, DIVA_SUPER_TRACE_NOTIFY_LINE_CHANGE);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ Move position to next vatianle in the chain
+ */
+static diva_man_var_header_t* get_next_var (diva_man_var_header_t* pVar) {
+ byte* msg = (byte*)pVar;
+ byte* start;
+ int msg_length;
+
+ if (*msg != ESC) return NULL;
+
+ start = msg + 2;
+ msg_length = *(msg+1);
+ msg = (start+msg_length);
+
+ if (*msg != ESC) return NULL;
+
+ return ((diva_man_var_header_t*)msg);
+}
+
+/*
+ Move position to variable with given name
+ */
+static diva_man_var_header_t* find_var (diva_man_var_header_t* pVar,
+ const char* name) {
+ const char* path;
+
+ do {
+ path = (char*)&pVar->path_length+1;
+
+ if (!strncmp (name, path, pVar->path_length)) {
+ break;
+ }
+ } while ((pVar = get_next_var (pVar)));
+
+ return (pVar);
+}
+
+static void diva_create_line_parse_table (diva_strace_context_t* pLib,
+ int Channel) {
+ diva_trace_line_state_t* pLine = &pLib->lines[Channel];
+ int nr = Channel+1;
+
+ if ((pLib->cur_parse_entry + LINE_PARSE_ENTRIES) >= pLib->parse_entries) {
+ diva_trace_error (pLib, -1, __FILE__, __LINE__);
+ return;
+ }
+
+ pLine->ChannelNumber = nr;
+
+ pLib->line_parse_entry_first[Channel] = pLib->cur_parse_entry;
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\Framing", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->Framing[0];
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\Line", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->Line[0];
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\Layer2", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->Layer2[0];
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\Layer3", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->Layer3[0];
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\Remote Address", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLine->RemoteAddress[0];
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\Remote SubAddr", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLine->RemoteSubAddress[0];
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\Local Address", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLine->LocalAddress[0];
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\Local SubAddr", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLine->LocalSubAddress[0];
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\BC", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->call_BC;
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\HLC", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->call_HLC;
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\LLC", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->call_LLC;
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\Charges", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->Charges;
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\Call Reference", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->CallReference;
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\Last Disc Cause", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLine->LastDisconnecCause;
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\User ID", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pLine->UserID[0];
+
+ pLib->line_parse_entry_last[Channel] = pLib->cur_parse_entry - 1;
+}
+
+static void diva_create_fax_parse_table (diva_strace_context_t* pLib,
+ int Channel) {
+ diva_trace_fax_state_t* pFax = &pLib->lines[Channel].fax;
+ int nr = Channel+1;
+
+ if ((pLib->cur_parse_entry + FAX_PARSE_ENTRIES) >= pLib->parse_entries) {
+ diva_trace_error (pLib, -1, __FILE__, __LINE__);
+ return;
+ }
+ pFax->ChannelNumber = nr;
+
+ pLib->fax_parse_entry_first[Channel] = pLib->cur_parse_entry;
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\FAX\\Event", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Event;
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\FAX\\Page Counter", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Page_Counter;
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\FAX\\Features", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Features;
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\FAX\\Station ID", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Station_ID[0];
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\FAX\\Subaddress", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Subaddress[0];
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\FAX\\Password", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Password[0];
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\FAX\\Speed", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Speed;
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\FAX\\Resolution", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Resolution;
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\FAX\\Paper Width", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Paper_Width;
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\FAX\\Paper Length", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Paper_Length;
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\FAX\\Scanline Time", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Scanline_Time;
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\FAX\\Disc Reason", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pFax->Disc_Reason;
+
+ pLib->fax_parse_entry_last[Channel] = pLib->cur_parse_entry - 1;
+}
+
+static void diva_create_modem_parse_table (diva_strace_context_t* pLib,
+ int Channel) {
+ diva_trace_modem_state_t* pModem = &pLib->lines[Channel].modem;
+ int nr = Channel+1;
+
+ if ((pLib->cur_parse_entry + MODEM_PARSE_ENTRIES) >= pLib->parse_entries) {
+ diva_trace_error (pLib, -1, __FILE__, __LINE__);
+ return;
+ }
+ pModem->ChannelNumber = nr;
+
+ pLib->modem_parse_entry_first[Channel] = pLib->cur_parse_entry;
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\Modem\\Event", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->Event;
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\Modem\\Norm", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->Norm;
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\Modem\\Options", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->Options;
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\Modem\\TX Speed", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->TxSpeed;
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\Modem\\RX Speed", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->RxSpeed;
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\Modem\\Roundtrip ms", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->RoundtripMsec;
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\Modem\\Symbol Rate", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->SymbolRate;
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\Modem\\RX Level dBm", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->RxLeveldBm;
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\Modem\\Echo Level dBm", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->EchoLeveldBm;
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\Modem\\SNR dB", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->SNRdb;
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\Modem\\MAE", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->MAE;
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\Modem\\Local Retrains", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->LocalRetrains;
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\Modem\\Remote Retrains", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->RemoteRetrains;
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\Modem\\Local Resyncs", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->LocalResyncs;
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\Modem\\Remote Resyncs", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->RemoteResyncs;
+
+ sprintf (pLib->parse_table[pLib->cur_parse_entry].path,
+ "State\\B%d\\Modem\\Disc Reason", nr);
+ pLib->parse_table[pLib->cur_parse_entry++].variable = &pModem->DiscReason;
+
+ pLib->modem_parse_entry_last[Channel] = pLib->cur_parse_entry - 1;
+}
+
+static void diva_create_parse_table (diva_strace_context_t* pLib) {
+ int i;
+
+ for (i = 0; i < pLib->Channels; i++) {
+ diva_create_line_parse_table (pLib, i);
+ diva_create_modem_parse_table (pLib, i);
+ diva_create_fax_parse_table (pLib, i);
+ }
+
+ pLib->statistic_parse_first = pLib->cur_parse_entry;
+
+ /*
+ Outgoing Calls
+ */
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\Outgoing Calls\\Calls");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.outg.Calls;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\Outgoing Calls\\Connected");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.outg.Connected;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\Outgoing Calls\\User Busy");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.outg.User_Busy;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\Outgoing Calls\\No Answer");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.outg.No_Answer;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\Outgoing Calls\\Wrong Number");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.outg.Wrong_Number;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\Outgoing Calls\\Call Rejected");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.outg.Call_Rejected;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\Outgoing Calls\\Other Failures");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.outg.Other_Failures;
+
+ /*
+ Incoming Calls
+ */
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\Incoming Calls\\Calls");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.inc.Calls;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\Incoming Calls\\Connected");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.inc.Connected;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\Incoming Calls\\User Busy");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.inc.User_Busy;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\Incoming Calls\\Call Rejected");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.inc.Call_Rejected;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\Incoming Calls\\Wrong Number");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.inc.Wrong_Number;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\Incoming Calls\\Incompatible Dst");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.inc.Incompatible_Dst;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\Incoming Calls\\Out of Order");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.inc.Out_of_Order;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\Incoming Calls\\Ignored");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.inc.Ignored;
+
+ /*
+ Modem Statistics
+ */
+ pLib->mdm_statistic_parse_first = pLib->cur_parse_entry;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\Modem\\Disc Normal");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.mdm.Disc_Normal;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\Modem\\Disc Unspecified");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.mdm.Disc_Unspecified;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\Modem\\Disc Busy Tone");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.mdm.Disc_Busy_Tone;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\Modem\\Disc Congestion");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.mdm.Disc_Congestion;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\Modem\\Disc Carr. Wait");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.mdm.Disc_Carr_Wait;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\Modem\\Disc Trn Timeout");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.mdm.Disc_Trn_Timeout;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\Modem\\Disc Incompat.");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.mdm.Disc_Incompat;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\Modem\\Disc Frame Rej.");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.mdm.Disc_Frame_Rej;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\Modem\\Disc V42bis");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.mdm.Disc_V42bis;
+
+ pLib->mdm_statistic_parse_last = pLib->cur_parse_entry - 1;
+
+ /*
+ Fax Statistics
+ */
+ pLib->fax_statistic_parse_first = pLib->cur_parse_entry;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\FAX\\Disc Normal");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.fax.Disc_Normal;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\FAX\\Disc Not Ident.");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.fax.Disc_Not_Ident;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\FAX\\Disc No Response");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.fax.Disc_No_Response;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\FAX\\Disc Retries");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.fax.Disc_Retries;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\FAX\\Disc Unexp. Msg.");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.fax.Disc_Unexp_Msg;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\FAX\\Disc No Polling.");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.fax.Disc_No_Polling;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\FAX\\Disc Training");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.fax.Disc_Training;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\FAX\\Disc Unexpected");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.fax.Disc_Unexpected;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\FAX\\Disc Application");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.fax.Disc_Application;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\FAX\\Disc Incompat.");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.fax.Disc_Incompat;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\FAX\\Disc No Command");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.fax.Disc_No_Command;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\FAX\\Disc Long Msg");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.fax.Disc_Long_Msg;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\FAX\\Disc Supervisor");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.fax.Disc_Supervisor;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\FAX\\Disc SUB SEP PWD");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.fax.Disc_SUB_SEP_PWD;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\FAX\\Disc Invalid Msg");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.fax.Disc_Invalid_Msg;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\FAX\\Disc Page Coding");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.fax.Disc_Page_Coding;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\FAX\\Disc App Timeout");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.fax.Disc_App_Timeout;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\FAX\\Disc Unspecified");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.fax.Disc_Unspecified;
+
+ pLib->fax_statistic_parse_last = pLib->cur_parse_entry - 1;
+
+ /*
+ B-Layer1"
+ */
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\B-Layer1\\X-Frames");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.b1.X_Frames;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\B-Layer1\\X-Bytes");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.b1.X_Bytes;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\B-Layer1\\X-Errors");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.b1.X_Errors;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\B-Layer1\\R-Frames");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.b1.R_Frames;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\B-Layer1\\R-Bytes");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.b1.R_Bytes;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\B-Layer1\\R-Errors");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.b1.R_Errors;
+
+ /*
+ B-Layer2
+ */
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\B-Layer2\\X-Frames");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.b2.X_Frames;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\B-Layer2\\X-Bytes");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.b2.X_Bytes;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\B-Layer2\\X-Errors");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.b2.X_Errors;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\B-Layer2\\R-Frames");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.b2.R_Frames;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\B-Layer2\\R-Bytes");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.b2.R_Bytes;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\B-Layer2\\R-Errors");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.b2.R_Errors;
+
+ /*
+ D-Layer1
+ */
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\D-Layer1\\X-Frames");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.d1.X_Frames;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\D-Layer1\\X-Bytes");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.d1.X_Bytes;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\D-Layer1\\X-Errors");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.d1.X_Errors;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\D-Layer1\\R-Frames");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.d1.R_Frames;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\D-Layer1\\R-Bytes");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.d1.R_Bytes;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\D-Layer1\\R-Errors");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.d1.R_Errors;
+
+ /*
+ D-Layer2
+ */
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\D-Layer2\\X-Frames");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.d2.X_Frames;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\D-Layer2\\X-Bytes");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.d2.X_Bytes;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\D-Layer2\\X-Errors");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.d2.X_Errors;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\D-Layer2\\R-Frames");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.d2.R_Frames;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\D-Layer2\\R-Bytes");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.d2.R_Bytes;
+
+ strcpy (pLib->parse_table[pLib->cur_parse_entry].path,
+ "Statistics\\D-Layer2\\R-Errors");
+ pLib->parse_table[pLib->cur_parse_entry++].variable = \
+ &pLib->InterfaceStat.d2.R_Errors;
+
+
+ pLib->statistic_parse_last = pLib->cur_parse_entry - 1;
+}
+
+static void diva_trace_error (diva_strace_context_t* pLib,
+ int error, const char* file, int line) {
+ if (pLib->user_proc_table.error_notify_proc) {
+ (*(pLib->user_proc_table.error_notify_proc))(\
+ pLib->user_proc_table.user_context,
+ &pLib->instance, pLib->Adapter,
+ error, file, line);
+ }
+}
+
+/*
+ Delivery notification to user
+ */
+static void diva_trace_notify_user (diva_strace_context_t* pLib,
+ int Channel,
+ int notify_subject) {
+ if (pLib->user_proc_table.notify_proc) {
+ (*(pLib->user_proc_table.notify_proc))(pLib->user_proc_table.user_context,
+ &pLib->instance,
+ pLib->Adapter,
+ &pLib->lines[Channel],
+ notify_subject);
+ }
+}
+
+/*
+ Read variable value to they destination based on the variable type
+ */
+static int diva_trace_read_variable (diva_man_var_header_t* pVar,
+ void* variable) {
+ switch (pVar->type) {
+ case 0x03: /* MI_ASCIIZ - syting */
+ return (diva_strace_read_asz (pVar, (char*)variable));
+ case 0x04: /* MI_ASCII - string */
+ return (diva_strace_read_asc (pVar, (char*)variable));
+ case 0x05: /* MI_NUMBER - counted sequence of bytes */
+ return (diva_strace_read_ie (pVar, (diva_trace_ie_t*)variable));
+ case 0x81: /* MI_INT - signed integer */
+ return (diva_strace_read_int (pVar, (int*)variable));
+ case 0x82: /* MI_UINT - unsigned integer */
+ return (diva_strace_read_uint (pVar, (dword*)variable));
+ case 0x83: /* MI_HINT - unsigned integer, hex representetion */
+ return (diva_strace_read_uint (pVar, (dword*)variable));
+ case 0x87: /* MI_BITFLD - unsigned integer, bit representation */
+ return (diva_strace_read_uint (pVar, (dword*)variable));
+ }
+
+ /*
+ This type of variable is not handled, indicate error
+ Or one problem in management interface, or in application recodeing
+ table, or this application should handle it.
+ */
+ return (-1);
+}
+
+/*
+ Read signed integer to destination
+ */
+static int diva_strace_read_int (diva_man_var_header_t* pVar, int* var) {
+ byte* ptr = (char*)&pVar->path_length;
+ int value;
+
+ ptr += (pVar->path_length + 1);
+
+ switch (pVar->value_length) {
+ case 1:
+ value = *(char*)ptr;
+ break;
+
+ case 2:
+ value = (short)GET_WORD(ptr);
+ break;
+
+ case 4:
+ value = (int)GET_DWORD(ptr);
+ break;
+
+ default:
+ return (-1);
+ }
+
+ *var = value;
+
+ return (0);
+}
+
+static int diva_strace_read_uint (diva_man_var_header_t* pVar, dword* var) {
+ byte* ptr = (char*)&pVar->path_length;
+ dword value;
+
+ ptr += (pVar->path_length + 1);
+
+ switch (pVar->value_length) {
+ case 1:
+ value = (byte)(*ptr);
+ break;
+
+ case 2:
+ value = (word)GET_WORD(ptr);
+ break;
+
+ case 3:
+ value = (dword)GET_DWORD(ptr);
+ value &= 0x00ffffff;
+ break;
+
+ case 4:
+ value = (dword)GET_DWORD(ptr);
+ break;
+
+ default:
+ return (-1);
+ }
+
+ *var = value;
+
+ return (0);
+}
+
+/*
+ Read zero terminated ASCII string
+ */
+static int diva_strace_read_asz (diva_man_var_header_t* pVar, char* var) {
+ char* ptr = (char*)&pVar->path_length;
+ int length;
+
+ ptr += (pVar->path_length + 1);
+
+ if (!(length = pVar->value_length)) {
+ length = strlen (ptr);
+ }
+ memcpy (var, ptr, length);
+ var[length] = 0;
+
+ return (0);
+}
+
+/*
+ Read counted (with leading length byte) ASCII string
+ */
+static int diva_strace_read_asc (diva_man_var_header_t* pVar, char* var) {
+ char* ptr = (char*)&pVar->path_length;
+
+ ptr += (pVar->path_length + 1);
+ memcpy (var, ptr+1, *ptr);
+ var[(int)*ptr] = 0;
+
+ return (0);
+}
+
+/*
+ Read one information element - i.e. one string of byte values with
+ one length byte in front
+ */
+static int diva_strace_read_ie (diva_man_var_header_t* pVar,
+ diva_trace_ie_t* var) {
+ char* ptr = (char*)&pVar->path_length;
+
+ ptr += (pVar->path_length + 1);
+
+ var->length = *ptr;
+ memcpy (&var->data[0], ptr+1, *ptr);
+
+ return (0);
+}
+
+static int SuperTraceSetAudioTap (void* hLib, int Channel, int on) {
+ diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+
+ if ((Channel < 1) || (Channel > pLib->Channels)) {
+ return (-1);
+ }
+ Channel--;
+
+ if (on) {
+ pLib->audio_tap_mask |= (1L << Channel);
+ } else {
+ pLib->audio_tap_mask &= ~(1L << Channel);
+ }
+
+ /*
+ EYE patterns have TM_M_DATA set as additional
+ condition
+ */
+ if (pLib->audio_tap_mask) {
+ pLib->trace_event_mask |= TM_M_DATA;
+ } else {
+ pLib->trace_event_mask &= ~TM_M_DATA;
+ }
+
+ return (ScheduleNextTraceRequest (pLib));
+}
+
+static int SuperTraceSetBChannel (void* hLib, int Channel, int on) {
+ diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+
+ if ((Channel < 1) || (Channel > pLib->Channels)) {
+ return (-1);
+ }
+ Channel--;
+
+ if (on) {
+ pLib->bchannel_trace_mask |= (1L << Channel);
+ } else {
+ pLib->bchannel_trace_mask &= ~(1L << Channel);
+ }
+
+ return (ScheduleNextTraceRequest (pLib));
+}
+
+static int SuperTraceSetDChannel (void* hLib, int on) {
+ diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+
+ if (on) {
+ pLib->trace_event_mask |= (TM_D_CHAN | TM_C_COMM | TM_DL_ERR | TM_LAYER1);
+ } else {
+ pLib->trace_event_mask &= ~(TM_D_CHAN | TM_C_COMM | TM_DL_ERR | TM_LAYER1);
+ }
+
+ return (ScheduleNextTraceRequest (pLib));
+}
+
+static int SuperTraceSetInfo (void* hLib, int on) {
+ diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+
+ if (on) {
+ pLib->trace_event_mask |= TM_STRING;
+ } else {
+ pLib->trace_event_mask &= ~TM_STRING;
+ }
+
+ return (ScheduleNextTraceRequest (pLib));
+}
+
+static int SuperTraceClearCall (void* hLib, int Channel) {
+ diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+
+ if ((Channel < 1) || (Channel > pLib->Channels)) {
+ return (-1);
+ }
+ Channel--;
+
+ pLib->clear_call_command |= (1L << Channel);
+
+ return (ScheduleNextTraceRequest (pLib));
+}
+
+/*
+ Parse and update cumulative statistice
+ */
+static int diva_ifc_statistics (diva_strace_context_t* pLib,
+ diva_man_var_header_t* pVar) {
+ diva_man_var_header_t* cur;
+ int i, one_updated = 0, mdm_updated = 0, fax_updated = 0;
+
+ for (i = pLib->statistic_parse_first; i <= pLib->statistic_parse_last; i++) {
+ if ((cur = find_var (pVar, pLib->parse_table[i].path))) {
+ if (diva_trace_read_variable (cur, pLib->parse_table[i].variable)) {
+ diva_trace_error (pLib, -3 , __FILE__, __LINE__);
+ return (-1);
+ }
+ one_updated = 1;
+ if ((i >= pLib->mdm_statistic_parse_first) && (i <= pLib->mdm_statistic_parse_last)) {
+ mdm_updated = 1;
+ }
+ if ((i >= pLib->fax_statistic_parse_first) && (i <= pLib->fax_statistic_parse_last)) {
+ fax_updated = 1;
+ }
+ }
+ }
+
+ /*
+ We do not use first event to notify user - this is the event that is
+ generated as result of EVENT ON operation and is used only to initialize
+ internal variables of application
+ */
+ if (mdm_updated) {
+ diva_trace_notify_user (pLib, 0, DIVA_SUPER_TRACE_NOTIFY_MDM_STAT_CHANGE);
+ } else if (fax_updated) {
+ diva_trace_notify_user (pLib, 0, DIVA_SUPER_TRACE_NOTIFY_FAX_STAT_CHANGE);
+ } else if (one_updated) {
+ diva_trace_notify_user (pLib, 0, DIVA_SUPER_TRACE_NOTIFY_STAT_CHANGE);
+ }
+
+ return (one_updated ? 0 : -1);
+}
+
+static int SuperTraceGetOutgoingCallStatistics (void* hLib) {
+ diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+ pLib->outgoing_ifc_stats = 1;
+ return (ScheduleNextTraceRequest (pLib));
+}
+
+static int SuperTraceGetIncomingCallStatistics (void* hLib) {
+ diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+ pLib->incoming_ifc_stats = 1;
+ return (ScheduleNextTraceRequest (pLib));
+}
+
+static int SuperTraceGetModemStatistics (void* hLib) {
+ diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+ pLib->modem_ifc_stats = 1;
+ return (ScheduleNextTraceRequest (pLib));
+}
+
+static int SuperTraceGetFaxStatistics (void* hLib) {
+ diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+ pLib->fax_ifc_stats = 1;
+ return (ScheduleNextTraceRequest (pLib));
+}
+
+static int SuperTraceGetBLayer1Statistics (void* hLib) {
+ diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+ pLib->b1_ifc_stats = 1;
+ return (ScheduleNextTraceRequest (pLib));
+}
+
+static int SuperTraceGetBLayer2Statistics (void* hLib) {
+ diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+ pLib->b2_ifc_stats = 1;
+ return (ScheduleNextTraceRequest (pLib));
+}
+
+static int SuperTraceGetDLayer1Statistics (void* hLib) {
+ diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+ pLib->d1_ifc_stats = 1;
+ return (ScheduleNextTraceRequest (pLib));
+}
+
+static int SuperTraceGetDLayer2Statistics (void* hLib) {
+ diva_strace_context_t* pLib = (diva_strace_context_t*)hLib;
+ pLib->d2_ifc_stats = 1;
+ return (ScheduleNextTraceRequest (pLib));
+}
+
+dword DivaSTraceGetMemotyRequirement (int channels) {
+ dword parse_entries = (MODEM_PARSE_ENTRIES + FAX_PARSE_ENTRIES + \
+ STAT_PARSE_ENTRIES + \
+ LINE_PARSE_ENTRIES + 1) * channels;
+ return (sizeof(diva_strace_context_t) + \
+ (parse_entries * sizeof(diva_strace_path2action_t)));
+}
+
diff --git a/drivers/isdn/hardware/eicon/maintidi.h b/drivers/isdn/hardware/eicon/maintidi.h
new file mode 100644
index 0000000..4f06294
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/maintidi.h
@@ -0,0 +1,172 @@
+/*
+ *
+ Copyright (c) Eicon Networks, 2000.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 1.9
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+#ifndef __DIVA_EICON_TRACE_IDI_IFC_H__
+#define __DIVA_EICON_TRACE_IDI_IFC_H__
+
+void* SuperTraceOpenAdapter (int AdapterNumber);
+int SuperTraceCloseAdapter (void* AdapterHandle);
+int SuperTraceWrite (void* AdapterHandle,
+ const void* data, int length);
+int SuperTraceReadRequest (void* AdapterHandle,const char* name,byte* data);
+int SuperTraceGetNumberOfChannels (void* AdapterHandle);
+int SuperTraceASSIGN (void* AdapterHandle, byte* data);
+int SuperTraceREMOVE (void* AdapterHandle);
+int SuperTraceTraceOnRequest(void* hAdapter, const char* name, byte* data);
+int SuperTraceWriteVar (void* AdapterHandle,
+ byte* data,
+ const char* name,
+ void* var,
+ byte type,
+ byte var_length);
+int SuperTraceExecuteRequest (void* AdapterHandle,
+ const char* name,
+ byte* data);
+
+typedef struct _diva_strace_path2action {
+ char path[64]; /* Full path to variable */
+ void* variable; /* Variable that will receive value */
+} diva_strace_path2action_t;
+
+#define DIVA_MAX_MANAGEMENT_TRANSFER_SIZE 4096
+
+typedef struct _diva_strace_context {
+ diva_strace_library_interface_t instance;
+
+ int Adapter;
+ void* hAdapter;
+
+ int Channels;
+ int req_busy;
+
+ ENTITY e;
+ IDI_CALL request;
+ BUFFERS XData;
+ BUFFERS RData;
+ byte buffer[DIVA_MAX_MANAGEMENT_TRANSFER_SIZE + 1];
+ int removal_state;
+ int general_b_ch_event;
+ int general_fax_event;
+ int general_mdm_event;
+
+ byte rc_ok;
+
+ /*
+ Initialization request state machine
+ */
+ int ChannelsTraceActive;
+ int ModemTraceActive;
+ int FaxTraceActive;
+ int IncomingCallsCallsActive;
+ int IncomingCallsConnectedActive;
+ int OutgoingCallsCallsActive;
+ int OutgoingCallsConnectedActive;
+
+ int trace_mask_init;
+ int audio_trace_init;
+ int bchannel_init;
+ int trace_length_init;
+ int trace_on;
+ int trace_events_down;
+ int l1_trace;
+ int l2_trace;
+
+ /*
+ Trace\Event Enable
+ */
+ word trace_event_mask;
+ word current_trace_event_mask;
+
+ dword audio_tap_mask;
+ dword current_audio_tap_mask;
+ dword current_eye_pattern_mask;
+ int audio_tap_pending;
+ int eye_pattern_pending;
+
+ dword bchannel_trace_mask;
+ dword current_bchannel_trace_mask;
+
+
+ diva_trace_line_state_t lines[30];
+
+ int parse_entries;
+ int cur_parse_entry;
+ diva_strace_path2action_t* parse_table;
+
+ diva_trace_library_user_interface_t user_proc_table;
+
+ int line_parse_entry_first[30];
+ int line_parse_entry_last[30];
+
+ int modem_parse_entry_first[30];
+ int modem_parse_entry_last[30];
+
+ int fax_parse_entry_first[30];
+ int fax_parse_entry_last[30];
+
+ int statistic_parse_first;
+ int statistic_parse_last;
+
+ int mdm_statistic_parse_first;
+ int mdm_statistic_parse_last;
+
+ int fax_statistic_parse_first;
+ int fax_statistic_parse_last;
+
+ dword line_init_event;
+ dword modem_init_event;
+ dword fax_init_event;
+
+ dword pending_line_status;
+ dword pending_modem_status;
+ dword pending_fax_status;
+
+ dword clear_call_command;
+
+ int outgoing_ifc_stats;
+ int incoming_ifc_stats;
+ int modem_ifc_stats;
+ int fax_ifc_stats;
+ int b1_ifc_stats;
+ int b2_ifc_stats;
+ int d1_ifc_stats;
+ int d2_ifc_stats;
+
+ diva_trace_interface_state_t Interface;
+ diva_ifc_statistics_t InterfaceStat;
+} diva_strace_context_t;
+
+typedef struct _diva_man_var_header {
+ byte escape;
+ byte length;
+ byte management_id;
+ byte type;
+ byte attribute;
+ byte status;
+ byte value_length;
+ byte path_length;
+} diva_man_var_header_t;
+
+#endif
+
diff --git a/drivers/isdn/hardware/eicon/man_defs.h b/drivers/isdn/hardware/eicon/man_defs.h
new file mode 100644
index 0000000..cb4ef4c
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/man_defs.h
@@ -0,0 +1,133 @@
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 1.9
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+/* Definitions for use with the Management Information Element */
+
+/*------------------------------------------------------------------*/
+/* Management information element */
+/* ---------------------------------------------------------- */
+/* Byte Coding Comment */
+/* ---------------------------------------------------------- */
+/* 0 | 0 1 1 1 1 1 1 1 | ESC */
+/* 1 | 0 x x x x x x x | Length of information element (m-1) */
+/* 2 | 1 0 0 0 0 0 0 0 | Management Information Id */
+/* 3 | x x x x x x x x | Type */
+/* 4 | x x x x x x x x | Attribute */
+/* 5 | x x x x x x x x | Status */
+/* 6 | x x x x x x x x | Variable Value Length (m-n) */
+/* 7 | x x x x x x x x | Path / Variable Name String Length (n-8)*/
+/* 8..n | x x x x x x x x | Path/Node Name String separated by '\' */
+/* n..m | x x x x x x x x | Variable content */
+/*------------------------------------------------------------------*/
+
+/*------------------------------------------------------------------*/
+/* Type Field */
+/* */
+/* MAN_READ: not used */
+/* MAN_WRITE: not used */
+/* MAN_EVENT_ON: not used */
+/* MAN_EVENT_OFF: not used */
+/* MAN_INFO_IND: type of variable */
+/* MAN_EVENT_IND: type of variable */
+/* MAN_TRACE_IND not used */
+/*------------------------------------------------------------------*/
+#define MI_DIR 0x01 /* Directory string (zero terminated) */
+#define MI_EXECUTE 0x02 /* Executable function (has no value) */
+#define MI_ASCIIZ 0x03 /* Zero terminated string */
+#define MI_ASCII 0x04 /* String, first byte is length */
+#define MI_NUMBER 0x05 /* Number string, first byte is length*/
+#define MI_TRACE 0x06 /* Trace information, format see below*/
+
+#define MI_FIXED_LENGTH 0x80 /* get length from MAN_INFO max_len */
+#define MI_INT 0x81 /* number to display as signed int */
+#define MI_UINT 0x82 /* number to display as unsigned int */
+#define MI_HINT 0x83 /* number to display in hex format */
+#define MI_HSTR 0x84 /* number to display as a hex string */
+#define MI_BOOLEAN 0x85 /* number to display as boolean */
+#define MI_IP_ADDRESS 0x86 /* number to display as IP address */
+#define MI_BITFLD 0x87 /* number to display as bit field */
+#define MI_SPID_STATE 0x88 /* state# of SPID initialisation */
+
+/*------------------------------------------------------------------*/
+/* Attribute Field */
+/* */
+/* MAN_READ: not used */
+/* MAN_WRITE: not used */
+/* MAN_EVENT_ON: not used */
+/* MAN_EVENT_OFF: not used */
+/* MAN_INFO_IND: set according to capabilities of that variable */
+/* MAN_EVENT_IND: not used */
+/* MAN_TRACE_IND not used */
+/*------------------------------------------------------------------*/
+#define MI_WRITE 0x01 /* Variable is writeable */
+#define MI_EVENT 0x02 /* Variable can indicate changes */
+
+/*------------------------------------------------------------------*/
+/* Status Field */
+/* */
+/* MAN_READ: not used */
+/* MAN_WRITE: not used */
+/* MAN_EVENT_ON: not used */
+/* MAN_EVENT_OFF: not used */
+/* MAN_INFO_IND: set according to the actual status */
+/* MAN_EVENT_IND: set according to the actual statu */
+/* MAN_TRACE_IND not used */
+/*------------------------------------------------------------------*/
+#define MI_LOCKED 0x01 /* write protected by another instance*/
+#define MI_EVENT_ON 0x02 /* Event logging switched on */
+#define MI_PROTECTED 0x04 /* write protected by this instance */
+
+/*------------------------------------------------------------------*/
+/* Data Format used for MAN_TRACE_IND (no MI-element used) */
+/*------------------------------------------------------------------*/
+typedef struct mi_xlog_hdr_s MI_XLOG_HDR;
+struct mi_xlog_hdr_s
+{
+ unsigned long time; /* Timestamp in msec units */
+ unsigned short size; /* Size of data that follows */
+ unsigned short code; /* code of trace event */
+}; /* unspecified data follows this header */
+
+/*------------------------------------------------------------------*/
+/* Trace mask definitions for trace events except B channel and */
+/* debug trace events */
+/*------------------------------------------------------------------*/
+#define TM_D_CHAN 0x0001 /* D-Channel (D-.) Code 3,4 */
+#define TM_L_LAYER 0x0002 /* Low Layer (LL) Code 6,7 */
+#define TM_N_LAYER 0x0004 /* Network Layer (N) Code 14,15 */
+#define TM_DL_ERR 0x0008 /* Data Link Error (MDL) Code 9 */
+#define TM_LAYER1 0x0010 /* Layer 1 Code 20 */
+#define TM_C_COMM 0x0020 /* Call Comment (SIG) Code 5,21,22 */
+#define TM_M_DATA 0x0040 /* Modulation Data (EYE) Code 23 */
+#define TM_STRING 0x0080 /* Sting data Code 24 */
+#define TM_N_USED2 0x0100 /* not used */
+#define TM_N_USED3 0x0200 /* not used */
+#define TM_N_USED4 0x0400 /* not used */
+#define TM_N_USED5 0x0800 /* not used */
+#define TM_N_USED6 0x1000 /* not used */
+#define TM_N_USED7 0x2000 /* not used */
+#define TM_N_USED8 0x4000 /* not used */
+#define TM_REST 0x8000 /* Codes 10,11,12,13,16,18,19,128,129 */
+
+/*------ End of file -----------------------------------------------*/
diff --git a/drivers/isdn/hardware/eicon/mdm_msg.h b/drivers/isdn/hardware/eicon/mdm_msg.h
new file mode 100644
index 0000000..7a737e1
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/mdm_msg.h
@@ -0,0 +1,346 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+#ifndef __EICON_MDM_MSG_H__
+#define __EICON_MDM_MSG_H__
+#define DSP_UDATA_INDICATION_DCD_OFF 0x01
+#define DSP_UDATA_INDICATION_DCD_ON 0x02
+#define DSP_UDATA_INDICATION_CTS_OFF 0x03
+#define DSP_UDATA_INDICATION_CTS_ON 0x04
+/* =====================================================================
+DCD_OFF Message:
+ <word> time of DCD off (sampled from counter at 8kHz)
+DCD_ON Message:
+ <word> time of DCD on (sampled from counter at 8kHz)
+ <byte> connected norm
+ <word> connected options
+ <dword> connected speed (bit/s, max of tx and rx speed)
+ <word> roundtrip delay (ms)
+ <dword> connected speed tx (bit/s)
+ <dword> connected speed rx (bit/s)
+ Size of this message == 19 bytes, but we will receive only 11
+ ===================================================================== */
+#define DSP_CONNECTED_NORM_UNSPECIFIED 0
+#define DSP_CONNECTED_NORM_V21 1
+#define DSP_CONNECTED_NORM_V23 2
+#define DSP_CONNECTED_NORM_V22 3
+#define DSP_CONNECTED_NORM_V22_BIS 4
+#define DSP_CONNECTED_NORM_V32_BIS 5
+#define DSP_CONNECTED_NORM_V34 6
+#define DSP_CONNECTED_NORM_V8 7
+#define DSP_CONNECTED_NORM_BELL_212A 8
+#define DSP_CONNECTED_NORM_BELL_103 9
+#define DSP_CONNECTED_NORM_V29_LEASED_LINE 10
+#define DSP_CONNECTED_NORM_V33_LEASED_LINE 11
+#define DSP_CONNECTED_NORM_V90 12
+#define DSP_CONNECTED_NORM_V21_CH2 13
+#define DSP_CONNECTED_NORM_V27_TER 14
+#define DSP_CONNECTED_NORM_V29 15
+#define DSP_CONNECTED_NORM_V33 16
+#define DSP_CONNECTED_NORM_V17 17
+#define DSP_CONNECTED_NORM_V32 18
+#define DSP_CONNECTED_NORM_K56_FLEX 19
+#define DSP_CONNECTED_NORM_X2 20
+#define DSP_CONNECTED_NORM_V18 21
+#define DSP_CONNECTED_NORM_V18_LOW_HIGH 22
+#define DSP_CONNECTED_NORM_V18_HIGH_LOW 23
+#define DSP_CONNECTED_NORM_V21_LOW_HIGH 24
+#define DSP_CONNECTED_NORM_V21_HIGH_LOW 25
+#define DSP_CONNECTED_NORM_BELL103_LOW_HIGH 26
+#define DSP_CONNECTED_NORM_BELL103_HIGH_LOW 27
+#define DSP_CONNECTED_NORM_V23_75_1200 28
+#define DSP_CONNECTED_NORM_V23_1200_75 29
+#define DSP_CONNECTED_NORM_EDT_110 30
+#define DSP_CONNECTED_NORM_BAUDOT_45 31
+#define DSP_CONNECTED_NORM_BAUDOT_47 32
+#define DSP_CONNECTED_NORM_BAUDOT_50 33
+#define DSP_CONNECTED_NORM_DTMF 34
+#define DSP_CONNECTED_NORM_V18_RESERVED_13 35
+#define DSP_CONNECTED_NORM_V18_RESERVED_14 36
+#define DSP_CONNECTED_NORM_V18_RESERVED_15 37
+#define DSP_CONNECTED_NORM_VOWN 38
+#define DSP_CONNECTED_NORM_V23_OFF_HOOK 39
+#define DSP_CONNECTED_NORM_V23_ON_HOOK 40
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_3 41
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_4 42
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_5 43
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_6 44
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_7 45
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_8 46
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_9 47
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_10 48
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_11 49
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_12 50
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_13 51
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_14 52
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_15 53
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_16 54
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_17 55
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_18 56
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_19 57
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_20 58
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_21 59
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_22 60
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_23 61
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_24 62
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_25 63
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_26 64
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_27 65
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_28 66
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_29 67
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_30 68
+#define DSP_CONNECTED_NORM_VOWN_RESERVED_31 69
+#define DSP_CONNECTED_OPTION_TRELLIS 0x0001
+#define DSP_CONNECTED_OPTION_V42_TRANS 0x0002
+#define DSP_CONNECTED_OPTION_V42_LAPM 0x0004
+#define DSP_CONNECTED_OPTION_SHORT_TRAIN 0x0008
+#define DSP_CONNECTED_OPTION_TALKER_ECHO_PROTECT 0x0010
+#define DSP_CONNECTED_OPTION_V42BIS 0x0020
+#define DSP_CONNECTED_OPTION_MNP2 0x0040
+#define DSP_CONNECTED_OPTION_MNP3 0x0080
+#define DSP_CONNECTED_OPTION_MNP4 0x00c0
+#define DSP_CONNECTED_OPTION_MNP5 0x0100
+#define DSP_CONNECTED_OPTION_MNP10 0x0200
+#define DSP_CONNECTED_OPTION_MASK_V42 0x0024
+#define DSP_CONNECTED_OPTION_MASK_MNP 0x03c0
+#define DSP_CONNECTED_OPTION_MASK_ERROR_CORRECT 0x03e4
+#define DSP_CONNECTED_OPTION_MASK_COMPRESSION 0x0320
+#define DSP_UDATA_INDICATION_DISCONNECT 5
+/*
+returns:
+ <byte> cause
+*/
+/* ==========================================================
+ DLC: B2 modem configuration
+ ========================================================== */
+/*
+Fields in assign DLC information element for modem protocol V.42/MNP:
+ <byte> length of information element
+ <word> information field length
+ <byte> address A (not used, default 3)
+ <byte> address B (not used, default 1)
+ <byte> modulo mode (not used, default 7)
+ <byte> window size (not used, default 7)
+ <word> XID length (not used, default 0)
+ ... XID information (not used, default empty)
+ <byte> modem protocol negotiation options
+ <byte> modem protocol options
+ <byte> modem protocol break configuration
+ <byte> modem protocol application options
+*/
+#define DLC_MODEMPROT_DISABLE_V42_V42BIS 0x01
+#define DLC_MODEMPROT_DISABLE_MNP_MNP5 0x02
+#define DLC_MODEMPROT_REQUIRE_PROTOCOL 0x04
+#define DLC_MODEMPROT_DISABLE_V42_DETECT 0x08
+#define DLC_MODEMPROT_DISABLE_COMPRESSION 0x10
+#define DLC_MODEMPROT_REQUIRE_PROTOCOL_V34UP 0x20
+#define DLC_MODEMPROT_NO_PROTOCOL_IF_1200 0x01
+#define DLC_MODEMPROT_BUFFER_IN_V42_DETECT 0x02
+#define DLC_MODEMPROT_DISABLE_V42_SREJ 0x04
+#define DLC_MODEMPROT_DISABLE_MNP3 0x08
+#define DLC_MODEMPROT_DISABLE_MNP4 0x10
+#define DLC_MODEMPROT_DISABLE_MNP10 0x20
+#define DLC_MODEMPROT_NO_PROTOCOL_IF_V22BIS 0x40
+#define DLC_MODEMPROT_NO_PROTOCOL_IF_V32BIS 0x80
+#define DLC_MODEMPROT_BREAK_DISABLED 0x00
+#define DLC_MODEMPROT_BREAK_NORMAL 0x01
+#define DLC_MODEMPROT_BREAK_EXPEDITED 0x02
+#define DLC_MODEMPROT_BREAK_DESTRUCTIVE 0x03
+#define DLC_MODEMPROT_BREAK_CONFIG_MASK 0x03
+#define DLC_MODEMPROT_APPL_EARLY_CONNECT 0x01
+#define DLC_MODEMPROT_APPL_PASS_INDICATIONS 0x02
+/* ==========================================================
+ CAI parameters used for the modem L1 configuration
+ ========================================================== */
+/*
+Fields in assign CAI information element:
+ <byte> length of information element
+ <byte> info field and B-channel hardware
+ <byte> rate adaptation bit rate
+ <byte> async framing parameters
+ <byte> reserved
+ <word> packet length
+ <byte> modem line taking options
+ <byte> modem modulation negotiation parameters
+ <byte> modem modulation options
+ <byte> modem disabled modulations mask low
+ <byte> modem disabled modulations mask high
+ <byte> modem enabled modulations mask
+ <word> modem min TX speed
+ <word> modem max TX speed
+ <word> modem min RX speed
+ <word> modem max RX speed
+ <byte> modem disabled symbol rates mask
+ <byte> modem info options mask
+ <byte> modem transmit level adjust
+ <byte> modem speaker parameters
+ <word> modem private debug config
+ <struct> modem reserved
+ <struct> v18 config parameters
+ <struct> v18 probing sequence
+ <struct> v18 probing message
+*/
+#define DSP_CAI_HARDWARE_HDLC_64K 0x05
+#define DSP_CAI_HARDWARE_HDLC_56K 0x08
+#define DSP_CAI_HARDWARE_TRANSP 0x09
+#define DSP_CAI_HARDWARE_V110_SYNC 0x0c
+#define DSP_CAI_HARDWARE_V110_ASYNC 0x0d
+#define DSP_CAI_HARDWARE_HDLC_128K 0x0f
+#define DSP_CAI_HARDWARE_FAX 0x10
+#define DSP_CAI_HARDWARE_MODEM_ASYNC 0x11
+#define DSP_CAI_HARDWARE_MODEM_SYNC 0x12
+#define DSP_CAI_HARDWARE_V110_HDLCA 0x13
+#define DSP_CAI_HARDWARE_ADVANCED_VOICE 0x14
+#define DSP_CAI_HARDWARE_TRANSP_DTMF 0x16
+#define DSP_CAI_HARDWARE_DTMF_VOICE_ISDN 0x17
+#define DSP_CAI_HARDWARE_DTMF_VOICE_LOCAL 0x18
+#define DSP_CAI_HARDWARE_MASK 0x3f
+#define DSP_CAI_ENABLE_INFO_INDICATIONS 0x80
+#define DSP_CAI_RATE_ADAPTATION_300 0x00
+#define DSP_CAI_RATE_ADAPTATION_600 0x01
+#define DSP_CAI_RATE_ADAPTATION_1200 0x02
+#define DSP_CAI_RATE_ADAPTATION_2400 0x03
+#define DSP_CAI_RATE_ADAPTATION_4800 0x04
+#define DSP_CAI_RATE_ADAPTATION_9600 0x05
+#define DSP_CAI_RATE_ADAPTATION_19200 0x06
+#define DSP_CAI_RATE_ADAPTATION_38400 0x07
+#define DSP_CAI_RATE_ADAPTATION_48000 0x08
+#define DSP_CAI_RATE_ADAPTATION_56000 0x09
+#define DSP_CAI_RATE_ADAPTATION_7200 0x0a
+#define DSP_CAI_RATE_ADAPTATION_14400 0x0b
+#define DSP_CAI_RATE_ADAPTATION_28800 0x0c
+#define DSP_CAI_RATE_ADAPTATION_12000 0x0d
+#define DSP_CAI_RATE_ADAPTATION_1200_75 0x0e
+#define DSP_CAI_RATE_ADAPTATION_75_1200 0x0f
+#define DSP_CAI_RATE_ADAPTATION_MASK 0x0f
+#define DSP_CAI_ASYNC_PARITY_ENABLE 0x01
+#define DSP_CAI_ASYNC_PARITY_SPACE 0x00
+#define DSP_CAI_ASYNC_PARITY_ODD 0x02
+#define DSP_CAI_ASYNC_PARITY_EVEN 0x04
+#define DSP_CAI_ASYNC_PARITY_MARK 0x06
+#define DSP_CAI_ASYNC_PARITY_MASK 0x06
+#define DSP_CAI_ASYNC_ONE_STOP_BIT 0x00
+#define DSP_CAI_ASYNC_TWO_STOP_BITS 0x20
+#define DSP_CAI_ASYNC_CHAR_LENGTH_8 0x00
+#define DSP_CAI_ASYNC_CHAR_LENGTH_7 0x40
+#define DSP_CAI_ASYNC_CHAR_LENGTH_6 0x80
+#define DSP_CAI_ASYNC_CHAR_LENGTH_5 0xc0
+#define DSP_CAI_ASYNC_CHAR_LENGTH_MASK 0xc0
+#define DSP_CAI_MODEM_LEASED_LINE_MODE 0x01
+#define DSP_CAI_MODEM_4_WIRE_OPERATION 0x02
+#define DSP_CAI_MODEM_DISABLE_BUSY_DETECT 0x04
+#define DSP_CAI_MODEM_DISABLE_CALLING_TONE 0x08
+#define DSP_CAI_MODEM_DISABLE_ANSWER_TONE 0x10
+#define DSP_CAI_MODEM_ENABLE_DIAL_TONE_DET 0x20
+#define DSP_CAI_MODEM_USE_POTS_INTERFACE 0x40
+#define DSP_CAI_MODEM_FORCE_RAY_TAYLOR_FAX 0x80
+#define DSP_CAI_MODEM_NEGOTIATE_HIGHEST 0x00
+#define DSP_CAI_MODEM_NEGOTIATE_DISABLED 0x01
+#define DSP_CAI_MODEM_NEGOTIATE_IN_CLASS 0x02
+#define DSP_CAI_MODEM_NEGOTIATE_V100 0x03
+#define DSP_CAI_MODEM_NEGOTIATE_V8 0x04
+#define DSP_CAI_MODEM_NEGOTIATE_V8BIS 0x05
+#define DSP_CAI_MODEM_NEGOTIATE_MASK 0x07
+#define DSP_CAI_MODEM_GUARD_TONE_NONE 0x00
+#define DSP_CAI_MODEM_GUARD_TONE_550HZ 0x40
+#define DSP_CAI_MODEM_GUARD_TONE_1800HZ 0x80
+#define DSP_CAI_MODEM_GUARD_TONE_MASK 0xc0
+#define DSP_CAI_MODEM_DISABLE_RETRAIN 0x01
+#define DSP_CAI_MODEM_DISABLE_STEPUPDOWN 0x02
+#define DSP_CAI_MODEM_DISABLE_SPLIT_SPEED 0x04
+#define DSP_CAI_MODEM_DISABLE_TRELLIS 0x08
+#define DSP_CAI_MODEM_ALLOW_RDL_TEST_LOOP 0x10
+#define DSP_CAI_MODEM_DISABLE_FLUSH_TIMER 0x40
+#define DSP_CAI_MODEM_REVERSE_DIRECTION 0x80
+#define DSP_CAI_MODEM_DISABLE_V21 0x01
+#define DSP_CAI_MODEM_DISABLE_V23 0x02
+#define DSP_CAI_MODEM_DISABLE_V22 0x04
+#define DSP_CAI_MODEM_DISABLE_V22BIS 0x08
+#define DSP_CAI_MODEM_DISABLE_V32 0x10
+#define DSP_CAI_MODEM_DISABLE_V32BIS 0x20
+#define DSP_CAI_MODEM_DISABLE_V34 0x40
+#define DSP_CAI_MODEM_DISABLE_V90 0x80
+#define DSP_CAI_MODEM_DISABLE_BELL103 0x01
+#define DSP_CAI_MODEM_DISABLE_BELL212A 0x02
+#define DSP_CAI_MODEM_DISABLE_VFC 0x04
+#define DSP_CAI_MODEM_DISABLE_K56FLEX 0x08
+#define DSP_CAI_MODEM_DISABLE_X2 0x10
+#define DSP_CAI_MODEM_ENABLE_V29FDX 0x01
+#define DSP_CAI_MODEM_ENABLE_V33 0x02
+#define DSP_CAI_MODEM_DISABLE_2400_SYMBOLS 0x01
+#define DSP_CAI_MODEM_DISABLE_2743_SYMBOLS 0x02
+#define DSP_CAI_MODEM_DISABLE_2800_SYMBOLS 0x04
+#define DSP_CAI_MODEM_DISABLE_3000_SYMBOLS 0x08
+#define DSP_CAI_MODEM_DISABLE_3200_SYMBOLS 0x10
+#define DSP_CAI_MODEM_DISABLE_3429_SYMBOLS 0x20
+#define DSP_CAI_MODEM_DISABLE_TX_REDUCTION 0x01
+#define DSP_CAI_MODEM_DISABLE_PRECODING 0x02
+#define DSP_CAI_MODEM_DISABLE_PREEMPHASIS 0x04
+#define DSP_CAI_MODEM_DISABLE_SHAPING 0x08
+#define DSP_CAI_MODEM_DISABLE_NONLINEAR_EN 0x10
+#define DSP_CAI_MODEM_SPEAKER_OFF 0x00
+#define DSP_CAI_MODEM_SPEAKER_DURING_TRAIN 0x01
+#define DSP_CAI_MODEM_SPEAKER_TIL_CONNECT 0x02
+#define DSP_CAI_MODEM_SPEAKER_ALWAYS_ON 0x03
+#define DSP_CAI_MODEM_SPEAKER_CONTROL_MASK 0x03
+#define DSP_CAI_MODEM_SPEAKER_VOLUME_MIN 0x00
+#define DSP_CAI_MODEM_SPEAKER_VOLUME_LOW 0x04
+#define DSP_CAI_MODEM_SPEAKER_VOLUME_HIGH 0x08
+#define DSP_CAI_MODEM_SPEAKER_VOLUME_MAX 0x0c
+#define DSP_CAI_MODEM_SPEAKER_VOLUME_MASK 0x0c
+/* ==========================================================
+ DCD/CTS State
+ ========================================================== */
+#define MDM_WANT_CONNECT_B3_ACTIVE_I 0x01
+#define MDM_NCPI_VALID 0x02
+#define MDM_NCPI_CTS_ON_RECEIVED 0x04
+#define MDM_NCPI_DCD_ON_RECEIVED 0x08
+/* ==========================================================
+ CAPI NCPI Constants
+ ========================================================== */
+#define MDM_NCPI_ECM_V42 0x0001
+#define MDM_NCPI_ECM_MNP 0x0002
+#define MDM_NCPI_TRANSPARENT 0x0004
+#define MDM_NCPI_COMPRESSED 0x0010
+/* ==========================================================
+ CAPI B2 Config Constants
+ ========================================================== */
+#define MDM_B2_DISABLE_V42bis 0x0001
+#define MDM_B2_DISABLE_MNP 0x0002
+#define MDM_B2_DISABLE_TRANS 0x0004
+#define MDM_B2_DISABLE_V42 0x0008
+#define MDM_B2_DISABLE_COMP 0x0010
+/* ==========================================================
+ CAPI B1 Config Constants
+ ========================================================== */
+#define MDM_CAPI_DISABLE_RETRAIN 0x0001
+#define MDM_CAPI_DISABLE_RING_TONE 0x0002
+#define MDM_CAPI_GUARD_1800 0x0004
+#define MDM_CAPI_GUARD_550 0x0008
+#define MDM_CAPI_NEG_V8 0x0003
+#define MDM_CAPI_NEG_V100 0x0002
+#define MDM_CAPI_NEG_MOD_CLASS 0x0001
+#define MDM_CAPI_NEG_DISABLED 0x0000
+#endif
diff --git a/drivers/isdn/hardware/eicon/message.c b/drivers/isdn/hardware/eicon/message.c
new file mode 100644
index 0000000..599fed8
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/message.c
@@ -0,0 +1,15045 @@
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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 "platform.h"
+#include "di_defs.h"
+#include "pc.h"
+#include "capi20.h"
+#include "divacapi.h"
+#include "mdm_msg.h"
+#include "divasync.h"
+
+
+
+#define FILE_ "MESSAGE.C"
+#define dprintf
+
+
+
+
+
+
+
+
+
+/*------------------------------------------------------------------*/
+/* This is options supported for all adapters that are server by */
+/* XDI driver. Allo it is not necessary to ask it from every adapter*/
+/* and it is not necessary to save it separate for every adapter */
+/* Macrose defined here have only local meaning */
+/*------------------------------------------------------------------*/
+static dword diva_xdi_extended_features = 0;
+
+#define DIVA_CAPI_USE_CMA 0x00000001
+#define DIVA_CAPI_XDI_PROVIDES_SDRAM_BAR 0x00000002
+#define DIVA_CAPI_XDI_PROVIDES_NO_CANCEL 0x00000004
+#define DIVA_CAPI_XDI_PROVIDES_RX_DMA 0x00000008
+
+/*
+ CAPI can request to process all return codes self only if:
+ protocol code supports this && xdi supports this
+ */
+#define DIVA_CAPI_SUPPORTS_NO_CANCEL(__a__) (((__a__)->manufacturer_features&MANUFACTURER_FEATURE_XONOFF_FLOW_CONTROL)&& ((__a__)->manufacturer_features & MANUFACTURER_FEATURE_OK_FC_LABEL) && (diva_xdi_extended_features & DIVA_CAPI_XDI_PROVIDES_NO_CANCEL))
+
+/*------------------------------------------------------------------*/
+/* local function prototypes */
+/*------------------------------------------------------------------*/
+
+static void group_optimization(DIVA_CAPI_ADAPTER * a, PLCI * plci);
+static void set_group_ind_mask (PLCI *plci);
+static void clear_group_ind_mask_bit (PLCI *plci, word b);
+static byte test_group_ind_mask_bit (PLCI *plci, word b);
+void AutomaticLaw(DIVA_CAPI_ADAPTER *);
+word CapiRelease(word);
+word CapiRegister(word);
+word api_put(APPL *, CAPI_MSG *);
+static word api_parse(byte *, word, byte *, API_PARSE *);
+static void api_save_msg(API_PARSE *in, byte *format, API_SAVE *out);
+static void api_load_msg(API_SAVE *in, API_PARSE *out);
+
+word api_remove_start(void);
+void api_remove_complete(void);
+
+static void plci_remove(PLCI *);
+static void diva_get_extended_adapter_features (DIVA_CAPI_ADAPTER * a);
+static void diva_ask_for_xdi_sdram_bar (DIVA_CAPI_ADAPTER *, IDI_SYNC_REQ *);
+
+void callback(ENTITY *);
+
+static void control_rc(PLCI *, byte, byte, byte, byte, byte);
+static void data_rc(PLCI *, byte);
+static void data_ack(PLCI *, byte);
+static void sig_ind(PLCI *);
+static void SendInfo(PLCI *, dword, byte * *, byte);
+static void SendSetupInfo(APPL *, PLCI *, dword, byte * *, byte);
+static void SendSSExtInd(APPL *, PLCI * plci, dword Id, byte * * parms);
+
+static void VSwitchReqInd(PLCI *plci, dword Id, byte **parms);
+
+static void nl_ind(PLCI *);
+
+static byte connect_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
+static byte connect_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
+static byte connect_a_res(dword,word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
+static byte disconnect_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
+static byte disconnect_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
+static byte listen_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
+static byte info_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
+static byte info_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
+static byte alert_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
+static byte facility_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
+static byte facility_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
+static byte connect_b3_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
+static byte connect_b3_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
+static byte connect_b3_a_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
+static byte disconnect_b3_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
+static byte disconnect_b3_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
+static byte data_b3_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
+static byte data_b3_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
+static byte reset_b3_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
+static byte reset_b3_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
+static byte connect_b3_t90_a_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
+static byte select_b_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
+static byte manufacturer_req(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
+static byte manufacturer_res(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
+
+static word get_plci(DIVA_CAPI_ADAPTER *);
+static void add_p(PLCI *, byte, byte *);
+static void add_s(PLCI * plci, byte code, API_PARSE * p);
+static void add_ss(PLCI * plci, byte code, API_PARSE * p);
+static void add_ie(PLCI * plci, byte code, byte * p, word p_length);
+static void add_d(PLCI *, word, byte *);
+static void add_ai(PLCI *, API_PARSE *);
+static word add_b1(PLCI *, API_PARSE *, word, word);
+static word add_b23(PLCI *, API_PARSE *);
+static word add_modem_b23 (PLCI * plci, API_PARSE* bp_parms);
+static void sig_req(PLCI *, byte, byte);
+static void nl_req_ncci(PLCI *, byte, byte);
+static void send_req(PLCI *);
+static void send_data(PLCI *);
+static word plci_remove_check(PLCI *);
+static void listen_check(DIVA_CAPI_ADAPTER *);
+static byte AddInfo(byte **, byte **, byte *, byte *);
+static byte getChannel(API_PARSE *);
+static void IndParse(PLCI *, word *, byte **, byte);
+static byte ie_compare(byte *, byte *);
+static word find_cip(DIVA_CAPI_ADAPTER *, byte *, byte *);
+static word CPN_filter_ok(byte *cpn,DIVA_CAPI_ADAPTER *,word);
+
+/*
+ XON protocol helpers
+ */
+static void channel_flow_control_remove (PLCI * plci);
+static void channel_x_off (PLCI * plci, byte ch, byte flag);
+static void channel_x_on (PLCI * plci, byte ch);
+static void channel_request_xon (PLCI * plci, byte ch);
+static void channel_xmit_xon (PLCI * plci);
+static int channel_can_xon (PLCI * plci, byte ch);
+static void channel_xmit_extended_xon (PLCI * plci);
+
+static byte SendMultiIE(PLCI * plci, dword Id, byte * * parms, byte ie_type, dword info_mask, byte setupParse);
+static word AdvCodecSupport(DIVA_CAPI_ADAPTER *, PLCI *, APPL *, byte);
+static void CodecIdCheck(DIVA_CAPI_ADAPTER *, PLCI *);
+static void SetVoiceChannel(PLCI *, byte *, DIVA_CAPI_ADAPTER * );
+static void VoiceChannelOff(PLCI *plci);
+static void adv_voice_write_coefs (PLCI *plci, word write_command);
+static void adv_voice_clear_config (PLCI *plci);
+
+static word get_b1_facilities (PLCI * plci, byte b1_resource);
+static byte add_b1_facilities (PLCI * plci, byte b1_resource, word b1_facilities);
+static void adjust_b1_facilities (PLCI *plci, byte new_b1_resource, word new_b1_facilities);
+static word adjust_b_process (dword Id, PLCI *plci, byte Rc);
+static void adjust_b1_resource (dword Id, PLCI *plci, API_SAVE *bp_msg, word b1_facilities, word internal_command);
+static void adjust_b_restore (dword Id, PLCI *plci, byte Rc);
+static void reset_b3_command (dword Id, PLCI *plci, byte Rc);
+static void select_b_command (dword Id, PLCI *plci, byte Rc);
+static void fax_connect_ack_command (dword Id, PLCI *plci, byte Rc);
+static void fax_edata_ack_command (dword Id, PLCI *plci, byte Rc);
+static void fax_connect_info_command (dword Id, PLCI *plci, byte Rc);
+static void fax_adjust_b23_command (dword Id, PLCI *plci, byte Rc);
+static void fax_disconnect_command (dword Id, PLCI *plci, byte Rc);
+static void hold_save_command (dword Id, PLCI *plci, byte Rc);
+static void retrieve_restore_command (dword Id, PLCI *plci, byte Rc);
+static void init_b1_config (PLCI *plci);
+static void clear_b1_config (PLCI *plci);
+
+static void dtmf_command (dword Id, PLCI *plci, byte Rc);
+static byte dtmf_request (dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, API_PARSE *msg);
+static void dtmf_confirmation (dword Id, PLCI *plci);
+static void dtmf_indication (dword Id, PLCI *plci, byte *msg, word length);
+static void dtmf_parameter_write (PLCI *plci);
+
+
+static void mixer_set_bchannel_id_esc (PLCI *plci, byte bchannel_id);
+static void mixer_set_bchannel_id (PLCI *plci, byte *chi);
+static void mixer_clear_config (PLCI *plci);
+static void mixer_notify_update (PLCI *plci, byte others);
+static void mixer_command (dword Id, PLCI *plci, byte Rc);
+static byte mixer_request (dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, API_PARSE *msg);
+static void mixer_indication_coefs_set (dword Id, PLCI *plci);
+static void mixer_indication_xconnect_from (dword Id, PLCI *plci, byte *msg, word length);
+static void mixer_indication_xconnect_to (dword Id, PLCI *plci, byte *msg, word length);
+static void mixer_remove (PLCI *plci);
+
+
+static void ec_command (dword Id, PLCI *plci, byte Rc);
+static byte ec_request (dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, API_PARSE *msg);
+static void ec_indication (dword Id, PLCI *plci, byte *msg, word length);
+
+
+static void rtp_connect_b3_req_command (dword Id, PLCI *plci, byte Rc);
+static void rtp_connect_b3_res_command (dword Id, PLCI *plci, byte Rc);
+
+
+static int diva_get_dma_descriptor (PLCI *plci, dword *dma_magic);
+static void diva_free_dma_descriptor (PLCI *plci, int nr);
+
+/*------------------------------------------------------------------*/
+/* external function prototypes */
+/*------------------------------------------------------------------*/
+
+extern byte MapController (byte);
+extern byte UnMapController (byte);
+#define MapId(Id) (((Id) & 0xffffff00L) | MapController ((byte)(Id)))
+#define UnMapId(Id) (((Id) & 0xffffff00L) | UnMapController ((byte)(Id)))
+
+void sendf(APPL *, word, dword, word, byte *, ...);
+void * TransmitBufferSet(APPL * appl, dword ref);
+void * TransmitBufferGet(APPL * appl, void * p);
+void TransmitBufferFree(APPL * appl, void * p);
+void * ReceiveBufferGet(APPL * appl, int Num);
+
+int fax_head_line_time (char *buffer);
+
+
+/*------------------------------------------------------------------*/
+/* Global data definitions */
+/*------------------------------------------------------------------*/
+extern byte max_adapter;
+extern byte max_appl;
+extern DIVA_CAPI_ADAPTER * adapter;
+extern APPL * application;
+
+
+
+
+
+
+
+static byte remove_started = false;
+static PLCI dummy_plci;
+
+
+static struct _ftable {
+ word command;
+ byte * format;
+ byte (* function)(dword, word, DIVA_CAPI_ADAPTER *, PLCI *, APPL *, API_PARSE *);
+} ftable[] = {
+ {_DATA_B3_R, "dwww", data_b3_req},
+ {_DATA_B3_I|RESPONSE, "w", data_b3_res},
+ {_INFO_R, "ss", info_req},
+ {_INFO_I|RESPONSE, "", info_res},
+ {_CONNECT_R, "wsssssssss", connect_req},
+ {_CONNECT_I|RESPONSE, "wsssss", connect_res},
+ {_CONNECT_ACTIVE_I|RESPONSE, "", connect_a_res},
+ {_DISCONNECT_R, "s", disconnect_req},
+ {_DISCONNECT_I|RESPONSE, "", disconnect_res},
+ {_LISTEN_R, "dddss", listen_req},
+ {_ALERT_R, "s", alert_req},
+ {_FACILITY_R, "ws", facility_req},
+ {_FACILITY_I|RESPONSE, "ws", facility_res},
+ {_CONNECT_B3_R, "s", connect_b3_req},
+ {_CONNECT_B3_I|RESPONSE, "ws", connect_b3_res},
+ {_CONNECT_B3_ACTIVE_I|RESPONSE, "", connect_b3_a_res},
+ {_DISCONNECT_B3_R, "s", disconnect_b3_req},
+ {_DISCONNECT_B3_I|RESPONSE, "", disconnect_b3_res},
+ {_RESET_B3_R, "s", reset_b3_req},
+ {_RESET_B3_I|RESPONSE, "", reset_b3_res},
+ {_CONNECT_B3_T90_ACTIVE_I|RESPONSE, "ws", connect_b3_t90_a_res},
+ {_CONNECT_B3_T90_ACTIVE_I|RESPONSE, "", connect_b3_t90_a_res},
+ {_SELECT_B_REQ, "s", select_b_req},
+ {_MANUFACTURER_R, "dws", manufacturer_req},
+ {_MANUFACTURER_I|RESPONSE, "dws", manufacturer_res},
+ {_MANUFACTURER_I|RESPONSE, "", manufacturer_res}
+};
+
+static byte * cip_bc[29][2] = {
+ { "", "" }, /* 0 */
+ { "\x03\x80\x90\xa3", "\x03\x80\x90\xa2" }, /* 1 */
+ { "\x02\x88\x90", "\x02\x88\x90" }, /* 2 */
+ { "\x02\x89\x90", "\x02\x89\x90" }, /* 3 */
+ { "\x03\x90\x90\xa3", "\x03\x90\x90\xa2" }, /* 4 */
+ { "\x03\x91\x90\xa5", "\x03\x91\x90\xa5" }, /* 5 */
+ { "\x02\x98\x90", "\x02\x98\x90" }, /* 6 */
+ { "\x04\x88\xc0\xc6\xe6", "\x04\x88\xc0\xc6\xe6" }, /* 7 */
+ { "\x04\x88\x90\x21\x8f", "\x04\x88\x90\x21\x8f" }, /* 8 */
+ { "\x03\x91\x90\xa5", "\x03\x91\x90\xa5" }, /* 9 */
+ { "", "" }, /* 10 */
+ { "", "" }, /* 11 */
+ { "", "" }, /* 12 */
+ { "", "" }, /* 13 */
+ { "", "" }, /* 14 */
+ { "", "" }, /* 15 */
+
+ { "\x03\x80\x90\xa3", "\x03\x80\x90\xa2" }, /* 16 */
+ { "\x03\x90\x90\xa3", "\x03\x90\x90\xa2" }, /* 17 */
+ { "\x02\x88\x90", "\x02\x88\x90" }, /* 18 */
+ { "\x02\x88\x90", "\x02\x88\x90" }, /* 19 */
+ { "\x02\x88\x90", "\x02\x88\x90" }, /* 20 */
+ { "\x02\x88\x90", "\x02\x88\x90" }, /* 21 */
+ { "\x02\x88\x90", "\x02\x88\x90" }, /* 22 */
+ { "\x02\x88\x90", "\x02\x88\x90" }, /* 23 */
+ { "\x02\x88\x90", "\x02\x88\x90" }, /* 24 */
+ { "\x02\x88\x90", "\x02\x88\x90" }, /* 25 */
+ { "\x03\x91\x90\xa5", "\x03\x91\x90\xa5" }, /* 26 */
+ { "\x03\x91\x90\xa5", "\x03\x91\x90\xa5" }, /* 27 */
+ { "\x02\x88\x90", "\x02\x88\x90" } /* 28 */
+};
+
+static byte * cip_hlc[29] = {
+ "", /* 0 */
+ "", /* 1 */
+ "", /* 2 */
+ "", /* 3 */
+ "", /* 4 */
+ "", /* 5 */
+ "", /* 6 */
+ "", /* 7 */
+ "", /* 8 */
+ "", /* 9 */
+ "", /* 10 */
+ "", /* 11 */
+ "", /* 12 */
+ "", /* 13 */
+ "", /* 14 */
+ "", /* 15 */
+
+ "\x02\x91\x81", /* 16 */
+ "\x02\x91\x84", /* 17 */
+ "\x02\x91\xa1", /* 18 */
+ "\x02\x91\xa4", /* 19 */
+ "\x02\x91\xa8", /* 20 */
+ "\x02\x91\xb1", /* 21 */
+ "\x02\x91\xb2", /* 22 */
+ "\x02\x91\xb5", /* 23 */
+ "\x02\x91\xb8", /* 24 */
+ "\x02\x91\xc1", /* 25 */
+ "\x02\x91\x81", /* 26 */
+ "\x03\x91\xe0\x01", /* 27 */
+ "\x03\x91\xe0\x02" /* 28 */
+};
+
+/*------------------------------------------------------------------*/
+
+#define V120_HEADER_LENGTH 1
+#define V120_HEADER_EXTEND_BIT 0x80
+#define V120_HEADER_BREAK_BIT 0x40
+#define V120_HEADER_C1_BIT 0x04
+#define V120_HEADER_C2_BIT 0x08
+#define V120_HEADER_FLUSH_COND (V120_HEADER_BREAK_BIT | V120_HEADER_C1_BIT | V120_HEADER_C2_BIT)
+
+static byte v120_default_header[] =
+{
+
+ 0x83 /* Ext, BR , res, res, C2 , C1 , B , F */
+
+};
+
+static byte v120_break_header[] =
+{
+
+ 0xc3 | V120_HEADER_BREAK_BIT /* Ext, BR , res, res, C2 , C1 , B , F */
+
+};
+
+
+/*------------------------------------------------------------------*/
+/* API_PUT function */
+/*------------------------------------------------------------------*/
+
+word api_put(APPL * appl, CAPI_MSG * msg)
+{
+ word i, j, k, l, n;
+ word ret;
+ byte c;
+ byte controller;
+ DIVA_CAPI_ADAPTER * a;
+ PLCI * plci;
+ NCCI * ncci_ptr;
+ word ncci;
+ CAPI_MSG *m;
+ API_PARSE msg_parms[MAX_MSG_PARMS+1];
+
+ if (msg->header.length < sizeof (msg->header) ||
+ msg->header.length > MAX_MSG_SIZE) {
+ dbug(1,dprintf("bad len"));
+ return _BAD_MSG;
+ }
+
+ controller = (byte)((msg->header.controller &0x7f)-1);
+
+ /* controller starts with 0 up to (max_adapter - 1) */
+ if ( controller >= max_adapter )
+ {
+ dbug(1,dprintf("invalid ctrl"));
+ return _BAD_MSG;
+ }
+
+ a = &adapter[controller];
+ plci = NULL;
+ if ((msg->header.plci != 0) && (msg->header.plci <= a->max_plci) && !a->adapter_disabled)
+ {
+ dbug(1,dprintf("plci=%x",msg->header.plci));
+ plci = &a->plci[msg->header.plci-1];
+ ncci = GET_WORD(&msg->header.ncci);
+ if (plci->Id
+ && (plci->appl
+ || (plci->State == INC_CON_PENDING)
+ || (plci->State == INC_CON_ALERT)
+ || (msg->header.command == (_DISCONNECT_I|RESPONSE)))
+ && ((ncci == 0)
+ || (msg->header.command == (_DISCONNECT_B3_I|RESPONSE))
+ || ((ncci < MAX_NCCI+1) && (a->ncci_plci[ncci] == plci->Id))))
+ {
+ i = plci->msg_in_read_pos;
+ j = plci->msg_in_write_pos;
+ if (j >= i)
+ {
+ if (j + msg->header.length + MSG_IN_OVERHEAD <= MSG_IN_QUEUE_SIZE)
+ i += MSG_IN_QUEUE_SIZE - j;
+ else
+ j = 0;
+ }
+ else
+ {
+
+ n = (((CAPI_MSG *)(plci->msg_in_queue))->header.length + MSG_IN_OVERHEAD + 3) & 0xfffc;
+
+ if (i > MSG_IN_QUEUE_SIZE - n)
+ i = MSG_IN_QUEUE_SIZE - n + 1;
+ i -= j;
+ }
+
+ if (i <= ((msg->header.length + MSG_IN_OVERHEAD + 3) & 0xfffc))
+
+ {
+ dbug(0,dprintf("Q-FULL1(msg) - len=%d write=%d read=%d wrap=%d free=%d",
+ msg->header.length, plci->msg_in_write_pos,
+ plci->msg_in_read_pos, plci->msg_in_wrap_pos, i));
+
+ return _QUEUE_FULL;
+ }
+ c = false;
+ if ((((byte *) msg) < ((byte *)(plci->msg_in_queue)))
+ || (((byte *) msg) >= ((byte *)(plci->msg_in_queue)) + sizeof(plci->msg_in_queue)))
+ {
+ if (plci->msg_in_write_pos != plci->msg_in_read_pos)
+ c = true;
+ }
+ if (msg->header.command == _DATA_B3_R)
+ {
+ if (msg->header.length < 20)
+ {
+ dbug(1,dprintf("DATA_B3 REQ wrong length %d", msg->header.length));
+ return _BAD_MSG;
+ }
+ ncci_ptr = &(a->ncci[ncci]);
+ n = ncci_ptr->data_pending;
+ l = ncci_ptr->data_ack_pending;
+ k = plci->msg_in_read_pos;
+ while (k != plci->msg_in_write_pos)
+ {
+ if (k == plci->msg_in_wrap_pos)
+ k = 0;
+ if ((((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[k]))->header.command == _DATA_B3_R)
+ && (((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[k]))->header.ncci == ncci))
+ {
+ n++;
+ if (((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[k]))->info.data_b3_req.Flags & 0x0004)
+ l++;
+ }
+
+ k += (((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[k]))->header.length +
+ MSG_IN_OVERHEAD + 3) & 0xfffc;
+
+ }
+ if ((n >= MAX_DATA_B3) || (l >= MAX_DATA_ACK))
+ {
+ dbug(0,dprintf("Q-FULL2(data) - pending=%d/%d ack_pending=%d/%d",
+ ncci_ptr->data_pending, n, ncci_ptr->data_ack_pending, l));
+
+ return _QUEUE_FULL;
+ }
+ if (plci->req_in || plci->internal_command)
+ {
+ if ((((byte *) msg) >= ((byte *)(plci->msg_in_queue)))
+ && (((byte *) msg) < ((byte *)(plci->msg_in_queue)) + sizeof(plci->msg_in_queue)))
+ {
+ dbug(0,dprintf("Q-FULL3(requeue)"));
+
+ return _QUEUE_FULL;
+ }
+ c = true;
+ }
+ }
+ else
+ {
+ if (plci->req_in || plci->internal_command)
+ c = true;
+ else
+ {
+ plci->command = msg->header.command;
+ plci->number = msg->header.number;
+ }
+ }
+ if (c)
+ {
+ dbug(1,dprintf("enqueue msg(0x%04x,0x%x,0x%x) - len=%d write=%d read=%d wrap=%d free=%d",
+ msg->header.command, plci->req_in, plci->internal_command,
+ msg->header.length, plci->msg_in_write_pos,
+ plci->msg_in_read_pos, plci->msg_in_wrap_pos, i));
+ if (j == 0)
+ plci->msg_in_wrap_pos = plci->msg_in_write_pos;
+ m = (CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[j]);
+ for (i = 0; i < msg->header.length; i++)
+ ((byte *)(plci->msg_in_queue))[j++] = ((byte *) msg)[i];
+ if (m->header.command == _DATA_B3_R)
+ {
+
+ m->info.data_b3_req.Data = (dword)(long)(TransmitBufferSet (appl, m->info.data_b3_req.Data));
+
+ }
+
+ j = (j + 3) & 0xfffc;
+
+ *((APPL * *)(&((byte *)(plci->msg_in_queue))[j])) = appl;
+ plci->msg_in_write_pos = j + MSG_IN_OVERHEAD;
+ return 0;
+ }
+ }
+ else
+ {
+ plci = NULL;
+ }
+ }
+ dbug(1,dprintf("com=%x",msg->header.command));
+
+ for(j=0;j<MAX_MSG_PARMS+1;j++) msg_parms[j].length = 0;
+ for(i=0, ret = _BAD_MSG;
+ i<(sizeof(ftable)/sizeof(struct _ftable));
+ i++) {
+
+ if(ftable[i].command==msg->header.command) {
+ /* break loop if the message is correct, otherwise continue scan */
+ /* (for example: CONNECT_B3_T90_ACT_RES has two specifications) */
+ if(!api_parse(msg->info.b,(word)(msg->header.length-12),ftable[i].format,msg_parms)) {
+ ret = 0;
+ break;
+ }
+ for(j=0;j<MAX_MSG_PARMS+1;j++) msg_parms[j].length = 0;
+ }
+ }
+ if(ret) {
+ dbug(1,dprintf("BAD_MSG"));
+ if(plci) plci->command = 0;
+ return ret;
+ }
+
+
+ c = ftable[i].function(GET_DWORD(&msg->header.controller),
+ msg->header.number,
+ a,
+ plci,
+ appl,
+ msg_parms);
+
+ channel_xmit_extended_xon (plci);
+
+ if(c==1) send_req(plci);
+ if(c==2 && plci) plci->req_in = plci->req_in_start = plci->req_out = 0;
+ if(plci && !plci->req_in) plci->command = 0;
+ return 0;
+}
+
+
+/*------------------------------------------------------------------*/
+/* api_parse function, check the format of api messages */
+/*------------------------------------------------------------------*/
+
+word api_parse(byte * msg, word length, byte * format, API_PARSE * parms)
+{
+ word i;
+ word p;
+
+ for(i=0,p=0; format[i]; i++) {
+ if(parms)
+ {
+ parms[i].info = &msg[p];
+ }
+ switch(format[i]) {
+ case 'b':
+ p +=1;
+ break;
+ case 'w':
+ p +=2;
+ break;
+ case 'd':
+ p +=4;
+ break;
+ case 's':
+ if(msg[p]==0xff) {
+ parms[i].info +=2;
+ parms[i].length = msg[p+1] + (msg[p+2]<<8);
+ p +=(parms[i].length +3);
+ }
+ else {
+ parms[i].length = msg[p];
+ p +=(parms[i].length +1);
+ }
+ break;
+ }
+
+ if(p>length) return true;
+ }
+ if(parms) parms[i].info = NULL;
+ return false;
+}
+
+void api_save_msg(API_PARSE *in, byte *format, API_SAVE *out)
+{
+ word i, j, n = 0;
+ byte *p;
+
+ p = out->info;
+ for (i = 0; format[i] != '\0'; i++)
+ {
+ out->parms[i].info = p;
+ out->parms[i].length = in[i].length;
+ switch (format[i])
+ {
+ case 'b':
+ n = 1;
+ break;
+ case 'w':
+ n = 2;
+ break;
+ case 'd':
+ n = 4;
+ break;
+ case 's':
+ n = in[i].length + 1;
+ break;
+ }
+ for (j = 0; j < n; j++)
+ *(p++) = in[i].info[j];
+ }
+ out->parms[i].info = NULL;
+ out->parms[i].length = 0;
+}
+
+void api_load_msg(API_SAVE *in, API_PARSE *out)
+{
+ word i;
+
+ i = 0;
+ do
+ {
+ out[i].info = in->parms[i].info;
+ out[i].length = in->parms[i].length;
+ } while (in->parms[i++].info);
+}
+
+
+/*------------------------------------------------------------------*/
+/* CAPI remove function */
+/*------------------------------------------------------------------*/
+
+word api_remove_start(void)
+{
+ word i;
+ word j;
+
+ if(!remove_started) {
+ remove_started = true;
+ for(i=0;i<max_adapter;i++) {
+ if(adapter[i].request) {
+ for(j=0;j<adapter[i].max_plci;j++) {
+ if(adapter[i].plci[j].Sig.Id) plci_remove(&adapter[i].plci[j]);
+ }
+ }
+ }
+ return 1;
+ }
+ else {
+ for(i=0;i<max_adapter;i++) {
+ if(adapter[i].request) {
+ for(j=0;j<adapter[i].max_plci;j++) {
+ if(adapter[i].plci[j].Sig.Id) return 1;
+ }
+ }
+ }
+ }
+ api_remove_complete();
+ return 0;
+}
+
+
+/*------------------------------------------------------------------*/
+/* internal command queue */
+/*------------------------------------------------------------------*/
+
+static void init_internal_command_queue (PLCI *plci)
+{
+ word i;
+
+ dbug (1, dprintf ("%s,%d: init_internal_command_queue",
+ (char *)(FILE_), __LINE__));
+
+ plci->internal_command = 0;
+ for (i = 0; i < MAX_INTERNAL_COMMAND_LEVELS; i++)
+ plci->internal_command_queue[i] = NULL;
+}
+
+
+static void start_internal_command (dword Id, PLCI *plci, t_std_internal_command command_function)
+{
+ word i;
+
+ dbug (1, dprintf ("[%06lx] %s,%d: start_internal_command",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+
+ if (plci->internal_command == 0)
+ {
+ plci->internal_command_queue[0] = command_function;
+ (* command_function)(Id, plci, OK);
+ }
+ else
+ {
+ i = 1;
+ while (plci->internal_command_queue[i] != NULL)
+ i++;
+ plci->internal_command_queue[i] = command_function;
+ }
+}
+
+
+static void next_internal_command (dword Id, PLCI *plci)
+{
+ word i;
+
+ dbug (1, dprintf ("[%06lx] %s,%d: next_internal_command",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+
+ plci->internal_command = 0;
+ plci->internal_command_queue[0] = NULL;
+ while (plci->internal_command_queue[1] != NULL)
+ {
+ for (i = 0; i < MAX_INTERNAL_COMMAND_LEVELS - 1; i++)
+ plci->internal_command_queue[i] = plci->internal_command_queue[i+1];
+ plci->internal_command_queue[MAX_INTERNAL_COMMAND_LEVELS - 1] = NULL;
+ (*(plci->internal_command_queue[0]))(Id, plci, OK);
+ if (plci->internal_command != 0)
+ return;
+ plci->internal_command_queue[0] = NULL;
+ }
+}
+
+
+/*------------------------------------------------------------------*/
+/* NCCI allocate/remove function */
+/*------------------------------------------------------------------*/
+
+static dword ncci_mapping_bug = 0;
+
+static word get_ncci (PLCI *plci, byte ch, word force_ncci)
+{
+ DIVA_CAPI_ADAPTER *a;
+ word ncci, i, j, k;
+
+ a = plci->adapter;
+ if (!ch || a->ch_ncci[ch])
+ {
+ ncci_mapping_bug++;
+ dbug(1,dprintf("NCCI mapping exists %ld %02x %02x %02x-%02x",
+ ncci_mapping_bug, ch, force_ncci, a->ncci_ch[a->ch_ncci[ch]], a->ch_ncci[ch]));
+ ncci = ch;
+ }
+ else
+ {
+ if (force_ncci)
+ ncci = force_ncci;
+ else
+ {
+ if ((ch < MAX_NCCI+1) && !a->ncci_ch[ch])
+ ncci = ch;
+ else
+ {
+ ncci = 1;
+ while ((ncci < MAX_NCCI+1) && a->ncci_ch[ncci])
+ ncci++;
+ if (ncci == MAX_NCCI+1)
+ {
+ ncci_mapping_bug++;
+ i = 1;
+ do
+ {
+ j = 1;
+ while ((j < MAX_NCCI+1) && (a->ncci_ch[j] != i))
+ j++;
+ k = j;
+ if (j < MAX_NCCI+1)
+ {
+ do
+ {
+ j++;
+ } while ((j < MAX_NCCI+1) && (a->ncci_ch[j] != i));
+ }
+ } while ((i < MAX_NL_CHANNEL+1) && (j < MAX_NCCI+1));
+ if (i < MAX_NL_CHANNEL+1)
+ {
+ dbug(1,dprintf("NCCI mapping overflow %ld %02x %02x %02x-%02x-%02x",
+ ncci_mapping_bug, ch, force_ncci, i, k, j));
+ }
+ else
+ {
+ dbug(1,dprintf("NCCI mapping overflow %ld %02x %02x",
+ ncci_mapping_bug, ch, force_ncci));
+ }
+ ncci = ch;
+ }
+ }
+ a->ncci_plci[ncci] = plci->Id;
+ a->ncci_state[ncci] = IDLE;
+ if (!plci->ncci_ring_list)
+ plci->ncci_ring_list = ncci;
+ else
+ a->ncci_next[ncci] = a->ncci_next[plci->ncci_ring_list];
+ a->ncci_next[plci->ncci_ring_list] = (byte) ncci;
+ }
+ a->ncci_ch[ncci] = ch;
+ a->ch_ncci[ch] = (byte) ncci;
+ dbug(1,dprintf("NCCI mapping established %ld %02x %02x %02x-%02x",
+ ncci_mapping_bug, ch, force_ncci, ch, ncci));
+ }
+ return (ncci);
+}
+
+
+static void ncci_free_receive_buffers (PLCI *plci, word ncci)
+{
+ DIVA_CAPI_ADAPTER *a;
+ APPL *appl;
+ word i, ncci_code;
+ dword Id;
+
+ a = plci->adapter;
+ Id = (((dword) ncci) << 16) | (((word)(plci->Id)) << 8) | a->Id;
+ if (ncci)
+ {
+ if (a->ncci_plci[ncci] == plci->Id)
+ {
+ if (!plci->appl)
+ {
+ ncci_mapping_bug++;
+ dbug(1,dprintf("NCCI mapping appl expected %ld %08lx",
+ ncci_mapping_bug, Id));
+ }
+ else
+ {
+ appl = plci->appl;
+ ncci_code = ncci | (((word) a->Id) << 8);
+ for (i = 0; i < appl->MaxBuffer; i++)
+ {
+ if ((appl->DataNCCI[i] == ncci_code)
+ && (((byte)(appl->DataFlags[i] >> 8)) == plci->Id))
+ {
+ appl->DataNCCI[i] = 0;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ for (ncci = 1; ncci < MAX_NCCI+1; ncci++)
+ {
+ if (a->ncci_plci[ncci] == plci->Id)
+ {
+ if (!plci->appl)
+ {
+ ncci_mapping_bug++;
+ dbug(1,dprintf("NCCI mapping no appl %ld %08lx",
+ ncci_mapping_bug, Id));
+ }
+ else
+ {
+ appl = plci->appl;
+ ncci_code = ncci | (((word) a->Id) << 8);
+ for (i = 0; i < appl->MaxBuffer; i++)
+ {
+ if ((appl->DataNCCI[i] == ncci_code)
+ && (((byte)(appl->DataFlags[i] >> 8)) == plci->Id))
+ {
+ appl->DataNCCI[i] = 0;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+static void cleanup_ncci_data (PLCI *plci, word ncci)
+{
+ NCCI *ncci_ptr;
+
+ if (ncci && (plci->adapter->ncci_plci[ncci] == plci->Id))
+ {
+ ncci_ptr = &(plci->adapter->ncci[ncci]);
+ if (plci->appl)
+ {
+ while (ncci_ptr->data_pending != 0)
+ {
+ if (!plci->data_sent || (ncci_ptr->DBuffer[ncci_ptr->data_out].P != plci->data_sent_ptr))
+ TransmitBufferFree (plci->appl, ncci_ptr->DBuffer[ncci_ptr->data_out].P);
+ (ncci_ptr->data_out)++;
+ if (ncci_ptr->data_out == MAX_DATA_B3)
+ ncci_ptr->data_out = 0;
+ (ncci_ptr->data_pending)--;
+ }
+ }
+ ncci_ptr->data_out = 0;
+ ncci_ptr->data_pending = 0;
+ ncci_ptr->data_ack_out = 0;
+ ncci_ptr->data_ack_pending = 0;
+ }
+}
+
+
+static void ncci_remove (PLCI *plci, word ncci, byte preserve_ncci)
+{
+ DIVA_CAPI_ADAPTER *a;
+ dword Id;
+ word i;
+
+ a = plci->adapter;
+ Id = (((dword) ncci) << 16) | (((word)(plci->Id)) << 8) | a->Id;
+ if (!preserve_ncci)
+ ncci_free_receive_buffers (plci, ncci);
+ if (ncci)
+ {
+ if (a->ncci_plci[ncci] != plci->Id)
+ {
+ ncci_mapping_bug++;
+ dbug(1,dprintf("NCCI mapping doesn't exist %ld %08lx %02x",
+ ncci_mapping_bug, Id, preserve_ncci));
+ }
+ else
+ {
+ cleanup_ncci_data (plci, ncci);
+ dbug(1,dprintf("NCCI mapping released %ld %08lx %02x %02x-%02x",
+ ncci_mapping_bug, Id, preserve_ncci, a->ncci_ch[ncci], ncci));
+ a->ch_ncci[a->ncci_ch[ncci]] = 0;
+ if (!preserve_ncci)
+ {
+ a->ncci_ch[ncci] = 0;
+ a->ncci_plci[ncci] = 0;
+ a->ncci_state[ncci] = IDLE;
+ i = plci->ncci_ring_list;
+ while ((i != 0) && (a->ncci_next[i] != plci->ncci_ring_list) && (a->ncci_next[i] != ncci))
+ i = a->ncci_next[i];
+ if ((i != 0) && (a->ncci_next[i] == ncci))
+ {
+ if (i == ncci)
+ plci->ncci_ring_list = 0;
+ else if (plci->ncci_ring_list == ncci)
+ plci->ncci_ring_list = i;
+ a->ncci_next[i] = a->ncci_next[ncci];
+ }
+ a->ncci_next[ncci] = 0;
+ }
+ }
+ }
+ else
+ {
+ for (ncci = 1; ncci < MAX_NCCI+1; ncci++)
+ {
+ if (a->ncci_plci[ncci] == plci->Id)
+ {
+ cleanup_ncci_data (plci, ncci);
+ dbug(1,dprintf("NCCI mapping released %ld %08lx %02x %02x-%02x",
+ ncci_mapping_bug, Id, preserve_ncci, a->ncci_ch[ncci], ncci));
+ a->ch_ncci[a->ncci_ch[ncci]] = 0;
+ if (!preserve_ncci)
+ {
+ a->ncci_ch[ncci] = 0;
+ a->ncci_plci[ncci] = 0;
+ a->ncci_state[ncci] = IDLE;
+ a->ncci_next[ncci] = 0;
+ }
+ }
+ }
+ if (!preserve_ncci)
+ plci->ncci_ring_list = 0;
+ }
+}
+
+
+/*------------------------------------------------------------------*/
+/* PLCI remove function */
+/*------------------------------------------------------------------*/
+
+static void plci_free_msg_in_queue (PLCI *plci)
+{
+ word i;
+
+ if (plci->appl)
+ {
+ i = plci->msg_in_read_pos;
+ while (i != plci->msg_in_write_pos)
+ {
+ if (i == plci->msg_in_wrap_pos)
+ i = 0;
+ if (((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[i]))->header.command == _DATA_B3_R)
+ {
+
+ TransmitBufferFree (plci->appl,
+ (byte *)(long)(((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[i]))->info.data_b3_req.Data));
+
+ }
+
+ i += (((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[i]))->header.length +
+ MSG_IN_OVERHEAD + 3) & 0xfffc;
+
+ }
+ }
+ plci->msg_in_write_pos = MSG_IN_QUEUE_SIZE;
+ plci->msg_in_read_pos = MSG_IN_QUEUE_SIZE;
+ plci->msg_in_wrap_pos = MSG_IN_QUEUE_SIZE;
+}
+
+
+static void plci_remove(PLCI * plci)
+{
+
+ if(!plci) {
+ dbug(1,dprintf("plci_remove(no plci)"));
+ return;
+ }
+ init_internal_command_queue (plci);
+ dbug(1,dprintf("plci_remove(%x,tel=%x)",plci->Id,plci->tel));
+ if(plci_remove_check(plci))
+ {
+ return;
+ }
+ if (plci->Sig.Id == 0xff)
+ {
+ dbug(1,dprintf("D-channel X.25 plci->NL.Id:%0x", plci->NL.Id));
+ if (plci->NL.Id && !plci->nl_remove_id)
+ {
+ nl_req_ncci(plci,REMOVE,0);
+ send_req(plci);
+ }
+ }
+ else
+ {
+ if (!plci->sig_remove_id
+ && (plci->Sig.Id
+ || (plci->req_in!=plci->req_out)
+ || (plci->nl_req || plci->sig_req)))
+ {
+ sig_req(plci,HANGUP,0);
+ send_req(plci);
+ }
+ }
+ ncci_remove (plci, 0, false);
+ plci_free_msg_in_queue (plci);
+
+ plci->channels = 0;
+ plci->appl = NULL;
+ if ((plci->State == INC_CON_PENDING) || (plci->State == INC_CON_ALERT))
+ plci->State = OUTG_DIS_PENDING;
+}
+
+/*------------------------------------------------------------------*/
+/* Application Group function helpers */
+/*------------------------------------------------------------------*/
+
+static void set_group_ind_mask (PLCI *plci)
+{
+ word i;
+
+ for (i = 0; i < C_IND_MASK_DWORDS; i++)
+ plci->group_optimization_mask_table[i] = 0xffffffffL;
+}
+
+static void clear_group_ind_mask_bit (PLCI *plci, word b)
+{
+ plci->group_optimization_mask_table[b >> 5] &= ~(1L << (b & 0x1f));
+}
+
+static byte test_group_ind_mask_bit (PLCI *plci, word b)
+{
+ return ((plci->group_optimization_mask_table[b >> 5] & (1L << (b & 0x1f))) != 0);
+}
+
+/*------------------------------------------------------------------*/
+/* c_ind_mask operations for arbitrary MAX_APPL */
+/*------------------------------------------------------------------*/
+
+static void clear_c_ind_mask (PLCI *plci)
+{
+ word i;
+
+ for (i = 0; i < C_IND_MASK_DWORDS; i++)
+ plci->c_ind_mask_table[i] = 0;
+}
+
+static byte c_ind_mask_empty (PLCI *plci)
+{
+ word i;
+
+ i = 0;
+ while ((i < C_IND_MASK_DWORDS) && (plci->c_ind_mask_table[i] == 0))
+ i++;
+ return (i == C_IND_MASK_DWORDS);
+}
+
+static void set_c_ind_mask_bit (PLCI *plci, word b)
+{
+ plci->c_ind_mask_table[b >> 5] |= (1L << (b & 0x1f));
+}
+
+static void clear_c_ind_mask_bit (PLCI *plci, word b)
+{
+ plci->c_ind_mask_table[b >> 5] &= ~(1L << (b & 0x1f));
+}
+
+static byte test_c_ind_mask_bit (PLCI *plci, word b)
+{
+ return ((plci->c_ind_mask_table[b >> 5] & (1L << (b & 0x1f))) != 0);
+}
+
+static void dump_c_ind_mask (PLCI *plci)
+{
+static char hex_digit_table[0x10] =
+ {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
+ word i, j, k;
+ dword d;
+ char *p;
+ char buf[40];
+
+ for (i = 0; i < C_IND_MASK_DWORDS; i += 4)
+ {
+ p = buf + 36;
+ *p = '\0';
+ for (j = 0; j < 4; j++)
+ {
+ if (i+j < C_IND_MASK_DWORDS)
+ {
+ d = plci->c_ind_mask_table[i+j];
+ for (k = 0; k < 8; k++)
+ {
+ *(--p) = hex_digit_table[d & 0xf];
+ d >>= 4;
+ }
+ }
+ else if (i != 0)
+ {
+ for (k = 0; k < 8; k++)
+ *(--p) = ' ';
+ }
+ *(--p) = ' ';
+ }
+ dbug(1,dprintf ("c_ind_mask =%s", (char *) p));
+ }
+}
+
+
+
+
+
+#define dump_plcis(a)
+
+
+
+/*------------------------------------------------------------------*/
+/* translation function for each message */
+/*------------------------------------------------------------------*/
+
+byte connect_req(dword Id, word Number, DIVA_CAPI_ADAPTER * a, PLCI * plci, APPL * appl, API_PARSE * parms)
+{
+ word ch;
+ word i;
+ word Info;
+ word CIP;
+ byte LinkLayer;
+ API_PARSE * ai;
+ API_PARSE * bp;
+ API_PARSE ai_parms[5];
+ word channel = 0;
+ dword ch_mask;
+ byte m;
+ static byte esc_chi[35] = {0x02,0x18,0x01};
+ static byte lli[2] = {0x01,0x00};
+ byte noCh = 0;
+ word dir = 0;
+ byte *p_chi = "";
+
+ for(i=0;i<5;i++) ai_parms[i].length = 0;
+
+ dbug(1,dprintf("connect_req(%d)",parms->length));
+ Info = _WRONG_IDENTIFIER;
+ if(a)
+ {
+ if(a->adapter_disabled)
+ {
+ dbug(1,dprintf("adapter disabled"));
+ Id = ((word)1<<8)|a->Id;
+ sendf(appl,_CONNECT_R|CONFIRM,Id,Number,"w",0);
+ sendf(appl, _DISCONNECT_I, Id, 0, "w", _L1_ERROR);
+ return false;
+ }
+ Info = _OUT_OF_PLCI;
+ if((i=get_plci(a)))
+ {
+ Info = 0;
+ plci = &a->plci[i-1];
+ plci->appl = appl;
+ plci->call_dir = CALL_DIR_OUT | CALL_DIR_ORIGINATE;
+ /* check 'external controller' bit for codec support */
+ if(Id & EXT_CONTROLLER)
+ {
+ if(AdvCodecSupport(a, plci, appl, 0) )
+ {
+ plci->Id = 0;
+ sendf(appl, _CONNECT_R|CONFIRM, Id, Number, "w", _WRONG_IDENTIFIER);
+ return 2;
+ }
+ }
+ ai = &parms[9];
+ bp = &parms[5];
+ ch = 0;
+ if(bp->length)LinkLayer = bp->info[3];
+ else LinkLayer = 0;
+ if(ai->length)
+ {
+ ch=0xffff;
+ if(!api_parse(&ai->info[1],(word)ai->length,"ssss",ai_parms))
+ {
+ ch = 0;
+ if(ai_parms[0].length)
+ {
+ ch = GET_WORD(ai_parms[0].info+1);
+ if(ch>4) ch=0; /* safety -> ignore ChannelID */
+ if(ch==4) /* explizit CHI in message */
+ {
+ /* check length of B-CH struct */
+ if((ai_parms[0].info)[3]>=1)
+ {
+ if((ai_parms[0].info)[4]==CHI)
+ {
+ p_chi = &((ai_parms[0].info)[5]);
+ }
+ else
+ {
+ p_chi = &((ai_parms[0].info)[3]);
+ }
+ if(p_chi[0]>35) /* check length of channel ID */
+ {
+ Info = _WRONG_MESSAGE_FORMAT;
+ }
+ }
+ else Info = _WRONG_MESSAGE_FORMAT;
+ }
+
+ if(ch==3 && ai_parms[0].length>=7 && ai_parms[0].length<=36)
+ {
+ dir = GET_WORD(ai_parms[0].info+3);
+ ch_mask = 0;
+ m = 0x3f;
+ for(i=0; i+5<=ai_parms[0].length; i++)
+ {
+ if(ai_parms[0].info[i+5]!=0)
+ {
+ if((ai_parms[0].info[i+5] | m) != 0xff)
+ Info = _WRONG_MESSAGE_FORMAT;
+ else
+ {
+ if (ch_mask == 0)
+ channel = i;
+ ch_mask |= 1L << i;
+ }
+ }
+ m = 0;
+ }
+ if (ch_mask == 0)
+ Info = _WRONG_MESSAGE_FORMAT;
+ if (!Info)
+ {
+ if ((ai_parms[0].length == 36) || (ch_mask != ((dword)(1L << channel))))
+ {
+ esc_chi[0] = (byte)(ai_parms[0].length - 2);
+ for(i=0; i+5<=ai_parms[0].length; i++)
+ esc_chi[i+3] = ai_parms[0].info[i+5];
+ }
+ else
+ esc_chi[0] = 2;
+ esc_chi[2] = (byte)channel;
+ plci->b_channel = (byte)channel; /* not correct for ETSI ch 17..31 */
+ add_p(plci,LLI,lli);
+ add_p(plci,ESC,esc_chi);
+ plci->State = LOCAL_CONNECT;
+ if(!dir) plci->call_dir |= CALL_DIR_FORCE_OUTG_NL; /* dir 0=DTE, 1=DCE */
+ }
+ }
+ }
+ }
+ else Info = _WRONG_MESSAGE_FORMAT;
+ }
+
+ dbug(1,dprintf("ch=%x,dir=%x,p_ch=%d",ch,dir,channel));
+ plci->command = _CONNECT_R;
+ plci->number = Number;
+ /* x.31 or D-ch free SAPI in LinkLayer? */
+ if(ch==1 && LinkLayer!=3 && LinkLayer!=12) noCh = true;
+ if((ch==0 || ch==2 || noCh || ch==3 || ch==4) && !Info)
+ {
+ /* B-channel used for B3 connections (ch==0), or no B channel */
+ /* is used (ch==2) or perm. connection (3) is used do a CALL */
+ if(noCh) Info = add_b1(plci,&parms[5],2,0); /* no resource */
+ else Info = add_b1(plci,&parms[5],ch,0);
+ add_s(plci,OAD,&parms[2]);
+ add_s(plci,OSA,&parms[4]);
+ add_s(plci,BC,&parms[6]);
+ add_s(plci,LLC,&parms[7]);
+ add_s(plci,HLC,&parms[8]);
+ CIP = GET_WORD(parms[0].info);
+ if (a->Info_Mask[appl->Id-1] & 0x200)
+ {
+ /* early B3 connect (CIP mask bit 9) no release after a disc */
+ add_p(plci,LLI,"\x01\x01");
+ }
+ if(GET_WORD(parms[0].info)<29) {
+ add_p(plci,BC,cip_bc[GET_WORD(parms[0].info)][a->u_law]);
+ add_p(plci,HLC,cip_hlc[GET_WORD(parms[0].info)]);
+ }
+ add_p(plci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+ sig_req(plci,ASSIGN,DSIG_ID);
+ }
+ else if(ch==1) {
+
+ /* D-Channel used for B3 connections */
+ plci->Sig.Id = 0xff;
+ Info = 0;
+ }
+
+ if(!Info && ch!=2 && !noCh ) {
+ Info = add_b23(plci,&parms[5]);
+ if(!Info) {
+ if(!(plci->tel && !plci->adv_nl))nl_req_ncci(plci,ASSIGN,0);
+ }
+ }
+
+ if(!Info)
+ {
+ if(ch==0 || ch==2 || ch==3 || noCh || ch==4)
+ {
+ if(plci->spoofed_msg==SPOOFING_REQUIRED)
+ {
+ api_save_msg(parms, "wsssssssss", &plci->saved_msg);
+ plci->spoofed_msg = CALL_REQ;
+ plci->internal_command = BLOCK_PLCI;
+ plci->command = 0;
+ dbug(1,dprintf("Spoof"));
+ send_req(plci);
+ return false;
+ }
+ if(ch==4)add_p(plci,CHI,p_chi);
+ add_s(plci,CPN,&parms[1]);
+ add_s(plci,DSA,&parms[3]);
+ if(noCh) add_p(plci,ESC,"\x02\x18\xfd"); /* D-channel, no B-L3 */
+ add_ai(plci,&parms[9]);
+ if(!dir)sig_req(plci,CALL_REQ,0);
+ else
+ {
+ plci->command = PERM_LIST_REQ;
+ plci->appl = appl;
+ sig_req(plci,LISTEN_REQ,0);
+ send_req(plci);
+ return false;
+ }
+ }
+ send_req(plci);
+ return false;
+ }
+ plci->Id = 0;
+ }
+ }
+ sendf(appl,
+ _CONNECT_R|CONFIRM,
+ Id,
+ Number,
+ "w",Info);
+ return 2;
+}
+
+byte connect_res(dword Id, word Number, DIVA_CAPI_ADAPTER * a, PLCI * plci, APPL * appl, API_PARSE * parms)
+{
+ word i, Info;
+ word Reject;
+ static byte cau_t[] = {0,0,0x90,0x91,0xac,0x9d,0x86,0xd8,0x9b};
+ static byte esc_t[] = {0x03,0x08,0x00,0x00};
+ API_PARSE * ai;
+ API_PARSE ai_parms[5];
+ word ch=0;
+
+ if(!plci) {
+ dbug(1,dprintf("connect_res(no plci)"));
+ return 0; /* no plci, no send */
+ }
+
+ dbug(1,dprintf("connect_res(State=0x%x)",plci->State));
+ for(i=0;i<5;i++) ai_parms[i].length = 0;
+ ai = &parms[5];
+ dbug(1,dprintf("ai->length=%d",ai->length));
+
+ if(ai->length)
+ {
+ if(!api_parse(&ai->info[1],(word)ai->length,"ssss",ai_parms))
+ {
+ dbug(1,dprintf("ai_parms[0].length=%d/0x%x",ai_parms[0].length,GET_WORD(ai_parms[0].info+1)));
+ ch = 0;
+ if(ai_parms[0].length)
+ {
+ ch = GET_WORD(ai_parms[0].info+1);
+ dbug(1,dprintf("BCH-I=0x%x",ch));
+ }
+ }
+ }
+
+ if(plci->State==INC_CON_CONNECTED_ALERT)
+ {
+ dbug(1,dprintf("Connected Alert Call_Res"));
+ if (a->Info_Mask[appl->Id-1] & 0x200)
+ {
+ /* early B3 connect (CIP mask bit 9) no release after a disc */
+ add_p(plci,LLI,"\x01\x01");
+ }
+ add_s(plci, CONN_NR, &parms[2]);
+ add_s(plci, LLC, &parms[4]);
+ add_ai(plci, &parms[5]);
+ plci->State = INC_CON_ACCEPT;
+ sig_req(plci, CALL_RES,0);
+ return 1;
+ }
+ else if(plci->State==INC_CON_PENDING || plci->State==INC_CON_ALERT) {
+ clear_c_ind_mask_bit (plci, (word)(appl->Id-1));
+ dump_c_ind_mask (plci);
+ Reject = GET_WORD(parms[0].info);
+ dbug(1,dprintf("Reject=0x%x",Reject));
+ if(Reject)
+ {
+ if(c_ind_mask_empty (plci))
+ {
+ if((Reject&0xff00)==0x3400)
+ {
+ esc_t[2] = ((byte)(Reject&0x00ff)) | 0x80;
+ add_p(plci,ESC,esc_t);
+ add_ai(plci, &parms[5]);
+ sig_req(plci,REJECT,0);
+ }
+ else if(Reject==1 || Reject>9)
+ {
+ add_ai(plci, &parms[5]);
+ sig_req(plci,HANGUP,0);
+ }
+ else
+ {
+ esc_t[2] = cau_t[(Reject&0x000f)];
+ add_p(plci,ESC,esc_t);
+ add_ai(plci, &parms[5]);
+ sig_req(plci,REJECT,0);
+ }
+ plci->appl = appl;
+ }
+ else
+ {
+ sendf(appl, _DISCONNECT_I, Id, 0, "w", _OTHER_APPL_CONNECTED);
+ }
+ }
+ else {
+ plci->appl = appl;
+ if(Id & EXT_CONTROLLER){
+ if(AdvCodecSupport(a, plci, appl, 0)){
+ dbug(1,dprintf("connect_res(error from AdvCodecSupport)"));
+ sig_req(plci,HANGUP,0);
+ return 1;
+ }
+ if(plci->tel == ADV_VOICE && a->AdvCodecPLCI)
+ {
+ Info = add_b23(plci, &parms[1]);
+ if (Info)
+ {
+ dbug(1,dprintf("connect_res(error from add_b23)"));
+ sig_req(plci,HANGUP,0);
+ return 1;
+ }
+ if(plci->adv_nl)
+ {
+ nl_req_ncci(plci, ASSIGN, 0);
+ }
+ }
+ }
+ else
+ {
+ plci->tel = 0;
+ if(ch!=2)
+ {
+ Info = add_b23(plci, &parms[1]);
+ if (Info)
+ {
+ dbug(1,dprintf("connect_res(error from add_b23 2)"));
+ sig_req(plci,HANGUP,0);
+ return 1;
+ }
+ }
+ nl_req_ncci(plci, ASSIGN, 0);
+ }
+
+ if(plci->spoofed_msg==SPOOFING_REQUIRED)
+ {
+ api_save_msg(parms, "wsssss", &plci->saved_msg);
+ plci->spoofed_msg = CALL_RES;
+ plci->internal_command = BLOCK_PLCI;
+ plci->command = 0;
+ dbug(1,dprintf("Spoof"));
+ }
+ else
+ {
+ add_b1 (plci, &parms[1], ch, plci->B1_facilities);
+ if (a->Info_Mask[appl->Id-1] & 0x200)
+ {
+ /* early B3 connect (CIP mask bit 9) no release after a disc */
+ add_p(plci,LLI,"\x01\x01");
+ }
+ add_s(plci, CONN_NR, &parms[2]);
+ add_s(plci, LLC, &parms[4]);
+ add_ai(plci, &parms[5]);
+ plci->State = INC_CON_ACCEPT;
+ sig_req(plci, CALL_RES,0);
+ }
+
+ for(i=0; i<max_appl; i++) {
+ if(test_c_ind_mask_bit (plci, i)) {
+ sendf(&application[i], _DISCONNECT_I, Id, 0, "w", _OTHER_APPL_CONNECTED);
+ }
+ }
+ }
+ }
+ return 1;
+}
+
+byte connect_a_res(dword Id, word Number, DIVA_CAPI_ADAPTER * a, PLCI * plci, APPL * appl, API_PARSE * msg)
+{
+ dbug(1,dprintf("connect_a_res"));
+ return false;
+}
+
+byte disconnect_req(dword Id, word Number, DIVA_CAPI_ADAPTER * a, PLCI * plci, APPL * appl, API_PARSE * msg)
+{
+ word Info;
+ word i;
+
+ dbug(1,dprintf("disconnect_req"));
+
+ Info = _WRONG_IDENTIFIER;
+
+ if(plci)
+ {
+ if(plci->State==INC_CON_PENDING || plci->State==INC_CON_ALERT)
+ {
+ clear_c_ind_mask_bit (plci, (word)(appl->Id-1));
+ plci->appl = appl;
+ for(i=0; i<max_appl; i++)
+ {
+ if(test_c_ind_mask_bit (plci, i))
+ sendf(&application[i], _DISCONNECT_I, Id, 0, "w", 0);
+ }
+ plci->State = OUTG_DIS_PENDING;
+ }
+ if(plci->Sig.Id && plci->appl)
+ {
+ Info = 0;
+ if(plci->Sig.Id!=0xff)
+ {
+ if(plci->State!=INC_DIS_PENDING)
+ {
+ add_ai(plci, &msg[0]);
+ sig_req(plci,HANGUP,0);
+ plci->State = OUTG_DIS_PENDING;
+ return 1;
+ }
+ }
+ else
+ {
+ if (plci->NL.Id && !plci->nl_remove_id)
+ {
+ mixer_remove (plci);
+ nl_req_ncci(plci,REMOVE,0);
+ sendf(appl,_DISCONNECT_R|CONFIRM,Id,Number,"w",0);
+ sendf(appl, _DISCONNECT_I, Id, 0, "w", 0);
+ plci->State = INC_DIS_PENDING;
+ }
+ return 1;
+ }
+ }
+ }
+
+ if(!appl) return false;
+ sendf(appl, _DISCONNECT_R|CONFIRM, Id, Number, "w",Info);
+ return false;
+}
+
+byte disconnect_res(dword Id, word Number, DIVA_CAPI_ADAPTER * a, PLCI * plci, APPL * appl, API_PARSE * msg)
+{
+ dbug(1,dprintf("disconnect_res"));
+ if(plci)
+ {
+ /* clear ind mask bit, just in case of collsion of */
+ /* DISCONNECT_IND and CONNECT_RES */
+ clear_c_ind_mask_bit (plci, (word)(appl->Id-1));
+ ncci_free_receive_buffers (plci, 0);
+ if(plci_remove_check(plci))
+ {
+ return 0;
+ }
+ if(plci->State==INC_DIS_PENDING
+ || plci->State==SUSPENDING) {
+ if(c_ind_mask_empty (plci)) {
+ if(plci->State!=SUSPENDING)plci->State = IDLE;
+ dbug(1,dprintf("chs=%d",plci->channels));
+ if(!plci->channels) {
+ plci_remove(plci);
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+byte listen_req(dword Id, word Number, DIVA_CAPI_ADAPTER * a, PLCI * plci, APPL * appl, API_PARSE * parms)
+{
+ word Info;
+ byte i;
+
+ dbug(1,dprintf("listen_req(Appl=0x%x)",appl->Id));
+
+ Info = _WRONG_IDENTIFIER;
+ if(a) {
+ Info = 0;
+ a->Info_Mask[appl->Id-1] = GET_DWORD(parms[0].info);
+ a->CIP_Mask[appl->Id-1] = GET_DWORD(parms[1].info);
+ dbug(1,dprintf("CIP_MASK=0x%lx",GET_DWORD(parms[1].info)));
+ if (a->Info_Mask[appl->Id-1] & 0x200){ /* early B3 connect provides */
+ a->Info_Mask[appl->Id-1] |= 0x10; /* call progression infos */
+ }
+
+ /* check if external controller listen and switch listen on or off*/
+ if(Id&EXT_CONTROLLER && GET_DWORD(parms[1].info)){
+ if(a->profile.Global_Options & ON_BOARD_CODEC) {
+ dummy_plci.State = IDLE;
+ a->codec_listen[appl->Id-1] = &dummy_plci;
+ a->TelOAD[0] = (byte)(parms[3].length);
+ for(i=1;parms[3].length>=i && i<22;i++) {
+ a->TelOAD[i] = parms[3].info[i];
+ }
+ a->TelOAD[i] = 0;
+ a->TelOSA[0] = (byte)(parms[4].length);
+ for(i=1;parms[4].length>=i && i<22;i++) {
+ a->TelOSA[i] = parms[4].info[i];
+ }
+ a->TelOSA[i] = 0;
+ }
+ else Info = 0x2002; /* wrong controller, codec not supported */
+ }
+ else{ /* clear listen */
+ a->codec_listen[appl->Id-1] = (PLCI *)0;
+ }
+ }
+ sendf(appl,
+ _LISTEN_R|CONFIRM,
+ Id,
+ Number,
+ "w",Info);
+
+ if (a) listen_check(a);
+ return false;
+}
+
+byte info_req(dword Id, word Number, DIVA_CAPI_ADAPTER * a, PLCI * plci, APPL * appl, API_PARSE * msg)
+{
+ word i;
+ API_PARSE * ai;
+ PLCI * rc_plci = NULL;
+ API_PARSE ai_parms[5];
+ word Info = 0;
+
+ dbug(1,dprintf("info_req"));
+ for(i=0;i<5;i++) ai_parms[i].length = 0;
+
+ ai = &msg[1];
+
+ if(ai->length)
+ {
+ if(api_parse(&ai->info[1],(word)ai->length,"ssss",ai_parms))
+ {
+ dbug(1,dprintf("AddInfo wrong"));
+ Info = _WRONG_MESSAGE_FORMAT;
+ }
+ }
+ if(!a) Info = _WRONG_STATE;
+
+ if(!Info && plci)
+ { /* no fac, with CPN, or KEY */
+ rc_plci = plci;
+ if(!ai_parms[3].length && plci->State && (msg[0].length || ai_parms[1].length) )
+ {
+ /* overlap sending option */
+ dbug(1,dprintf("OvlSnd"));
+ add_s(plci,CPN,&msg[0]);
+ add_s(plci,KEY,&ai_parms[1]);
+ sig_req(plci,INFO_REQ,0);
+ send_req(plci);
+ return false;
+ }
+
+ if(plci->State && ai_parms[2].length)
+ {
+ /* User_Info option */
+ dbug(1,dprintf("UUI"));
+ add_s(plci,UUI,&ai_parms[2]);
+ sig_req(plci,USER_DATA,0);
+ }
+ else if(plci->State && ai_parms[3].length)
+ {
+ /* Facility option */
+ dbug(1,dprintf("FAC"));
+ add_s(plci,CPN,&msg[0]);
+ add_ai(plci, &msg[1]);
+ sig_req(plci,FACILITY_REQ,0);
+ }
+ else
+ {
+ Info = _WRONG_STATE;
+ }
+ }
+ else if((ai_parms[1].length || ai_parms[2].length || ai_parms[3].length) && !Info)
+ {
+ /* NCR_Facility option -> send UUI and Keypad too */
+ dbug(1,dprintf("NCR_FAC"));
+ if((i=get_plci(a)))
+ {
+ rc_plci = &a->plci[i-1];
+ appl->NullCREnable = true;
+ rc_plci->internal_command = C_NCR_FAC_REQ;
+ rc_plci->appl = appl;
+ add_p(rc_plci,CAI,"\x01\x80");
+ add_p(rc_plci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+ sig_req(rc_plci,ASSIGN,DSIG_ID);
+ send_req(rc_plci);
+ }
+ else
+ {
+ Info = _OUT_OF_PLCI;
+ }
+
+ if(!Info)
+ {
+ add_s(rc_plci,CPN,&msg[0]);
+ add_ai(rc_plci, &msg[1]);
+ sig_req(rc_plci,NCR_FACILITY,0);
+ send_req(rc_plci);
+ return false;
+ /* for application controlled supplementary services */
+ }
+ }
+
+ if (!rc_plci)
+ {
+ Info = _WRONG_MESSAGE_FORMAT;
+ }
+
+ if(!Info)
+ {
+ send_req(rc_plci);
+ }
+ else
+ { /* appl is not assigned to a PLCI or error condition */
+ dbug(1,dprintf("localInfoCon"));
+ sendf(appl,
+ _INFO_R|CONFIRM,
+ Id,
+ Number,
+ "w",Info);
+ }
+ return false;
+}
+
+byte info_res(dword Id, word Number, DIVA_CAPI_ADAPTER * a, PLCI * plci, APPL * appl, API_PARSE * msg)
+{
+ dbug(1,dprintf("info_res"));
+ return false;
+}
+
+byte alert_req(dword Id, word Number, DIVA_CAPI_ADAPTER * a, PLCI * plci, APPL * appl, API_PARSE * msg)
+{
+ word Info;
+ byte ret;
+
+ dbug(1,dprintf("alert_req"));
+
+ Info = _WRONG_IDENTIFIER;
+ ret = false;
+ if(plci) {
+ Info = _ALERT_IGNORED;
+ if(plci->State!=INC_CON_ALERT) {
+ Info = _WRONG_STATE;
+ if(plci->State==INC_CON_PENDING) {
+ Info = 0;
+ plci->State=INC_CON_ALERT;
+ add_ai(plci, &msg[0]);
+ sig_req(plci,CALL_ALERT,0);
+ ret = 1;
+ }
+ }
+ }
+ sendf(appl,
+ _ALERT_R|CONFIRM,
+ Id,
+ Number,
+ "w",Info);
+ return ret;
+}
+
+byte facility_req(dword Id, word Number, DIVA_CAPI_ADAPTER * a, PLCI * plci, APPL * appl, API_PARSE * msg)
+{
+ word Info = 0;
+ word i = 0;
+
+ word selector;
+ word SSreq;
+ long relatedPLCIvalue;
+ DIVA_CAPI_ADAPTER * relatedadapter;
+ byte * SSparms = "";
+ byte RCparms[] = "\x05\x00\x00\x02\x00\x00";
+ byte SSstruct[] = "\x09\x00\x00\x06\x00\x00\x00\x00\x00\x00";
+ API_PARSE * parms;
+ API_PARSE ss_parms[11];
+ PLCI *rplci;
+ byte cai[15];
+ dword d;
+ API_PARSE dummy;
+
+ dbug(1,dprintf("facility_req"));
+ for(i=0;i<9;i++) ss_parms[i].length = 0;
+
+ parms = &msg[1];
+
+ if(!a)
+ {
+ dbug(1,dprintf("wrong Ctrl"));
+ Info = _WRONG_IDENTIFIER;
+ }
+
+ selector = GET_WORD(msg[0].info);
+
+ if(!Info)
+ {
+ switch(selector)
+ {
+ case SELECTOR_HANDSET:
+ Info = AdvCodecSupport(a, plci, appl, HOOK_SUPPORT);
+ break;
+
+ case SELECTOR_SU_SERV:
+ if(!msg[1].length)
+ {
+ Info = _WRONG_MESSAGE_FORMAT;
+ break;
+ }
+ SSreq = GET_WORD(&(msg[1].info[1]));
+ PUT_WORD(&RCparms[1],SSreq);
+ SSparms = RCparms;
+ switch(SSreq)
+ {
+ case S_GET_SUPPORTED_SERVICES:
+ if((i=get_plci(a)))
+ {
+ rplci = &a->plci[i-1];
+ rplci->appl = appl;
+ add_p(rplci,CAI,"\x01\x80");
+ add_p(rplci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+ sig_req(rplci,ASSIGN,DSIG_ID);
+ send_req(rplci);
+ }
+ else
+ {
+ PUT_DWORD(&SSstruct[6], MASK_TERMINAL_PORTABILITY);
+ SSparms = (byte *)SSstruct;
+ break;
+ }
+ rplci->internal_command = GETSERV_REQ_PEND;
+ rplci->number = Number;
+ rplci->appl = appl;
+ sig_req(rplci,S_SUPPORTED,0);
+ send_req(rplci);
+ return false;
+ break;
+
+ case S_LISTEN:
+ if(parms->length==7)
+ {
+ if(api_parse(&parms->info[1],(word)parms->length,"wbd",ss_parms))
+ {
+ dbug(1,dprintf("format wrong"));
+ Info = _WRONG_MESSAGE_FORMAT;
+ break;
+ }
+ }
+ else
+ {
+ Info = _WRONG_MESSAGE_FORMAT;
+ break;
+ }
+ a->Notification_Mask[appl->Id-1] = GET_DWORD(ss_parms[2].info);
+ if(a->Notification_Mask[appl->Id-1] & SMASK_MWI) /* MWI active? */
+ {
+ if((i=get_plci(a)))
+ {
+ rplci = &a->plci[i-1];
+ rplci->appl = appl;
+ add_p(rplci,CAI,"\x01\x80");
+ add_p(rplci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+ sig_req(rplci,ASSIGN,DSIG_ID);
+ send_req(rplci);
+ }
+ else
+ {
+ break;
+ }
+ rplci->internal_command = GET_MWI_STATE;
+ rplci->number = Number;
+ sig_req(rplci,MWI_POLL,0);
+ send_req(rplci);
+ }
+ break;
+
+ case S_HOLD:
+ api_parse(&parms->info[1],(word)parms->length,"ws",ss_parms);
+ if(plci && plci->State && plci->SuppState==IDLE)
+ {
+ plci->SuppState = HOLD_REQUEST;
+ plci->command = C_HOLD_REQ;
+ add_s(plci,CAI,&ss_parms[1]);
+ sig_req(plci,CALL_HOLD,0);
+ send_req(plci);
+ return false;
+ }
+ else Info = 0x3010; /* wrong state */
+ break;
+ case S_RETRIEVE:
+ if(plci && plci->State && plci->SuppState==CALL_HELD)
+ {
+ if(Id & EXT_CONTROLLER)
+ {
+ if(AdvCodecSupport(a, plci, appl, 0))
+ {
+ Info = 0x3010; /* wrong state */
+ break;
+ }
+ }
+ else plci->tel = 0;
+
+ plci->SuppState = RETRIEVE_REQUEST;
+ plci->command = C_RETRIEVE_REQ;
+ if(plci->spoofed_msg==SPOOFING_REQUIRED)
+ {
+ plci->spoofed_msg = CALL_RETRIEVE;
+ plci->internal_command = BLOCK_PLCI;
+ plci->command = 0;
+ dbug(1,dprintf("Spoof"));
+ return false;
+ }
+ else
+ {
+ sig_req(plci,CALL_RETRIEVE,0);
+ send_req(plci);
+ return false;
+ }
+ }
+ else Info = 0x3010; /* wrong state */
+ break;
+ case S_SUSPEND:
+ if(parms->length)
+ {
+ if(api_parse(&parms->info[1],(word)parms->length,"wbs",ss_parms))
+ {
+ dbug(1,dprintf("format wrong"));
+ Info = _WRONG_MESSAGE_FORMAT;
+ break;
+ }
+ }
+ if(plci && plci->State)
+ {
+ add_s(plci,CAI,&ss_parms[2]);
+ plci->command = SUSPEND_REQ;
+ sig_req(plci,SUSPEND,0);
+ plci->State = SUSPENDING;
+ send_req(plci);
+ }
+ else Info = 0x3010; /* wrong state */
+ break;
+
+ case S_RESUME:
+ if(!(i=get_plci(a)) )
+ {
+ Info = _OUT_OF_PLCI;
+ break;
+ }
+ rplci = &a->plci[i-1];
+ rplci->appl = appl;
+ rplci->number = Number;
+ rplci->tel = 0;
+ rplci->call_dir = CALL_DIR_OUT | CALL_DIR_ORIGINATE;
+ /* check 'external controller' bit for codec support */
+ if(Id & EXT_CONTROLLER)
+ {
+ if(AdvCodecSupport(a, rplci, appl, 0) )
+ {
+ rplci->Id = 0;
+ Info = 0x300A;
+ break;
+ }
+ }
+ if(parms->length)
+ {
+ if(api_parse(&parms->info[1],(word)parms->length,"wbs",ss_parms))
+ {
+ dbug(1,dprintf("format wrong"));
+ rplci->Id = 0;
+ Info = _WRONG_MESSAGE_FORMAT;
+ break;
+ }
+ }
+ dummy.length = 0;
+ dummy.info = "\x00";
+ add_b1(rplci, &dummy, 0, 0);
+ if (a->Info_Mask[appl->Id-1] & 0x200)
+ {
+ /* early B3 connect (CIP mask bit 9) no release after a disc */
+ add_p(rplci,LLI,"\x01\x01");
+ }
+ add_p(rplci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+ sig_req(rplci,ASSIGN,DSIG_ID);
+ send_req(rplci);
+ add_s(rplci,CAI,&ss_parms[2]);
+ rplci->command = RESUME_REQ;
+ sig_req(rplci,RESUME,0);
+ rplci->State = RESUMING;
+ send_req(rplci);
+ break;
+
+ case S_CONF_BEGIN: /* Request */
+ case S_CONF_DROP:
+ case S_CONF_ISOLATE:
+ case S_CONF_REATTACH:
+ if(api_parse(&parms->info[1],(word)parms->length,"wbd",ss_parms))
+ {
+ dbug(1,dprintf("format wrong"));
+ Info = _WRONG_MESSAGE_FORMAT;
+ break;
+ }
+ if(plci && plci->State && ((plci->SuppState==IDLE)||(plci->SuppState==CALL_HELD)))
+ {
+ d = GET_DWORD(ss_parms[2].info);
+ if(d>=0x80)
+ {
+ dbug(1,dprintf("format wrong"));
+ Info = _WRONG_MESSAGE_FORMAT;
+ break;
+ }
+ plci->ptyState = (byte)SSreq;
+ plci->command = 0;
+ cai[0] = 2;
+ switch(SSreq)
+ {
+ case S_CONF_BEGIN:
+ cai[1] = CONF_BEGIN;
+ plci->internal_command = CONF_BEGIN_REQ_PEND;
+ break;
+ case S_CONF_DROP:
+ cai[1] = CONF_DROP;
+ plci->internal_command = CONF_DROP_REQ_PEND;
+ break;
+ case S_CONF_ISOLATE:
+ cai[1] = CONF_ISOLATE;
+ plci->internal_command = CONF_ISOLATE_REQ_PEND;
+ break;
+ case S_CONF_REATTACH:
+ cai[1] = CONF_REATTACH;
+ plci->internal_command = CONF_REATTACH_REQ_PEND;
+ break;
+ }
+ cai[2] = (byte)d; /* Conference Size resp. PartyId */
+ add_p(plci,CAI,cai);
+ sig_req(plci,S_SERVICE,0);
+ send_req(plci);
+ return false;
+ }
+ else Info = 0x3010; /* wrong state */
+ break;
+
+ case S_ECT:
+ case S_3PTY_BEGIN:
+ case S_3PTY_END:
+ case S_CONF_ADD:
+ if(parms->length==7)
+ {
+ if(api_parse(&parms->info[1],(word)parms->length,"wbd",ss_parms))
+ {
+ dbug(1,dprintf("format wrong"));
+ Info = _WRONG_MESSAGE_FORMAT;
+ break;
+ }
+ }
+ else if(parms->length==8) /* workaround for the T-View-S */
+ {
+ if(api_parse(&parms->info[1],(word)parms->length,"wbdb",ss_parms))
+ {
+ dbug(1,dprintf("format wrong"));
+ Info = _WRONG_MESSAGE_FORMAT;
+ break;
+ }
+ }
+ else
+ {
+ Info = _WRONG_MESSAGE_FORMAT;
+ break;
+ }
+ if(!msg[1].length)
+ {
+ Info = _WRONG_MESSAGE_FORMAT;
+ break;
+ }
+ if (!plci)
+ {
+ Info = _WRONG_IDENTIFIER;
+ break;
+ }
+ relatedPLCIvalue = GET_DWORD(ss_parms[2].info);
+ relatedPLCIvalue &= 0x0000FFFF;
+ dbug(1,dprintf("PTY/ECT/addCONF,relPLCI=%lx",relatedPLCIvalue));
+ /* controller starts with 0 up to (max_adapter - 1) */
+ if (((relatedPLCIvalue & 0x7f) == 0)
+ || (MapController ((byte)(relatedPLCIvalue & 0x7f)) == 0)
+ || (MapController ((byte)(relatedPLCIvalue & 0x7f)) > max_adapter))
+ {
+ if(SSreq==S_3PTY_END)
+ {
+ dbug(1, dprintf("wrong Controller use 2nd PLCI=PLCI"));
+ rplci = plci;
+ }
+ else
+ {
+ Info = 0x3010; /* wrong state */
+ break;
+ }
+ }
+ else
+ {
+ relatedadapter = &adapter[MapController ((byte)(relatedPLCIvalue & 0x7f))-1];
+ relatedPLCIvalue >>=8;
+ /* find PLCI PTR*/
+ for(i=0,rplci=NULL;i<relatedadapter->max_plci;i++)
+ {
+ if(relatedadapter->plci[i].Id == (byte)relatedPLCIvalue)
+ {
+ rplci = &relatedadapter->plci[i];
+ }
+ }
+ if(!rplci || !relatedPLCIvalue)
+ {
+ if(SSreq==S_3PTY_END)
+ {
+ dbug(1, dprintf("use 2nd PLCI=PLCI"));
+ rplci = plci;
+ }
+ else
+ {
+ Info = 0x3010; /* wrong state */
+ break;
+ }
+ }
+ }
+/*
+ dbug(1,dprintf("rplci:%x",rplci));
+ dbug(1,dprintf("plci:%x",plci));
+ dbug(1,dprintf("rplci->ptyState:%x",rplci->ptyState));
+ dbug(1,dprintf("plci->ptyState:%x",plci->ptyState));
+ dbug(1,dprintf("SSreq:%x",SSreq));
+ dbug(1,dprintf("rplci->internal_command:%x",rplci->internal_command));
+ dbug(1,dprintf("rplci->appl:%x",rplci->appl));
+ dbug(1,dprintf("rplci->Id:%x",rplci->Id));
+*/
+ /* send PTY/ECT req, cannot check all states because of US stuff */
+ if( !rplci->internal_command && rplci->appl )
+ {
+ plci->command = 0;
+ rplci->relatedPTYPLCI = plci;
+ plci->relatedPTYPLCI = rplci;
+ rplci->ptyState = (byte)SSreq;
+ if(SSreq==S_ECT)
+ {
+ rplci->internal_command = ECT_REQ_PEND;
+ cai[1] = ECT_EXECUTE;
+
+ rplci->vswitchstate=0;
+ rplci->vsprot=0;
+ rplci->vsprotdialect=0;
+ plci->vswitchstate=0;
+ plci->vsprot=0;
+ plci->vsprotdialect=0;
+
+ }
+ else if(SSreq==S_CONF_ADD)
+ {
+ rplci->internal_command = CONF_ADD_REQ_PEND;
+ cai[1] = CONF_ADD;
+ }
+ else
+ {
+ rplci->internal_command = PTY_REQ_PEND;
+ cai[1] = (byte)(SSreq-3);
+ }
+ rplci->number = Number;
+ if(plci!=rplci) /* explicit invocation */
+ {
+ cai[0] = 2;
+ cai[2] = plci->Sig.Id;
+ dbug(1,dprintf("explicit invocation"));
+ }
+ else
+ {
+ dbug(1,dprintf("implicit invocation"));
+ cai[0] = 1;
+ }
+ add_p(rplci,CAI,cai);
+ sig_req(rplci,S_SERVICE,0);
+ send_req(rplci);
+ return false;
+ }
+ else
+ {
+ dbug(0,dprintf("Wrong line"));
+ Info = 0x3010; /* wrong state */
+ break;
+ }
+ break;
+
+ case S_CALL_DEFLECTION:
+ if(api_parse(&parms->info[1],(word)parms->length,"wbwss",ss_parms))
+ {
+ dbug(1,dprintf("format wrong"));
+ Info = _WRONG_MESSAGE_FORMAT;
+ break;
+ }
+ if (!plci)
+ {
+ Info = _WRONG_IDENTIFIER;
+ break;
+ }
+ /* reuse unused screening indicator */
+ ss_parms[3].info[3] = (byte)GET_WORD(&(ss_parms[2].info[0]));
+ plci->command = 0;
+ plci->internal_command = CD_REQ_PEND;
+ appl->CDEnable = true;
+ cai[0] = 1;
+ cai[1] = CALL_DEFLECTION;
+ add_p(plci,CAI,cai);
+ add_p(plci,CPN,ss_parms[3].info);
+ sig_req(plci,S_SERVICE,0);
+ send_req(plci);
+ return false;
+ break;
+
+ case S_CALL_FORWARDING_START:
+ if(api_parse(&parms->info[1],(word)parms->length,"wbdwwsss",ss_parms))
+ {
+ dbug(1,dprintf("format wrong"));
+ Info = _WRONG_MESSAGE_FORMAT;
+ break;
+ }
+
+ if((i=get_plci(a)))
+ {
+ rplci = &a->plci[i-1];
+ rplci->appl = appl;
+ add_p(rplci,CAI,"\x01\x80");
+ add_p(rplci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+ sig_req(rplci,ASSIGN,DSIG_ID);
+ send_req(rplci);
+ }
+ else
+ {
+ Info = _OUT_OF_PLCI;
+ break;
+ }
+
+ /* reuse unused screening indicator */
+ rplci->internal_command = CF_START_PEND;
+ rplci->appl = appl;
+ rplci->number = Number;
+ appl->S_Handle = GET_DWORD(&(ss_parms[2].info[0]));
+ cai[0] = 2;
+ cai[1] = 0x70|(byte)GET_WORD(&(ss_parms[3].info[0])); /* Function */
+ cai[2] = (byte)GET_WORD(&(ss_parms[4].info[0])); /* Basic Service */
+ add_p(rplci,CAI,cai);
+ add_p(rplci,OAD,ss_parms[5].info);
+ add_p(rplci,CPN,ss_parms[6].info);
+ sig_req(rplci,S_SERVICE,0);
+ send_req(rplci);
+ return false;
+ break;
+
+ case S_INTERROGATE_DIVERSION:
+ case S_INTERROGATE_NUMBERS:
+ case S_CALL_FORWARDING_STOP:
+ case S_CCBS_REQUEST:
+ case S_CCBS_DEACTIVATE:
+ case S_CCBS_INTERROGATE:
+ switch(SSreq)
+ {
+ case S_INTERROGATE_NUMBERS:
+ if(api_parse(&parms->info[1],(word)parms->length,"wbd",ss_parms))
+ {
+ dbug(0,dprintf("format wrong"));
+ Info = _WRONG_MESSAGE_FORMAT;
+ }
+ break;
+ case S_CCBS_REQUEST:
+ case S_CCBS_DEACTIVATE:
+ if(api_parse(&parms->info[1],(word)parms->length,"wbdw",ss_parms))
+ {
+ dbug(0,dprintf("format wrong"));
+ Info = _WRONG_MESSAGE_FORMAT;
+ }
+ break;
+ case S_CCBS_INTERROGATE:
+ if(api_parse(&parms->info[1],(word)parms->length,"wbdws",ss_parms))
+ {
+ dbug(0,dprintf("format wrong"));
+ Info = _WRONG_MESSAGE_FORMAT;
+ }
+ break;
+ default:
+ if(api_parse(&parms->info[1],(word)parms->length,"wbdwws",ss_parms))
+ {
+ dbug(0,dprintf("format wrong"));
+ Info = _WRONG_MESSAGE_FORMAT;
+ break;
+ }
+ break;
+ }
+
+ if(Info) break;
+ if((i=get_plci(a)))
+ {
+ rplci = &a->plci[i-1];
+ switch(SSreq)
+ {
+ case S_INTERROGATE_DIVERSION: /* use cai with S_SERVICE below */
+ cai[1] = 0x60|(byte)GET_WORD(&(ss_parms[3].info[0])); /* Function */
+ rplci->internal_command = INTERR_DIVERSION_REQ_PEND; /* move to rplci if assigned */
+ break;
+ case S_INTERROGATE_NUMBERS: /* use cai with S_SERVICE below */
+ cai[1] = DIVERSION_INTERROGATE_NUM; /* Function */
+ rplci->internal_command = INTERR_NUMBERS_REQ_PEND; /* move to rplci if assigned */
+ break;
+ case S_CALL_FORWARDING_STOP:
+ rplci->internal_command = CF_STOP_PEND;
+ cai[1] = 0x80|(byte)GET_WORD(&(ss_parms[3].info[0])); /* Function */
+ break;
+ case S_CCBS_REQUEST:
+ cai[1] = CCBS_REQUEST;
+ rplci->internal_command = CCBS_REQUEST_REQ_PEND;
+ break;
+ case S_CCBS_DEACTIVATE:
+ cai[1] = CCBS_DEACTIVATE;
+ rplci->internal_command = CCBS_DEACTIVATE_REQ_PEND;
+ break;
+ case S_CCBS_INTERROGATE:
+ cai[1] = CCBS_INTERROGATE;
+ rplci->internal_command = CCBS_INTERROGATE_REQ_PEND;
+ break;
+ default:
+ cai[1] = 0;
+ break;
+ }
+ rplci->appl = appl;
+ rplci->number = Number;
+ add_p(rplci,CAI,"\x01\x80");
+ add_p(rplci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+ sig_req(rplci,ASSIGN,DSIG_ID);
+ send_req(rplci);
+ }
+ else
+ {
+ Info = _OUT_OF_PLCI;
+ break;
+ }
+
+ appl->S_Handle = GET_DWORD(&(ss_parms[2].info[0]));
+ switch(SSreq)
+ {
+ case S_INTERROGATE_NUMBERS:
+ cai[0] = 1;
+ add_p(rplci,CAI,cai);
+ break;
+ case S_CCBS_REQUEST:
+ case S_CCBS_DEACTIVATE:
+ cai[0] = 3;
+ PUT_WORD(&cai[2],GET_WORD(&(ss_parms[3].info[0])));
+ add_p(rplci,CAI,cai);
+ break;
+ case S_CCBS_INTERROGATE:
+ cai[0] = 3;
+ PUT_WORD(&cai[2],GET_WORD(&(ss_parms[3].info[0])));
+ add_p(rplci,CAI,cai);
+ add_p(rplci,OAD,ss_parms[4].info);
+ break;
+ default:
+ cai[0] = 2;
+ cai[2] = (byte)GET_WORD(&(ss_parms[4].info[0])); /* Basic Service */
+ add_p(rplci,CAI,cai);
+ add_p(rplci,OAD,ss_parms[5].info);
+ break;
+ }
+
+ sig_req(rplci,S_SERVICE,0);
+ send_req(rplci);
+ return false;
+ break;
+
+ case S_MWI_ACTIVATE:
+ if(api_parse(&parms->info[1],(word)parms->length,"wbwdwwwssss",ss_parms))
+ {
+ dbug(1,dprintf("format wrong"));
+ Info = _WRONG_MESSAGE_FORMAT;
+ break;
+ }
+ if(!plci)
+ {
+ if((i=get_plci(a)))
+ {
+ rplci = &a->plci[i-1];
+ rplci->appl = appl;
+ rplci->cr_enquiry=true;
+ add_p(rplci,CAI,"\x01\x80");
+ add_p(rplci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+ sig_req(rplci,ASSIGN,DSIG_ID);
+ send_req(rplci);
+ }
+ else
+ {
+ Info = _OUT_OF_PLCI;
+ break;
+ }
+ }
+ else
+ {
+ rplci = plci;
+ rplci->cr_enquiry=false;
+ }
+
+ rplci->command = 0;
+ rplci->internal_command = MWI_ACTIVATE_REQ_PEND;
+ rplci->appl = appl;
+ rplci->number = Number;
+
+ cai[0] = 13;
+ cai[1] = ACTIVATION_MWI; /* Function */
+ PUT_WORD(&cai[2],GET_WORD(&(ss_parms[2].info[0]))); /* Basic Service */
+ PUT_DWORD(&cai[4],GET_DWORD(&(ss_parms[3].info[0]))); /* Number of Messages */
+ PUT_WORD(&cai[8],GET_WORD(&(ss_parms[4].info[0]))); /* Message Status */
+ PUT_WORD(&cai[10],GET_WORD(&(ss_parms[5].info[0]))); /* Message Reference */
+ PUT_WORD(&cai[12],GET_WORD(&(ss_parms[6].info[0]))); /* Invocation Mode */
+ add_p(rplci,CAI,cai);
+ add_p(rplci,CPN,ss_parms[7].info); /* Receiving User Number */
+ add_p(rplci,OAD,ss_parms[8].info); /* Controlling User Number */
+ add_p(rplci,OSA,ss_parms[9].info); /* Controlling User Provided Number */
+ add_p(rplci,UID,ss_parms[10].info); /* Time */
+ sig_req(rplci,S_SERVICE,0);
+ send_req(rplci);
+ return false;
+
+ case S_MWI_DEACTIVATE:
+ if(api_parse(&parms->info[1],(word)parms->length,"wbwwss",ss_parms))
+ {
+ dbug(1,dprintf("format wrong"));
+ Info = _WRONG_MESSAGE_FORMAT;
+ break;
+ }
+ if(!plci)
+ {
+ if((i=get_plci(a)))
+ {
+ rplci = &a->plci[i-1];
+ rplci->appl = appl;
+ rplci->cr_enquiry=true;
+ add_p(rplci,CAI,"\x01\x80");
+ add_p(rplci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+ sig_req(rplci,ASSIGN,DSIG_ID);
+ send_req(rplci);
+ }
+ else
+ {
+ Info = _OUT_OF_PLCI;
+ break;
+ }
+ }
+ else
+ {
+ rplci = plci;
+ rplci->cr_enquiry=false;
+ }
+
+ rplci->command = 0;
+ rplci->internal_command = MWI_DEACTIVATE_REQ_PEND;
+ rplci->appl = appl;
+ rplci->number = Number;
+
+ cai[0] = 5;
+ cai[1] = DEACTIVATION_MWI; /* Function */
+ PUT_WORD(&cai[2],GET_WORD(&(ss_parms[2].info[0]))); /* Basic Service */
+ PUT_WORD(&cai[4],GET_WORD(&(ss_parms[3].info[0]))); /* Invocation Mode */
+ add_p(rplci,CAI,cai);
+ add_p(rplci,CPN,ss_parms[4].info); /* Receiving User Number */
+ add_p(rplci,OAD,ss_parms[5].info); /* Controlling User Number */
+ sig_req(rplci,S_SERVICE,0);
+ send_req(rplci);
+ return false;
+
+ default:
+ Info = 0x300E; /* not supported */
+ break;
+ }
+ break; /* case SELECTOR_SU_SERV: end */
+
+
+ case SELECTOR_DTMF:
+ return (dtmf_request (Id, Number, a, plci, appl, msg));
+
+
+
+ case SELECTOR_LINE_INTERCONNECT:
+ return (mixer_request (Id, Number, a, plci, appl, msg));
+
+
+
+ case PRIV_SELECTOR_ECHO_CANCELLER:
+ appl->appl_flags |= APPL_FLAG_PRIV_EC_SPEC;
+ return (ec_request (Id, Number, a, plci, appl, msg));
+
+ case SELECTOR_ECHO_CANCELLER:
+ appl->appl_flags &= ~APPL_FLAG_PRIV_EC_SPEC;
+ return (ec_request (Id, Number, a, plci, appl, msg));
+
+
+ case SELECTOR_V42BIS:
+ default:
+ Info = _FACILITY_NOT_SUPPORTED;
+ break;
+ } /* end of switch(selector) */
+ }
+
+ dbug(1,dprintf("SendFacRc"));
+ sendf(appl,
+ _FACILITY_R|CONFIRM,
+ Id,
+ Number,
+ "wws",Info,selector,SSparms);
+ return false;
+}
+
+byte facility_res(dword Id, word Number, DIVA_CAPI_ADAPTER * a, PLCI * plci, APPL * appl, API_PARSE * msg)
+{
+ dbug(1,dprintf("facility_res"));
+ return false;
+}
+
+byte connect_b3_req(dword Id, word Number, DIVA_CAPI_ADAPTER * a, PLCI * plci, APPL * appl, API_PARSE * parms)
+{
+ word Info = 0;
+ byte req;
+ byte len;
+ word w;
+ word fax_control_bits, fax_feature_bits, fax_info_change;
+ API_PARSE * ncpi;
+ byte pvc[2];
+
+ API_PARSE fax_parms[9];
+ word i;
+
+
+ dbug(1,dprintf("connect_b3_req"));
+ if(plci)
+ {
+ if ((plci->State == IDLE) || (plci->State == OUTG_DIS_PENDING)
+ || (plci->State == INC_DIS_PENDING) || (plci->SuppState != IDLE))
+ {
+ Info = _WRONG_STATE;
+ }
+ else
+ {
+ /* local reply if assign unsuccessfull
+ or B3 protocol allows only one layer 3 connection
+ and already connected
+ or B2 protocol not any LAPD
+ and connect_b3_req contradicts originate/answer direction */
+ if (!plci->NL.Id
+ || (((plci->B3_prot != B3_T90NL) && (plci->B3_prot != B3_ISO8208) && (plci->B3_prot != B3_X25_DCE))
+ && ((plci->channels != 0)
+ || (((plci->B2_prot != B2_SDLC) && (plci->B2_prot != B2_LAPD) && (plci->B2_prot != B2_LAPD_FREE_SAPI_SEL))
+ && ((plci->call_dir & CALL_DIR_ANSWER) && !(plci->call_dir & CALL_DIR_FORCE_OUTG_NL))))))
+ {
+ dbug(1,dprintf("B3 already connected=%d or no NL.Id=0x%x, dir=%d sstate=0x%x",
+ plci->channels,plci->NL.Id,plci->call_dir,plci->SuppState));
+ Info = _WRONG_STATE;
+ sendf(appl,
+ _CONNECT_B3_R|CONFIRM,
+ Id,
+ Number,
+ "w",Info);
+ return false;
+ }
+ plci->requested_options_conn = 0;
+
+ req = N_CONNECT;
+ ncpi = &parms[0];
+ if(plci->B3_prot==2 || plci->B3_prot==3)
+ {
+ if(ncpi->length>2)
+ {
+ /* check for PVC */
+ if(ncpi->info[2] || ncpi->info[3])
+ {
+ pvc[0] = ncpi->info[3];
+ pvc[1] = ncpi->info[2];
+ add_d(plci,2,pvc);
+ req = N_RESET;
+ }
+ else
+ {
+ if(ncpi->info[1] &1) req = N_CONNECT | N_D_BIT;
+ add_d(plci,(word)(ncpi->length-3),&ncpi->info[4]);
+ }
+ }
+ }
+ else if(plci->B3_prot==5)
+ {
+ if (plci->NL.Id && !plci->nl_remove_id)
+ {
+ fax_control_bits = GET_WORD(&((T30_INFO *)plci->fax_connect_info_buffer)->control_bits_low);
+ fax_feature_bits = GET_WORD(&((T30_INFO *)plci->fax_connect_info_buffer)->feature_bits_low);
+ if (!(fax_control_bits & T30_CONTROL_BIT_MORE_DOCUMENTS)
+ || (fax_feature_bits & T30_FEATURE_BIT_MORE_DOCUMENTS))
+ {
+ len = (byte)(&(((T30_INFO *) 0)->universal_6));
+ fax_info_change = false;
+ if (ncpi->length >= 4)
+ {
+ w = GET_WORD(&ncpi->info[3]);
+ if ((w & 0x0001) != ((word)(((T30_INFO *)(plci->fax_connect_info_buffer))->resolution & 0x0001)))
+ {
+ ((T30_INFO *)(plci->fax_connect_info_buffer))->resolution =
+ (byte)((((T30_INFO *)(plci->fax_connect_info_buffer))->resolution & ~T30_RESOLUTION_R8_0770_OR_200) |
+ ((w & 0x0001) ? T30_RESOLUTION_R8_0770_OR_200 : 0));
+ fax_info_change = true;
+ }
+ fax_control_bits &= ~(T30_CONTROL_BIT_REQUEST_POLLING | T30_CONTROL_BIT_MORE_DOCUMENTS);
+ if (w & 0x0002) /* Fax-polling request */
+ fax_control_bits |= T30_CONTROL_BIT_REQUEST_POLLING;
+ if ((w & 0x0004) /* Request to send / poll another document */
+ && (a->manufacturer_features & MANUFACTURER_FEATURE_FAX_MORE_DOCUMENTS))
+ {
+ fax_control_bits |= T30_CONTROL_BIT_MORE_DOCUMENTS;
+ }
+ if (ncpi->length >= 6)
+ {
+ w = GET_WORD(&ncpi->info[5]);
+ if (((byte) w) != ((T30_INFO *)(plci->fax_connect_info_buffer))->data_format)
+ {
+ ((T30_INFO *)(plci->fax_connect_info_buffer))->data_format = (byte) w;
+ fax_info_change = true;
+ }
+
+ if ((a->man_profile.private_options & (1L << PRIVATE_FAX_SUB_SEP_PWD))
+ && (GET_WORD(&ncpi->info[5]) & 0x8000)) /* Private SEP/SUB/PWD enable */
+ {
+ plci->requested_options_conn |= (1L << PRIVATE_FAX_SUB_SEP_PWD);
+ }
+ if ((a->man_profile.private_options & (1L << PRIVATE_FAX_NONSTANDARD))
+ && (GET_WORD(&ncpi->info[5]) & 0x4000)) /* Private non-standard facilities enable */
+ {
+ plci->requested_options_conn |= (1L << PRIVATE_FAX_NONSTANDARD);
+ }
+ fax_control_bits &= ~(T30_CONTROL_BIT_ACCEPT_SUBADDRESS | T30_CONTROL_BIT_ACCEPT_SEL_POLLING |
+ T30_CONTROL_BIT_ACCEPT_PASSWORD);
+ if ((plci->requested_options_conn | plci->requested_options | a->requested_options_table[appl->Id-1])
+ & ((1L << PRIVATE_FAX_SUB_SEP_PWD) | (1L << PRIVATE_FAX_NONSTANDARD)))
+ {
+ if (api_parse (&ncpi->info[1], ncpi->length, "wwwwsss", fax_parms))
+ Info = _WRONG_MESSAGE_FORMAT;
+ else
+ {
+ if ((plci->requested_options_conn | plci->requested_options | a->requested_options_table[appl->Id-1])
+ & (1L << PRIVATE_FAX_SUB_SEP_PWD))
+ {
+ fax_control_bits |= T30_CONTROL_BIT_ACCEPT_SUBADDRESS | T30_CONTROL_BIT_ACCEPT_PASSWORD;
+ if (fax_control_bits & T30_CONTROL_BIT_ACCEPT_POLLING)
+ fax_control_bits |= T30_CONTROL_BIT_ACCEPT_SEL_POLLING;
+ }
+ w = fax_parms[4].length;
+ if (w > 20)
+ w = 20;
+ ((T30_INFO *)(plci->fax_connect_info_buffer))->station_id_len = (byte) w;
+ for (i = 0; i < w; i++)
+ ((T30_INFO *)(plci->fax_connect_info_buffer))->station_id[i] = fax_parms[4].info[1+i];
+ ((T30_INFO *)(plci->fax_connect_info_buffer))->head_line_len = 0;
+ len = (byte)(((T30_INFO *) 0)->station_id + 20);
+ w = fax_parms[5].length;
+ if (w > 20)
+ w = 20;
+ plci->fax_connect_info_buffer[len++] = (byte) w;
+ for (i = 0; i < w; i++)
+ plci->fax_connect_info_buffer[len++] = fax_parms[5].info[1+i];
+ w = fax_parms[6].length;
+ if (w > 20)
+ w = 20;
+ plci->fax_connect_info_buffer[len++] = (byte) w;
+ for (i = 0; i < w; i++)
+ plci->fax_connect_info_buffer[len++] = fax_parms[6].info[1+i];
+ if ((plci->requested_options_conn | plci->requested_options | a->requested_options_table[appl->Id-1])
+ & (1L << PRIVATE_FAX_NONSTANDARD))
+ {
+ if (api_parse (&ncpi->info[1], ncpi->length, "wwwwssss", fax_parms))
+ {
+ dbug(1,dprintf("non-standard facilities info missing or wrong format"));
+ plci->fax_connect_info_buffer[len++] = 0;
+ }
+ else
+ {
+ if ((fax_parms[7].length >= 3) && (fax_parms[7].info[1] >= 2))
+ plci->nsf_control_bits = GET_WORD(&fax_parms[7].info[2]);
+ plci->fax_connect_info_buffer[len++] = (byte)(fax_parms[7].length);
+ for (i = 0; i < fax_parms[7].length; i++)
+ plci->fax_connect_info_buffer[len++] = fax_parms[7].info[1+i];
+ }
+ }
+ }
+ }
+ else
+ {
+ len = (byte)(&(((T30_INFO *) 0)->universal_6));
+ }
+ fax_info_change = true;
+
+ }
+ if (fax_control_bits != GET_WORD(&((T30_INFO *)plci->fax_connect_info_buffer)->control_bits_low))
+ {
+ PUT_WORD (&((T30_INFO *)plci->fax_connect_info_buffer)->control_bits_low, fax_control_bits);
+ fax_info_change = true;
+ }
+ }
+ if (Info == GOOD)
+ {
+ plci->fax_connect_info_length = len;
+ if (fax_info_change)
+ {
+ if (fax_feature_bits & T30_FEATURE_BIT_MORE_DOCUMENTS)
+ {
+ start_internal_command (Id, plci, fax_connect_info_command);
+ return false;
+ }
+ else
+ {
+ start_internal_command (Id, plci, fax_adjust_b23_command);
+ return false;
+ }
+ }
+ }
+ }
+ else Info = _WRONG_STATE;
+ }
+ else Info = _WRONG_STATE;
+ }
+
+ else if (plci->B3_prot == B3_RTP)
+ {
+ plci->internal_req_buffer[0] = ncpi->length + 1;
+ plci->internal_req_buffer[1] = UDATA_REQUEST_RTP_RECONFIGURE;
+ for (w = 0; w < ncpi->length; w++)
+ plci->internal_req_buffer[2+w] = ncpi->info[1+w];
+ start_internal_command (Id, plci, rtp_connect_b3_req_command);
+ return false;
+ }
+
+ if(!Info)
+ {
+ nl_req_ncci(plci,req,0);
+ return 1;
+ }
+ }
+ }
+ else Info = _WRONG_IDENTIFIER;
+
+ sendf(appl,
+ _CONNECT_B3_R|CONFIRM,
+ Id,
+ Number,
+ "w",Info);
+ return false;
+}
+
+byte connect_b3_res(dword Id, word Number, DIVA_CAPI_ADAPTER * a, PLCI * plci, APPL * appl, API_PARSE * parms)
+{
+ word ncci;
+ API_PARSE * ncpi;
+ byte req;
+
+ word w;
+
+
+ API_PARSE fax_parms[9];
+ word i;
+ byte len;
+
+
+ dbug(1,dprintf("connect_b3_res"));
+
+ ncci = (word)(Id>>16);
+ if(plci && ncci) {
+ if(a->ncci_state[ncci]==INC_CON_PENDING) {
+ if (GET_WORD (&parms[0].info[0]) != 0)
+ {
+ a->ncci_state[ncci] = OUTG_REJ_PENDING;
+ channel_request_xon (plci, a->ncci_ch[ncci]);
+ channel_xmit_xon (plci);
+ cleanup_ncci_data (plci, ncci);
+ nl_req_ncci(plci,N_DISC,(byte)ncci);
+ return 1;
+ }
+ a->ncci_state[ncci] = INC_ACT_PENDING;
+
+ req = N_CONNECT_ACK;
+ ncpi = &parms[1];
+ if ((plci->B3_prot == 4) || (plci->B3_prot == 5) || (plci->B3_prot == 7))
+ {
+
+ if ((plci->requested_options_conn | plci->requested_options | a->requested_options_table[plci->appl->Id-1])
+ & (1L << PRIVATE_FAX_NONSTANDARD))
+ {
+ if (((plci->B3_prot == 4) || (plci->B3_prot == 5))
+ && (plci->nsf_control_bits & T30_NSF_CONTROL_BIT_ENABLE_NSF)
+ && (plci->nsf_control_bits & T30_NSF_CONTROL_BIT_NEGOTIATE_RESP))
+ {
+ len = ((byte)(((T30_INFO *) 0)->station_id + 20));
+ if (plci->fax_connect_info_length < len)
+ {
+ ((T30_INFO *)(plci->fax_connect_info_buffer))->station_id_len = 0;
+ ((T30_INFO *)(plci->fax_connect_info_buffer))->head_line_len = 0;
+ }
+ if (api_parse (&ncpi->info[1], ncpi->length, "wwwwssss", fax_parms))
+ {
+ dbug(1,dprintf("non-standard facilities info missing or wrong format"));
+ }
+ else
+ {
+ if (plci->fax_connect_info_length <= len)
+ plci->fax_connect_info_buffer[len] = 0;
+ len += 1 + plci->fax_connect_info_buffer[len];
+ if (plci->fax_connect_info_length <= len)
+ plci->fax_connect_info_buffer[len] = 0;
+ len += 1 + plci->fax_connect_info_buffer[len];
+ if ((fax_parms[7].length >= 3) && (fax_parms[7].info[1] >= 2))
+ plci->nsf_control_bits = GET_WORD(&fax_parms[7].info[2]);
+ plci->fax_connect_info_buffer[len++] = (byte)(fax_parms[7].length);
+ for (i = 0; i < fax_parms[7].length; i++)
+ plci->fax_connect_info_buffer[len++] = fax_parms[7].info[1+i];
+ }
+ plci->fax_connect_info_length = len;
+ ((T30_INFO *)(plci->fax_connect_info_buffer))->code = 0;
+ start_internal_command (Id, plci, fax_connect_ack_command);
+ return false;
+ }
+ }
+
+ nl_req_ncci(plci,req,(byte)ncci);
+ if ((plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT)
+ && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT))
+ {
+ if (plci->B3_prot == 4)
+ sendf(appl,_CONNECT_B3_ACTIVE_I,Id,0,"s","");
+ else
+ sendf(appl,_CONNECT_B3_ACTIVE_I,Id,0,"S",plci->ncpi_buffer);
+ plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT;
+ }
+ }
+
+ else if (plci->B3_prot == B3_RTP)
+ {
+ plci->internal_req_buffer[0] = ncpi->length + 1;
+ plci->internal_req_buffer[1] = UDATA_REQUEST_RTP_RECONFIGURE;
+ for (w = 0; w < ncpi->length; w++)
+ plci->internal_req_buffer[2+w] = ncpi->info[1+w];
+ start_internal_command (Id, plci, rtp_connect_b3_res_command);
+ return false;
+ }
+
+ else
+ {
+ if(ncpi->length>2) {
+ if(ncpi->info[1] &1) req = N_CONNECT_ACK | N_D_BIT;
+ add_d(plci,(word)(ncpi->length-3),&ncpi->info[4]);
+ }
+ nl_req_ncci(plci,req,(byte)ncci);
+ sendf(appl,_CONNECT_B3_ACTIVE_I,Id,0,"s","");
+ if (plci->adjust_b_restore)
+ {
+ plci->adjust_b_restore = false;
+ start_internal_command (Id, plci, adjust_b_restore);
+ }
+ }
+ return 1;
+ }
+ }
+ return false;
+}
+
+byte connect_b3_a_res(dword Id, word Number, DIVA_CAPI_ADAPTER * a, PLCI * plci, APPL * appl, API_PARSE * parms)
+{
+ word ncci;
+
+ ncci = (word)(Id>>16);
+ dbug(1,dprintf("connect_b3_a_res(ncci=0x%x)",ncci));
+
+ if (plci && ncci && (plci->State != IDLE) && (plci->State != INC_DIS_PENDING)
+ && (plci->State != OUTG_DIS_PENDING))
+ {
+ if(a->ncci_state[ncci]==INC_ACT_PENDING) {
+ a->ncci_state[ncci] = CONNECTED;
+ if(plci->State!=INC_CON_CONNECTED_ALERT) plci->State = CONNECTED;
+ channel_request_xon (plci, a->ncci_ch[ncci]);
+ channel_xmit_xon (plci);
+ }
+ }
+ return false;
+}
+
+byte disconnect_b3_req(dword Id, word Number, DIVA_CAPI_ADAPTER * a, PLCI * plci, APPL * appl, API_PARSE * parms)
+{
+ word Info;
+ word ncci;
+ API_PARSE * ncpi;
+
+ dbug(1,dprintf("disconnect_b3_req"));
+
+ Info = _WRONG_IDENTIFIER;
+ ncci = (word)(Id>>16);
+ if (plci && ncci)
+ {
+ Info = _WRONG_STATE;
+ if ((a->ncci_state[ncci] == CONNECTED)
+ || (a->ncci_state[ncci] == OUTG_CON_PENDING)
+ || (a->ncci_state[ncci] == INC_CON_PENDING)
+ || (a->ncci_state[ncci] == INC_ACT_PENDING))
+ {
+ a->ncci_state[ncci] = OUTG_DIS_PENDING;
+ channel_request_xon (plci, a->ncci_ch[ncci]);
+ channel_xmit_xon (plci);
+
+ if (a->ncci[ncci].data_pending
+ && ((plci->B3_prot == B3_TRANSPARENT)
+ || (plci->B3_prot == B3_T30)
+ || (plci->B3_prot == B3_T30_WITH_EXTENSIONS)))
+ {
+ plci->send_disc = (byte)ncci;
+ plci->command = 0;
+ return false;
+ }
+ else
+ {
+ cleanup_ncci_data (plci, ncci);
+
+ if(plci->B3_prot==2 || plci->B3_prot==3)
+ {
+ ncpi = &parms[0];
+ if(ncpi->length>3)
+ {
+ add_d(plci, (word)(ncpi->length - 3) ,(byte *)&(ncpi->info[4]));
+ }
+ }
+ nl_req_ncci(plci,N_DISC,(byte)ncci);
+ }
+ return 1;
+ }
+ }
+ sendf(appl,
+ _DISCONNECT_B3_R|CONFIRM,
+ Id,
+ Number,
+ "w",Info);
+ return false;
+}
+
+byte disconnect_b3_res(dword Id, word Number, DIVA_CAPI_ADAPTER * a, PLCI * plci, APPL * appl, API_PARSE * parms)
+{
+ word ncci;
+ word i;
+
+ ncci = (word)(Id>>16);
+ dbug(1,dprintf("disconnect_b3_res(ncci=0x%x",ncci));
+ if(plci && ncci) {
+ plci->requested_options_conn = 0;
+ plci->fax_connect_info_length = 0;
+ plci->ncpi_state = 0x00;
+ if (((plci->B3_prot != B3_T90NL) && (plci->B3_prot != B3_ISO8208) && (plci->B3_prot != B3_X25_DCE))
+ && ((plci->B2_prot != B2_LAPD) && (plci->B2_prot != B2_LAPD_FREE_SAPI_SEL)))
+ {
+ plci->call_dir |= CALL_DIR_FORCE_OUTG_NL;
+ }
+ for(i=0; i<MAX_CHANNELS_PER_PLCI && plci->inc_dis_ncci_table[i]!=(byte)ncci; i++);
+ if(i<MAX_CHANNELS_PER_PLCI) {
+ if(plci->channels)plci->channels--;
+ for(; i<MAX_CHANNELS_PER_PLCI-1; i++) plci->inc_dis_ncci_table[i] = plci->inc_dis_ncci_table[i+1];
+ plci->inc_dis_ncci_table[MAX_CHANNELS_PER_PLCI-1] = 0;
+
+ ncci_free_receive_buffers (plci, ncci);
+
+ if((plci->State==IDLE || plci->State==SUSPENDING) && !plci->channels){
+ if(plci->State == SUSPENDING){
+ sendf(plci->appl,
+ _FACILITY_I,
+ Id & 0xffffL,
+ 0,
+ "ws", (word)3, "\x03\x04\x00\x00");
+ sendf(plci->appl, _DISCONNECT_I, Id & 0xffffL, 0, "w", 0);
+ }
+ plci_remove(plci);
+ plci->State=IDLE;
+ }
+ }
+ else
+ {
+ if ((a->manufacturer_features & MANUFACTURER_FEATURE_FAX_PAPER_FORMATS)
+ && ((plci->B3_prot == 4) || (plci->B3_prot == 5))
+ && (a->ncci_state[ncci] == INC_DIS_PENDING))
+ {
+ ncci_free_receive_buffers (plci, ncci);
+
+ nl_req_ncci(plci,N_EDATA,(byte)ncci);
+
+ plci->adapter->ncci_state[ncci] = IDLE;
+ start_internal_command (Id, plci, fax_disconnect_command);
+ return 1;
+ }
+ }
+ }
+ return false;
+}
+
+byte data_b3_req(dword Id, word Number, DIVA_CAPI_ADAPTER * a, PLCI * plci, APPL * appl, API_PARSE * parms)
+{
+ NCCI *ncci_ptr;
+ DATA_B3_DESC *data;
+ word Info;
+ word ncci;
+ word i;
+
+ dbug(1,dprintf("data_b3_req"));
+
+ Info = _WRONG_IDENTIFIER;
+ ncci = (word)(Id>>16);
+ dbug(1,dprintf("ncci=0x%x, plci=0x%x",ncci,plci));
+
+ if (plci && ncci)
+ {
+ Info = _WRONG_STATE;
+ if ((a->ncci_state[ncci] == CONNECTED)
+ || (a->ncci_state[ncci] == INC_ACT_PENDING))
+ {
+ /* queue data */
+ ncci_ptr = &(a->ncci[ncci]);
+ i = ncci_ptr->data_out + ncci_ptr->data_pending;
+ if (i >= MAX_DATA_B3)
+ i -= MAX_DATA_B3;
+ data = &(ncci_ptr->DBuffer[i]);
+ data->Number = Number;
+ if ((((byte *)(parms[0].info)) >= ((byte *)(plci->msg_in_queue)))
+ && (((byte *)(parms[0].info)) < ((byte *)(plci->msg_in_queue)) + sizeof(plci->msg_in_queue)))
+ {
+
+ data->P = (byte *)(long)(*((dword *)(parms[0].info)));
+
+ }
+ else
+ data->P = TransmitBufferSet(appl,*(dword *)parms[0].info);
+ data->Length = GET_WORD(parms[1].info);
+ data->Handle = GET_WORD(parms[2].info);
+ data->Flags = GET_WORD(parms[3].info);
+ (ncci_ptr->data_pending)++;
+
+ /* check for delivery confirmation */
+ if (data->Flags & 0x0004)
+ {
+ i = ncci_ptr->data_ack_out + ncci_ptr->data_ack_pending;
+ if (i >= MAX_DATA_ACK)
+ i -= MAX_DATA_ACK;
+ ncci_ptr->DataAck[i].Number = data->Number;
+ ncci_ptr->DataAck[i].Handle = data->Handle;
+ (ncci_ptr->data_ack_pending)++;
+ }
+
+ send_data(plci);
+ return false;
+ }
+ }
+ if (appl)
+ {
+ if (plci)
+ {
+ if ((((byte *)(parms[0].info)) >= ((byte *)(plci->msg_in_queue)))
+ && (((byte *)(parms[0].info)) < ((byte *)(plci->msg_in_queue)) + sizeof(plci->msg_in_queue)))
+ {
+
+ TransmitBufferFree (appl, (byte *)(long)(*((dword *)(parms[0].info))));
+
+ }
+ }
+ sendf(appl,
+ _DATA_B3_R|CONFIRM,
+ Id,
+ Number,
+ "ww",GET_WORD(parms[2].info),Info);
+ }
+ return false;
+}
+
+byte data_b3_res(dword Id, word Number, DIVA_CAPI_ADAPTER * a, PLCI * plci, APPL * appl, API_PARSE * parms)
+{
+ word n;
+ word ncci;
+ word NCCIcode;
+
+ dbug(1,dprintf("data_b3_res"));
+
+ ncci = (word)(Id>>16);
+ if(plci && ncci) {
+ n = GET_WORD(parms[0].info);
+ dbug(1,dprintf("free(%d)",n));
+ NCCIcode = ncci | (((word) a->Id) << 8);
+ if(n<appl->MaxBuffer &&
+ appl->DataNCCI[n]==NCCIcode &&
+ (byte)(appl->DataFlags[n]>>8)==plci->Id) {
+ dbug(1,dprintf("found"));
+ appl->DataNCCI[n] = 0;
+
+ if (channel_can_xon (plci, a->ncci_ch[ncci])) {
+ channel_request_xon (plci, a->ncci_ch[ncci]);
+ }
+ channel_xmit_xon (plci);
+
+ if(appl->DataFlags[n] &4) {
+ nl_req_ncci(plci,N_DATA_ACK,(byte)ncci);
+ return 1;
+ }
+ }
+ }
+ return false;
+}
+
+byte reset_b3_req(dword Id, word Number, DIVA_CAPI_ADAPTER * a, PLCI * plci, APPL * appl, API_PARSE * parms)
+{
+ word Info;
+ word ncci;
+
+ dbug(1,dprintf("reset_b3_req"));
+
+ Info = _WRONG_IDENTIFIER;
+ ncci = (word)(Id>>16);
+ if(plci && ncci)
+ {
+ Info = _WRONG_STATE;
+ switch (plci->B3_prot)
+ {
+ case B3_ISO8208:
+ case B3_X25_DCE:
+ if(a->ncci_state[ncci]==CONNECTED)
+ {
+ nl_req_ncci(plci,N_RESET,(byte)ncci);
+ send_req(plci);
+ Info = GOOD;
+ }
+ break;
+ case B3_TRANSPARENT:
+ if(a->ncci_state[ncci]==CONNECTED)
+ {
+ start_internal_command (Id, plci, reset_b3_command);
+ Info = GOOD;
+ }
+ break;
+ }
+ }
+ /* reset_b3 must result in a reset_b3_con & reset_b3_Ind */
+ sendf(appl,
+ _RESET_B3_R|CONFIRM,
+ Id,
+ Number,
+ "w",Info);
+ return false;
+}
+
+byte reset_b3_res(dword Id, word Number, DIVA_CAPI_ADAPTER * a, PLCI * plci, APPL * appl, API_PARSE * parms)
+{
+ word ncci;
+
+ dbug(1,dprintf("reset_b3_res"));
+
+ ncci = (word)(Id>>16);
+ if(plci && ncci) {
+ switch (plci->B3_prot)
+ {
+ case B3_ISO8208:
+ case B3_X25_DCE:
+ if(a->ncci_state[ncci]==INC_RES_PENDING)
+ {
+ a->ncci_state[ncci] = CONNECTED;
+ nl_req_ncci(plci,N_RESET_ACK,(byte)ncci);
+ return true;
+ }
+ break;
+ }
+ }
+ return false;
+}
+
+byte connect_b3_t90_a_res(dword Id, word Number, DIVA_CAPI_ADAPTER * a, PLCI * plci, APPL * appl, API_PARSE * parms)
+{
+ word ncci;
+ API_PARSE * ncpi;
+ byte req;
+
+ dbug(1,dprintf("connect_b3_t90_a_res"));
+
+ ncci = (word)(Id>>16);
+ if(plci && ncci) {
+ if(a->ncci_state[ncci]==INC_ACT_PENDING) {
+ a->ncci_state[ncci] = CONNECTED;
+ }
+ else if(a->ncci_state[ncci]==INC_CON_PENDING) {
+ a->ncci_state[ncci] = CONNECTED;
+
+ req = N_CONNECT_ACK;
+
+ /* parms[0]==0 for CAPI original message definition! */
+ if(parms[0].info) {
+ ncpi = &parms[1];
+ if(ncpi->length>2) {
+ if(ncpi->info[1] &1) req = N_CONNECT_ACK | N_D_BIT;
+ add_d(plci,(word)(ncpi->length-3),&ncpi->info[4]);
+ }
+ }
+ nl_req_ncci(plci,req,(byte)ncci);
+ return 1;
+ }
+ }
+ return false;
+}
+
+
+byte select_b_req(dword Id, word Number, DIVA_CAPI_ADAPTER * a, PLCI * plci, APPL * appl, API_PARSE * msg)
+{
+ word Info=0;
+ word i;
+ byte tel;
+ API_PARSE bp_parms[7];
+
+ if(!plci || !msg)
+ {
+ Info = _WRONG_IDENTIFIER;
+ }
+ else
+ {
+ dbug(1,dprintf("select_b_req[%d],PLCI=0x%x,Tel=0x%x,NL=0x%x,appl=0x%x,sstate=0x%x",
+ msg->length,plci->Id,plci->tel,plci->NL.Id,plci->appl,plci->SuppState));
+ dbug(1,dprintf("PlciState=0x%x",plci->State));
+ for(i=0;i<7;i++) bp_parms[i].length = 0;
+
+ /* check if no channel is open, no B3 connected only */
+ if((plci->State == IDLE) || (plci->State == OUTG_DIS_PENDING) || (plci->State == INC_DIS_PENDING)
+ || (plci->SuppState != IDLE) || plci->channels || plci->nl_remove_id)
+ {
+ Info = _WRONG_STATE;
+ }
+ /* check message format and fill bp_parms pointer */
+ else if(msg->length && api_parse(&msg->info[1], (word)msg->length, "wwwsss", bp_parms))
+ {
+ Info = _WRONG_MESSAGE_FORMAT;
+ }
+ else
+ {
+ if((plci->State==INC_CON_PENDING) || (plci->State==INC_CON_ALERT)) /* send alert tone inband to the network, */
+ { /* e.g. Qsig or RBS or Cornet-N or xess PRI */
+ if(Id & EXT_CONTROLLER)
+ {
+ sendf(appl, _SELECT_B_REQ|CONFIRM, Id, Number, "w", 0x2002); /* wrong controller */
+ return 0;
+ }
+ plci->State=INC_CON_CONNECTED_ALERT;
+ plci->appl = appl;
+ clear_c_ind_mask_bit (plci, (word)(appl->Id-1));
+ dump_c_ind_mask (plci);
+ for(i=0; i<max_appl; i++) /* disconnect the other appls */
+ { /* its quasi a connect */
+ if(test_c_ind_mask_bit (plci, i))
+ sendf(&application[i], _DISCONNECT_I, Id, 0, "w", _OTHER_APPL_CONNECTED);
+ }
+ }
+
+ api_save_msg(msg, "s", &plci->saved_msg);
+ tel = plci->tel;
+ if(Id & EXT_CONTROLLER)
+ {
+ if(tel) /* external controller in use by this PLCI */
+ {
+ if(a->AdvSignalAppl && a->AdvSignalAppl!=appl)
+ {
+ dbug(1,dprintf("Ext_Ctrl in use 1"));
+ Info = _WRONG_STATE;
+ }
+ }
+ else /* external controller NOT in use by this PLCI ? */
+ {
+ if(a->AdvSignalPLCI)
+ {
+ dbug(1,dprintf("Ext_Ctrl in use 2"));
+ Info = _WRONG_STATE;
+ }
+ else /* activate the codec */
+ {
+ dbug(1,dprintf("Ext_Ctrl start"));
+ if(AdvCodecSupport(a, plci, appl, 0) )
+ {
+ dbug(1,dprintf("Error in codec procedures"));
+ Info = _WRONG_STATE;
+ }
+ else if(plci->spoofed_msg==SPOOFING_REQUIRED) /* wait until codec is active */
+ {
+ plci->spoofed_msg = AWAITING_SELECT_B;
+ plci->internal_command = BLOCK_PLCI; /* lock other commands */
+ plci->command = 0;
+ dbug(1,dprintf("continue if codec loaded"));
+ return false;
+ }
+ }
+ }
+ }
+ else /* external controller bit is OFF */
+ {
+ if(tel) /* external controller in use, need to switch off */
+ {
+ if(a->AdvSignalAppl==appl)
+ {
+ CodecIdCheck(a, plci);
+ plci->tel = 0;
+ plci->adv_nl = 0;
+ dbug(1,dprintf("Ext_Ctrl disable"));
+ }
+ else
+ {
+ dbug(1,dprintf("Ext_Ctrl not requested"));
+ }
+ }
+ }
+ if (!Info)
+ {
+ if (plci->call_dir & CALL_DIR_OUT)
+ plci->call_dir = CALL_DIR_OUT | CALL_DIR_ORIGINATE;
+ else if (plci->call_dir & CALL_DIR_IN)
+ plci->call_dir = CALL_DIR_IN | CALL_DIR_ANSWER;
+ start_internal_command (Id, plci, select_b_command);
+ return false;
+ }
+ }
+ }
+ sendf(appl, _SELECT_B_REQ|CONFIRM, Id, Number, "w", Info);
+ return false;
+}
+
+byte manufacturer_req(dword Id, word Number, DIVA_CAPI_ADAPTER * a, PLCI * plci, APPL * appl, API_PARSE * parms)
+{
+ word command;
+ word i;
+ word ncci;
+ API_PARSE * m;
+ API_PARSE m_parms[5];
+ word codec;
+ byte req;
+ byte ch;
+ byte dir;
+ static byte chi[2] = {0x01,0x00};
+ static byte lli[2] = {0x01,0x00};
+ static byte codec_cai[2] = {0x01,0x01};
+ static byte null_msg = {0};
+ static API_PARSE null_parms = { 0, &null_msg };
+ PLCI * v_plci;
+ word Info=0;
+
+ dbug(1,dprintf("manufacturer_req"));
+ for(i=0;i<5;i++) m_parms[i].length = 0;
+
+ if(GET_DWORD(parms[0].info)!=_DI_MANU_ID) {
+ Info = _WRONG_MESSAGE_FORMAT;
+ }
+ command = GET_WORD(parms[1].info);
+ m = &parms[2];
+ if (!Info)
+ {
+ switch(command) {
+ case _DI_ASSIGN_PLCI:
+ if(api_parse(&m->info[1],(word)m->length,"wbbs",m_parms)) {
+ Info = _WRONG_MESSAGE_FORMAT;
+ break;
+ }
+ codec = GET_WORD(m_parms[0].info);
+ ch = m_parms[1].info[0];
+ dir = m_parms[2].info[0];
+ if((i=get_plci(a))) {
+ plci = &a->plci[i-1];
+ plci->appl = appl;
+ plci->command = _MANUFACTURER_R;
+ plci->m_command = command;
+ plci->number = Number;
+ plci->State = LOCAL_CONNECT;
+ Id = ( ((word)plci->Id<<8)|plci->adapter->Id|0x80);
+ dbug(1,dprintf("ManCMD,plci=0x%x",Id));
+
+ if((ch==1 || ch==2) && (dir<=2)) {
+ chi[1] = (byte)(0x80|ch);
+ lli[1] = 0;
+ plci->call_dir = CALL_DIR_OUT | CALL_DIR_ORIGINATE;
+ switch(codec)
+ {
+ case 0:
+ Info = add_b1(plci,&m_parms[3],0,0);
+ break;
+ case 1:
+ add_p(plci,CAI,codec_cai);
+ break;
+ /* manual 'swich on' to the codec support without signalling */
+ /* first 'assign plci' with this function, then use */
+ case 2:
+ if(AdvCodecSupport(a, plci, appl, 0) ) {
+ Info = _RESOURCE_ERROR;
+ }
+ else {
+ Info = add_b1(plci,&null_parms,0,B1_FACILITY_LOCAL);
+ lli[1] = 0x10; /* local call codec stream */
+ }
+ break;
+ }
+
+ plci->State = LOCAL_CONNECT;
+ plci->manufacturer = true;
+ plci->command = _MANUFACTURER_R;
+ plci->m_command = command;
+ plci->number = Number;
+
+ if(!Info)
+ {
+ add_p(plci,LLI,lli);
+ add_p(plci,CHI,chi);
+ add_p(plci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+ sig_req(plci,ASSIGN,DSIG_ID);
+
+ if(!codec)
+ {
+ Info = add_b23(plci,&m_parms[3]);
+ if(!Info)
+ {
+ nl_req_ncci(plci,ASSIGN,0);
+ send_req(plci);
+ }
+ }
+ if(!Info)
+ {
+ dbug(1,dprintf("dir=0x%x,spoof=0x%x",dir,plci->spoofed_msg));
+ if (plci->spoofed_msg==SPOOFING_REQUIRED)
+ {
+ api_save_msg (m_parms, "wbbs", &plci->saved_msg);
+ plci->spoofed_msg = AWAITING_MANUF_CON;
+ plci->internal_command = BLOCK_PLCI; /* reject other req meanwhile */
+ plci->command = 0;
+ send_req(plci);
+ return false;
+ }
+ if(dir==1) {
+ sig_req(plci,CALL_REQ,0);
+ }
+ else if(!dir){
+ sig_req(plci,LISTEN_REQ,0);
+ }
+ send_req(plci);
+ }
+ else
+ {
+ sendf(appl,
+ _MANUFACTURER_R|CONFIRM,
+ Id,
+ Number,
+ "dww",_DI_MANU_ID,command,Info);
+ return 2;
+ }
+ }
+ }
+ }
+ else Info = _OUT_OF_PLCI;
+ break;
+
+ case _DI_IDI_CTRL:
+ if(!plci)
+ {
+ Info = _WRONG_IDENTIFIER;
+ break;
+ }
+ if(api_parse(&m->info[1],(word)m->length,"bs",m_parms)) {
+ Info = _WRONG_MESSAGE_FORMAT;
+ break;
+ }
+ req = m_parms[0].info[0];
+ plci->command = _MANUFACTURER_R;
+ plci->m_command = command;
+ plci->number = Number;
+ if(req==CALL_REQ)
+ {
+ plci->b_channel = getChannel(&m_parms[1]);
+ mixer_set_bchannel_id_esc (plci, plci->b_channel);
+ if(plci->spoofed_msg==SPOOFING_REQUIRED)
+ {
+ plci->spoofed_msg = CALL_REQ | AWAITING_MANUF_CON;
+ plci->internal_command = BLOCK_PLCI; /* reject other req meanwhile */
+ plci->command = 0;
+ break;
+ }
+ }
+ else if(req==LAW_REQ)
+ {
+ plci->cr_enquiry = true;
+ }
+ add_ss(plci,FTY,&m_parms[1]);
+ sig_req(plci,req,0);
+ send_req(plci);
+ if(req==HANGUP)
+ {
+ if (plci->NL.Id && !plci->nl_remove_id)
+ {
+ if (plci->channels)
+ {
+ for (ncci = 1; ncci < MAX_NCCI+1; ncci++)
+ {
+ if ((a->ncci_plci[ncci] == plci->Id) && (a->ncci_state[ncci] == CONNECTED))
+ {
+ a->ncci_state[ncci] = OUTG_DIS_PENDING;
+ cleanup_ncci_data (plci, ncci);
+ nl_req_ncci(plci,N_DISC,(byte)ncci);
+ }
+ }
+ }
+ mixer_remove (plci);
+ nl_req_ncci(plci,REMOVE,0);
+ send_req(plci);
+ }
+ }
+ break;
+
+ case _DI_SIG_CTRL:
+ /* signalling control for loop activation B-channel */
+ if(!plci)
+ {
+ Info = _WRONG_IDENTIFIER;
+ break;
+ }
+ if(m->length){
+ plci->command = _MANUFACTURER_R;
+ plci->number = Number;
+ add_ss(plci,FTY,m);
+ sig_req(plci,SIG_CTRL,0);
+ send_req(plci);
+ }
+ else Info = _WRONG_MESSAGE_FORMAT;
+ break;
+
+ case _DI_RXT_CTRL:
+ /* activation control for receiver/transmitter B-channel */
+ if(!plci)
+ {
+ Info = _WRONG_IDENTIFIER;
+ break;
+ }
+ if(m->length){
+ plci->command = _MANUFACTURER_R;
+ plci->number = Number;
+ add_ss(plci,FTY,m);
+ sig_req(plci,DSP_CTRL,0);
+ send_req(plci);
+ }
+ else Info = _WRONG_MESSAGE_FORMAT;
+ break;
+
+ case _DI_ADV_CODEC:
+ case _DI_DSP_CTRL:
+ /* TEL_CTRL commands to support non standard adjustments: */
+ /* Ring on/off, Handset micro volume, external micro vol. */
+ /* handset+external speaker volume, receiver+transm. gain,*/
+ /* handsfree on (hookinfo off), set mixer command */
+
+ if(command == _DI_ADV_CODEC)
+ {
+ if(!a->AdvCodecPLCI) {
+ Info = _WRONG_STATE;
+ break;
+ }
+ v_plci = a->AdvCodecPLCI;
+ }
+ else
+ {
+ if (plci
+ && (m->length >= 3)
+ && (m->info[1] == 0x1c)
+ && (m->info[2] >= 1))
+ {
+ if (m->info[3] == DSP_CTRL_OLD_SET_MIXER_COEFFICIENTS)
+ {
+ if ((plci->tel != ADV_VOICE) || (plci != a->AdvSignalPLCI))
+ {
+ Info = _WRONG_STATE;
+ break;
+ }
+ a->adv_voice_coef_length = m->info[2] - 1;
+ if (a->adv_voice_coef_length > m->length - 3)
+ a->adv_voice_coef_length = (byte)(m->length - 3);
+ if (a->adv_voice_coef_length > ADV_VOICE_COEF_BUFFER_SIZE)
+ a->adv_voice_coef_length = ADV_VOICE_COEF_BUFFER_SIZE;
+ for (i = 0; i < a->adv_voice_coef_length; i++)
+ a->adv_voice_coef_buffer[i] = m->info[4 + i];
+ if (plci->B1_facilities & B1_FACILITY_VOICE)
+ adv_voice_write_coefs (plci, ADV_VOICE_WRITE_UPDATE);
+ break;
+ }
+ else if (m->info[3] == DSP_CTRL_SET_DTMF_PARAMETERS)
+ {
+ if (!(a->manufacturer_features & MANUFACTURER_FEATURE_DTMF_PARAMETERS))
+ {
+ Info = _FACILITY_NOT_SUPPORTED;
+ break;
+ }
+
+ plci->dtmf_parameter_length = m->info[2] - 1;
+ if (plci->dtmf_parameter_length > m->length - 3)
+ plci->dtmf_parameter_length = (byte)(m->length - 3);
+ if (plci->dtmf_parameter_length > DTMF_PARAMETER_BUFFER_SIZE)
+ plci->dtmf_parameter_length = DTMF_PARAMETER_BUFFER_SIZE;
+ for (i = 0; i < plci->dtmf_parameter_length; i++)
+ plci->dtmf_parameter_buffer[i] = m->info[4+i];
+ if (plci->B1_facilities & B1_FACILITY_DTMFR)
+ dtmf_parameter_write (plci);
+ break;
+
+ }
+ }
+ v_plci = plci;
+ }
+
+ if(!v_plci)
+ {
+ Info = _WRONG_IDENTIFIER;
+ break;
+ }
+ if(m->length){
+ add_ss(v_plci,FTY,m);
+ sig_req(v_plci,TEL_CTRL,0);
+ send_req(v_plci);
+ }
+ else Info = _WRONG_MESSAGE_FORMAT;
+
+ break;
+
+ case _DI_OPTIONS_REQUEST:
+ if(api_parse(&m->info[1],(word)m->length,"d",m_parms)) {
+ Info = _WRONG_MESSAGE_FORMAT;
+ break;
+ }
+ if (GET_DWORD (m_parms[0].info) & ~a->man_profile.private_options)
+ {
+ Info = _FACILITY_NOT_SUPPORTED;
+ break;
+ }
+ a->requested_options_table[appl->Id-1] = GET_DWORD (m_parms[0].info);
+ break;
+
+
+
+ default:
+ Info = _WRONG_MESSAGE_FORMAT;
+ break;
+ }
+ }
+
+ sendf(appl,
+ _MANUFACTURER_R|CONFIRM,
+ Id,
+ Number,
+ "dww",_DI_MANU_ID,command,Info);
+ return false;
+}
+
+
+byte manufacturer_res(dword Id, word Number, DIVA_CAPI_ADAPTER * a, PLCI * plci, APPL * appl, API_PARSE * msg)
+{
+ word indication;
+
+ API_PARSE m_parms[3];
+ API_PARSE *ncpi;
+ API_PARSE fax_parms[9];
+ word i;
+ byte len;
+
+
+ dbug(1,dprintf("manufacturer_res"));
+
+ if ((msg[0].length == 0)
+ || (msg[1].length == 0)
+ || (GET_DWORD(msg[0].info)!=_DI_MANU_ID))
+ {
+ return false;
+ }
+ indication = GET_WORD(msg[1].info);
+ switch (indication)
+ {
+
+ case _DI_NEGOTIATE_B3:
+ if(!plci)
+ break;
+ if (((plci->B3_prot != 4) && (plci->B3_prot != 5))
+ || !(plci->ncpi_state & NCPI_NEGOTIATE_B3_SENT))
+ {
+ dbug(1,dprintf("wrong state for NEGOTIATE_B3 parameters"));
+ break;
+ }
+ if (api_parse (&msg[2].info[1], msg[2].length, "ws", m_parms))
+ {
+ dbug(1,dprintf("wrong format in NEGOTIATE_B3 parameters"));
+ break;
+ }
+ ncpi = &m_parms[1];
+ len = ((byte)(((T30_INFO *) 0)->station_id + 20));
+ if (plci->fax_connect_info_length < len)
+ {
+ ((T30_INFO *)(plci->fax_connect_info_buffer))->station_id_len = 0;
+ ((T30_INFO *)(plci->fax_connect_info_buffer))->head_line_len = 0;
+ }
+ if (api_parse (&ncpi->info[1], ncpi->length, "wwwwssss", fax_parms))
+ {
+ dbug(1,dprintf("non-standard facilities info missing or wrong format"));
+ }
+ else
+ {
+ if (plci->fax_connect_info_length <= len)
+ plci->fax_connect_info_buffer[len] = 0;
+ len += 1 + plci->fax_connect_info_buffer[len];
+ if (plci->fax_connect_info_length <= len)
+ plci->fax_connect_info_buffer[len] = 0;
+ len += 1 + plci->fax_connect_info_buffer[len];
+ if ((fax_parms[7].length >= 3) && (fax_parms[7].info[1] >= 2))
+ plci->nsf_control_bits = GET_WORD(&fax_parms[7].info[2]);
+ plci->fax_connect_info_buffer[len++] = (byte)(fax_parms[7].length);
+ for (i = 0; i < fax_parms[7].length; i++)
+ plci->fax_connect_info_buffer[len++] = fax_parms[7].info[1+i];
+ }
+ plci->fax_connect_info_length = len;
+ plci->fax_edata_ack_length = plci->fax_connect_info_length;
+ start_internal_command (Id, plci, fax_edata_ack_command);
+ break;
+
+ }
+ return false;
+}
+
+/*------------------------------------------------------------------*/
+/* IDI callback function */
+/*------------------------------------------------------------------*/
+
+void callback(ENTITY * e)
+{
+ DIVA_CAPI_ADAPTER * a;
+ APPL * appl;
+ PLCI * plci;
+ CAPI_MSG *m;
+ word i, j;
+ byte rc;
+ byte ch;
+ byte req;
+ byte global_req;
+ int no_cancel_rc;
+
+ dbug(1,dprintf("%x:CB(%x:Req=%x,Rc=%x,Ind=%x)",
+ (e->user[0]+1)&0x7fff,e->Id,e->Req,e->Rc,e->Ind));
+
+ a = &(adapter[(byte)e->user[0]]);
+ plci = &(a->plci[e->user[1]]);
+ no_cancel_rc = DIVA_CAPI_SUPPORTS_NO_CANCEL(a);
+
+ /*
+ If new protocol code and new XDI is used then CAPI should work
+ fully in accordance with IDI cpec an look on callback field instead
+ of Rc field for return codes.
+ */
+ if (((e->complete == 0xff) && no_cancel_rc) ||
+ (e->Rc && !no_cancel_rc)) {
+ rc = e->Rc;
+ ch = e->RcCh;
+ req = e->Req;
+ e->Rc = 0;
+
+ if (e->user[0] & 0x8000)
+ {
+ /*
+ If REMOVE request was sent then we have to wait until
+ return code with Id set to zero arrives.
+ All other return codes should be ignored.
+ */
+ if (req == REMOVE)
+ {
+ if (e->Id)
+ {
+ dbug(1,dprintf("cancel RC in REMOVE state"));
+ return;
+ }
+ channel_flow_control_remove (plci);
+ for (i = 0; i < 256; i++)
+ {
+ if (a->FlowControlIdTable[i] == plci->nl_remove_id)
+ a->FlowControlIdTable[i] = 0;
+ }
+ plci->nl_remove_id = 0;
+ if (plci->rx_dma_descriptor > 0) {
+ diva_free_dma_descriptor (plci, plci->rx_dma_descriptor - 1);
+ plci->rx_dma_descriptor = 0;
+ }
+ }
+ if (rc == OK_FC)
+ {
+ a->FlowControlIdTable[ch] = e->Id;
+ a->FlowControlSkipTable[ch] = 0;
+
+ a->ch_flow_control[ch] |= N_OK_FC_PENDING;
+ a->ch_flow_plci[ch] = plci->Id;
+ plci->nl_req = 0;
+ }
+ else
+ {
+ /*
+ Cancel return codes self, if feature was requested
+ */
+ if (no_cancel_rc && (a->FlowControlIdTable[ch] == e->Id) && e->Id) {
+ a->FlowControlIdTable[ch] = 0;
+ if ((rc == OK) && a->FlowControlSkipTable[ch]) {
+ dbug(3,dprintf ("XDI CAPI: RC cancelled Id:0x02, Ch:%02x", e->Id, ch));
+ return;
+ }
+ }
+
+ if (a->ch_flow_control[ch] & N_OK_FC_PENDING)
+ {
+ a->ch_flow_control[ch] &= ~N_OK_FC_PENDING;
+ if (ch == e->ReqCh)
+ plci->nl_req = 0;
+ }
+ else
+ plci->nl_req = 0;
+ }
+ if (plci->nl_req)
+ control_rc (plci, 0, rc, ch, 0, true);
+ else
+ {
+ if (req == N_XON)
+ {
+ channel_x_on (plci, ch);
+ if (plci->internal_command)
+ control_rc (plci, req, rc, ch, 0, true);
+ }
+ else
+ {
+ if (plci->nl_global_req)
+ {
+ global_req = plci->nl_global_req;
+ plci->nl_global_req = 0;
+ if (rc != ASSIGN_OK) {
+ e->Id = 0;
+ if (plci->rx_dma_descriptor > 0) {
+ diva_free_dma_descriptor (plci, plci->rx_dma_descriptor - 1);
+ plci->rx_dma_descriptor = 0;
+ }
+ }
+ channel_xmit_xon (plci);
+ control_rc (plci, 0, rc, ch, global_req, true);
+ }
+ else if (plci->data_sent)
+ {
+ channel_xmit_xon (plci);
+ plci->data_sent = false;
+ plci->NL.XNum = 1;
+ data_rc (plci, ch);
+ if (plci->internal_command)
+ control_rc (plci, req, rc, ch, 0, true);
+ }
+ else
+ {
+ channel_xmit_xon (plci);
+ control_rc (plci, req, rc, ch, 0, true);
+ }
+ }
+ }
+ }
+ else
+ {
+ /*
+ If REMOVE request was sent then we have to wait until
+ return code with Id set to zero arrives.
+ All other return codes should be ignored.
+ */
+ if (req == REMOVE)
+ {
+ if (e->Id)
+ {
+ dbug(1,dprintf("cancel RC in REMOVE state"));
+ return;
+ }
+ plci->sig_remove_id = 0;
+ }
+ plci->sig_req = 0;
+ if (plci->sig_global_req)
+ {
+ global_req = plci->sig_global_req;
+ plci->sig_global_req = 0;
+ if (rc != ASSIGN_OK)
+ e->Id = 0;
+ channel_xmit_xon (plci);
+ control_rc (plci, 0, rc, ch, global_req, false);
+ }
+ else
+ {
+ channel_xmit_xon (plci);
+ control_rc (plci, req, rc, ch, 0, false);
+ }
+ }
+ /*
+ Again: in accordance with IDI spec Rc and Ind can't be delivered in the
+ same callback. Also if new XDI and protocol code used then jump
+ direct to finish.
+ */
+ if (no_cancel_rc) {
+ channel_xmit_xon(plci);
+ goto capi_callback_suffix;
+ }
+ }
+
+ channel_xmit_xon(plci);
+
+ if (e->Ind) {
+ if (e->user[0] &0x8000) {
+ byte Ind = e->Ind & 0x0f;
+ byte Ch = e->IndCh;
+ if (((Ind==N_DISC) || (Ind==N_DISC_ACK)) &&
+ (a->ch_flow_plci[Ch] == plci->Id)) {
+ if (a->ch_flow_control[Ch] & N_RX_FLOW_CONTROL_MASK) {
+ dbug(3,dprintf ("XDI CAPI: I: pending N-XON Ch:%02x", Ch));
+ }
+ a->ch_flow_control[Ch] &= ~N_RX_FLOW_CONTROL_MASK;
+ }
+ nl_ind(plci);
+ if ((e->RNR != 1) &&
+ (a->ch_flow_plci[Ch] == plci->Id) &&
+ (a->ch_flow_control[Ch] & N_RX_FLOW_CONTROL_MASK)) {
+ a->ch_flow_control[Ch] &= ~N_RX_FLOW_CONTROL_MASK;
+ dbug(3,dprintf ("XDI CAPI: I: remove faked N-XON Ch:%02x", Ch));
+ }
+ } else {
+ sig_ind(plci);
+ }
+ e->Ind = 0;
+ }
+
+capi_callback_suffix:
+
+ while (!plci->req_in
+ && !plci->internal_command
+ && (plci->msg_in_write_pos != plci->msg_in_read_pos))
+ {
+ j = (plci->msg_in_read_pos == plci->msg_in_wrap_pos) ? 0 : plci->msg_in_read_pos;
+
+ i = (((CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[j]))->header.length + 3) & 0xfffc;
+
+ m = (CAPI_MSG *)(&((byte *)(plci->msg_in_queue))[j]);
+ appl = *((APPL * *)(&((byte *)(plci->msg_in_queue))[j+i]));
+ dbug(1,dprintf("dequeue msg(0x%04x) - write=%d read=%d wrap=%d",
+ m->header.command, plci->msg_in_write_pos, plci->msg_in_read_pos, plci->msg_in_wrap_pos));
+ if (plci->msg_in_read_pos == plci->msg_in_wrap_pos)
+ {
+ plci->msg_in_wrap_pos = MSG_IN_QUEUE_SIZE;
+ plci->msg_in_read_pos = i + MSG_IN_OVERHEAD;
+ }
+ else
+ {
+ plci->msg_in_read_pos = j + i + MSG_IN_OVERHEAD;
+ }
+ if (plci->msg_in_read_pos == plci->msg_in_write_pos)
+ {
+ plci->msg_in_write_pos = MSG_IN_QUEUE_SIZE;
+ plci->msg_in_read_pos = MSG_IN_QUEUE_SIZE;
+ }
+ else if (plci->msg_in_read_pos == plci->msg_in_wrap_pos)
+ {
+ plci->msg_in_read_pos = MSG_IN_QUEUE_SIZE;
+ plci->msg_in_wrap_pos = MSG_IN_QUEUE_SIZE;
+ }
+ i = api_put (appl, m);
+ if (i != 0)
+ {
+ if (m->header.command == _DATA_B3_R)
+
+ TransmitBufferFree (appl, (byte *)(long)(m->info.data_b3_req.Data));
+
+ dbug(1,dprintf("Error 0x%04x from msg(0x%04x)", i, m->header.command));
+ break;
+ }
+
+ if (plci->li_notify_update)
+ {
+ plci->li_notify_update = false;
+ mixer_notify_update (plci, false);
+ }
+
+ }
+ send_data(plci);
+ send_req(plci);
+}
+
+
+void control_rc(PLCI * plci, byte req, byte rc, byte ch, byte global_req, byte nl_rc)
+{
+ dword Id;
+ dword rId;
+ word Number;
+ word Info=0;
+ word i;
+ word ncci;
+ DIVA_CAPI_ADAPTER * a;
+ APPL * appl;
+ PLCI * rplci;
+ byte SSparms[] = "\x05\x00\x00\x02\x00\x00";
+ byte SSstruct[] = "\x09\x00\x00\x06\x00\x00\x00\x00\x00\x00";
+
+ if (!plci) {
+ dbug(0,dprintf("A: control_rc, no plci %02x:%02x:%02x:%02x:%02x", req, rc, ch, global_req, nl_rc));
+ return;
+ }
+ dbug(1,dprintf("req0_in/out=%d/%d",plci->req_in,plci->req_out));
+ if(plci->req_in!=plci->req_out)
+ {
+ if (nl_rc || (global_req != ASSIGN) || (rc == ASSIGN_OK))
+ {
+ dbug(1,dprintf("req_1return"));
+ return;
+ }
+ /* cancel outstanding request on the PLCI after SIG ASSIGN failure */
+ }
+ plci->req_in = plci->req_in_start = plci->req_out = 0;
+ dbug(1,dprintf("control_rc"));
+
+ appl = plci->appl;
+ a = plci->adapter;
+ ncci = a->ch_ncci[ch];
+ if(appl)
+ {
+ Id = (((dword)(ncci ? ncci : ch)) << 16) | ((word)plci->Id << 8) | a->Id;
+ if(plci->tel && plci->SuppState!=CALL_HELD) Id|=EXT_CONTROLLER;
+ Number = plci->number;
+ dbug(1,dprintf("Contr_RC-Id=%08lx,plci=%x,tel=%x, entity=0x%x, command=0x%x, int_command=0x%x",Id,plci->Id,plci->tel,plci->Sig.Id,plci->command,plci->internal_command));
+ dbug(1,dprintf("channels=0x%x",plci->channels));
+ if (plci_remove_check(plci))
+ return;
+ if(req==REMOVE && rc==ASSIGN_OK)
+ {
+ sig_req(plci,HANGUP,0);
+ sig_req(plci,REMOVE,0);
+ send_req(plci);
+ }
+ if(plci->command)
+ {
+ switch(plci->command)
+ {
+ case C_HOLD_REQ:
+ dbug(1,dprintf("HoldRC=0x%x",rc));
+ SSparms[1] = (byte)S_HOLD;
+ if(rc!=OK)
+ {
+ plci->SuppState = IDLE;
+ Info = 0x2001;
+ }
+ sendf(appl,_FACILITY_R|CONFIRM,Id,Number,"wws",Info,3,SSparms);
+ break;
+
+ case C_RETRIEVE_REQ:
+ dbug(1,dprintf("RetrieveRC=0x%x",rc));
+ SSparms[1] = (byte)S_RETRIEVE;
+ if(rc!=OK)
+ {
+ plci->SuppState = CALL_HELD;
+ Info = 0x2001;
+ }
+ sendf(appl,_FACILITY_R|CONFIRM,Id,Number,"wws",Info,3,SSparms);
+ break;
+
+ case _INFO_R:
+ dbug(1,dprintf("InfoRC=0x%x",rc));
+ if(rc!=OK) Info=_WRONG_STATE;
+ sendf(appl,_INFO_R|CONFIRM,Id,Number,"w",Info);
+ break;
+
+ case _CONNECT_R:
+ dbug(1,dprintf("Connect_R=0x%x/0x%x/0x%x/0x%x",req,rc,global_req,nl_rc));
+ if (plci->State == INC_DIS_PENDING)
+ break;
+ if(plci->Sig.Id!=0xff)
+ {
+ if (((global_req == ASSIGN) && (rc != ASSIGN_OK))
+ || (!nl_rc && (req == CALL_REQ) && (rc != OK)))
+ {
+ dbug(1,dprintf("No more IDs/Call_Req failed"));
+ sendf(appl,_CONNECT_R|CONFIRM,Id&0xffL,Number,"w",_OUT_OF_PLCI);
+ plci_remove(plci);
+ plci->State = IDLE;
+ break;
+ }
+ if(plci->State!=LOCAL_CONNECT)plci->State = OUTG_CON_PENDING;
+ sendf(appl,_CONNECT_R|CONFIRM,Id,Number,"w",0);
+ }
+ else /* D-ch activation */
+ {
+ if (rc != ASSIGN_OK)
+ {
+ dbug(1,dprintf("No more IDs/X.25 Call_Req failed"));
+ sendf(appl,_CONNECT_R|CONFIRM,Id&0xffL,Number,"w",_OUT_OF_PLCI);
+ plci_remove(plci);
+ plci->State = IDLE;
+ break;
+ }
+ sendf(appl,_CONNECT_R|CONFIRM,Id,Number,"w",0);
+ sendf(plci->appl,_CONNECT_ACTIVE_I,Id,0,"sss","","","");
+ plci->State = INC_ACT_PENDING;
+ }
+ break;
+
+ case _CONNECT_I|RESPONSE:
+ if (plci->State != INC_DIS_PENDING)
+ plci->State = INC_CON_ACCEPT;
+ break;
+
+ case _DISCONNECT_R:
+ if (plci->State == INC_DIS_PENDING)
+ break;
+ if(plci->Sig.Id!=0xff)
+ {
+ plci->State = OUTG_DIS_PENDING;
+ sendf(appl,_DISCONNECT_R|CONFIRM,Id,Number,"w",0);
+ }
+ break;
+
+ case SUSPEND_REQ:
+ break;
+
+ case RESUME_REQ:
+ break;
+
+ case _CONNECT_B3_R:
+ if(rc!=OK)
+ {
+ sendf(appl,_CONNECT_B3_R|CONFIRM,Id,Number,"w",_WRONG_IDENTIFIER);
+ break;
+ }
+ ncci = get_ncci (plci, ch, 0);
+ Id = (Id & 0xffff) | (((dword) ncci) << 16);
+ plci->channels++;
+ if(req==N_RESET)
+ {
+ a->ncci_state[ncci] = INC_ACT_PENDING;
+ sendf(appl,_CONNECT_B3_R|CONFIRM,Id,Number,"w",0);
+ sendf(appl,_CONNECT_B3_ACTIVE_I,Id,0,"s","");
+ }
+ else
+ {
+ a->ncci_state[ncci] = OUTG_CON_PENDING;
+ sendf(appl,_CONNECT_B3_R|CONFIRM,Id,Number,"w",0);
+ }
+ break;
+
+ case _CONNECT_B3_I|RESPONSE:
+ break;
+
+ case _RESET_B3_R:
+/* sendf(appl,_RESET_B3_R|CONFIRM,Id,Number,"w",0);*/
+ break;
+
+ case _DISCONNECT_B3_R:
+ sendf(appl,_DISCONNECT_B3_R|CONFIRM,Id,Number,"w",0);
+ break;
+
+ case _MANUFACTURER_R:
+ break;
+
+ case PERM_LIST_REQ:
+ if(rc!=OK)
+ {
+ Info = _WRONG_IDENTIFIER;
+ sendf(plci->appl,_CONNECT_R|CONFIRM,Id,Number,"w",Info);
+ plci_remove(plci);
+ }
+ else
+ sendf(plci->appl,_CONNECT_R|CONFIRM,Id,Number,"w",Info);
+ break;
+
+ default:
+ break;
+ }
+ plci->command = 0;
+ }
+ else if (plci->internal_command)
+ {
+ switch(plci->internal_command)
+ {
+ case BLOCK_PLCI:
+ return;
+
+ case GET_MWI_STATE:
+ if(rc==OK) /* command supported, wait for indication */
+ {
+ return;
+ }
+ plci_remove(plci);
+ break;
+
+ /* Get Supported Services */
+ case GETSERV_REQ_PEND:
+ if(rc==OK) /* command supported, wait for indication */
+ {
+ break;
+ }
+ PUT_DWORD(&SSstruct[6], MASK_TERMINAL_PORTABILITY);
+ sendf(appl, _FACILITY_R|CONFIRM, Id, Number, "wws",0,3,SSstruct);
+ plci_remove(plci);
+ break;
+
+ case INTERR_DIVERSION_REQ_PEND: /* Interrogate Parameters */
+ case INTERR_NUMBERS_REQ_PEND:
+ case CF_START_PEND: /* Call Forwarding Start pending */
+ case CF_STOP_PEND: /* Call Forwarding Stop pending */
+ case CCBS_REQUEST_REQ_PEND:
+ case CCBS_DEACTIVATE_REQ_PEND:
+ case CCBS_INTERROGATE_REQ_PEND:
+ switch(plci->internal_command)
+ {
+ case INTERR_DIVERSION_REQ_PEND:
+ SSparms[1] = S_INTERROGATE_DIVERSION;
+ break;
+ case INTERR_NUMBERS_REQ_PEND:
+ SSparms[1] = S_INTERROGATE_NUMBERS;
+ break;
+ case CF_START_PEND:
+ SSparms[1] = S_CALL_FORWARDING_START;
+ break;
+ case CF_STOP_PEND:
+ SSparms[1] = S_CALL_FORWARDING_STOP;
+ break;
+ case CCBS_REQUEST_REQ_PEND:
+ SSparms[1] = S_CCBS_REQUEST;
+ break;
+ case CCBS_DEACTIVATE_REQ_PEND:
+ SSparms[1] = S_CCBS_DEACTIVATE;
+ break;
+ case CCBS_INTERROGATE_REQ_PEND:
+ SSparms[1] = S_CCBS_INTERROGATE;
+ break;
+ }
+ if(global_req==ASSIGN)
+ {
+ dbug(1,dprintf("AssignDiversion_RC=0x%x/0x%x",req,rc));
+ return;
+ }
+ if(!plci->appl) break;
+ if(rc==ISDN_GUARD_REJ)
+ {
+ Info = _CAPI_GUARD_ERROR;
+ }
+ else if(rc!=OK)
+ {
+ Info = _SUPPLEMENTARY_SERVICE_NOT_SUPPORTED;
+ }
+ sendf(plci->appl,_FACILITY_R|CONFIRM,Id&0x7,
+ plci->number,"wws",Info,(word)3,SSparms);
+ if(Info) plci_remove(plci);
+ break;
+
+ /* 3pty conference pending */
+ case PTY_REQ_PEND:
+ if(!plci->relatedPTYPLCI) break;
+ rplci = plci->relatedPTYPLCI;
+ SSparms[1] = plci->ptyState;
+ rId = ((word)rplci->Id<<8)|rplci->adapter->Id;
+ if(rplci->tel) rId|=EXT_CONTROLLER;
+ if(rc!=OK)
+ {
+ Info = 0x300E; /* not supported */
+ plci->relatedPTYPLCI = NULL;
+ plci->ptyState = 0;
+ }
+ sendf(rplci->appl,
+ _FACILITY_R|CONFIRM,
+ rId,
+ plci->number,
+ "wws",Info,(word)3,SSparms);
+ break;
+
+ /* Explicit Call Transfer pending */
+ case ECT_REQ_PEND:
+ dbug(1,dprintf("ECT_RC=0x%x/0x%x",req,rc));
+ if(!plci->relatedPTYPLCI) break;
+ rplci = plci->relatedPTYPLCI;
+ SSparms[1] = S_ECT;
+ rId = ((word)rplci->Id<<8)|rplci->adapter->Id;
+ if(rplci->tel) rId|=EXT_CONTROLLER;
+ if(rc!=OK)
+ {
+ Info = 0x300E; /* not supported */
+ plci->relatedPTYPLCI = NULL;
+ plci->ptyState = 0;
+ }
+ sendf(rplci->appl,
+ _FACILITY_R|CONFIRM,
+ rId,
+ plci->number,
+ "wws",Info,(word)3,SSparms);
+ break;
+
+ case _MANUFACTURER_R:
+ dbug(1,dprintf("_Manufacturer_R=0x%x/0x%x",req,rc));
+ if ((global_req == ASSIGN) && (rc != ASSIGN_OK))
+ {
+ dbug(1,dprintf("No more IDs"));
+ sendf(appl,_MANUFACTURER_R|CONFIRM,Id,Number,"dww",_DI_MANU_ID,_MANUFACTURER_R,_OUT_OF_PLCI);
+ plci_remove(plci); /* after codec init, internal codec commands pending */
+ }
+ break;
+
+ case _CONNECT_R:
+ dbug(1,dprintf("_Connect_R=0x%x/0x%x",req,rc));
+ if ((global_req == ASSIGN) && (rc != ASSIGN_OK))
+ {
+ dbug(1,dprintf("No more IDs"));
+ sendf(appl,_CONNECT_R|CONFIRM,Id&0xffL,Number,"w",_OUT_OF_PLCI);
+ plci_remove(plci); /* after codec init, internal codec commands pending */
+ }
+ break;
+
+ case PERM_COD_HOOK: /* finished with Hook_Ind */
+ return;
+
+ case PERM_COD_CALL:
+ dbug(1,dprintf("***Codec Connect_Pending A, Rc = 0x%x",rc));
+ plci->internal_command = PERM_COD_CONN_PEND;
+ return;
+
+ case PERM_COD_ASSIGN:
+ dbug(1,dprintf("***Codec Assign A, Rc = 0x%x",rc));
+ if(rc!=ASSIGN_OK) break;
+ sig_req(plci,CALL_REQ,0);
+ send_req(plci);
+ plci->internal_command = PERM_COD_CALL;
+ return;
+
+ /* Null Call Reference Request pending */
+ case C_NCR_FAC_REQ:
+ dbug(1,dprintf("NCR_FAC=0x%x/0x%x",req,rc));
+ if(global_req==ASSIGN)
+ {
+ if(rc==ASSIGN_OK)
+ {
+ return;
+ }
+ else
+ {
+ sendf(appl,_INFO_R|CONFIRM,Id&0xf,Number,"w",_WRONG_STATE);
+ appl->NullCREnable = false;
+ plci_remove(plci);
+ }
+ }
+ else if(req==NCR_FACILITY)
+ {
+ if(rc==OK)
+ {
+ sendf(appl,_INFO_R|CONFIRM,Id&0xf,Number,"w",0);
+ }
+ else
+ {
+ sendf(appl,_INFO_R|CONFIRM,Id&0xf,Number,"w",_WRONG_STATE);
+ appl->NullCREnable = false;
+ }
+ plci_remove(plci);
+ }
+ break;
+
+ case HOOK_ON_REQ:
+ if(plci->channels)
+ {
+ if(a->ncci_state[ncci]==CONNECTED)
+ {
+ a->ncci_state[ncci] = OUTG_DIS_PENDING;
+ cleanup_ncci_data (plci, ncci);
+ nl_req_ncci(plci,N_DISC,(byte)ncci);
+ }
+ break;
+ }
+ break;
+
+ case HOOK_OFF_REQ:
+ if (plci->State == INC_DIS_PENDING)
+ break;
+ sig_req(plci,CALL_REQ,0);
+ send_req(plci);
+ plci->State=OUTG_CON_PENDING;
+ break;
+
+
+ case MWI_ACTIVATE_REQ_PEND:
+ case MWI_DEACTIVATE_REQ_PEND:
+ if(global_req == ASSIGN && rc==ASSIGN_OK)
+ {
+ dbug(1,dprintf("MWI_REQ assigned"));
+ return;
+ }
+ else if(rc!=OK)
+ {
+ if(rc==WRONG_IE)
+ {
+ Info = 0x2007; /* Illegal message parameter coding */
+ dbug(1,dprintf("MWI_REQ invalid parameter"));
+ }
+ else
+ {
+ Info = 0x300B; /* not supported */
+ dbug(1,dprintf("MWI_REQ not supported"));
+ }
+ /* 0x3010: Request not allowed in this state */
+ PUT_WORD(&SSparms[4],0x300E); /* SS not supported */
+
+ }
+ if(plci->internal_command==MWI_ACTIVATE_REQ_PEND)
+ {
+ PUT_WORD(&SSparms[1],S_MWI_ACTIVATE);
+ }
+ else PUT_WORD(&SSparms[1],S_MWI_DEACTIVATE);
+
+ if(plci->cr_enquiry)
+ {
+ sendf(plci->appl,
+ _FACILITY_R|CONFIRM,
+ Id&0xf,
+ plci->number,
+ "wws",Info,(word)3,SSparms);
+ if(rc!=OK) plci_remove(plci);
+ }
+ else
+ {
+ sendf(plci->appl,
+ _FACILITY_R|CONFIRM,
+ Id,
+ plci->number,
+ "wws",Info,(word)3,SSparms);
+ }
+ break;
+
+ case CONF_BEGIN_REQ_PEND:
+ case CONF_ADD_REQ_PEND:
+ case CONF_SPLIT_REQ_PEND:
+ case CONF_DROP_REQ_PEND:
+ case CONF_ISOLATE_REQ_PEND:
+ case CONF_REATTACH_REQ_PEND:
+ dbug(1,dprintf("CONF_RC=0x%x/0x%x",req,rc));
+ if((plci->internal_command==CONF_ADD_REQ_PEND)&&(!plci->relatedPTYPLCI)) break;
+ rplci = plci;
+ rId = Id;
+ switch(plci->internal_command)
+ {
+ case CONF_BEGIN_REQ_PEND:
+ SSparms[1] = S_CONF_BEGIN;
+ break;
+ case CONF_ADD_REQ_PEND:
+ SSparms[1] = S_CONF_ADD;
+ rplci = plci->relatedPTYPLCI;
+ rId = ((word)rplci->Id<<8)|rplci->adapter->Id;
+ break;
+ case CONF_SPLIT_REQ_PEND:
+ SSparms[1] = S_CONF_SPLIT;
+ break;
+ case CONF_DROP_REQ_PEND:
+ SSparms[1] = S_CONF_DROP;
+ break;
+ case CONF_ISOLATE_REQ_PEND:
+ SSparms[1] = S_CONF_ISOLATE;
+ break;
+ case CONF_REATTACH_REQ_PEND:
+ SSparms[1] = S_CONF_REATTACH;
+ break;
+ }
+
+ if(rc!=OK)
+ {
+ Info = 0x300E; /* not supported */
+ plci->relatedPTYPLCI = NULL;
+ plci->ptyState = 0;
+ }
+ sendf(rplci->appl,
+ _FACILITY_R|CONFIRM,
+ rId,
+ plci->number,
+ "wws",Info,(word)3,SSparms);
+ break;
+
+ case VSWITCH_REQ_PEND:
+ if(rc!=OK)
+ {
+ if(plci->relatedPTYPLCI)
+ {
+ plci->relatedPTYPLCI->vswitchstate=0;
+ plci->relatedPTYPLCI->vsprot=0;
+ plci->relatedPTYPLCI->vsprotdialect=0;
+ }
+ plci->vswitchstate=0;
+ plci->vsprot=0;
+ plci->vsprotdialect=0;
+ }
+ else
+ {
+ if(plci->relatedPTYPLCI &&
+ plci->vswitchstate==1 &&
+ plci->relatedPTYPLCI->vswitchstate==3) /* join complete */
+ plci->vswitchstate=3;
+ }
+ break;
+
+ /* Call Deflection Request pending (SSCT) */
+ case CD_REQ_PEND:
+ SSparms[1] = S_CALL_DEFLECTION;
+ if(rc!=OK)
+ {
+ Info = 0x300E; /* not supported */
+ plci->appl->CDEnable = 0;
+ }
+ sendf(plci->appl,_FACILITY_R|CONFIRM,Id,
+ plci->number,"wws",Info,(word)3,SSparms);
+ break;
+
+ case RTP_CONNECT_B3_REQ_COMMAND_2:
+ if (rc == OK)
+ {
+ ncci = get_ncci (plci, ch, 0);
+ Id = (Id & 0xffff) | (((dword) ncci) << 16);
+ plci->channels++;
+ a->ncci_state[ncci] = OUTG_CON_PENDING;
+ }
+
+ default:
+ if (plci->internal_command_queue[0])
+ {
+ (*(plci->internal_command_queue[0]))(Id, plci, rc);
+ if (plci->internal_command)
+ return;
+ }
+ break;
+ }
+ next_internal_command (Id, plci);
+ }
+ }
+ else /* appl==0 */
+ {
+ Id = ((word)plci->Id<<8)|plci->adapter->Id;
+ if(plci->tel) Id|=EXT_CONTROLLER;
+
+ switch(plci->internal_command)
+ {
+ case BLOCK_PLCI:
+ return;
+
+ case START_L1_SIG_ASSIGN_PEND:
+ case REM_L1_SIG_ASSIGN_PEND:
+ if(global_req == ASSIGN)
+ {
+ break;
+ }
+ else
+ {
+ dbug(1,dprintf("***L1 Req rem PLCI"));
+ plci->internal_command = 0;
+ sig_req(plci,REMOVE,0);
+ send_req(plci);
+ }
+ break;
+
+ /* Call Deflection Request pending, just no appl ptr assigned */
+ case CD_REQ_PEND:
+ SSparms[1] = S_CALL_DEFLECTION;
+ if(rc!=OK)
+ {
+ Info = 0x300E; /* not supported */
+ }
+ for(i=0; i<max_appl; i++)
+ {
+ if(application[i].CDEnable)
+ {
+ if(!application[i].Id) application[i].CDEnable = 0;
+ else
+ {
+ sendf(&application[i],_FACILITY_R|CONFIRM,Id,
+ plci->number,"wws",Info,(word)3,SSparms);
+ if(Info) application[i].CDEnable = 0;
+ }
+ }
+ }
+ plci->internal_command = 0;
+ break;
+
+ case PERM_COD_HOOK: /* finished with Hook_Ind */
+ return;
+
+ case PERM_COD_CALL:
+ plci->internal_command = PERM_COD_CONN_PEND;
+ dbug(1,dprintf("***Codec Connect_Pending, Rc = 0x%x",rc));
+ return;
+
+ case PERM_COD_ASSIGN:
+ dbug(1,dprintf("***Codec Assign, Rc = 0x%x",rc));
+ plci->internal_command = 0;
+ if(rc!=ASSIGN_OK) break;
+ plci->internal_command = PERM_COD_CALL;
+ sig_req(plci,CALL_REQ,0);
+ send_req(plci);
+ return;
+
+ case LISTEN_SIG_ASSIGN_PEND:
+ if(rc == ASSIGN_OK)
+ {
+ plci->internal_command = 0;
+ dbug(1,dprintf("ListenCheck, new SIG_ID = 0x%x",plci->Sig.Id));
+ add_p(plci,ESC,"\x02\x18\x00"); /* support call waiting */
+ sig_req(plci,INDICATE_REQ,0);
+ send_req(plci);
+ }
+ else
+ {
+ dbug(1,dprintf("ListenCheck failed (assignRc=0x%x)",rc));
+ a->listen_active--;
+ plci_remove(plci);
+ plci->State = IDLE;
+ }
+ break;
+
+ case USELAW_REQ:
+ if(global_req == ASSIGN)
+ {
+ if (rc==ASSIGN_OK)
+ {
+ sig_req(plci,LAW_REQ,0);
+ send_req(plci);
+ dbug(1,dprintf("Auto-Law assigned"));
+ }
+ else
+ {
+ dbug(1,dprintf("Auto-Law assign failed"));
+ a->automatic_law = 3;
+ plci->internal_command = 0;
+ a->automatic_lawPLCI = NULL;
+ }
+ break;
+ }
+ else if(req == LAW_REQ && rc==OK)
+ {
+ dbug(1,dprintf("Auto-Law initiated"));
+ a->automatic_law = 2;
+ plci->internal_command = 0;
+ }
+ else
+ {
+ dbug(1,dprintf("Auto-Law not supported"));
+ a->automatic_law = 3;
+ plci->internal_command = 0;
+ sig_req(plci,REMOVE,0);
+ send_req(plci);
+ a->automatic_lawPLCI = NULL;
+ }
+ break;
+ }
+ plci_remove_check(plci);
+ }
+}
+
+void data_rc(PLCI * plci, byte ch)
+{
+ dword Id;
+ DIVA_CAPI_ADAPTER * a;
+ NCCI *ncci_ptr;
+ DATA_B3_DESC *data;
+ word ncci;
+
+ if (plci->appl)
+ {
+ TransmitBufferFree (plci->appl, plci->data_sent_ptr);
+ a = plci->adapter;
+ ncci = a->ch_ncci[ch];
+ if (ncci && (a->ncci_plci[ncci] == plci->Id))
+ {
+ ncci_ptr = &(a->ncci[ncci]);
+ dbug(1,dprintf("data_out=%d, data_pending=%d",ncci_ptr->data_out,ncci_ptr->data_pending));
+ if (ncci_ptr->data_pending)
+ {
+ data = &(ncci_ptr->DBuffer[ncci_ptr->data_out]);
+ if (!(data->Flags &4) && a->ncci_state[ncci])
+ {
+ Id = (((dword)ncci)<<16)|((word)plci->Id<<8)|a->Id;
+ if(plci->tel) Id|=EXT_CONTROLLER;
+ sendf(plci->appl,_DATA_B3_R|CONFIRM,Id,data->Number,
+ "ww",data->Handle,0);
+ }
+ (ncci_ptr->data_out)++;
+ if (ncci_ptr->data_out == MAX_DATA_B3)
+ ncci_ptr->data_out = 0;
+ (ncci_ptr->data_pending)--;
+ }
+ }
+ }
+}
+
+void data_ack(PLCI * plci, byte ch)
+{
+ dword Id;
+ DIVA_CAPI_ADAPTER * a;
+ NCCI *ncci_ptr;
+ word ncci;
+
+ a = plci->adapter;
+ ncci = a->ch_ncci[ch];
+ ncci_ptr = &(a->ncci[ncci]);
+ if (ncci_ptr->data_ack_pending)
+ {
+ if (a->ncci_state[ncci] && (a->ncci_plci[ncci] == plci->Id))
+ {
+ Id = (((dword)ncci)<<16)|((word)plci->Id<<8)|a->Id;
+ if(plci->tel) Id|=EXT_CONTROLLER;
+ sendf(plci->appl,_DATA_B3_R|CONFIRM,Id,ncci_ptr->DataAck[ncci_ptr->data_ack_out].Number,
+ "ww",ncci_ptr->DataAck[ncci_ptr->data_ack_out].Handle,0);
+ }
+ (ncci_ptr->data_ack_out)++;
+ if (ncci_ptr->data_ack_out == MAX_DATA_ACK)
+ ncci_ptr->data_ack_out = 0;
+ (ncci_ptr->data_ack_pending)--;
+ }
+}
+
+void sig_ind(PLCI * plci)
+{
+ dword x_Id;
+ dword Id;
+ dword rId;
+ word Number = 0;
+ word i;
+ word cip;
+ dword cip_mask;
+ byte *ie;
+ DIVA_CAPI_ADAPTER * a;
+ API_PARSE saved_parms[MAX_MSG_PARMS+1];
+#define MAXPARMSIDS 31
+ byte * parms[MAXPARMSIDS];
+ byte * add_i[4];
+ byte * multi_fac_parms[MAX_MULTI_IE];
+ byte * multi_pi_parms [MAX_MULTI_IE];
+ byte * multi_ssext_parms [MAX_MULTI_IE];
+ byte * multi_CiPN_parms [MAX_MULTI_IE];
+
+ byte * multi_vswitch_parms [MAX_MULTI_IE];
+
+ byte ai_len;
+ byte *esc_chi = "";
+ byte *esc_law = "";
+ byte *pty_cai = "";
+ byte *esc_cr = "";
+ byte *esc_profile = "";
+
+ byte facility[256];
+ PLCI * tplci = NULL;
+ byte chi[] = "\x02\x18\x01";
+ byte voice_cai[] = "\x06\x14\x00\x00\x00\x00\x08";
+ byte resume_cau[] = "\x05\x05\x00\x02\x00\x00";
+ /* ESC_MSGTYPE must be the last but one message, a new IE has to be */
+ /* included before the ESC_MSGTYPE and MAXPARMSIDS has to be incremented */
+ /* SMSG is situated at the end because its 0 (for compatibility reasons */
+ /* (see Info_Mask Bit 4, first IE. then the message type) */
+ word parms_id[] =
+ {MAXPARMSIDS, CPN, 0xff, DSA, OSA, BC, LLC, HLC, ESC_CAUSE, DSP, DT, CHA,
+ UUI, CONG_RR, CONG_RNR, ESC_CHI, KEY, CHI, CAU, ESC_LAW,
+ RDN, RDX, CONN_NR, RIN, NI, CAI, ESC_CR,
+ CST, ESC_PROFILE, 0xff, ESC_MSGTYPE, SMSG};
+ /* 14 FTY repl by ESC_CHI */
+ /* 18 PI repl by ESC_LAW */
+ /* removed OAD changed to 0xff for future use, OAD is multiIE now */
+ word multi_fac_id[] = {1, FTY};
+ word multi_pi_id[] = {1, PI};
+ word multi_CiPN_id[] = {1, OAD};
+ word multi_ssext_id[] = {1, ESC_SSEXT};
+
+ word multi_vswitch_id[] = {1, ESC_VSWITCH};
+
+ byte * cau;
+ word ncci;
+ byte SS_Ind[] = "\x05\x02\x00\x02\x00\x00"; /* Hold_Ind struct*/
+ byte CF_Ind[] = "\x09\x02\x00\x06\x00\x00\x00\x00\x00\x00";
+ byte Interr_Err_Ind[] = "\x0a\x02\x00\x07\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
+ byte CONF_Ind[] = "\x09\x16\x00\x06\x00\x00\0x00\0x00\0x00\0x00";
+ byte force_mt_info = false;
+ byte dir;
+ dword d;
+ word w;
+
+ a = plci->adapter;
+ Id = ((word)plci->Id<<8)|a->Id;
+ PUT_WORD(&SS_Ind[4],0x0000);
+
+ if (plci->sig_remove_id)
+ {
+ plci->Sig.RNR = 2; /* discard */
+ dbug(1,dprintf("SIG discard while remove pending"));
+ return;
+ }
+ if(plci->tel && plci->SuppState!=CALL_HELD) Id|=EXT_CONTROLLER;
+ dbug(1,dprintf("SigInd-Id=%08lx,plci=%x,tel=%x,state=0x%x,channels=%d,Discflowcl=%d",
+ Id,plci->Id,plci->tel,plci->State,plci->channels,plci->hangup_flow_ctrl_timer));
+ if(plci->Sig.Ind==CALL_HOLD_ACK && plci->channels)
+ {
+ plci->Sig.RNR = 1;
+ return;
+ }
+ if(plci->Sig.Ind==HANGUP && plci->channels)
+ {
+ plci->Sig.RNR = 1;
+ plci->hangup_flow_ctrl_timer++;
+ /* recover the network layer after timeout */
+ if(plci->hangup_flow_ctrl_timer==100)
+ {
+ dbug(1,dprintf("Exceptional disc"));
+ plci->Sig.RNR = 0;
+ plci->hangup_flow_ctrl_timer = 0;
+ for (ncci = 1; ncci < MAX_NCCI+1; ncci++)
+ {
+ if (a->ncci_plci[ncci] == plci->Id)
+ {
+ cleanup_ncci_data (plci, ncci);
+ if(plci->channels)plci->channels--;
+ if (plci->appl)
+ sendf(plci->appl,_DISCONNECT_B3_I, (((dword) ncci) << 16) | Id,0,"ws",0,"");
+ }
+ }
+ if (plci->appl)
+ sendf(plci->appl, _DISCONNECT_I, Id, 0, "w", 0);
+ plci_remove(plci);
+ plci->State=IDLE;
+ }
+ return;
+ }
+
+ /* do first parse the info with no OAD in, because OAD will be converted */
+ /* first the multiple facility IE, then mult. progress ind. */
+ /* then the parameters for the info_ind + conn_ind */
+ IndParse(plci,multi_fac_id,multi_fac_parms,MAX_MULTI_IE);
+ IndParse(plci,multi_pi_id,multi_pi_parms,MAX_MULTI_IE);
+ IndParse(plci,multi_ssext_id,multi_ssext_parms,MAX_MULTI_IE);
+
+ IndParse(plci,multi_vswitch_id,multi_vswitch_parms,MAX_MULTI_IE);
+
+ IndParse(plci,parms_id,parms,0);
+ IndParse(plci,multi_CiPN_id,multi_CiPN_parms,MAX_MULTI_IE);
+ esc_chi = parms[14];
+ esc_law = parms[18];
+ pty_cai = parms[24];
+ esc_cr = parms[25];
+ esc_profile = parms[27];
+ if(esc_cr[0] && plci)
+ {
+ if(plci->cr_enquiry && plci->appl)
+ {
+ plci->cr_enquiry = false;
+ /* d = MANU_ID */
+ /* w = m_command */
+ /* b = total length */
+ /* b = indication type */
+ /* b = length of all IEs */
+ /* b = IE1 */
+ /* S = IE1 length + cont. */
+ /* b = IE2 */
+ /* S = IE2 length + cont. */
+ sendf(plci->appl,
+ _MANUFACTURER_I,
+ Id,
+ 0,
+ "dwbbbbSbS",_DI_MANU_ID,plci->m_command,
+ 2+1+1+esc_cr[0]+1+1+esc_law[0],plci->Sig.Ind,1+1+esc_cr[0]+1+1+esc_law[0],ESC,esc_cr,ESC,esc_law);
+ }
+ }
+ /* create the additional info structure */
+ add_i[1] = parms[15]; /* KEY of additional info */
+ add_i[2] = parms[11]; /* UUI of additional info */
+ ai_len = AddInfo(add_i,multi_fac_parms, esc_chi, facility);
+
+ /* the ESC_LAW indicates if u-Law or a-Law is actually used by the card */
+ /* indication returns by the card if requested by the function */
+ /* AutomaticLaw() after driver init */
+ if (a->automatic_law<4)
+ {
+ if(esc_law[0]){
+ if(esc_law[2]){
+ dbug(0,dprintf("u-Law selected"));
+ a->u_law = 1;
+ }
+ else {
+ dbug(0,dprintf("a-Law selected"));
+ a->u_law = 0;
+ }
+ a->automatic_law = 4;
+ if(plci==a->automatic_lawPLCI) {
+ plci->internal_command = 0;
+ sig_req(plci,REMOVE,0);
+ send_req(plci);
+ a->automatic_lawPLCI = NULL;
+ }
+ }
+ if (esc_profile[0])
+ {
+ dbug (1, dprintf ("[%06x] CardProfile: %lx %lx %lx %lx %lx",
+ UnMapController (a->Id), GET_DWORD (&esc_profile[6]),
+ GET_DWORD (&esc_profile[10]), GET_DWORD (&esc_profile[14]),
+ GET_DWORD (&esc_profile[18]), GET_DWORD (&esc_profile[46])));
+
+ a->profile.Global_Options &= 0x000000ffL;
+ a->profile.B1_Protocols &= 0x000003ffL;
+ a->profile.B2_Protocols &= 0x00001fdfL;
+ a->profile.B3_Protocols &= 0x000000b7L;
+
+ a->profile.Global_Options &= GET_DWORD (&esc_profile[6]) |
+ GL_BCHANNEL_OPERATION_SUPPORTED;
+ a->profile.B1_Protocols &= GET_DWORD (&esc_profile[10]);
+ a->profile.B2_Protocols &= GET_DWORD (&esc_profile[14]);
+ a->profile.B3_Protocols &= GET_DWORD (&esc_profile[18]);
+ a->manufacturer_features = GET_DWORD (&esc_profile[46]);
+ a->man_profile.private_options = 0;
+
+ if (a->manufacturer_features & MANUFACTURER_FEATURE_ECHO_CANCELLER)
+ {
+ a->man_profile.private_options |= 1L << PRIVATE_ECHO_CANCELLER;
+ a->profile.Global_Options |= GL_ECHO_CANCELLER_SUPPORTED;
+ }
+
+
+ if (a->manufacturer_features & MANUFACTURER_FEATURE_RTP)
+ a->man_profile.private_options |= 1L << PRIVATE_RTP;
+ a->man_profile.rtp_primary_payloads = GET_DWORD (&esc_profile[50]);
+ a->man_profile.rtp_additional_payloads = GET_DWORD (&esc_profile[54]);
+
+
+ if (a->manufacturer_features & MANUFACTURER_FEATURE_T38)
+ a->man_profile.private_options |= 1L << PRIVATE_T38;
+
+
+ if (a->manufacturer_features & MANUFACTURER_FEATURE_FAX_SUB_SEP_PWD)
+ a->man_profile.private_options |= 1L << PRIVATE_FAX_SUB_SEP_PWD;
+
+
+ if (a->manufacturer_features & MANUFACTURER_FEATURE_V18)
+ a->man_profile.private_options |= 1L << PRIVATE_V18;
+
+
+ if (a->manufacturer_features & MANUFACTURER_FEATURE_DTMF_TONE)
+ a->man_profile.private_options |= 1L << PRIVATE_DTMF_TONE;
+
+
+ if (a->manufacturer_features & MANUFACTURER_FEATURE_PIAFS)
+ a->man_profile.private_options |= 1L << PRIVATE_PIAFS;
+
+
+ if (a->manufacturer_features & MANUFACTURER_FEATURE_FAX_PAPER_FORMATS)
+ a->man_profile.private_options |= 1L << PRIVATE_FAX_PAPER_FORMATS;
+
+
+ if (a->manufacturer_features & MANUFACTURER_FEATURE_VOWN)
+ a->man_profile.private_options |= 1L << PRIVATE_VOWN;
+
+
+ if (a->manufacturer_features & MANUFACTURER_FEATURE_FAX_NONSTANDARD)
+ a->man_profile.private_options |= 1L << PRIVATE_FAX_NONSTANDARD;
+
+ }
+ else
+ {
+ a->profile.Global_Options &= 0x0000007fL;
+ a->profile.B1_Protocols &= 0x000003dfL;
+ a->profile.B2_Protocols &= 0x00001adfL;
+ a->profile.B3_Protocols &= 0x000000b7L;
+ a->manufacturer_features &= MANUFACTURER_FEATURE_HARDDTMF;
+ }
+ if (a->manufacturer_features & (MANUFACTURER_FEATURE_HARDDTMF |
+ MANUFACTURER_FEATURE_SOFTDTMF_SEND | MANUFACTURER_FEATURE_SOFTDTMF_RECEIVE))
+ {
+ a->profile.Global_Options |= GL_DTMF_SUPPORTED;
+ }
+ a->manufacturer_features &= ~MANUFACTURER_FEATURE_OOB_CHANNEL;
+ dbug (1, dprintf ("[%06x] Profile: %lx %lx %lx %lx %lx",
+ UnMapController (a->Id), a->profile.Global_Options,
+ a->profile.B1_Protocols, a->profile.B2_Protocols,
+ a->profile.B3_Protocols, a->manufacturer_features));
+ }
+ /* codec plci for the handset/hook state support is just an internal id */
+ if(plci!=a->AdvCodecPLCI)
+ {
+ force_mt_info = SendMultiIE(plci,Id,multi_fac_parms, FTY, 0x20, 0);
+ force_mt_info |= SendMultiIE(plci,Id,multi_pi_parms, PI, 0x210, 0);
+ SendSSExtInd(NULL,plci,Id,multi_ssext_parms);
+ SendInfo(plci,Id, parms, force_mt_info);
+
+ VSwitchReqInd(plci,Id,multi_vswitch_parms);
+
+ }
+
+ /* switch the codec to the b-channel */
+ if(esc_chi[0] && plci && !plci->SuppState){
+ plci->b_channel = esc_chi[esc_chi[0]]&0x1f;
+ mixer_set_bchannel_id_esc (plci, plci->b_channel);
+ dbug(1,dprintf("storeChannel=0x%x",plci->b_channel));
+ if(plci->tel==ADV_VOICE && plci->appl) {
+ SetVoiceChannel(a->AdvCodecPLCI, esc_chi, a);
+ }
+ }
+
+ if(plci->appl) Number = plci->appl->Number++;
+
+ switch(plci->Sig.Ind) {
+ /* Response to Get_Supported_Services request */
+ case S_SUPPORTED:
+ dbug(1,dprintf("S_Supported"));
+ if(!plci->appl) break;
+ if(pty_cai[0]==4)
+ {
+ PUT_DWORD(&CF_Ind[6],GET_DWORD(&pty_cai[1]) );
+ }
+ else
+ {
+ PUT_DWORD(&CF_Ind[6],MASK_TERMINAL_PORTABILITY | MASK_HOLD_RETRIEVE);
+ }
+ PUT_WORD (&CF_Ind[1], 0);
+ PUT_WORD (&CF_Ind[4], 0);
+ sendf(plci->appl,_FACILITY_R|CONFIRM,Id&0x7,plci->number, "wws",0,3,CF_Ind);
+ plci_remove(plci);
+ break;
+
+ /* Supplementary Service rejected */
+ case S_SERVICE_REJ:
+ dbug(1,dprintf("S_Reject=0x%x",pty_cai[5]));
+ if(!pty_cai[0]) break;
+ switch (pty_cai[5])
+ {
+ case ECT_EXECUTE:
+ case THREE_PTY_END:
+ case THREE_PTY_BEGIN:
+ if(!plci->relatedPTYPLCI) break;
+ tplci = plci->relatedPTYPLCI;
+ rId = ( (word)tplci->Id<<8)|tplci->adapter->Id;
+ if(tplci->tel) rId|=EXT_CONTROLLER;
+ if(pty_cai[5]==ECT_EXECUTE)
+ {
+ PUT_WORD(&SS_Ind[1],S_ECT);
+
+ plci->vswitchstate=0;
+ plci->relatedPTYPLCI->vswitchstate=0;
+
+ }
+ else
+ {
+ PUT_WORD(&SS_Ind[1],pty_cai[5]+3);
+ }
+ if(pty_cai[2]!=0xff)
+ {
+ PUT_WORD(&SS_Ind[4],0x3600|(word)pty_cai[2]);
+ }
+ else
+ {
+ PUT_WORD(&SS_Ind[4],0x300E);
+ }
+ plci->relatedPTYPLCI = NULL;
+ plci->ptyState = 0;
+ sendf(tplci->appl,_FACILITY_I,rId,0,"ws",3, SS_Ind);
+ break;
+
+ case CALL_DEFLECTION:
+ if(pty_cai[2]!=0xff)
+ {
+ PUT_WORD(&SS_Ind[4],0x3600|(word)pty_cai[2]);
+ }
+ else
+ {
+ PUT_WORD(&SS_Ind[4],0x300E);
+ }
+ PUT_WORD(&SS_Ind[1],pty_cai[5]);
+ for(i=0; i<max_appl; i++)
+ {
+ if(application[i].CDEnable)
+ {
+ if(application[i].Id) sendf(&application[i],_FACILITY_I,Id,0,"ws",3, SS_Ind);
+ application[i].CDEnable = false;
+ }
+ }
+ break;
+
+ case DEACTIVATION_DIVERSION:
+ case ACTIVATION_DIVERSION:
+ case DIVERSION_INTERROGATE_CFU:
+ case DIVERSION_INTERROGATE_CFB:
+ case DIVERSION_INTERROGATE_CFNR:
+ case DIVERSION_INTERROGATE_NUM:
+ case CCBS_REQUEST:
+ case CCBS_DEACTIVATE:
+ case CCBS_INTERROGATE:
+ if(!plci->appl) break;
+ if(pty_cai[2]!=0xff)
+ {
+ PUT_WORD(&Interr_Err_Ind[4],0x3600|(word)pty_cai[2]);
+ }
+ else
+ {
+ PUT_WORD(&Interr_Err_Ind[4],0x300E);
+ }
+ switch (pty_cai[5])
+ {
+ case DEACTIVATION_DIVERSION:
+ dbug(1,dprintf("Deact_Div"));
+ Interr_Err_Ind[0]=0x9;
+ Interr_Err_Ind[3]=0x6;
+ PUT_WORD(&Interr_Err_Ind[1],S_CALL_FORWARDING_STOP);
+ break;
+ case ACTIVATION_DIVERSION:
+ dbug(1,dprintf("Act_Div"));
+ Interr_Err_Ind[0]=0x9;
+ Interr_Err_Ind[3]=0x6;
+ PUT_WORD(&Interr_Err_Ind[1],S_CALL_FORWARDING_START);
+ break;
+ case DIVERSION_INTERROGATE_CFU:
+ case DIVERSION_INTERROGATE_CFB:
+ case DIVERSION_INTERROGATE_CFNR:
+ dbug(1,dprintf("Interr_Div"));
+ Interr_Err_Ind[0]=0xa;
+ Interr_Err_Ind[3]=0x7;
+ PUT_WORD(&Interr_Err_Ind[1],S_INTERROGATE_DIVERSION);
+ break;
+ case DIVERSION_INTERROGATE_NUM:
+ dbug(1,dprintf("Interr_Num"));
+ Interr_Err_Ind[0]=0xa;
+ Interr_Err_Ind[3]=0x7;
+ PUT_WORD(&Interr_Err_Ind[1],S_INTERROGATE_NUMBERS);
+ break;
+ case CCBS_REQUEST:
+ dbug(1,dprintf("CCBS Request"));
+ Interr_Err_Ind[0]=0xd;
+ Interr_Err_Ind[3]=0xa;
+ PUT_WORD(&Interr_Err_Ind[1],S_CCBS_REQUEST);
+ break;
+ case CCBS_DEACTIVATE:
+ dbug(1,dprintf("CCBS Deactivate"));
+ Interr_Err_Ind[0]=0x9;
+ Interr_Err_Ind[3]=0x6;
+ PUT_WORD(&Interr_Err_Ind[1],S_CCBS_DEACTIVATE);
+ break;
+ case CCBS_INTERROGATE:
+ dbug(1,dprintf("CCBS Interrogate"));
+ Interr_Err_Ind[0]=0xb;
+ Interr_Err_Ind[3]=0x8;
+ PUT_WORD(&Interr_Err_Ind[1],S_CCBS_INTERROGATE);
+ break;
+ }
+ PUT_DWORD(&Interr_Err_Ind[6],plci->appl->S_Handle);
+ sendf(plci->appl,_FACILITY_I,Id&0x7,0,"ws",3, Interr_Err_Ind);
+ plci_remove(plci);
+ break;
+ case ACTIVATION_MWI:
+ case DEACTIVATION_MWI:
+ if(pty_cai[5]==ACTIVATION_MWI)
+ {
+ PUT_WORD(&SS_Ind[1],S_MWI_ACTIVATE);
+ }
+ else PUT_WORD(&SS_Ind[1],S_MWI_DEACTIVATE);
+
+ if(pty_cai[2]!=0xff)
+ {
+ PUT_WORD(&SS_Ind[4],0x3600|(word)pty_cai[2]);
+ }
+ else
+ {
+ PUT_WORD(&SS_Ind[4],0x300E);
+ }
+
+ if(plci->cr_enquiry)
+ {
+ sendf(plci->appl,_FACILITY_I,Id&0xf,0,"ws",3, SS_Ind);
+ plci_remove(plci);
+ }
+ else
+ {
+ sendf(plci->appl,_FACILITY_I,Id,0,"ws",3, SS_Ind);
+ }
+ break;
+ case CONF_ADD: /* ERROR */
+ case CONF_BEGIN:
+ case CONF_DROP:
+ case CONF_ISOLATE:
+ case CONF_REATTACH:
+ CONF_Ind[0]=9;
+ CONF_Ind[3]=6;
+ switch(pty_cai[5])
+ {
+ case CONF_BEGIN:
+ PUT_WORD(&CONF_Ind[1],S_CONF_BEGIN);
+ plci->ptyState = 0;
+ break;
+ case CONF_DROP:
+ CONF_Ind[0]=5;
+ CONF_Ind[3]=2;
+ PUT_WORD(&CONF_Ind[1],S_CONF_DROP);
+ plci->ptyState = CONNECTED;
+ break;
+ case CONF_ISOLATE:
+ CONF_Ind[0]=5;
+ CONF_Ind[3]=2;
+ PUT_WORD(&CONF_Ind[1],S_CONF_ISOLATE);
+ plci->ptyState = CONNECTED;
+ break;
+ case CONF_REATTACH:
+ CONF_Ind[0]=5;
+ CONF_Ind[3]=2;
+ PUT_WORD(&CONF_Ind[1],S_CONF_REATTACH);
+ plci->ptyState = CONNECTED;
+ break;
+ case CONF_ADD:
+ PUT_WORD(&CONF_Ind[1],S_CONF_ADD);
+ plci->relatedPTYPLCI = NULL;
+ tplci=plci->relatedPTYPLCI;
+ if(tplci) tplci->ptyState = CONNECTED;
+ plci->ptyState = CONNECTED;
+ break;
+ }
+
+ if(pty_cai[2]!=0xff)
+ {
+ PUT_WORD(&CONF_Ind[4],0x3600|(word)pty_cai[2]);
+ }
+ else
+ {
+ PUT_WORD(&CONF_Ind[4],0x3303); /* Time-out: network did not respond
+ within the required time */
+ }
+
+ PUT_DWORD(&CONF_Ind[6],0x0);
+ sendf(plci->appl,_FACILITY_I,Id,0,"ws",3, CONF_Ind);
+ break;
+ }
+ break;
+
+ /* Supplementary Service indicates success */
+ case S_SERVICE:
+ dbug(1,dprintf("Service_Ind"));
+ PUT_WORD (&CF_Ind[4], 0);
+ switch (pty_cai[5])
+ {
+ case THREE_PTY_END:
+ case THREE_PTY_BEGIN:
+ case ECT_EXECUTE:
+ if(!plci->relatedPTYPLCI) break;
+ tplci = plci->relatedPTYPLCI;
+ rId = ( (word)tplci->Id<<8)|tplci->adapter->Id;
+ if(tplci->tel) rId|=EXT_CONTROLLER;
+ if(pty_cai[5]==ECT_EXECUTE)
+ {
+ PUT_WORD(&SS_Ind[1],S_ECT);
+
+ if(plci->vswitchstate!=3)
+ {
+
+ plci->ptyState = IDLE;
+ plci->relatedPTYPLCI = NULL;
+ plci->ptyState = 0;
+
+ }
+
+ dbug(1,dprintf("ECT OK"));
+ sendf(tplci->appl,_FACILITY_I,rId,0,"ws",3, SS_Ind);
+
+
+
+ }
+ else
+ {
+ switch (plci->ptyState)
+ {
+ case S_3PTY_BEGIN:
+ plci->ptyState = CONNECTED;
+ dbug(1,dprintf("3PTY ON"));
+ break;
+
+ case S_3PTY_END:
+ plci->ptyState = IDLE;
+ plci->relatedPTYPLCI = NULL;
+ plci->ptyState = 0;
+ dbug(1,dprintf("3PTY OFF"));
+ break;
+ }
+ PUT_WORD(&SS_Ind[1],pty_cai[5]+3);
+ sendf(tplci->appl,_FACILITY_I,rId,0,"ws",3, SS_Ind);
+ }
+ break;
+
+ case CALL_DEFLECTION:
+ PUT_WORD(&SS_Ind[1],pty_cai[5]);
+ for(i=0; i<max_appl; i++)
+ {
+ if(application[i].CDEnable)
+ {
+ if(application[i].Id) sendf(&application[i],_FACILITY_I,Id,0,"ws",3, SS_Ind);
+ application[i].CDEnable = false;
+ }
+ }
+ break;
+
+ case DEACTIVATION_DIVERSION:
+ case ACTIVATION_DIVERSION:
+ if(!plci->appl) break;
+ PUT_WORD(&CF_Ind[1],pty_cai[5]+2);
+ PUT_DWORD(&CF_Ind[6],plci->appl->S_Handle);
+ sendf(plci->appl,_FACILITY_I,Id&0x7,0,"ws",3, CF_Ind);
+ plci_remove(plci);
+ break;
+
+ case DIVERSION_INTERROGATE_CFU:
+ case DIVERSION_INTERROGATE_CFB:
+ case DIVERSION_INTERROGATE_CFNR:
+ case DIVERSION_INTERROGATE_NUM:
+ case CCBS_REQUEST:
+ case CCBS_DEACTIVATE:
+ case CCBS_INTERROGATE:
+ if(!plci->appl) break;
+ switch (pty_cai[5])
+ {
+ case DIVERSION_INTERROGATE_CFU:
+ case DIVERSION_INTERROGATE_CFB:
+ case DIVERSION_INTERROGATE_CFNR:
+ dbug(1,dprintf("Interr_Div"));
+ PUT_WORD(&pty_cai[1],S_INTERROGATE_DIVERSION);
+ pty_cai[3]=pty_cai[0]-3; /* Supplementary Service-specific parameter len */
+ break;
+ case DIVERSION_INTERROGATE_NUM:
+ dbug(1,dprintf("Interr_Num"));
+ PUT_WORD(&pty_cai[1],S_INTERROGATE_NUMBERS);
+ pty_cai[3]=pty_cai[0]-3; /* Supplementary Service-specific parameter len */
+ break;
+ case CCBS_REQUEST:
+ dbug(1,dprintf("CCBS Request"));
+ PUT_WORD(&pty_cai[1],S_CCBS_REQUEST);
+ pty_cai[3]=pty_cai[0]-3; /* Supplementary Service-specific parameter len */
+ break;
+ case CCBS_DEACTIVATE:
+ dbug(1,dprintf("CCBS Deactivate"));
+ PUT_WORD(&pty_cai[1],S_CCBS_DEACTIVATE);
+ pty_cai[3]=pty_cai[0]-3; /* Supplementary Service-specific parameter len */
+ break;
+ case CCBS_INTERROGATE:
+ dbug(1,dprintf("CCBS Interrogate"));
+ PUT_WORD(&pty_cai[1],S_CCBS_INTERROGATE);
+ pty_cai[3]=pty_cai[0]-3; /* Supplementary Service-specific parameter len */
+ break;
+ }
+ PUT_WORD(&pty_cai[4],0); /* Supplementary Service Reason */
+ PUT_DWORD(&pty_cai[6],plci->appl->S_Handle);
+ sendf(plci->appl,_FACILITY_I,Id&0x7,0,"wS",3, pty_cai);
+ plci_remove(plci);
+ break;
+
+ case ACTIVATION_MWI:
+ case DEACTIVATION_MWI:
+ if(pty_cai[5]==ACTIVATION_MWI)
+ {
+ PUT_WORD(&SS_Ind[1],S_MWI_ACTIVATE);
+ }
+ else PUT_WORD(&SS_Ind[1],S_MWI_DEACTIVATE);
+ if(plci->cr_enquiry)
+ {
+ sendf(plci->appl,_FACILITY_I,Id&0xf,0,"ws",3, SS_Ind);
+ plci_remove(plci);
+ }
+ else
+ {
+ sendf(plci->appl,_FACILITY_I,Id,0,"ws",3, SS_Ind);
+ }
+ break;
+ case MWI_INDICATION:
+ if(pty_cai[0]>=0x12)
+ {
+ PUT_WORD(&pty_cai[3],S_MWI_INDICATE);
+ pty_cai[2]=pty_cai[0]-2; /* len Parameter */
+ pty_cai[5]=pty_cai[0]-5; /* Supplementary Service-specific parameter len */
+ if(plci->appl && (a->Notification_Mask[plci->appl->Id-1]&SMASK_MWI))
+ {
+ if(plci->internal_command==GET_MWI_STATE) /* result on Message Waiting Listen */
+ {
+ sendf(plci->appl,_FACILITY_I,Id&0xf,0,"wS",3, &pty_cai[2]);
+ plci_remove(plci);
+ return;
+ }
+ else sendf(plci->appl,_FACILITY_I,Id,0,"wS",3, &pty_cai[2]);
+ pty_cai[0]=0;
+ }
+ else
+ {
+ for(i=0; i<max_appl; i++)
+ {
+ if(a->Notification_Mask[i]&SMASK_MWI)
+ {
+ sendf(&application[i],_FACILITY_I,Id&0x7,0,"wS",3, &pty_cai[2]);
+ pty_cai[0]=0;
+ }
+ }
+ }
+
+ if(!pty_cai[0])
+ { /* acknowledge */
+ facility[2]= 0; /* returncode */
+ }
+ else facility[2]= 0xff;
+ }
+ else
+ {
+ /* reject */
+ facility[2]= 0xff; /* returncode */
+ }
+ facility[0]= 2;
+ facility[1]= MWI_RESPONSE; /* Function */
+ add_p(plci,CAI,facility);
+ add_p(plci,ESC,multi_ssext_parms[0]); /* remembered parameter -> only one possible */
+ sig_req(plci,S_SERVICE,0);
+ send_req(plci);
+ plci->command = 0;
+ next_internal_command (Id, plci);
+ break;
+ case CONF_ADD: /* OK */
+ case CONF_BEGIN:
+ case CONF_DROP:
+ case CONF_ISOLATE:
+ case CONF_REATTACH:
+ case CONF_PARTYDISC:
+ CONF_Ind[0]=9;
+ CONF_Ind[3]=6;
+ switch(pty_cai[5])
+ {
+ case CONF_BEGIN:
+ PUT_WORD(&CONF_Ind[1],S_CONF_BEGIN);
+ if(pty_cai[0]==6)
+ {
+ d=pty_cai[6];
+ PUT_DWORD(&CONF_Ind[6],d); /* PartyID */
+ }
+ else
+ {
+ PUT_DWORD(&CONF_Ind[6],0x0);
+ }
+ break;
+ case CONF_ISOLATE:
+ PUT_WORD(&CONF_Ind[1],S_CONF_ISOLATE);
+ CONF_Ind[0]=5;
+ CONF_Ind[3]=2;
+ break;
+ case CONF_REATTACH:
+ PUT_WORD(&CONF_Ind[1],S_CONF_REATTACH);
+ CONF_Ind[0]=5;
+ CONF_Ind[3]=2;
+ break;
+ case CONF_DROP:
+ PUT_WORD(&CONF_Ind[1],S_CONF_DROP);
+ CONF_Ind[0]=5;
+ CONF_Ind[3]=2;
+ break;
+ case CONF_ADD:
+ PUT_WORD(&CONF_Ind[1],S_CONF_ADD);
+ d=pty_cai[6];
+ PUT_DWORD(&CONF_Ind[6],d); /* PartyID */
+ tplci=plci->relatedPTYPLCI;
+ if(tplci) tplci->ptyState = CONNECTED;
+ break;
+ case CONF_PARTYDISC:
+ CONF_Ind[0]=7;
+ CONF_Ind[3]=4;
+ PUT_WORD(&CONF_Ind[1],S_CONF_PARTYDISC);
+ d=pty_cai[6];
+ PUT_DWORD(&CONF_Ind[4],d); /* PartyID */
+ break;
+ }
+ plci->ptyState = CONNECTED;
+ sendf(plci->appl,_FACILITY_I,Id,0,"ws",3, CONF_Ind);
+ break;
+ case CCBS_INFO_RETAIN:
+ case CCBS_ERASECALLLINKAGEID:
+ case CCBS_STOP_ALERTING:
+ CONF_Ind[0]=5;
+ CONF_Ind[3]=2;
+ switch(pty_cai[5])
+ {
+ case CCBS_INFO_RETAIN:
+ PUT_WORD(&CONF_Ind[1],S_CCBS_INFO_RETAIN);
+ break;
+ case CCBS_STOP_ALERTING:
+ PUT_WORD(&CONF_Ind[1],S_CCBS_STOP_ALERTING);
+ break;
+ case CCBS_ERASECALLLINKAGEID:
+ PUT_WORD(&CONF_Ind[1],S_CCBS_ERASECALLLINKAGEID);
+ CONF_Ind[0]=7;
+ CONF_Ind[3]=4;
+ CONF_Ind[6]=0;
+ CONF_Ind[7]=0;
+ break;
+ }
+ w=pty_cai[6];
+ PUT_WORD(&CONF_Ind[4],w); /* PartyID */
+
+ if(plci->appl && (a->Notification_Mask[plci->appl->Id-1]&SMASK_CCBS))
+ {
+ sendf(plci->appl,_FACILITY_I,Id,0,"ws",3, CONF_Ind);
+ }
+ else
+ {
+ for(i=0; i<max_appl; i++)
+ if(a->Notification_Mask[i]&SMASK_CCBS)
+ sendf(&application[i],_FACILITY_I,Id&0x7,0,"ws",3, CONF_Ind);
+ }
+ break;
+ }
+ break;
+ case CALL_HOLD_REJ:
+ cau = parms[7];
+ if(cau)
+ {
+ i = _L3_CAUSE | cau[2];
+ if(cau[2]==0) i = 0x3603;
+ }
+ else
+ {
+ i = 0x3603;
+ }
+ PUT_WORD(&SS_Ind[1],S_HOLD);
+ PUT_WORD(&SS_Ind[4],i);
+ if(plci->SuppState == HOLD_REQUEST)
+ {
+ plci->SuppState = IDLE;
+ sendf(plci->appl,_FACILITY_I,Id,0,"ws",3, SS_Ind);
+ }
+ break;
+
+ case CALL_HOLD_ACK:
+ if(plci->SuppState == HOLD_REQUEST)
+ {
+ plci->SuppState = CALL_HELD;
+ CodecIdCheck(a, plci);
+ start_internal_command (Id, plci, hold_save_command);
+ }
+ break;
+
+ case CALL_RETRIEVE_REJ:
+ cau = parms[7];
+ if(cau)
+ {
+ i = _L3_CAUSE | cau[2];
+ if(cau[2]==0) i = 0x3603;
+ }
+ else
+ {
+ i = 0x3603;
+ }
+ PUT_WORD(&SS_Ind[1],S_RETRIEVE);
+ PUT_WORD(&SS_Ind[4],i);
+ if(plci->SuppState == RETRIEVE_REQUEST)
+ {
+ plci->SuppState = CALL_HELD;
+ CodecIdCheck(a, plci);
+ sendf(plci->appl,_FACILITY_I,Id,0,"ws",3, SS_Ind);
+ }
+ break;
+
+ case CALL_RETRIEVE_ACK:
+ PUT_WORD(&SS_Ind[1],S_RETRIEVE);
+ if(plci->SuppState == RETRIEVE_REQUEST)
+ {
+ plci->SuppState = IDLE;
+ plci->call_dir |= CALL_DIR_FORCE_OUTG_NL;
+ plci->b_channel = esc_chi[esc_chi[0]]&0x1f;
+ if(plci->tel)
+ {
+ mixer_set_bchannel_id_esc (plci, plci->b_channel);
+ dbug(1,dprintf("RetrChannel=0x%x",plci->b_channel));
+ SetVoiceChannel(a->AdvCodecPLCI, esc_chi, a);
+ if(plci->B2_prot==B2_TRANSPARENT && plci->B3_prot==B3_TRANSPARENT)
+ {
+ dbug(1,dprintf("Get B-ch"));
+ start_internal_command (Id, plci, retrieve_restore_command);
+ }
+ else
+ sendf(plci->appl,_FACILITY_I,Id,0,"ws",3, SS_Ind);
+ }
+ else
+ start_internal_command (Id, plci, retrieve_restore_command);
+ }
+ break;
+
+ case INDICATE_IND:
+ if(plci->State != LISTENING) {
+ sig_req(plci,HANGUP,0);
+ send_req(plci);
+ break;
+ }
+ cip = find_cip(a,parms[4],parms[6]);
+ cip_mask = 1L<<cip;
+ dbug(1,dprintf("cip=%d,cip_mask=%lx",cip,cip_mask));
+ clear_c_ind_mask (plci);
+ if (!remove_started && !a->adapter_disabled)
+ {
+ set_c_ind_mask_bit (plci, MAX_APPL);
+ group_optimization(a, plci);
+ for(i=0; i<max_appl; i++) {
+ if(application[i].Id
+ && (a->CIP_Mask[i]&1 || a->CIP_Mask[i]&cip_mask)
+ && CPN_filter_ok(parms[0],a,i)
+ && test_group_ind_mask_bit (plci, i) ) {
+ dbug(1,dprintf("storedcip_mask[%d]=0x%lx",i,a->CIP_Mask[i] ));
+ set_c_ind_mask_bit (plci, i);
+ dump_c_ind_mask (plci);
+ plci->State = INC_CON_PENDING;
+ plci->call_dir = (plci->call_dir & ~(CALL_DIR_OUT | CALL_DIR_ORIGINATE)) |
+ CALL_DIR_IN | CALL_DIR_ANSWER;
+ if(esc_chi[0]) {
+ plci->b_channel = esc_chi[esc_chi[0]]&0x1f;
+ mixer_set_bchannel_id_esc (plci, plci->b_channel);
+ }
+ /* if a listen on the ext controller is done, check if hook states */
+ /* are supported or if just a on board codec must be activated */
+ if(a->codec_listen[i] && !a->AdvSignalPLCI) {
+ if(a->profile.Global_Options & HANDSET)
+ plci->tel = ADV_VOICE;
+ else if(a->profile.Global_Options & ON_BOARD_CODEC)
+ plci->tel = CODEC;
+ if(plci->tel) Id|=EXT_CONTROLLER;
+ a->codec_listen[i] = plci;
+ }
+
+ sendf(&application[i],_CONNECT_I,Id,0,
+ "wSSSSSSSbSSSSS", cip, /* CIP */
+ parms[0], /* CalledPartyNumber */
+ multi_CiPN_parms[0], /* CallingPartyNumber */
+ parms[2], /* CalledPartySubad */
+ parms[3], /* CallingPartySubad */
+ parms[4], /* BearerCapability */
+ parms[5], /* LowLC */
+ parms[6], /* HighLC */
+ ai_len, /* nested struct add_i */
+ add_i[0], /* B channel info */
+ add_i[1], /* keypad facility */
+ add_i[2], /* user user data */
+ add_i[3], /* nested facility */
+ multi_CiPN_parms[1] /* second CiPN(SCR) */
+ );
+ SendSSExtInd(&application[i],
+ plci,
+ Id,
+ multi_ssext_parms);
+ SendSetupInfo(&application[i],
+ plci,
+ Id,
+ parms,
+ SendMultiIE(plci,Id,multi_pi_parms, PI, 0x210, true));
+ }
+ }
+ clear_c_ind_mask_bit (plci, MAX_APPL);
+ dump_c_ind_mask (plci);
+ }
+ if(c_ind_mask_empty (plci)) {
+ sig_req(plci,HANGUP,0);
+ send_req(plci);
+ plci->State = IDLE;
+ }
+ plci->notifiedcall = 0;
+ a->listen_active--;
+ listen_check(a);
+ break;
+
+ case CALL_PEND_NOTIFY:
+ plci->notifiedcall = 1;
+ listen_check(a);
+ break;
+
+ case CALL_IND:
+ case CALL_CON:
+ if(plci->State==ADVANCED_VOICE_SIG || plci->State==ADVANCED_VOICE_NOSIG)
+ {
+ if(plci->internal_command==PERM_COD_CONN_PEND)
+ {
+ if(plci->State==ADVANCED_VOICE_NOSIG)
+ {
+ dbug(1,dprintf("***Codec OK"));
+ if(a->AdvSignalPLCI)
+ {
+ tplci = a->AdvSignalPLCI;
+ if(tplci->spoofed_msg)
+ {
+ dbug(1,dprintf("***Spoofed Msg(0x%x)",tplci->spoofed_msg));
+ tplci->command = 0;
+ tplci->internal_command = 0;
+ x_Id = ((word)tplci->Id<<8)|tplci->adapter->Id | 0x80;
+ switch (tplci->spoofed_msg)
+ {
+ case CALL_RES:
+ tplci->command = _CONNECT_I|RESPONSE;
+ api_load_msg (&tplci->saved_msg, saved_parms);
+ add_b1(tplci,&saved_parms[1],0,tplci->B1_facilities);
+ if (tplci->adapter->Info_Mask[tplci->appl->Id-1] & 0x200)
+ {
+ /* early B3 connect (CIP mask bit 9) no release after a disc */
+ add_p(tplci,LLI,"\x01\x01");
+ }
+ add_s(tplci, CONN_NR, &saved_parms[2]);
+ add_s(tplci, LLC, &saved_parms[4]);
+ add_ai(tplci, &saved_parms[5]);
+ tplci->State = INC_CON_ACCEPT;
+ sig_req(tplci, CALL_RES,0);
+ send_req(tplci);
+ break;
+
+ case AWAITING_SELECT_B:
+ dbug(1,dprintf("Select_B continue"));
+ start_internal_command (x_Id, tplci, select_b_command);
+ break;
+
+ case AWAITING_MANUF_CON: /* Get_Plci per Manufacturer_Req to ext controller */
+ if(!tplci->Sig.Id)
+ {
+ dbug(1,dprintf("No SigID!"));
+ sendf(tplci->appl, _MANUFACTURER_R|CONFIRM,x_Id,tplci->number, "dww",_DI_MANU_ID,_MANUFACTURER_R,_OUT_OF_PLCI);
+ plci_remove(tplci);
+ break;
+ }
+ tplci->command = _MANUFACTURER_R;
+ api_load_msg (&tplci->saved_msg, saved_parms);
+ dir = saved_parms[2].info[0];
+ if(dir==1) {
+ sig_req(tplci,CALL_REQ,0);
+ }
+ else if(!dir){
+ sig_req(tplci,LISTEN_REQ,0);
+ }
+ send_req(tplci);
+ sendf(tplci->appl, _MANUFACTURER_R|CONFIRM,x_Id,tplci->number, "dww",_DI_MANU_ID,_MANUFACTURER_R,0);
+ break;
+
+ case (CALL_REQ|AWAITING_MANUF_CON):
+ sig_req(tplci,CALL_REQ,0);
+ send_req(tplci);
+ break;
+
+ case CALL_REQ:
+ if(!tplci->Sig.Id)
+ {
+ dbug(1,dprintf("No SigID!"));
+ sendf(tplci->appl,_CONNECT_R|CONFIRM,tplci->adapter->Id,0,"w",_OUT_OF_PLCI);
+ plci_remove(tplci);
+ break;
+ }
+ tplci->command = _CONNECT_R;
+ api_load_msg (&tplci->saved_msg, saved_parms);
+ add_s(tplci,CPN,&saved_parms[1]);
+ add_s(tplci,DSA,&saved_parms[3]);
+ add_ai(tplci,&saved_parms[9]);
+ sig_req(tplci,CALL_REQ,0);
+ send_req(tplci);
+ break;
+
+ case CALL_RETRIEVE:
+ tplci->command = C_RETRIEVE_REQ;
+ sig_req(tplci,CALL_RETRIEVE,0);
+ send_req(tplci);
+ break;
+ }
+ tplci->spoofed_msg = 0;
+ if (tplci->internal_command == 0)
+ next_internal_command (x_Id, tplci);
+ }
+ }
+ next_internal_command (Id, plci);
+ break;
+ }
+ dbug(1,dprintf("***Codec Hook Init Req"));
+ plci->internal_command = PERM_COD_HOOK;
+ add_p(plci,FTY,"\x01\x09"); /* Get Hook State*/
+ sig_req(plci,TEL_CTRL,0);
+ send_req(plci);
+ }
+ }
+ else if(plci->command != _MANUFACTURER_R /* old style permanent connect */
+ && plci->State!=INC_ACT_PENDING)
+ {
+ mixer_set_bchannel_id_esc (plci, plci->b_channel);
+ if(plci->tel == ADV_VOICE && plci->SuppState == IDLE) /* with permanent codec switch on immediately */
+ {
+ chi[2] = plci->b_channel;
+ SetVoiceChannel(a->AdvCodecPLCI, chi, a);
+ }
+ sendf(plci->appl,_CONNECT_ACTIVE_I,Id,0,"Sss",parms[21],"","");
+ plci->State = INC_ACT_PENDING;
+ }
+ break;
+
+ case TEL_CTRL:
+ Number = 0;
+ ie = multi_fac_parms[0]; /* inspect the facility hook indications */
+ if(plci->State==ADVANCED_VOICE_SIG && ie[0]){
+ switch (ie[1]&0x91) {
+ case 0x80: /* hook off */
+ case 0x81:
+ if(plci->internal_command==PERM_COD_HOOK)
+ {
+ dbug(1,dprintf("init:hook_off"));
+ plci->hook_state = ie[1];
+ next_internal_command (Id, plci);
+ break;
+ }
+ else /* ignore doubled hook indications */
+ {
+ if( ((plci->hook_state)&0xf0)==0x80)
+ {
+ dbug(1,dprintf("ignore hook"));
+ break;
+ }
+ plci->hook_state = ie[1]&0x91;
+ }
+ /* check for incoming call pending */
+ /* and signal '+'.Appl must decide */
+ /* with connect_res if call must */
+ /* accepted or not */
+ for(i=0, tplci=NULL;i<max_appl;i++){
+ if(a->codec_listen[i]
+ && (a->codec_listen[i]->State==INC_CON_PENDING
+ ||a->codec_listen[i]->State==INC_CON_ALERT) ){
+ tplci = a->codec_listen[i];
+ tplci->appl = &application[i];
+ }
+ }
+ /* no incoming call, do outgoing call */
+ /* and signal '+' if outg. setup */
+ if(!a->AdvSignalPLCI && !tplci){
+ if((i=get_plci(a))) {
+ a->AdvSignalPLCI = &a->plci[i-1];
+ tplci = a->AdvSignalPLCI;
+ tplci->tel = ADV_VOICE;
+ PUT_WORD(&voice_cai[5],a->AdvSignalAppl->MaxDataLength);
+ if (a->Info_Mask[a->AdvSignalAppl->Id-1] & 0x200){
+ /* early B3 connect (CIP mask bit 9) no release after a disc */
+ add_p(tplci,LLI,"\x01\x01");
+ }
+ add_p(tplci, CAI, voice_cai);
+ add_p(tplci, OAD, a->TelOAD);
+ add_p(tplci, OSA, a->TelOSA);
+ add_p(tplci,SHIFT|6,NULL);
+ add_p(tplci,SIN,"\x02\x01\x00");
+ add_p(tplci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+ sig_req(tplci,ASSIGN,DSIG_ID);
+ a->AdvSignalPLCI->internal_command = HOOK_OFF_REQ;
+ a->AdvSignalPLCI->command = 0;
+ tplci->appl = a->AdvSignalAppl;
+ tplci->call_dir = CALL_DIR_OUT | CALL_DIR_ORIGINATE;
+ send_req(tplci);
+ }
+
+ }
+
+ if(!tplci) break;
+ Id = ((word)tplci->Id<<8)|a->Id;
+ Id|=EXT_CONTROLLER;
+ sendf(tplci->appl,
+ _FACILITY_I,
+ Id,
+ 0,
+ "ws", (word)0, "\x01+");
+ break;
+
+ case 0x90: /* hook on */
+ case 0x91:
+ if(plci->internal_command==PERM_COD_HOOK)
+ {
+ dbug(1,dprintf("init:hook_on"));
+ plci->hook_state = ie[1]&0x91;
+ next_internal_command (Id, plci);
+ break;
+ }
+ else /* ignore doubled hook indications */
+ {
+ if( ((plci->hook_state)&0xf0)==0x90) break;
+ plci->hook_state = ie[1]&0x91;
+ }
+ /* hangup the adv. voice call and signal '-' to the appl */
+ if(a->AdvSignalPLCI) {
+ Id = ((word)a->AdvSignalPLCI->Id<<8)|a->Id;
+ if(plci->tel) Id|=EXT_CONTROLLER;
+ sendf(a->AdvSignalAppl,
+ _FACILITY_I,
+ Id,
+ 0,
+ "ws", (word)0, "\x01-");
+ a->AdvSignalPLCI->internal_command = HOOK_ON_REQ;
+ a->AdvSignalPLCI->command = 0;
+ sig_req(a->AdvSignalPLCI,HANGUP,0);
+ send_req(a->AdvSignalPLCI);
+ }
+ break;
+ }
+ }
+ break;
+
+ case RESUME:
+ clear_c_ind_mask_bit (plci, (word)(plci->appl->Id-1));
+ PUT_WORD(&resume_cau[4],GOOD);
+ sendf(plci->appl,_FACILITY_I,Id,0,"ws", (word)3, resume_cau);
+ break;
+
+ case SUSPEND:
+ clear_c_ind_mask (plci);
+
+ if (plci->NL.Id && !plci->nl_remove_id) {
+ mixer_remove (plci);
+ nl_req_ncci(plci,REMOVE,0);
+ }
+ if (!plci->sig_remove_id) {
+ plci->internal_command = 0;
+ sig_req(plci,REMOVE,0);
+ }
+ send_req(plci);
+ if(!plci->channels) {
+ sendf(plci->appl,_FACILITY_I,Id,0,"ws", (word)3, "\x05\x04\x00\x02\x00\x00");
+ sendf(plci->appl, _DISCONNECT_I, Id, 0, "w", 0);
+ }
+ break;
+
+ case SUSPEND_REJ:
+ break;
+
+ case HANGUP:
+ plci->hangup_flow_ctrl_timer=0;
+ if(plci->manufacturer && plci->State==LOCAL_CONNECT) break;
+ cau = parms[7];
+ if(cau) {
+ i = _L3_CAUSE | cau[2];
+ if(cau[2]==0) i = 0;
+ else if(cau[2]==8) i = _L1_ERROR;
+ else if(cau[2]==9 || cau[2]==10) i = _L2_ERROR;
+ else if(cau[2]==5) i = _CAPI_GUARD_ERROR;
+ }
+ else {
+ i = _L3_ERROR;
+ }
+
+ if(plci->State==INC_CON_PENDING || plci->State==INC_CON_ALERT)
+ {
+ for(i=0; i<max_appl; i++)
+ {
+ if(test_c_ind_mask_bit (plci, i))
+ sendf(&application[i], _DISCONNECT_I, Id, 0, "w", 0);
+ }
+ }
+ else
+ {
+ clear_c_ind_mask (plci);
+ }
+ if(!plci->appl)
+ {
+ if (plci->State == LISTENING)
+ {
+ plci->notifiedcall=0;
+ a->listen_active--;
+ }
+ plci->State = INC_DIS_PENDING;
+ if(c_ind_mask_empty (plci))
+ {
+ plci->State = IDLE;
+ if (plci->NL.Id && !plci->nl_remove_id)
+ {
+ mixer_remove (plci);
+ nl_req_ncci(plci,REMOVE,0);
+ }
+ if (!plci->sig_remove_id)
+ {
+ plci->internal_command = 0;
+ sig_req(plci,REMOVE,0);
+ }
+ send_req(plci);
+ }
+ }
+ else
+ {
+ /* collision of DISCONNECT or CONNECT_RES with HANGUP can */
+ /* result in a second HANGUP! Don't generate another */
+ /* DISCONNECT */
+ if(plci->State!=IDLE && plci->State!=INC_DIS_PENDING)
+ {
+ if(plci->State==RESUMING)
+ {
+ PUT_WORD(&resume_cau[4],i);
+ sendf(plci->appl,_FACILITY_I,Id,0,"ws", (word)3, resume_cau);
+ }
+ plci->State = INC_DIS_PENDING;
+ sendf(plci->appl,_DISCONNECT_I,Id,0,"w",i);
+ }
+ }
+ break;
+
+ case SSEXT_IND:
+ SendSSExtInd(NULL,plci,Id,multi_ssext_parms);
+ break;
+
+ case VSWITCH_REQ:
+ VSwitchReqInd(plci,Id,multi_vswitch_parms);
+ break;
+ case VSWITCH_IND:
+ if(plci->relatedPTYPLCI &&
+ plci->vswitchstate==3 &&
+ plci->relatedPTYPLCI->vswitchstate==3 &&
+ parms[MAXPARMSIDS-1][0])
+ {
+ add_p(plci->relatedPTYPLCI,SMSG,parms[MAXPARMSIDS-1]);
+ sig_req(plci->relatedPTYPLCI,VSWITCH_REQ,0);
+ send_req(plci->relatedPTYPLCI);
+ }
+ else VSwitchReqInd(plci,Id,multi_vswitch_parms);
+ break;
+
+ }
+}
+
+
+static void SendSetupInfo(APPL * appl, PLCI * plci, dword Id, byte * * parms, byte Info_Sent_Flag)
+{
+ word i;
+ byte * ie;
+ word Info_Number;
+ byte * Info_Element;
+ word Info_Mask = 0;
+
+ dbug(1,dprintf("SetupInfo"));
+
+ for(i=0; i<MAXPARMSIDS; i++) {
+ ie = parms[i];
+ Info_Number = 0;
+ Info_Element = ie;
+ if(ie[0]) {
+ switch(i) {
+ case 0:
+ dbug(1,dprintf("CPN "));
+ Info_Number = 0x0070;
+ Info_Mask = 0x80;
+ Info_Sent_Flag = true;
+ break;
+ case 8: /* display */
+ dbug(1,dprintf("display(%d)",i));
+ Info_Number = 0x0028;
+ Info_Mask = 0x04;
+ Info_Sent_Flag = true;
+ break;
+ case 16: /* Channel Id */
+ dbug(1,dprintf("CHI"));
+ Info_Number = 0x0018;
+ Info_Mask = 0x100;
+ Info_Sent_Flag = true;
+ mixer_set_bchannel_id (plci, Info_Element);
+ break;
+ case 19: /* Redirected Number */
+ dbug(1,dprintf("RDN"));
+ Info_Number = 0x0074;
+ Info_Mask = 0x400;
+ Info_Sent_Flag = true;
+ break;
+ case 20: /* Redirected Number extended */
+ dbug(1,dprintf("RDX"));
+ Info_Number = 0x0073;
+ Info_Mask = 0x400;
+ Info_Sent_Flag = true;
+ break;
+ case 22: /* Redirecing Number */
+ dbug(1,dprintf("RIN"));
+ Info_Number = 0x0076;
+ Info_Mask = 0x400;
+ Info_Sent_Flag = true;
+ break;
+ default:
+ Info_Number = 0;
+ break;
+ }
+ }
+
+ if(i==MAXPARMSIDS-2){ /* to indicate the message type "Setup" */
+ Info_Number = 0x8000 |5;
+ Info_Mask = 0x10;
+ Info_Element = "";
+ }
+
+ if(Info_Sent_Flag && Info_Number){
+ if(plci->adapter->Info_Mask[appl->Id-1] & Info_Mask) {
+ sendf(appl,_INFO_I,Id,0,"wS",Info_Number,Info_Element);
+ }
+ }
+ }
+}
+
+
+void SendInfo(PLCI * plci, dword Id, byte * * parms, byte iesent)
+{
+ word i;
+ word j;
+ word k;
+ byte * ie;
+ word Info_Number;
+ byte * Info_Element;
+ word Info_Mask = 0;
+ static byte charges[5] = {4,0,0,0,0};
+ static byte cause[] = {0x02,0x80,0x00};
+ APPL *appl;
+
+ dbug(1,dprintf("InfoParse "));
+
+ if(
+ !plci->appl
+ && !plci->State
+ && plci->Sig.Ind!=NCR_FACILITY
+ )
+ {
+ dbug(1,dprintf("NoParse "));
+ return;
+ }
+ cause[2] = 0;
+ for(i=0; i<MAXPARMSIDS; i++) {
+ ie = parms[i];
+ Info_Number = 0;
+ Info_Element = ie;
+ if(ie[0]) {
+ switch(i) {
+ case 0:
+ dbug(1,dprintf("CPN "));
+ Info_Number = 0x0070;
+ Info_Mask = 0x80;
+ break;
+ case 7: /* ESC_CAU */
+ dbug(1,dprintf("cau(0x%x)",ie[2]));
+ Info_Number = 0x0008;
+ Info_Mask = 0x00;
+ cause[2] = ie[2];
+ Info_Element = NULL;
+ break;
+ case 8: /* display */
+ dbug(1,dprintf("display(%d)",i));
+ Info_Number = 0x0028;
+ Info_Mask = 0x04;
+ break;
+ case 9: /* Date display */
+ dbug(1,dprintf("date(%d)",i));
+ Info_Number = 0x0029;
+ Info_Mask = 0x02;
+ break;
+ case 10: /* charges */
+ for(j=0;j<4;j++) charges[1+j] = 0;
+ for(j=0; j<ie[0] && !(ie[1+j]&0x80); j++);
+ for(k=1,j++; j<ie[0] && k<=4; j++,k++) charges[k] = ie[1+j];
+ Info_Number = 0x4000;
+ Info_Mask = 0x40;
+ Info_Element = charges;
+ break;
+ case 11: /* user user info */
+ dbug(1,dprintf("uui"));
+ Info_Number = 0x007E;
+ Info_Mask = 0x08;
+ break;
+ case 12: /* congestion receiver ready */
+ dbug(1,dprintf("clRDY"));
+ Info_Number = 0x00B0;
+ Info_Mask = 0x08;
+ Info_Element = "";
+ break;
+ case 13: /* congestion receiver not ready */
+ dbug(1,dprintf("clNRDY"));
+ Info_Number = 0x00BF;
+ Info_Mask = 0x08;
+ Info_Element = "";
+ break;
+ case 15: /* Keypad Facility */
+ dbug(1,dprintf("KEY"));
+ Info_Number = 0x002C;
+ Info_Mask = 0x20;
+ break;
+ case 16: /* Channel Id */
+ dbug(1,dprintf("CHI"));
+ Info_Number = 0x0018;
+ Info_Mask = 0x100;
+ mixer_set_bchannel_id (plci, Info_Element);
+ break;
+ case 17: /* if no 1tr6 cause, send full cause, else esc_cause */
+ dbug(1,dprintf("q9cau(0x%x)",ie[2]));
+ if(!cause[2] || cause[2]<0x80) break; /* eg. layer 1 error */
+ Info_Number = 0x0008;
+ Info_Mask = 0x01;
+ if(cause[2] != ie[2]) Info_Element = cause;
+ break;
+ case 19: /* Redirected Number */
+ dbug(1,dprintf("RDN"));
+ Info_Number = 0x0074;
+ Info_Mask = 0x400;
+ break;
+ case 22: /* Redirecing Number */
+ dbug(1,dprintf("RIN"));
+ Info_Number = 0x0076;
+ Info_Mask = 0x400;
+ break;
+ case 23: /* Notification Indicator */
+ dbug(1,dprintf("NI"));
+ Info_Number = (word)NI;
+ Info_Mask = 0x210;
+ break;
+ case 26: /* Call State */
+ dbug(1,dprintf("CST"));
+ Info_Number = (word)CST;
+ Info_Mask = 0x01; /* do with cause i.e. for now */
+ break;
+ case MAXPARMSIDS-2: /* Escape Message Type, must be the last indication */
+ dbug(1,dprintf("ESC/MT[0x%x]",ie[3]));
+ Info_Number = 0x8000 |ie[3];
+ if(iesent) Info_Mask = 0xffff;
+ else Info_Mask = 0x10;
+ Info_Element = "";
+ break;
+ default:
+ Info_Number = 0;
+ Info_Mask = 0;
+ Info_Element = "";
+ break;
+ }
+ }
+
+ if(plci->Sig.Ind==NCR_FACILITY) /* check controller broadcast */
+ {
+ for(j=0; j<max_appl; j++)
+ {
+ appl = &application[j];
+ if(Info_Number
+ && appl->Id
+ && plci->adapter->Info_Mask[appl->Id-1] &Info_Mask)
+ {
+ dbug(1,dprintf("NCR_Ind"));
+ iesent=true;
+ sendf(&application[j],_INFO_I,Id&0x0f,0,"wS",Info_Number,Info_Element);
+ }
+ }
+ }
+ else if(!plci->appl)
+ { /* overlap receiving broadcast */
+ if(Info_Number==CPN
+ || Info_Number==KEY
+ || Info_Number==NI
+ || Info_Number==DSP
+ || Info_Number==UUI )
+ {
+ for(j=0; j<max_appl; j++)
+ {
+ if(test_c_ind_mask_bit (plci, j))
+ {
+ dbug(1,dprintf("Ovl_Ind"));
+ iesent=true;
+ sendf(&application[j],_INFO_I,Id,0,"wS",Info_Number,Info_Element);
+ }
+ }
+ }
+ } /* all other signalling states */
+ else if(Info_Number
+ && plci->adapter->Info_Mask[plci->appl->Id-1] &Info_Mask)
+ {
+ dbug(1,dprintf("Std_Ind"));
+ iesent=true;
+ sendf(plci->appl,_INFO_I,Id,0,"wS",Info_Number,Info_Element);
+ }
+ }
+}
+
+
+byte SendMultiIE(PLCI * plci, dword Id, byte * * parms, byte ie_type, dword info_mask, byte setupParse)
+{
+ word i;
+ word j;
+ byte * ie;
+ word Info_Number;
+ byte * Info_Element;
+ APPL *appl;
+ word Info_Mask = 0;
+ byte iesent=0;
+
+ if(
+ !plci->appl
+ && !plci->State
+ && plci->Sig.Ind!=NCR_FACILITY
+ && !setupParse
+ )
+ {
+ dbug(1,dprintf("NoM-IEParse "));
+ return 0;
+ }
+ dbug(1,dprintf("M-IEParse "));
+
+ for(i=0; i<MAX_MULTI_IE; i++)
+ {
+ ie = parms[i];
+ Info_Number = 0;
+ Info_Element = ie;
+ if(ie[0])
+ {
+ dbug(1,dprintf("[Ind0x%x]:IE=0x%x",plci->Sig.Ind,ie_type));
+ Info_Number = (word)ie_type;
+ Info_Mask = (word)info_mask;
+ }
+
+ if(plci->Sig.Ind==NCR_FACILITY) /* check controller broadcast */
+ {
+ for(j=0; j<max_appl; j++)
+ {
+ appl = &application[j];
+ if(Info_Number
+ && appl->Id
+ && plci->adapter->Info_Mask[appl->Id-1] &Info_Mask)
+ {
+ iesent = true;
+ dbug(1,dprintf("Mlt_NCR_Ind"));
+ sendf(&application[j],_INFO_I,Id&0x0f,0,"wS",Info_Number,Info_Element);
+ }
+ }
+ }
+ else if(!plci->appl && Info_Number)
+ { /* overlap receiving broadcast */
+ for(j=0; j<max_appl; j++)
+ {
+ if(test_c_ind_mask_bit (plci, j))
+ {
+ iesent = true;
+ dbug(1,dprintf("Mlt_Ovl_Ind"));
+ sendf(&application[j],_INFO_I,Id,0,"wS",Info_Number,Info_Element);
+ }
+ }
+ } /* all other signalling states */
+ else if(Info_Number
+ && plci->adapter->Info_Mask[plci->appl->Id-1] &Info_Mask)
+ {
+ iesent = true;
+ dbug(1,dprintf("Mlt_Std_Ind"));
+ sendf(plci->appl,_INFO_I,Id,0,"wS",Info_Number,Info_Element);
+ }
+ }
+ return iesent;
+}
+
+static void SendSSExtInd(APPL * appl, PLCI * plci, dword Id, byte * * parms)
+{
+ word i;
+ /* Format of multi_ssext_parms[i][]:
+ 0 byte length
+ 1 byte SSEXTIE
+ 2 byte SSEXT_REQ/SSEXT_IND
+ 3 byte length
+ 4 word SSExtCommand
+ 6... Params
+ */
+ if(
+ plci
+ && plci->State
+ && plci->Sig.Ind!=NCR_FACILITY
+ )
+ for(i=0;i<MAX_MULTI_IE;i++)
+ {
+ if(parms[i][0]<6) continue;
+ if(parms[i][2]==SSEXT_REQ) continue;
+
+ if(appl)
+ {
+ parms[i][0]=0; /* kill it */
+ sendf(appl,_MANUFACTURER_I,
+ Id,
+ 0,
+ "dwS",
+ _DI_MANU_ID,
+ _DI_SSEXT_CTRL,
+ &parms[i][3]);
+ }
+ else if(plci->appl)
+ {
+ parms[i][0]=0; /* kill it */
+ sendf(plci->appl,_MANUFACTURER_I,
+ Id,
+ 0,
+ "dwS",
+ _DI_MANU_ID,
+ _DI_SSEXT_CTRL,
+ &parms[i][3]);
+ }
+ }
+};
+
+void nl_ind(PLCI * plci)
+{
+ byte ch;
+ word ncci;
+ dword Id;
+ DIVA_CAPI_ADAPTER * a;
+ word NCCIcode;
+ APPL * APPLptr;
+ word count;
+ word Num;
+ word i, ncpi_state;
+ byte len, ncci_state;
+ word msg;
+ word info = 0;
+ word fax_feature_bits;
+ byte fax_send_edata_ack;
+ static byte v120_header_buffer[2 + 3];
+ static word fax_info[] = {
+ 0, /* T30_SUCCESS */
+ _FAX_NO_CONNECTION, /* T30_ERR_NO_DIS_RECEIVED */
+ _FAX_PROTOCOL_ERROR, /* T30_ERR_TIMEOUT_NO_RESPONSE */
+ _FAX_PROTOCOL_ERROR, /* T30_ERR_RETRY_NO_RESPONSE */
+ _FAX_PROTOCOL_ERROR, /* T30_ERR_TOO_MANY_REPEATS */
+ _FAX_PROTOCOL_ERROR, /* T30_ERR_UNEXPECTED_MESSAGE */
+ _FAX_REMOTE_ABORT, /* T30_ERR_UNEXPECTED_DCN */
+ _FAX_LOCAL_ABORT, /* T30_ERR_DTC_UNSUPPORTED */
+ _FAX_TRAINING_ERROR, /* T30_ERR_ALL_RATES_FAILED */
+ _FAX_TRAINING_ERROR, /* T30_ERR_TOO_MANY_TRAINS */
+ _FAX_PARAMETER_ERROR, /* T30_ERR_RECEIVE_CORRUPTED */
+ _FAX_REMOTE_ABORT, /* T30_ERR_UNEXPECTED_DISC */
+ _FAX_LOCAL_ABORT, /* T30_ERR_APPLICATION_DISC */
+ _FAX_REMOTE_REJECT, /* T30_ERR_INCOMPATIBLE_DIS */
+ _FAX_LOCAL_ABORT, /* T30_ERR_INCOMPATIBLE_DCS */
+ _FAX_PROTOCOL_ERROR, /* T30_ERR_TIMEOUT_NO_COMMAND */
+ _FAX_PROTOCOL_ERROR, /* T30_ERR_RETRY_NO_COMMAND */
+ _FAX_PROTOCOL_ERROR, /* T30_ERR_TIMEOUT_COMMAND_TOO_LONG */
+ _FAX_PROTOCOL_ERROR, /* T30_ERR_TIMEOUT_RESPONSE_TOO_LONG */
+ _FAX_NO_CONNECTION, /* T30_ERR_NOT_IDENTIFIED */
+ _FAX_PROTOCOL_ERROR, /* T30_ERR_SUPERVISORY_TIMEOUT */
+ _FAX_PARAMETER_ERROR, /* T30_ERR_TOO_LONG_SCAN_LINE */
+ _FAX_PROTOCOL_ERROR, /* T30_ERR_RETRY_NO_PAGE_AFTER_MPS */
+ _FAX_PROTOCOL_ERROR, /* T30_ERR_RETRY_NO_PAGE_AFTER_CFR */
+ _FAX_PROTOCOL_ERROR, /* T30_ERR_RETRY_NO_DCS_AFTER_FTT */
+ _FAX_PROTOCOL_ERROR, /* T30_ERR_RETRY_NO_DCS_AFTER_EOM */
+ _FAX_PROTOCOL_ERROR, /* T30_ERR_RETRY_NO_DCS_AFTER_MPS */
+ _FAX_PROTOCOL_ERROR, /* T30_ERR_RETRY_NO_DCN_AFTER_MCF */
+ _FAX_PROTOCOL_ERROR, /* T30_ERR_RETRY_NO_DCN_AFTER_RTN */
+ _FAX_PROTOCOL_ERROR, /* T30_ERR_RETRY_NO_CFR */
+ _FAX_PROTOCOL_ERROR, /* T30_ERR_RETRY_NO_MCF_AFTER_EOP */
+ _FAX_PROTOCOL_ERROR, /* T30_ERR_RETRY_NO_MCF_AFTER_EOM */
+ _FAX_PROTOCOL_ERROR, /* T30_ERR_RETRY_NO_MCF_AFTER_MPS */
+ 0x331d, /* T30_ERR_SUB_SEP_UNSUPPORTED */
+ 0x331e, /* T30_ERR_PWD_UNSUPPORTED */
+ 0x331f, /* T30_ERR_SUB_SEP_PWD_UNSUPPORTED */
+ _FAX_PROTOCOL_ERROR, /* T30_ERR_INVALID_COMMAND_FRAME */
+ _FAX_PARAMETER_ERROR, /* T30_ERR_UNSUPPORTED_PAGE_CODING */
+ _FAX_PARAMETER_ERROR, /* T30_ERR_INVALID_PAGE_CODING */
+ _FAX_REMOTE_REJECT, /* T30_ERR_INCOMPATIBLE_PAGE_CONFIG */
+ _FAX_LOCAL_ABORT, /* T30_ERR_TIMEOUT_FROM_APPLICATION */
+ _FAX_PROTOCOL_ERROR, /* T30_ERR_V34FAX_NO_REACTION_ON_MARK */
+ _FAX_PROTOCOL_ERROR, /* T30_ERR_V34FAX_TRAINING_TIMEOUT */
+ _FAX_PROTOCOL_ERROR, /* T30_ERR_V34FAX_UNEXPECTED_V21 */
+ _FAX_PROTOCOL_ERROR, /* T30_ERR_V34FAX_PRIMARY_CTS_ON */
+ _FAX_LOCAL_ABORT, /* T30_ERR_V34FAX_TURNAROUND_POLLING */
+ _FAX_LOCAL_ABORT /* T30_ERR_V34FAX_V8_INCOMPATIBILITY */
+ };
+
+ byte dtmf_code_buffer[CAPIDTMF_RECV_DIGIT_BUFFER_SIZE + 1];
+
+
+ static word rtp_info[] = {
+ GOOD, /* RTP_SUCCESS */
+ 0x3600 /* RTP_ERR_SSRC_OR_PAYLOAD_CHANGE */
+ };
+
+ static dword udata_forwarding_table[0x100 / sizeof(dword)] =
+ {
+ 0x0020301e, 0x00000000, 0x00000000, 0x00000000,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000
+ };
+
+ ch = plci->NL.IndCh;
+ a = plci->adapter;
+ ncci = a->ch_ncci[ch];
+ Id = (((dword)(ncci ? ncci : ch)) << 16) | (((word) plci->Id) << 8) | a->Id;
+ if(plci->tel) Id|=EXT_CONTROLLER;
+ APPLptr = plci->appl;
+ dbug(1,dprintf("NL_IND-Id(NL:0x%x)=0x%08lx,plci=%x,tel=%x,state=0x%x,ch=0x%x,chs=%d,Ind=%x",
+ plci->NL.Id,Id,plci->Id,plci->tel,plci->State,ch,plci->channels,plci->NL.Ind &0x0f));
+
+ /* in the case if no connect_active_Ind was sent to the appl we wait for */
+
+ if (plci->nl_remove_id)
+ {
+ plci->NL.RNR = 2; /* discard */
+ dbug(1,dprintf("NL discard while remove pending"));
+ return;
+ }
+ if((plci->NL.Ind &0x0f)==N_CONNECT)
+ {
+ if(plci->State==INC_DIS_PENDING
+ || plci->State==OUTG_DIS_PENDING
+ || plci->State==IDLE)
+ {
+ plci->NL.RNR = 2; /* discard */
+ dbug(1,dprintf("discard n_connect"));
+ return;
+ }
+ if(plci->State < INC_ACT_PENDING)
+ {
+ plci->NL.RNR = 1; /* flow control */
+ channel_x_off (plci, ch, N_XON_CONNECT_IND);
+ return;
+ }
+ }
+
+ if(!APPLptr) /* no application or invalid data */
+ { /* while reloading the DSP */
+ dbug(1,dprintf("discard1"));
+ plci->NL.RNR = 2;
+ return;
+ }
+
+ if (((plci->NL.Ind &0x0f) == N_UDATA)
+ && (((plci->B2_prot != B2_SDLC) && ((plci->B1_resource == 17) || (plci->B1_resource == 18)))
+ || (plci->B2_prot == 7)
+ || (plci->B3_prot == 7)) )
+ {
+ plci->ncpi_buffer[0] = 0;
+
+ ncpi_state = plci->ncpi_state;
+ if (plci->NL.complete == 1)
+ {
+ byte * data = &plci->NL.RBuffer->P[0];
+
+ if ((plci->NL.RBuffer->length >= 12)
+ &&( (*data == DSP_UDATA_INDICATION_DCD_ON)
+ ||(*data == DSP_UDATA_INDICATION_CTS_ON)) )
+ {
+ word conn_opt, ncpi_opt = 0x00;
+/* HexDump ("MDM N_UDATA:", plci->NL.RBuffer->length, data); */
+
+ if (*data == DSP_UDATA_INDICATION_DCD_ON)
+ plci->ncpi_state |= NCPI_MDM_DCD_ON_RECEIVED;
+ if (*data == DSP_UDATA_INDICATION_CTS_ON)
+ plci->ncpi_state |= NCPI_MDM_CTS_ON_RECEIVED;
+
+ data++; /* indication code */
+ data += 2; /* timestamp */
+ if ((*data == DSP_CONNECTED_NORM_V18) || (*data == DSP_CONNECTED_NORM_VOWN))
+ ncpi_state &= ~(NCPI_MDM_DCD_ON_RECEIVED | NCPI_MDM_CTS_ON_RECEIVED);
+ data++; /* connected norm */
+ conn_opt = GET_WORD(data);
+ data += 2; /* connected options */
+
+ PUT_WORD (&(plci->ncpi_buffer[1]), (word)(GET_DWORD(data) & 0x0000FFFF));
+
+ if (conn_opt & DSP_CONNECTED_OPTION_MASK_V42)
+ {
+ ncpi_opt |= MDM_NCPI_ECM_V42;
+ }
+ else if (conn_opt & DSP_CONNECTED_OPTION_MASK_MNP)
+ {
+ ncpi_opt |= MDM_NCPI_ECM_MNP;
+ }
+ else
+ {
+ ncpi_opt |= MDM_NCPI_TRANSPARENT;
+ }
+ if (conn_opt & DSP_CONNECTED_OPTION_MASK_COMPRESSION)
+ {
+ ncpi_opt |= MDM_NCPI_COMPRESSED;
+ }
+ PUT_WORD (&(plci->ncpi_buffer[3]), ncpi_opt);
+ plci->ncpi_buffer[0] = 4;
+
+ plci->ncpi_state |= NCPI_VALID_CONNECT_B3_IND | NCPI_VALID_CONNECT_B3_ACT | NCPI_VALID_DISC_B3_IND;
+ }
+ }
+ if (plci->B3_prot == 7)
+ {
+ if (((a->ncci_state[ncci] == INC_ACT_PENDING) || (a->ncci_state[ncci] == OUTG_CON_PENDING))
+ && (plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT)
+ && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT))
+ {
+ a->ncci_state[ncci] = INC_ACT_PENDING;
+ sendf(plci->appl,_CONNECT_B3_ACTIVE_I,Id,0,"S",plci->ncpi_buffer);
+ plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT;
+ }
+ }
+
+ if (!((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[plci->appl->Id-1])
+ & ((1L << PRIVATE_V18) | (1L << PRIVATE_VOWN)))
+ || !(ncpi_state & NCPI_MDM_DCD_ON_RECEIVED)
+ || !(ncpi_state & NCPI_MDM_CTS_ON_RECEIVED))
+
+ {
+ plci->NL.RNR = 2;
+ return;
+ }
+ }
+
+ if(plci->NL.complete == 2)
+ {
+ if (((plci->NL.Ind &0x0f) == N_UDATA)
+ && !(udata_forwarding_table[plci->RData[0].P[0] >> 5] & (1L << (plci->RData[0].P[0] & 0x1f))))
+ {
+ switch(plci->RData[0].P[0])
+ {
+
+ case DTMF_UDATA_INDICATION_FAX_CALLING_TONE:
+ if (plci->dtmf_rec_active & DTMF_LISTEN_ACTIVE_FLAG)
+ sendf(plci->appl, _FACILITY_I, Id & 0xffffL, 0,"ws", SELECTOR_DTMF, "\x01X");
+ break;
+ case DTMF_UDATA_INDICATION_ANSWER_TONE:
+ if (plci->dtmf_rec_active & DTMF_LISTEN_ACTIVE_FLAG)
+ sendf(plci->appl, _FACILITY_I, Id & 0xffffL, 0,"ws", SELECTOR_DTMF, "\x01Y");
+ break;
+ case DTMF_UDATA_INDICATION_DIGITS_RECEIVED:
+ dtmf_indication (Id, plci, plci->RData[0].P, plci->RData[0].PLength);
+ break;
+ case DTMF_UDATA_INDICATION_DIGITS_SENT:
+ dtmf_confirmation (Id, plci);
+ break;
+
+
+ case UDATA_INDICATION_MIXER_TAP_DATA:
+ capidtmf_recv_process_block (&(plci->capidtmf_state), plci->RData[0].P + 1, (word)(plci->RData[0].PLength - 1));
+ i = capidtmf_indication (&(plci->capidtmf_state), dtmf_code_buffer + 1);
+ if (i != 0)
+ {
+ dtmf_code_buffer[0] = DTMF_UDATA_INDICATION_DIGITS_RECEIVED;
+ dtmf_indication (Id, plci, dtmf_code_buffer, (word)(i + 1));
+ }
+ break;
+
+
+ case UDATA_INDICATION_MIXER_COEFS_SET:
+ mixer_indication_coefs_set (Id, plci);
+ break;
+ case UDATA_INDICATION_XCONNECT_FROM:
+ mixer_indication_xconnect_from (Id, plci, plci->RData[0].P, plci->RData[0].PLength);
+ break;
+ case UDATA_INDICATION_XCONNECT_TO:
+ mixer_indication_xconnect_to (Id, plci, plci->RData[0].P, plci->RData[0].PLength);
+ break;
+
+
+ case LEC_UDATA_INDICATION_DISABLE_DETECT:
+ ec_indication (Id, plci, plci->RData[0].P, plci->RData[0].PLength);
+ break;
+
+
+
+ default:
+ break;
+ }
+ }
+ else
+ {
+ if ((plci->RData[0].PLength != 0)
+ && ((plci->B2_prot == B2_V120_ASYNC)
+ || (plci->B2_prot == B2_V120_ASYNC_V42BIS)
+ || (plci->B2_prot == B2_V120_BIT_TRANSPARENT)))
+ {
+
+ sendf(plci->appl,_DATA_B3_I,Id,0,
+ "dwww",
+ plci->RData[1].P,
+ (plci->NL.RNum < 2) ? 0 : plci->RData[1].PLength,
+ plci->RNum,
+ plci->RFlags);
+
+ }
+ else
+ {
+
+ sendf(plci->appl,_DATA_B3_I,Id,0,
+ "dwww",
+ plci->RData[0].P,
+ plci->RData[0].PLength,
+ plci->RNum,
+ plci->RFlags);
+
+ }
+ }
+ return;
+ }
+
+ fax_feature_bits = 0;
+ if((plci->NL.Ind &0x0f)==N_CONNECT ||
+ (plci->NL.Ind &0x0f)==N_CONNECT_ACK ||
+ (plci->NL.Ind &0x0f)==N_DISC ||
+ (plci->NL.Ind &0x0f)==N_EDATA ||
+ (plci->NL.Ind &0x0f)==N_DISC_ACK)
+ {
+ info = 0;
+ plci->ncpi_buffer[0] = 0;
+ switch (plci->B3_prot) {
+ case 0: /*XPARENT*/
+ case 1: /*T.90 NL*/
+ break; /* no network control protocol info - jfr */
+ case 2: /*ISO8202*/
+ case 3: /*X25 DCE*/
+ for(i=0; i<plci->NL.RLength; i++) plci->ncpi_buffer[4+i] = plci->NL.RBuffer->P[i];
+ plci->ncpi_buffer[0] = (byte)(i+3);
+ plci->ncpi_buffer[1] = (byte)(plci->NL.Ind &N_D_BIT? 1:0);
+ plci->ncpi_buffer[2] = 0;
+ plci->ncpi_buffer[3] = 0;
+ break;
+ case 4: /*T.30 - FAX*/
+ case 5: /*T.30 - FAX*/
+ if(plci->NL.RLength>=sizeof(T30_INFO))
+ {
+ dbug(1,dprintf("FaxStatus %04x", ((T30_INFO *)plci->NL.RBuffer->P)->code));
+ len = 9;
+ PUT_WORD(&(plci->ncpi_buffer[1]),((T30_INFO *)plci->NL.RBuffer->P)->rate_div_2400 * 2400);
+ fax_feature_bits = GET_WORD(&((T30_INFO *)plci->NL.RBuffer->P)->feature_bits_low);
+ i = (((T30_INFO *)plci->NL.RBuffer->P)->resolution & T30_RESOLUTION_R8_0770_OR_200) ? 0x0001 : 0x0000;
+ if (plci->B3_prot == 5)
+ {
+ if (!(fax_feature_bits & T30_FEATURE_BIT_ECM))
+ i |= 0x8000; /* This is not an ECM connection */
+ if (fax_feature_bits & T30_FEATURE_BIT_T6_CODING)
+ i |= 0x4000; /* This is a connection with MMR compression */
+ if (fax_feature_bits & T30_FEATURE_BIT_2D_CODING)
+ i |= 0x2000; /* This is a connection with MR compression */
+ if (fax_feature_bits & T30_FEATURE_BIT_MORE_DOCUMENTS)
+ i |= 0x0004; /* More documents */
+ if (fax_feature_bits & T30_FEATURE_BIT_POLLING)
+ i |= 0x0002; /* Fax-polling indication */
+ }
+ dbug(1,dprintf("FAX Options %04x %04x",fax_feature_bits,i));
+ PUT_WORD(&(plci->ncpi_buffer[3]),i);
+ PUT_WORD(&(plci->ncpi_buffer[5]),((T30_INFO *)plci->NL.RBuffer->P)->data_format);
+ plci->ncpi_buffer[7] = ((T30_INFO *)plci->NL.RBuffer->P)->pages_low;
+ plci->ncpi_buffer[8] = ((T30_INFO *)plci->NL.RBuffer->P)->pages_high;
+ plci->ncpi_buffer[len] = 0;
+ if(((T30_INFO *)plci->NL.RBuffer->P)->station_id_len)
+ {
+ plci->ncpi_buffer[len] = 20;
+ for (i = 0; i < 20; i++)
+ plci->ncpi_buffer[++len] = ((T30_INFO *)plci->NL.RBuffer->P)->station_id[i];
+ }
+ if (((plci->NL.Ind & 0x0f) == N_DISC) || ((plci->NL.Ind & 0x0f) == N_DISC_ACK))
+ {
+ if (((T30_INFO *)plci->NL.RBuffer->P)->code < ARRAY_SIZE(fax_info))
+ info = fax_info[((T30_INFO *)plci->NL.RBuffer->P)->code];
+ else
+ info = _FAX_PROTOCOL_ERROR;
+ }
+
+ if ((plci->requested_options_conn | plci->requested_options | a->requested_options_table[plci->appl->Id-1])
+ & ((1L << PRIVATE_FAX_SUB_SEP_PWD) | (1L << PRIVATE_FAX_NONSTANDARD)))
+ {
+ i = ((word)(((T30_INFO *) 0)->station_id + 20)) + ((T30_INFO *)plci->NL.RBuffer->P)->head_line_len;
+ while (i < plci->NL.RBuffer->length)
+ plci->ncpi_buffer[++len] = plci->NL.RBuffer->P[i++];
+ }
+
+ plci->ncpi_buffer[0] = len;
+ fax_feature_bits = GET_WORD(&((T30_INFO *)plci->NL.RBuffer->P)->feature_bits_low);
+ PUT_WORD(&((T30_INFO *)plci->fax_connect_info_buffer)->feature_bits_low, fax_feature_bits);
+
+ plci->ncpi_state |= NCPI_VALID_CONNECT_B3_IND;
+ if (((plci->NL.Ind &0x0f) == N_CONNECT_ACK)
+ || (((plci->NL.Ind &0x0f) == N_CONNECT)
+ && (fax_feature_bits & T30_FEATURE_BIT_POLLING))
+ || (((plci->NL.Ind &0x0f) == N_EDATA)
+ && ((((T30_INFO *)plci->NL.RBuffer->P)->code == EDATA_T30_TRAIN_OK)
+ || (((T30_INFO *)plci->NL.RBuffer->P)->code == EDATA_T30_DIS)
+ || (((T30_INFO *)plci->NL.RBuffer->P)->code == EDATA_T30_DTC))))
+ {
+ plci->ncpi_state |= NCPI_VALID_CONNECT_B3_ACT;
+ }
+ if (((plci->NL.Ind &0x0f) == N_DISC)
+ || ((plci->NL.Ind &0x0f) == N_DISC_ACK)
+ || (((plci->NL.Ind &0x0f) == N_EDATA)
+ && (((T30_INFO *)plci->NL.RBuffer->P)->code == EDATA_T30_EOP_CAPI)))
+ {
+ plci->ncpi_state |= NCPI_VALID_CONNECT_B3_ACT | NCPI_VALID_DISC_B3_IND;
+ }
+ }
+ break;
+
+ case B3_RTP:
+ if (((plci->NL.Ind & 0x0f) == N_DISC) || ((plci->NL.Ind & 0x0f) == N_DISC_ACK))
+ {
+ if (plci->NL.RLength != 0)
+ {
+ info = rtp_info[plci->NL.RBuffer->P[0]];
+ plci->ncpi_buffer[0] = plci->NL.RLength - 1;
+ for (i = 1; i < plci->NL.RLength; i++)
+ plci->ncpi_buffer[i] = plci->NL.RBuffer->P[i];
+ }
+ }
+ break;
+
+ }
+ plci->NL.RNR = 2;
+ }
+ switch(plci->NL.Ind &0x0f) {
+ case N_EDATA:
+ if ((plci->B3_prot == 4) || (plci->B3_prot == 5))
+ {
+ dbug(1,dprintf("EDATA ncci=0x%x state=%d code=%02x", ncci, a->ncci_state[ncci],
+ ((T30_INFO *)plci->NL.RBuffer->P)->code));
+ fax_send_edata_ack = (((T30_INFO *)(plci->fax_connect_info_buffer))->operating_mode == T30_OPERATING_MODE_CAPI_NEG);
+
+ if ((plci->nsf_control_bits & T30_NSF_CONTROL_BIT_ENABLE_NSF)
+ && (plci->nsf_control_bits & (T30_NSF_CONTROL_BIT_NEGOTIATE_IND | T30_NSF_CONTROL_BIT_NEGOTIATE_RESP))
+ && (((T30_INFO *)plci->NL.RBuffer->P)->code == EDATA_T30_DIS)
+ && (a->ncci_state[ncci] == OUTG_CON_PENDING)
+ && (plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT)
+ && !(plci->ncpi_state & NCPI_NEGOTIATE_B3_SENT))
+ {
+ ((T30_INFO *)(plci->fax_connect_info_buffer))->code = ((T30_INFO *)plci->NL.RBuffer->P)->code;
+ sendf(plci->appl,_MANUFACTURER_I,Id,0,"dwbS",_DI_MANU_ID,_DI_NEGOTIATE_B3,
+ (byte)(plci->ncpi_buffer[0] + 1), plci->ncpi_buffer);
+ plci->ncpi_state |= NCPI_NEGOTIATE_B3_SENT;
+ if (plci->nsf_control_bits & T30_NSF_CONTROL_BIT_NEGOTIATE_RESP)
+ fax_send_edata_ack = false;
+ }
+
+ if (a->manufacturer_features & MANUFACTURER_FEATURE_FAX_PAPER_FORMATS)
+ {
+ switch (((T30_INFO *)plci->NL.RBuffer->P)->code)
+ {
+ case EDATA_T30_DIS:
+ if ((a->ncci_state[ncci] == OUTG_CON_PENDING)
+ && !(GET_WORD(&((T30_INFO *)plci->fax_connect_info_buffer)->control_bits_low) & T30_CONTROL_BIT_REQUEST_POLLING)
+ && (plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT)
+ && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT))
+ {
+ a->ncci_state[ncci] = INC_ACT_PENDING;
+ if (plci->B3_prot == 4)
+ sendf(plci->appl,_CONNECT_B3_ACTIVE_I,Id,0,"s","");
+ else
+ sendf(plci->appl,_CONNECT_B3_ACTIVE_I,Id,0,"S",plci->ncpi_buffer);
+ plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT;
+ }
+ break;
+
+ case EDATA_T30_TRAIN_OK:
+ if ((a->ncci_state[ncci] == INC_ACT_PENDING)
+ && (plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT)
+ && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT))
+ {
+ if (plci->B3_prot == 4)
+ sendf(plci->appl,_CONNECT_B3_ACTIVE_I,Id,0,"s","");
+ else
+ sendf(plci->appl,_CONNECT_B3_ACTIVE_I,Id,0,"S",plci->ncpi_buffer);
+ plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT;
+ }
+ break;
+
+ case EDATA_T30_EOP_CAPI:
+ if (a->ncci_state[ncci] == CONNECTED)
+ {
+ sendf(plci->appl,_DISCONNECT_B3_I,Id,0,"wS",GOOD,plci->ncpi_buffer);
+ a->ncci_state[ncci] = INC_DIS_PENDING;
+ plci->ncpi_state = 0;
+ fax_send_edata_ack = false;
+ }
+ break;
+ }
+ }
+ else
+ {
+ switch (((T30_INFO *)plci->NL.RBuffer->P)->code)
+ {
+ case EDATA_T30_TRAIN_OK:
+ if ((a->ncci_state[ncci] == INC_ACT_PENDING)
+ && (plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT)
+ && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT))
+ {
+ if (plci->B3_prot == 4)
+ sendf(plci->appl,_CONNECT_B3_ACTIVE_I,Id,0,"s","");
+ else
+ sendf(plci->appl,_CONNECT_B3_ACTIVE_I,Id,0,"S",plci->ncpi_buffer);
+ plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT;
+ }
+ break;
+ }
+ }
+ if (fax_send_edata_ack)
+ {
+ ((T30_INFO *)(plci->fax_connect_info_buffer))->code = ((T30_INFO *)plci->NL.RBuffer->P)->code;
+ plci->fax_edata_ack_length = 1;
+ start_internal_command (Id, plci, fax_edata_ack_command);
+ }
+ }
+ else
+ {
+ dbug(1,dprintf("EDATA ncci=0x%x state=%d", ncci, a->ncci_state[ncci]));
+ }
+ break;
+ case N_CONNECT:
+ if (!a->ch_ncci[ch])
+ {
+ ncci = get_ncci (plci, ch, 0);
+ Id = (Id & 0xffff) | (((dword) ncci) << 16);
+ }
+ dbug(1,dprintf("N_CONNECT: ch=%d state=%d plci=%lx plci_Id=%lx plci_State=%d",
+ ch, a->ncci_state[ncci], a->ncci_plci[ncci], plci->Id, plci->State));
+
+ msg = _CONNECT_B3_I;
+ if (a->ncci_state[ncci] == IDLE)
+ plci->channels++;
+ else if (plci->B3_prot == 1)
+ msg = _CONNECT_B3_T90_ACTIVE_I;
+
+ a->ncci_state[ncci] = INC_CON_PENDING;
+ if(plci->B3_prot == 4)
+ sendf(plci->appl,msg,Id,0,"s","");
+ else
+ sendf(plci->appl,msg,Id,0,"S",plci->ncpi_buffer);
+ break;
+ case N_CONNECT_ACK:
+ dbug(1,dprintf("N_connect_Ack"));
+ if (plci->internal_command_queue[0]
+ && ((plci->adjust_b_state == ADJUST_B_CONNECT_2)
+ || (plci->adjust_b_state == ADJUST_B_CONNECT_3)
+ || (plci->adjust_b_state == ADJUST_B_CONNECT_4)))
+ {
+ (*(plci->internal_command_queue[0]))(Id, plci, 0);
+ if (!plci->internal_command)
+ next_internal_command (Id, plci);
+ break;
+ }
+ msg = _CONNECT_B3_ACTIVE_I;
+ if (plci->B3_prot == 1)
+ {
+ if (a->ncci_state[ncci] != OUTG_CON_PENDING)
+ msg = _CONNECT_B3_T90_ACTIVE_I;
+ a->ncci_state[ncci] = INC_ACT_PENDING;
+ sendf(plci->appl,msg,Id,0,"S",plci->ncpi_buffer);
+ }
+ else if ((plci->B3_prot == 4) || (plci->B3_prot == 5) || (plci->B3_prot == 7))
+ {
+ if ((a->ncci_state[ncci] == OUTG_CON_PENDING)
+ && (plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT)
+ && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT))
+ {
+ a->ncci_state[ncci] = INC_ACT_PENDING;
+ if (plci->B3_prot == 4)
+ sendf(plci->appl,msg,Id,0,"s","");
+ else
+ sendf(plci->appl,msg,Id,0,"S",plci->ncpi_buffer);
+ plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT;
+ }
+ }
+ else
+ {
+ a->ncci_state[ncci] = INC_ACT_PENDING;
+ sendf(plci->appl,msg,Id,0,"S",plci->ncpi_buffer);
+ }
+ if (plci->adjust_b_restore)
+ {
+ plci->adjust_b_restore = false;
+ start_internal_command (Id, plci, adjust_b_restore);
+ }
+ break;
+ case N_DISC:
+ case N_DISC_ACK:
+ if (plci->internal_command_queue[0]
+ && ((plci->internal_command == FAX_DISCONNECT_COMMAND_1)
+ || (plci->internal_command == FAX_DISCONNECT_COMMAND_2)
+ || (plci->internal_command == FAX_DISCONNECT_COMMAND_3)))
+ {
+ (*(plci->internal_command_queue[0]))(Id, plci, 0);
+ if (!plci->internal_command)
+ next_internal_command (Id, plci);
+ }
+ ncci_state = a->ncci_state[ncci];
+ ncci_remove (plci, ncci, false);
+
+ /* with N_DISC or N_DISC_ACK the IDI frees the respective */
+ /* channel, so we cannot store the state in ncci_state! The */
+ /* information which channel we received a N_DISC is thus */
+ /* stored in the inc_dis_ncci_table buffer. */
+ for(i=0; plci->inc_dis_ncci_table[i]; i++);
+ plci->inc_dis_ncci_table[i] = (byte) ncci;
+
+ /* need a connect_b3_ind before a disconnect_b3_ind with FAX */
+ if (!plci->channels
+ && (plci->B1_resource == 16)
+ && (plci->State <= CONNECTED))
+ {
+ len = 9;
+ i = ((T30_INFO *)plci->fax_connect_info_buffer)->rate_div_2400 * 2400;
+ PUT_WORD (&plci->ncpi_buffer[1], i);
+ PUT_WORD (&plci->ncpi_buffer[3], 0);
+ i = ((T30_INFO *)plci->fax_connect_info_buffer)->data_format;
+ PUT_WORD (&plci->ncpi_buffer[5], i);
+ PUT_WORD (&plci->ncpi_buffer[7], 0);
+ plci->ncpi_buffer[len] = 0;
+ plci->ncpi_buffer[0] = len;
+ if(plci->B3_prot == 4)
+ sendf(plci->appl,_CONNECT_B3_I,Id,0,"s","");
+ else
+ {
+
+ if ((plci->requested_options_conn | plci->requested_options | a->requested_options_table[plci->appl->Id-1])
+ & ((1L << PRIVATE_FAX_SUB_SEP_PWD) | (1L << PRIVATE_FAX_NONSTANDARD)))
+ {
+ plci->ncpi_buffer[++len] = 0;
+ plci->ncpi_buffer[++len] = 0;
+ plci->ncpi_buffer[++len] = 0;
+ plci->ncpi_buffer[0] = len;
+ }
+
+ sendf(plci->appl,_CONNECT_B3_I,Id,0,"S",plci->ncpi_buffer);
+ }
+ sendf(plci->appl,_DISCONNECT_B3_I,Id,0,"wS",info,plci->ncpi_buffer);
+ plci->ncpi_state = 0;
+ sig_req(plci,HANGUP,0);
+ send_req(plci);
+ plci->State = OUTG_DIS_PENDING;
+ /* disc here */
+ }
+ else if ((a->manufacturer_features & MANUFACTURER_FEATURE_FAX_PAPER_FORMATS)
+ && ((plci->B3_prot == 4) || (plci->B3_prot == 5))
+ && ((ncci_state == INC_DIS_PENDING) || (ncci_state == IDLE)))
+ {
+ if (ncci_state == IDLE)
+ {
+ if (plci->channels)
+ plci->channels--;
+ if((plci->State==IDLE || plci->State==SUSPENDING) && !plci->channels){
+ if(plci->State == SUSPENDING){
+ sendf(plci->appl,
+ _FACILITY_I,
+ Id & 0xffffL,
+ 0,
+ "ws", (word)3, "\x03\x04\x00\x00");
+ sendf(plci->appl, _DISCONNECT_I, Id & 0xffffL, 0, "w", 0);
+ }
+ plci_remove(plci);
+ plci->State=IDLE;
+ }
+ }
+ }
+ else if (plci->channels)
+ {
+ sendf(plci->appl,_DISCONNECT_B3_I,Id,0,"wS",info,plci->ncpi_buffer);
+ plci->ncpi_state = 0;
+ if ((ncci_state == OUTG_REJ_PENDING)
+ && ((plci->B3_prot != B3_T90NL) && (plci->B3_prot != B3_ISO8208) && (plci->B3_prot != B3_X25_DCE)))
+ {
+ sig_req(plci,HANGUP,0);
+ send_req(plci);
+ plci->State = OUTG_DIS_PENDING;
+ }
+ }
+ break;
+ case N_RESET:
+ a->ncci_state[ncci] = INC_RES_PENDING;
+ sendf(plci->appl,_RESET_B3_I,Id,0,"S",plci->ncpi_buffer);
+ break;
+ case N_RESET_ACK:
+ a->ncci_state[ncci] = CONNECTED;
+ sendf(plci->appl,_RESET_B3_I,Id,0,"S",plci->ncpi_buffer);
+ break;
+
+ case N_UDATA:
+ if (!(udata_forwarding_table[plci->NL.RBuffer->P[0] >> 5] & (1L << (plci->NL.RBuffer->P[0] & 0x1f))))
+ {
+ plci->RData[0].P = plci->internal_ind_buffer + (-((int)(long)(plci->internal_ind_buffer)) & 3);
+ plci->RData[0].PLength = INTERNAL_IND_BUFFER_SIZE;
+ plci->NL.R = plci->RData;
+ plci->NL.RNum = 1;
+ return;
+ }
+ case N_BDATA:
+ case N_DATA:
+ if (((a->ncci_state[ncci] != CONNECTED) && (plci->B2_prot == 1)) /* transparent */
+ || (a->ncci_state[ncci] == IDLE)
+ || (a->ncci_state[ncci] == INC_DIS_PENDING))
+ {
+ plci->NL.RNR = 2;
+ break;
+ }
+ if ((a->ncci_state[ncci] != CONNECTED)
+ && (a->ncci_state[ncci] != OUTG_DIS_PENDING)
+ && (a->ncci_state[ncci] != OUTG_REJ_PENDING))
+ {
+ dbug(1,dprintf("flow control"));
+ plci->NL.RNR = 1; /* flow control */
+ channel_x_off (plci, ch, 0);
+ break;
+ }
+
+ NCCIcode = ncci | (((word)a->Id) << 8);
+
+ /* count all buffers within the Application pool */
+ /* belonging to the same NCCI. If this is below the */
+ /* number of buffers available per NCCI we accept */
+ /* this packet, otherwise we reject it */
+ count = 0;
+ Num = 0xffff;
+ for(i=0; i<APPLptr->MaxBuffer; i++) {
+ if(NCCIcode==APPLptr->DataNCCI[i]) count++;
+ if(!APPLptr->DataNCCI[i] && Num==0xffff) Num = i;
+ }
+
+ if(count>=APPLptr->MaxNCCIData || Num==0xffff)
+ {
+ dbug(3,dprintf("Flow-Control"));
+ plci->NL.RNR = 1;
+ if( ++(APPLptr->NCCIDataFlowCtrlTimer)>=
+ (word)((a->manufacturer_features & MANUFACTURER_FEATURE_OOB_CHANNEL) ? 40 : 2000))
+ {
+ plci->NL.RNR = 2;
+ dbug(3,dprintf("DiscardData"));
+ } else {
+ channel_x_off (plci, ch, 0);
+ }
+ break;
+ }
+ else
+ {
+ APPLptr->NCCIDataFlowCtrlTimer = 0;
+ }
+
+ plci->RData[0].P = ReceiveBufferGet(APPLptr,Num);
+ if(!plci->RData[0].P) {
+ plci->NL.RNR = 1;
+ channel_x_off (plci, ch, 0);
+ break;
+ }
+
+ APPLptr->DataNCCI[Num] = NCCIcode;
+ APPLptr->DataFlags[Num] = (plci->Id<<8) | (plci->NL.Ind>>4);
+ dbug(3,dprintf("Buffer(%d), Max = %d",Num,APPLptr->MaxBuffer));
+
+ plci->RNum = Num;
+ plci->RFlags = plci->NL.Ind>>4;
+ plci->RData[0].PLength = APPLptr->MaxDataLength;
+ plci->NL.R = plci->RData;
+ if ((plci->NL.RLength != 0)
+ && ((plci->B2_prot == B2_V120_ASYNC)
+ || (plci->B2_prot == B2_V120_ASYNC_V42BIS)
+ || (plci->B2_prot == B2_V120_BIT_TRANSPARENT)))
+ {
+ plci->RData[1].P = plci->RData[0].P;
+ plci->RData[1].PLength = plci->RData[0].PLength;
+ plci->RData[0].P = v120_header_buffer + (-((int) v120_header_buffer) & 3);
+ if ((plci->NL.RBuffer->P[0] & V120_HEADER_EXTEND_BIT) || (plci->NL.RLength == 1))
+ plci->RData[0].PLength = 1;
+ else
+ plci->RData[0].PLength = 2;
+ if (plci->NL.RBuffer->P[0] & V120_HEADER_BREAK_BIT)
+ plci->RFlags |= 0x0010;
+ if (plci->NL.RBuffer->P[0] & (V120_HEADER_C1_BIT | V120_HEADER_C2_BIT))
+ plci->RFlags |= 0x8000;
+ plci->NL.RNum = 2;
+ }
+ else
+ {
+ if((plci->NL.Ind &0x0f)==N_UDATA)
+ plci->RFlags |= 0x0010;
+
+ else if ((plci->B3_prot == B3_RTP) && ((plci->NL.Ind & 0x0f) == N_BDATA))
+ plci->RFlags |= 0x0001;
+
+ plci->NL.RNum = 1;
+ }
+ break;
+ case N_DATA_ACK:
+ data_ack (plci, ch);
+ break;
+ default:
+ plci->NL.RNR = 2;
+ break;
+ }
+}
+
+/*------------------------------------------------------------------*/
+/* find a free PLCI */
+/*------------------------------------------------------------------*/
+
+word get_plci(DIVA_CAPI_ADAPTER * a)
+{
+ word i,j;
+ PLCI * plci;
+
+ dump_plcis (a);
+ for(i=0;i<a->max_plci && a->plci[i].Id;i++);
+ if(i==a->max_plci) {
+ dbug(1,dprintf("get_plci: out of PLCIs"));
+ return 0;
+ }
+ plci = &a->plci[i];
+ plci->Id = (byte)(i+1);
+
+ plci->Sig.Id = 0;
+ plci->NL.Id = 0;
+ plci->sig_req = 0;
+ plci->nl_req = 0;
+
+ plci->appl = NULL;
+ plci->relatedPTYPLCI = NULL;
+ plci->State = IDLE;
+ plci->SuppState = IDLE;
+ plci->channels = 0;
+ plci->tel = 0;
+ plci->B1_resource = 0;
+ plci->B2_prot = 0;
+ plci->B3_prot = 0;
+
+ plci->command = 0;
+ plci->m_command = 0;
+ init_internal_command_queue (plci);
+ plci->number = 0;
+ plci->req_in_start = 0;
+ plci->req_in = 0;
+ plci->req_out = 0;
+ plci->msg_in_write_pos = MSG_IN_QUEUE_SIZE;
+ plci->msg_in_read_pos = MSG_IN_QUEUE_SIZE;
+ plci->msg_in_wrap_pos = MSG_IN_QUEUE_SIZE;
+
+ plci->data_sent = false;
+ plci->send_disc = 0;
+ plci->sig_global_req = 0;
+ plci->sig_remove_id = 0;
+ plci->nl_global_req = 0;
+ plci->nl_remove_id = 0;
+ plci->adv_nl = 0;
+ plci->manufacturer = false;
+ plci->call_dir = CALL_DIR_OUT | CALL_DIR_ORIGINATE;
+ plci->spoofed_msg = 0;
+ plci->ptyState = 0;
+ plci->cr_enquiry = false;
+ plci->hangup_flow_ctrl_timer = 0;
+
+ plci->ncci_ring_list = 0;
+ for(j=0;j<MAX_CHANNELS_PER_PLCI;j++) plci->inc_dis_ncci_table[j] = 0;
+ clear_c_ind_mask (plci);
+ set_group_ind_mask (plci);
+ plci->fax_connect_info_length = 0;
+ plci->nsf_control_bits = 0;
+ plci->ncpi_state = 0x00;
+ plci->ncpi_buffer[0] = 0;
+
+ plci->requested_options_conn = 0;
+ plci->requested_options = 0;
+ plci->notifiedcall = 0;
+ plci->vswitchstate = 0;
+ plci->vsprot = 0;
+ plci->vsprotdialect = 0;
+ init_b1_config (plci);
+ dbug(1,dprintf("get_plci(%x)",plci->Id));
+ return i+1;
+}
+
+/*------------------------------------------------------------------*/
+/* put a parameter in the parameter buffer */
+/*------------------------------------------------------------------*/
+
+static void add_p(PLCI * plci, byte code, byte * p)
+{
+ word p_length;
+
+ p_length = 0;
+ if(p) p_length = p[0];
+ add_ie(plci, code, p, p_length);
+}
+
+/*------------------------------------------------------------------*/
+/* put a structure in the parameter buffer */
+/*------------------------------------------------------------------*/
+static void add_s(PLCI * plci, byte code, API_PARSE * p)
+{
+ if(p) add_ie(plci, code, p->info, (word)p->length);
+}
+
+/*------------------------------------------------------------------*/
+/* put multiple structures in the parameter buffer */
+/*------------------------------------------------------------------*/
+static void add_ss(PLCI * plci, byte code, API_PARSE * p)
+{
+ byte i;
+
+ if(p){
+ dbug(1,dprintf("add_ss(%x,len=%d)",code,p->length));
+ for(i=2;i<(byte)p->length;i+=p->info[i]+2){
+ dbug(1,dprintf("add_ss_ie(%x,len=%d)",p->info[i-1],p->info[i]));
+ add_ie(plci, p->info[i-1], (byte *)&(p->info[i]), (word)p->info[i]);
+ }
+ }
+}
+
+/*------------------------------------------------------------------*/
+/* return the channel number sent by the application in a esc_chi */
+/*------------------------------------------------------------------*/
+static byte getChannel(API_PARSE * p)
+{
+ byte i;
+
+ if(p){
+ for(i=2;i<(byte)p->length;i+=p->info[i]+2){
+ if(p->info[i]==2){
+ if(p->info[i-1]==ESC && p->info[i+1]==CHI) return (p->info[i+2]);
+ }
+ }
+ }
+ return 0;
+}
+
+
+/*------------------------------------------------------------------*/
+/* put an information element in the parameter buffer */
+/*------------------------------------------------------------------*/
+
+static void add_ie(PLCI * plci, byte code, byte * p, word p_length)
+{
+ word i;
+
+ if(!(code &0x80) && !p_length) return;
+
+ if(plci->req_in==plci->req_in_start) {
+ plci->req_in +=2;
+ }
+ else {
+ plci->req_in--;
+ }
+ plci->RBuffer[plci->req_in++] = code;
+
+ if(p) {
+ plci->RBuffer[plci->req_in++] = (byte)p_length;
+ for(i=0;i<p_length;i++) plci->RBuffer[plci->req_in++] = p[1+i];
+ }
+
+ plci->RBuffer[plci->req_in++] = 0;
+}
+
+/*------------------------------------------------------------------*/
+/* put a unstructured data into the buffer */
+/*------------------------------------------------------------------*/
+
+void add_d(PLCI * plci, word length, byte * p)
+{
+ word i;
+
+ if(plci->req_in==plci->req_in_start) {
+ plci->req_in +=2;
+ }
+ else {
+ plci->req_in--;
+ }
+ for(i=0;i<length;i++) plci->RBuffer[plci->req_in++] = p[i];
+}
+
+/*------------------------------------------------------------------*/
+/* put parameters from the Additional Info parameter in the */
+/* parameter buffer */
+/*------------------------------------------------------------------*/
+
+void add_ai(PLCI * plci, API_PARSE * ai)
+{
+ word i;
+ API_PARSE ai_parms[5];
+
+ for(i=0;i<5;i++) ai_parms[i].length = 0;
+
+ if(!ai->length)
+ return;
+ if(api_parse(&ai->info[1], (word)ai->length, "ssss", ai_parms))
+ return;
+
+ add_s (plci,KEY,&ai_parms[1]);
+ add_s (plci,UUI,&ai_parms[2]);
+ add_ss(plci,FTY,&ai_parms[3]);
+}
+
+/*------------------------------------------------------------------*/
+/* put parameter for b1 protocol in the parameter buffer */
+/*------------------------------------------------------------------*/
+
+word add_b1(PLCI * plci, API_PARSE * bp, word b_channel_info, word b1_facilities)
+{
+ API_PARSE bp_parms[8];
+ API_PARSE mdm_cfg[9];
+ API_PARSE global_config[2];
+ byte cai[256];
+ byte resource[] = {5,9,13,12,16,39,9,17,17,18};
+ byte voice_cai[] = "\x06\x14\x00\x00\x00\x00\x08";
+ word i;
+
+ API_PARSE mdm_cfg_v18[4];
+ word j, n, w;
+ dword d;
+
+
+ for(i=0;i<8;i++) bp_parms[i].length = 0;
+ for(i=0;i<2;i++) global_config[i].length = 0;
+
+ dbug(1,dprintf("add_b1"));
+ api_save_msg(bp, "s", &plci->B_protocol);
+
+ if(b_channel_info==2){
+ plci->B1_resource = 0;
+ adjust_b1_facilities (plci, plci->B1_resource, b1_facilities);
+ add_p(plci, CAI, "\x01\x00");
+ dbug(1,dprintf("Cai=1,0 (no resource)"));
+ return 0;
+ }
+
+ if(plci->tel == CODEC_PERMANENT) return 0;
+ else if(plci->tel == CODEC){
+ plci->B1_resource = 1;
+ adjust_b1_facilities (plci, plci->B1_resource, b1_facilities);
+ add_p(plci, CAI, "\x01\x01");
+ dbug(1,dprintf("Cai=1,1 (Codec)"));
+ return 0;
+ }
+ else if(plci->tel == ADV_VOICE){
+ plci->B1_resource = add_b1_facilities (plci, 9, (word)(b1_facilities | B1_FACILITY_VOICE));
+ adjust_b1_facilities (plci, plci->B1_resource, (word)(b1_facilities | B1_FACILITY_VOICE));
+ voice_cai[1] = plci->B1_resource;
+ PUT_WORD (&voice_cai[5], plci->appl->MaxDataLength);
+ add_p(plci, CAI, voice_cai);
+ dbug(1,dprintf("Cai=1,0x%x (AdvVoice)",voice_cai[1]));
+ return 0;
+ }
+ plci->call_dir &= ~(CALL_DIR_ORIGINATE | CALL_DIR_ANSWER);
+ if (plci->call_dir & CALL_DIR_OUT)
+ plci->call_dir |= CALL_DIR_ORIGINATE;
+ else if (plci->call_dir & CALL_DIR_IN)
+ plci->call_dir |= CALL_DIR_ANSWER;
+
+ if(!bp->length){
+ plci->B1_resource = 0x5;
+ adjust_b1_facilities (plci, plci->B1_resource, b1_facilities);
+ add_p(plci, CAI, "\x01\x05");
+ return 0;
+ }
+
+ dbug(1,dprintf("b_prot_len=%d",(word)bp->length));
+ if(bp->length>256) return _WRONG_MESSAGE_FORMAT;
+ if(api_parse(&bp->info[1], (word)bp->length, "wwwsssb", bp_parms))
+ {
+ bp_parms[6].length = 0;
+ if(api_parse(&bp->info[1], (word)bp->length, "wwwsss", bp_parms))
+ {
+ dbug(1,dprintf("b-form.!"));
+ return _WRONG_MESSAGE_FORMAT;
+ }
+ }
+ else if (api_parse(&bp->info[1], (word)bp->length, "wwwssss", bp_parms))
+ {
+ dbug(1,dprintf("b-form.!"));
+ return _WRONG_MESSAGE_FORMAT;
+ }
+
+ if(bp_parms[6].length)
+ {
+ if(api_parse(&bp_parms[6].info[1], (word)bp_parms[6].length, "w", global_config))
+ {
+ return _WRONG_MESSAGE_FORMAT;
+ }
+ switch(GET_WORD(global_config[0].info))
+ {
+ case 1:
+ plci->call_dir = (plci->call_dir & ~CALL_DIR_ANSWER) | CALL_DIR_ORIGINATE;
+ break;
+ case 2:
+ plci->call_dir = (plci->call_dir & ~CALL_DIR_ORIGINATE) | CALL_DIR_ANSWER;
+ break;
+ }
+ }
+ dbug(1,dprintf("call_dir=%04x", plci->call_dir));
+
+
+ if ((GET_WORD(bp_parms[0].info) == B1_RTP)
+ && (plci->adapter->man_profile.private_options & (1L << PRIVATE_RTP)))
+ {
+ plci->B1_resource = add_b1_facilities (plci, 31, (word)(b1_facilities & ~B1_FACILITY_VOICE));
+ adjust_b1_facilities (plci, plci->B1_resource, (word)(b1_facilities & ~B1_FACILITY_VOICE));
+ cai[1] = plci->B1_resource;
+ cai[2] = 0;
+ cai[3] = 0;
+ cai[4] = 0;
+ PUT_WORD(&cai[5],plci->appl->MaxDataLength);
+ for (i = 0; i < bp_parms[3].length; i++)
+ cai[7+i] = bp_parms[3].info[1+i];
+ cai[0] = 6 + bp_parms[3].length;
+ add_p(plci, CAI, cai);
+ return 0;
+ }
+
+
+ if ((GET_WORD(bp_parms[0].info) == B1_PIAFS)
+ && (plci->adapter->man_profile.private_options & (1L << PRIVATE_PIAFS)))
+ {
+ plci->B1_resource = add_b1_facilities (plci, 35/* PIAFS HARDWARE FACILITY */, (word)(b1_facilities & ~B1_FACILITY_VOICE));
+ adjust_b1_facilities (plci, plci->B1_resource, (word)(b1_facilities & ~B1_FACILITY_VOICE));
+ cai[1] = plci->B1_resource;
+ cai[2] = 0;
+ cai[3] = 0;
+ cai[4] = 0;
+ PUT_WORD(&cai[5],plci->appl->MaxDataLength);
+ cai[0] = 6;
+ add_p(plci, CAI, cai);
+ return 0;
+ }
+
+
+ if ((GET_WORD(bp_parms[0].info) >= 32)
+ || (!((1L << GET_WORD(bp_parms[0].info)) & plci->adapter->profile.B1_Protocols)
+ && ((GET_WORD(bp_parms[0].info) != 3)
+ || !((1L << B1_HDLC) & plci->adapter->profile.B1_Protocols)
+ || ((bp_parms[3].length != 0) && (GET_WORD(&bp_parms[3].info[1]) != 0) && (GET_WORD(&bp_parms[3].info[1]) != 56000)))))
+ {
+ return _B1_NOT_SUPPORTED;
+ }
+ plci->B1_resource = add_b1_facilities (plci, resource[GET_WORD(bp_parms[0].info)],
+ (word)(b1_facilities & ~B1_FACILITY_VOICE));
+ adjust_b1_facilities (plci, plci->B1_resource, (word)(b1_facilities & ~B1_FACILITY_VOICE));
+ cai[0] = 6;
+ cai[1] = plci->B1_resource;
+ for (i=2;i<sizeof(cai);i++) cai[i] = 0;
+
+ if ((GET_WORD(bp_parms[0].info) == B1_MODEM_ALL_NEGOTIATE)
+ || (GET_WORD(bp_parms[0].info) == B1_MODEM_ASYNC)
+ || (GET_WORD(bp_parms[0].info) == B1_MODEM_SYNC_HDLC))
+ { /* B1 - modem */
+ for (i=0;i<7;i++) mdm_cfg[i].length = 0;
+
+ if (bp_parms[3].length)
+ {
+ if(api_parse(&bp_parms[3].info[1],(word)bp_parms[3].length,"wwwwww", mdm_cfg))
+ {
+ return (_WRONG_MESSAGE_FORMAT);
+ }
+
+ cai[2] = 0; /* Bit rate for adaptation */
+
+ dbug(1,dprintf("MDM Max Bit Rate:<%d>", GET_WORD(mdm_cfg[0].info)));
+
+ PUT_WORD (&cai[13], 0); /* Min Tx speed */
+ PUT_WORD (&cai[15], GET_WORD(mdm_cfg[0].info)); /* Max Tx speed */
+ PUT_WORD (&cai[17], 0); /* Min Rx speed */
+ PUT_WORD (&cai[19], GET_WORD(mdm_cfg[0].info)); /* Max Rx speed */
+
+ cai[3] = 0; /* Async framing parameters */
+ switch (GET_WORD (mdm_cfg[2].info))
+ { /* Parity */
+ case 1: /* odd parity */
+ cai[3] |= (DSP_CAI_ASYNC_PARITY_ENABLE | DSP_CAI_ASYNC_PARITY_ODD);
+ dbug(1,dprintf("MDM: odd parity"));
+ break;
+
+ case 2: /* even parity */
+ cai[3] |= (DSP_CAI_ASYNC_PARITY_ENABLE | DSP_CAI_ASYNC_PARITY_EVEN);
+ dbug(1,dprintf("MDM: even parity"));
+ break;
+
+ default:
+ dbug(1,dprintf("MDM: no parity"));
+ break;
+ }
+
+ switch (GET_WORD (mdm_cfg[3].info))
+ { /* stop bits */
+ case 1: /* 2 stop bits */
+ cai[3] |= DSP_CAI_ASYNC_TWO_STOP_BITS;
+ dbug(1,dprintf("MDM: 2 stop bits"));
+ break;
+
+ default:
+ dbug(1,dprintf("MDM: 1 stop bit"));
+ break;
+ }
+
+ switch (GET_WORD (mdm_cfg[1].info))
+ { /* char length */
+ case 5:
+ cai[3] |= DSP_CAI_ASYNC_CHAR_LENGTH_5;
+ dbug(1,dprintf("MDM: 5 bits"));
+ break;
+
+ case 6:
+ cai[3] |= DSP_CAI_ASYNC_CHAR_LENGTH_6;
+ dbug(1,dprintf("MDM: 6 bits"));
+ break;
+
+ case 7:
+ cai[3] |= DSP_CAI_ASYNC_CHAR_LENGTH_7;
+ dbug(1,dprintf("MDM: 7 bits"));
+ break;
+
+ default:
+ dbug(1,dprintf("MDM: 8 bits"));
+ break;
+ }
+
+ cai[7] = 0; /* Line taking options */
+ cai[8] = 0; /* Modulation negotiation options */
+ cai[9] = 0; /* Modulation options */
+
+ if (((plci->call_dir & CALL_DIR_ORIGINATE) != 0) ^ ((plci->call_dir & CALL_DIR_OUT) != 0))
+ {
+ cai[9] |= DSP_CAI_MODEM_REVERSE_DIRECTION;
+ dbug(1, dprintf("MDM: Reverse direction"));
+ }
+
+ if (GET_WORD (mdm_cfg[4].info) & MDM_CAPI_DISABLE_RETRAIN)
+ {
+ cai[9] |= DSP_CAI_MODEM_DISABLE_RETRAIN;
+ dbug(1, dprintf("MDM: Disable retrain"));
+ }
+
+ if (GET_WORD (mdm_cfg[4].info) & MDM_CAPI_DISABLE_RING_TONE)
+ {
+ cai[7] |= DSP_CAI_MODEM_DISABLE_CALLING_TONE | DSP_CAI_MODEM_DISABLE_ANSWER_TONE;
+ dbug(1, dprintf("MDM: Disable ring tone"));
+ }
+
+ if (GET_WORD (mdm_cfg[4].info) & MDM_CAPI_GUARD_1800)
+ {
+ cai[8] |= DSP_CAI_MODEM_GUARD_TONE_1800HZ;
+ dbug(1, dprintf("MDM: 1800 guard tone"));
+ }
+ else if (GET_WORD (mdm_cfg[4].info) & MDM_CAPI_GUARD_550 )
+ {
+ cai[8] |= DSP_CAI_MODEM_GUARD_TONE_550HZ;
+ dbug(1, dprintf("MDM: 550 guard tone"));
+ }
+
+ if ((GET_WORD (mdm_cfg[5].info) & 0x00ff) == MDM_CAPI_NEG_V100)
+ {
+ cai[8] |= DSP_CAI_MODEM_NEGOTIATE_V100;
+ dbug(1, dprintf("MDM: V100"));
+ }
+ else if ((GET_WORD (mdm_cfg[5].info) & 0x00ff) == MDM_CAPI_NEG_MOD_CLASS)
+ {
+ cai[8] |= DSP_CAI_MODEM_NEGOTIATE_IN_CLASS;
+ dbug(1, dprintf("MDM: IN CLASS"));
+ }
+ else if ((GET_WORD (mdm_cfg[5].info) & 0x00ff) == MDM_CAPI_NEG_DISABLED)
+ {
+ cai[8] |= DSP_CAI_MODEM_NEGOTIATE_DISABLED;
+ dbug(1, dprintf("MDM: DISABLED"));
+ }
+ cai[0] = 20;
+
+ if ((plci->adapter->man_profile.private_options & (1L << PRIVATE_V18))
+ && (GET_WORD(mdm_cfg[5].info) & 0x8000)) /* Private V.18 enable */
+ {
+ plci->requested_options |= 1L << PRIVATE_V18;
+ }
+ if (GET_WORD(mdm_cfg[5].info) & 0x4000) /* Private VOWN enable */
+ plci->requested_options |= 1L << PRIVATE_VOWN;
+
+ if ((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[plci->appl->Id-1])
+ & ((1L << PRIVATE_V18) | (1L << PRIVATE_VOWN)))
+ {
+ if (!api_parse(&bp_parms[3].info[1],(word)bp_parms[3].length,"wwwwwws", mdm_cfg))
+ {
+ i = 27;
+ if (mdm_cfg[6].length >= 4)
+ {
+ d = GET_DWORD(&mdm_cfg[6].info[1]);
+ cai[7] |= (byte) d; /* line taking options */
+ cai[9] |= (byte)(d >> 8); /* modulation options */
+ cai[++i] = (byte)(d >> 16); /* vown modulation options */
+ cai[++i] = (byte)(d >> 24);
+ if (mdm_cfg[6].length >= 8)
+ {
+ d = GET_DWORD(&mdm_cfg[6].info[5]);
+ cai[10] |= (byte) d; /* disabled modulations mask */
+ cai[11] |= (byte)(d >> 8);
+ if (mdm_cfg[6].length >= 12)
+ {
+ d = GET_DWORD(&mdm_cfg[6].info[9]);
+ cai[12] = (byte) d; /* enabled modulations mask */
+ cai[++i] = (byte)(d >> 8); /* vown enabled modulations */
+ cai[++i] = (byte)(d >> 16);
+ cai[++i] = (byte)(d >> 24);
+ cai[++i] = 0;
+ if (mdm_cfg[6].length >= 14)
+ {
+ w = GET_WORD(&mdm_cfg[6].info[13]);
+ if (w != 0)
+ PUT_WORD(&cai[13], w); /* min tx speed */
+ if (mdm_cfg[6].length >= 16)
+ {
+ w = GET_WORD(&mdm_cfg[6].info[15]);
+ if (w != 0)
+ PUT_WORD(&cai[15], w); /* max tx speed */
+ if (mdm_cfg[6].length >= 18)
+ {
+ w = GET_WORD(&mdm_cfg[6].info[17]);
+ if (w != 0)
+ PUT_WORD(&cai[17], w); /* min rx speed */
+ if (mdm_cfg[6].length >= 20)
+ {
+ w = GET_WORD(&mdm_cfg[6].info[19]);
+ if (w != 0)
+ PUT_WORD(&cai[19], w); /* max rx speed */
+ if (mdm_cfg[6].length >= 22)
+ {
+ w = GET_WORD(&mdm_cfg[6].info[21]);
+ cai[23] = (byte)(-((short) w)); /* transmit level */
+ if (mdm_cfg[6].length >= 24)
+ {
+ w = GET_WORD(&mdm_cfg[6].info[23]);
+ cai[22] |= (byte) w; /* info options mask */
+ cai[21] |= (byte)(w >> 8); /* disabled symbol rates */
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ cai[27] = i - 27;
+ i++;
+ if (!api_parse(&bp_parms[3].info[1],(word)bp_parms[3].length,"wwwwwwss", mdm_cfg))
+ {
+ if (!api_parse(&mdm_cfg[7].info[1],(word)mdm_cfg[7].length,"sss", mdm_cfg_v18))
+ {
+ for (n = 0; n < 3; n++)
+ {
+ cai[i] = (byte)(mdm_cfg_v18[n].length);
+ for (j = 1; j < ((word)(cai[i] + 1)); j++)
+ cai[i+j] = mdm_cfg_v18[n].info[j];
+ i += cai[i] + 1;
+ }
+ }
+ }
+ cai[0] = (byte)(i - 1);
+ }
+ }
+
+ }
+ }
+ if(GET_WORD(bp_parms[0].info)==2 || /* V.110 async */
+ GET_WORD(bp_parms[0].info)==3 ) /* V.110 sync */
+ {
+ if(bp_parms[3].length){
+ dbug(1,dprintf("V.110,%d",GET_WORD(&bp_parms[3].info[1])));
+ switch(GET_WORD(&bp_parms[3].info[1])){ /* Rate */
+ case 0:
+ case 56000:
+ if(GET_WORD(bp_parms[0].info)==3){ /* V.110 sync 56k */
+ dbug(1,dprintf("56k sync HSCX"));
+ cai[1] = 8;
+ cai[2] = 0;
+ cai[3] = 0;
+ }
+ else if(GET_WORD(bp_parms[0].info)==2){
+ dbug(1,dprintf("56k async DSP"));
+ cai[2] = 9;
+ }
+ break;
+ case 50: cai[2] = 1; break;
+ case 75: cai[2] = 1; break;
+ case 110: cai[2] = 1; break;
+ case 150: cai[2] = 1; break;
+ case 200: cai[2] = 1; break;
+ case 300: cai[2] = 1; break;
+ case 600: cai[2] = 1; break;
+ case 1200: cai[2] = 2; break;
+ case 2400: cai[2] = 3; break;
+ case 4800: cai[2] = 4; break;
+ case 7200: cai[2] = 10; break;
+ case 9600: cai[2] = 5; break;
+ case 12000: cai[2] = 13; break;
+ case 24000: cai[2] = 0; break;
+ case 14400: cai[2] = 11; break;
+ case 19200: cai[2] = 6; break;
+ case 28800: cai[2] = 12; break;
+ case 38400: cai[2] = 7; break;
+ case 48000: cai[2] = 8; break;
+ case 76: cai[2] = 15; break; /* 75/1200 */
+ case 1201: cai[2] = 14; break; /* 1200/75 */
+ case 56001: cai[2] = 9; break; /* V.110 56000 */
+
+ default:
+ return _B1_PARM_NOT_SUPPORTED;
+ }
+ cai[3] = 0;
+ if (cai[1] == 13) /* v.110 async */
+ {
+ if (bp_parms[3].length >= 8)
+ {
+ switch (GET_WORD (&bp_parms[3].info[3]))
+ { /* char length */
+ case 5:
+ cai[3] |= DSP_CAI_ASYNC_CHAR_LENGTH_5;
+ break;
+ case 6:
+ cai[3] |= DSP_CAI_ASYNC_CHAR_LENGTH_6;
+ break;
+ case 7:
+ cai[3] |= DSP_CAI_ASYNC_CHAR_LENGTH_7;
+ break;
+ }
+ switch (GET_WORD (&bp_parms[3].info[5]))
+ { /* Parity */
+ case 1: /* odd parity */
+ cai[3] |= (DSP_CAI_ASYNC_PARITY_ENABLE | DSP_CAI_ASYNC_PARITY_ODD);
+ break;
+ case 2: /* even parity */
+ cai[3] |= (DSP_CAI_ASYNC_PARITY_ENABLE | DSP_CAI_ASYNC_PARITY_EVEN);
+ break;
+ }
+ switch (GET_WORD (&bp_parms[3].info[7]))
+ { /* stop bits */
+ case 1: /* 2 stop bits */
+ cai[3] |= DSP_CAI_ASYNC_TWO_STOP_BITS;
+ break;
+ }
+ }
+ }
+ }
+ else if(cai[1]==8 || GET_WORD(bp_parms[0].info)==3 ){
+ dbug(1,dprintf("V.110 default 56k sync"));
+ cai[1] = 8;
+ cai[2] = 0;
+ cai[3] = 0;
+ }
+ else {
+ dbug(1,dprintf("V.110 default 9600 async"));
+ cai[2] = 5;
+ }
+ }
+ PUT_WORD(&cai[5],plci->appl->MaxDataLength);
+ dbug(1,dprintf("CAI[%d]=%x,%x,%x,%x,%x,%x", cai[0], cai[1], cai[2], cai[3], cai[4], cai[5], cai[6]));
+/* HexDump ("CAI", sizeof(cai), &cai[0]); */
+
+ add_p(plci, CAI, cai);
+ return 0;
+}
+
+/*------------------------------------------------------------------*/
+/* put parameter for b2 and B3 protocol in the parameter buffer */
+/*------------------------------------------------------------------*/
+
+word add_b23(PLCI * plci, API_PARSE * bp)
+{
+ word i, fax_control_bits;
+ byte pos, len;
+ byte SAPI = 0x40; /* default SAPI 16 for x.31 */
+ API_PARSE bp_parms[8];
+ API_PARSE * b1_config;
+ API_PARSE * b2_config;
+ API_PARSE b2_config_parms[8];
+ API_PARSE * b3_config;
+ API_PARSE b3_config_parms[6];
+ API_PARSE global_config[2];
+
+ static byte llc[3] = {2,0,0};
+ static byte dlc[20] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+ static byte nlc[256];
+ static byte lli[12] = {1,1};
+
+ const byte llc2_out[] = {1,2,4,6,2,0,0,0, X75_V42BIS,V120_L2,V120_V42BIS,V120_L2,6};
+ const byte llc2_in[] = {1,3,4,6,3,0,0,0, X75_V42BIS,V120_L2,V120_V42BIS,V120_L2,6};
+
+ const byte llc3[] = {4,3,2,2,6,6,0};
+ const byte header[] = {0,2,3,3,0,0,0};
+
+ for(i=0;i<8;i++) bp_parms[i].length = 0;
+ for(i=0;i<6;i++) b2_config_parms[i].length = 0;
+ for(i=0;i<5;i++) b3_config_parms[i].length = 0;
+
+ lli[0] = 1;
+ lli[1] = 1;
+ if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_XONOFF_FLOW_CONTROL)
+ lli[1] |= 2;
+ if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_OOB_CHANNEL)
+ lli[1] |= 4;
+
+ if ((lli[1] & 0x02) && (diva_xdi_extended_features & DIVA_CAPI_USE_CMA)) {
+ lli[1] |= 0x10;
+ if (plci->rx_dma_descriptor <= 0) {
+ plci->rx_dma_descriptor=diva_get_dma_descriptor(plci,&plci->rx_dma_magic);
+ if (plci->rx_dma_descriptor >= 0)
+ plci->rx_dma_descriptor++;
+ }
+ if (plci->rx_dma_descriptor > 0) {
+ lli[0] = 6;
+ lli[1] |= 0x40;
+ lli[2] = (byte)(plci->rx_dma_descriptor - 1);
+ lli[3] = (byte)plci->rx_dma_magic;
+ lli[4] = (byte)(plci->rx_dma_magic >> 8);
+ lli[5] = (byte)(plci->rx_dma_magic >> 16);
+ lli[6] = (byte)(plci->rx_dma_magic >> 24);
+ }
+ }
+
+ if (DIVA_CAPI_SUPPORTS_NO_CANCEL(plci->adapter)) {
+ lli[1] |= 0x20;
+ }
+
+ dbug(1,dprintf("add_b23"));
+ api_save_msg(bp, "s", &plci->B_protocol);
+
+ if(!bp->length && plci->tel)
+ {
+ plci->adv_nl = true;
+ dbug(1,dprintf("Default adv.Nl"));
+ add_p(plci,LLI,lli);
+ plci->B2_prot = 1 /*XPARENT*/;
+ plci->B3_prot = 0 /*XPARENT*/;
+ llc[1] = 2;
+ llc[2] = 4;
+ add_p(plci, LLC, llc);
+ dlc[0] = 2;
+ PUT_WORD(&dlc[1],plci->appl->MaxDataLength);
+ add_p(plci, DLC, dlc);
+ return 0;
+ }
+
+ if(!bp->length) /*default*/
+ {
+ dbug(1,dprintf("ret default"));
+ add_p(plci,LLI,lli);
+ plci->B2_prot = 0 /*X.75 */;
+ plci->B3_prot = 0 /*XPARENT*/;
+ llc[1] = 1;
+ llc[2] = 4;
+ add_p(plci, LLC, llc);
+ dlc[0] = 2;
+ PUT_WORD(&dlc[1],plci->appl->MaxDataLength);
+ add_p(plci, DLC, dlc);
+ return 0;
+ }
+ dbug(1,dprintf("b_prot_len=%d",(word)bp->length));
+ if((word)bp->length > 256) return _WRONG_MESSAGE_FORMAT;
+
+ if(api_parse(&bp->info[1], (word)bp->length, "wwwsssb", bp_parms))
+ {
+ bp_parms[6].length = 0;
+ if(api_parse(&bp->info[1], (word)bp->length, "wwwsss", bp_parms))
+ {
+ dbug(1,dprintf("b-form.!"));
+ return _WRONG_MESSAGE_FORMAT;
+ }
+ }
+ else if (api_parse(&bp->info[1], (word)bp->length, "wwwssss", bp_parms))
+ {
+ dbug(1,dprintf("b-form.!"));
+ return _WRONG_MESSAGE_FORMAT;
+ }
+
+ if(plci->tel==ADV_VOICE) /* transparent B on advanced voice */
+ {
+ if(GET_WORD(bp_parms[1].info)!=1
+ || GET_WORD(bp_parms[2].info)!=0) return _B2_NOT_SUPPORTED;
+ plci->adv_nl = true;
+ }
+ else if(plci->tel) return _B2_NOT_SUPPORTED;
+
+
+ if ((GET_WORD(bp_parms[1].info) == B2_RTP)
+ && (GET_WORD(bp_parms[2].info) == B3_RTP)
+ && (plci->adapter->man_profile.private_options & (1L << PRIVATE_RTP)))
+ {
+ add_p(plci,LLI,lli);
+ plci->B2_prot = (byte) GET_WORD(bp_parms[1].info);
+ plci->B3_prot = (byte) GET_WORD(bp_parms[2].info);
+ llc[1] = (plci->call_dir & (CALL_DIR_ORIGINATE | CALL_DIR_FORCE_OUTG_NL)) ? 14 : 13;
+ llc[2] = 4;
+ add_p(plci, LLC, llc);
+ dlc[0] = 2;
+ PUT_WORD(&dlc[1],plci->appl->MaxDataLength);
+ dlc[3] = 3; /* Addr A */
+ dlc[4] = 1; /* Addr B */
+ dlc[5] = 7; /* modulo mode */
+ dlc[6] = 7; /* window size */
+ dlc[7] = 0; /* XID len Lo */
+ dlc[8] = 0; /* XID len Hi */
+ for (i = 0; i < bp_parms[4].length; i++)
+ dlc[9+i] = bp_parms[4].info[1+i];
+ dlc[0] = (byte)(8 + bp_parms[4].length);
+ add_p(plci, DLC, dlc);
+ for (i = 0; i < bp_parms[5].length; i++)
+ nlc[1+i] = bp_parms[5].info[1+i];
+ nlc[0] = (byte)(bp_parms[5].length);
+ add_p(plci, NLC, nlc);
+ return 0;
+ }
+
+
+
+ if ((GET_WORD(bp_parms[1].info) >= 32)
+ || (!((1L << GET_WORD(bp_parms[1].info)) & plci->adapter->profile.B2_Protocols)
+ && ((GET_WORD(bp_parms[1].info) != B2_PIAFS)
+ || !(plci->adapter->man_profile.private_options & (1L << PRIVATE_PIAFS)))))
+
+ {
+ return _B2_NOT_SUPPORTED;
+ }
+ if ((GET_WORD(bp_parms[2].info) >= 32)
+ || !((1L << GET_WORD(bp_parms[2].info)) & plci->adapter->profile.B3_Protocols))
+ {
+ return _B3_NOT_SUPPORTED;
+ }
+ if ((GET_WORD(bp_parms[1].info) != B2_SDLC)
+ && ((GET_WORD(bp_parms[0].info) == B1_MODEM_ALL_NEGOTIATE)
+ || (GET_WORD(bp_parms[0].info) == B1_MODEM_ASYNC)
+ || (GET_WORD(bp_parms[0].info) == B1_MODEM_SYNC_HDLC)))
+ {
+ return (add_modem_b23 (plci, bp_parms));
+ }
+
+ add_p(plci,LLI,lli);
+
+ plci->B2_prot = (byte) GET_WORD(bp_parms[1].info);
+ plci->B3_prot = (byte) GET_WORD(bp_parms[2].info);
+ if(plci->B2_prot==12) SAPI = 0; /* default SAPI D-channel */
+
+ if(bp_parms[6].length)
+ {
+ if(api_parse(&bp_parms[6].info[1], (word)bp_parms[6].length, "w", global_config))
+ {
+ return _WRONG_MESSAGE_FORMAT;
+ }
+ switch(GET_WORD(global_config[0].info))
+ {
+ case 1:
+ plci->call_dir = (plci->call_dir & ~CALL_DIR_ANSWER) | CALL_DIR_ORIGINATE;
+ break;
+ case 2:
+ plci->call_dir = (plci->call_dir & ~CALL_DIR_ORIGINATE) | CALL_DIR_ANSWER;
+ break;
+ }
+ }
+ dbug(1,dprintf("call_dir=%04x", plci->call_dir));
+
+
+ if (plci->B2_prot == B2_PIAFS)
+ llc[1] = PIAFS_CRC;
+ else
+/* IMPLEMENT_PIAFS */
+ {
+ llc[1] = (plci->call_dir & (CALL_DIR_ORIGINATE | CALL_DIR_FORCE_OUTG_NL)) ?
+ llc2_out[GET_WORD(bp_parms[1].info)] : llc2_in[GET_WORD(bp_parms[1].info)];
+ }
+ llc[2] = llc3[GET_WORD(bp_parms[2].info)];
+
+ add_p(plci, LLC, llc);
+
+ dlc[0] = 2;
+ PUT_WORD(&dlc[1], plci->appl->MaxDataLength +
+ header[GET_WORD(bp_parms[2].info)]);
+
+ b1_config = &bp_parms[3];
+ nlc[0] = 0;
+ if(plci->B3_prot == 4
+ || plci->B3_prot == 5)
+ {
+ for (i=0;i<sizeof(T30_INFO);i++) nlc[i] = 0;
+ nlc[0] = sizeof(T30_INFO);
+ if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_FAX_PAPER_FORMATS)
+ ((T30_INFO *)&nlc[1])->operating_mode = T30_OPERATING_MODE_CAPI;
+ ((T30_INFO *)&nlc[1])->rate_div_2400 = 0xff;
+ if(b1_config->length>=2)
+ {
+ ((T30_INFO *)&nlc[1])->rate_div_2400 = (byte)(GET_WORD(&b1_config->info[1])/2400);
+ }
+ }
+ b2_config = &bp_parms[4];
+
+
+ if (llc[1] == PIAFS_CRC)
+ {
+ if (plci->B3_prot != B3_TRANSPARENT)
+ {
+ return _B_STACK_NOT_SUPPORTED;
+ }
+ if(b2_config->length && api_parse(&b2_config->info[1], (word)b2_config->length, "bwww", b2_config_parms)) {
+ return _WRONG_MESSAGE_FORMAT;
+ }
+ PUT_WORD(&dlc[1],plci->appl->MaxDataLength);
+ dlc[3] = 0; /* Addr A */
+ dlc[4] = 0; /* Addr B */
+ dlc[5] = 0; /* modulo mode */
+ dlc[6] = 0; /* window size */
+ if (b2_config->length >= 7){
+ dlc[ 7] = 7;
+ dlc[ 8] = 0;
+ dlc[ 9] = b2_config_parms[0].info[0]; /* PIAFS protocol Speed configuration */
+ dlc[10] = b2_config_parms[1].info[0]; /* V.42bis P0 */
+ dlc[11] = b2_config_parms[1].info[1]; /* V.42bis P0 */
+ dlc[12] = b2_config_parms[2].info[0]; /* V.42bis P1 */
+ dlc[13] = b2_config_parms[2].info[1]; /* V.42bis P1 */
+ dlc[14] = b2_config_parms[3].info[0]; /* V.42bis P2 */
+ dlc[15] = b2_config_parms[3].info[1]; /* V.42bis P2 */
+ dlc[ 0] = 15;
+ if(b2_config->length >= 8) { /* PIAFS control abilities */
+ dlc[ 7] = 10;
+ dlc[16] = 2; /* Length of PIAFS extention */
+ dlc[17] = PIAFS_UDATA_ABILITIES; /* control (UDATA) ability */
+ dlc[18] = b2_config_parms[4].info[0]; /* value */
+ dlc[ 0] = 18;
+ }
+ }
+ else /* default values, 64K, variable, no compression */
+ {
+ dlc[ 7] = 7;
+ dlc[ 8] = 0;
+ dlc[ 9] = 0x03; /* PIAFS protocol Speed configuration */
+ dlc[10] = 0x03; /* V.42bis P0 */
+ dlc[11] = 0; /* V.42bis P0 */
+ dlc[12] = 0; /* V.42bis P1 */
+ dlc[13] = 0; /* V.42bis P1 */
+ dlc[14] = 0; /* V.42bis P2 */
+ dlc[15] = 0; /* V.42bis P2 */
+ dlc[ 0] = 15;
+ }
+ add_p(plci, DLC, dlc);
+ }
+ else
+
+ if ((llc[1] == V120_L2) || (llc[1] == V120_V42BIS))
+ {
+ if (plci->B3_prot != B3_TRANSPARENT)
+ return _B_STACK_NOT_SUPPORTED;
+
+ dlc[0] = 6;
+ PUT_WORD (&dlc[1], GET_WORD (&dlc[1]) + 2);
+ dlc[3] = 0x08;
+ dlc[4] = 0x01;
+ dlc[5] = 127;
+ dlc[6] = 7;
+ if (b2_config->length != 0)
+ {
+ if((llc[1]==V120_V42BIS) && api_parse(&b2_config->info[1], (word)b2_config->length, "bbbbwww", b2_config_parms)) {
+ return _WRONG_MESSAGE_FORMAT;
+ }
+ dlc[3] = (byte)((b2_config->info[2] << 3) | ((b2_config->info[1] >> 5) & 0x04));
+ dlc[4] = (byte)((b2_config->info[1] << 1) | 0x01);
+ if (b2_config->info[3] != 128)
+ {
+ dbug(1,dprintf("1D-dlc= %x %x %x %x %x", dlc[0], dlc[1], dlc[2], dlc[3], dlc[4]));
+ return _B2_PARM_NOT_SUPPORTED;
+ }
+ dlc[5] = (byte)(b2_config->info[3] - 1);
+ dlc[6] = b2_config->info[4];
+ if(llc[1]==V120_V42BIS){
+ if (b2_config->length >= 10){
+ dlc[ 7] = 6;
+ dlc[ 8] = 0;
+ dlc[ 9] = b2_config_parms[4].info[0];
+ dlc[10] = b2_config_parms[4].info[1];
+ dlc[11] = b2_config_parms[5].info[0];
+ dlc[12] = b2_config_parms[5].info[1];
+ dlc[13] = b2_config_parms[6].info[0];
+ dlc[14] = b2_config_parms[6].info[1];
+ dlc[ 0] = 14;
+ dbug(1,dprintf("b2_config_parms[4].info[0] [1]: %x %x", b2_config_parms[4].info[0], b2_config_parms[4].info[1]));
+ dbug(1,dprintf("b2_config_parms[5].info[0] [1]: %x %x", b2_config_parms[5].info[0], b2_config_parms[5].info[1]));
+ dbug(1,dprintf("b2_config_parms[6].info[0] [1]: %x %x", b2_config_parms[6].info[0], b2_config_parms[6].info[1]));
+ }
+ else {
+ dlc[ 6] = 14;
+ }
+ }
+ }
+ }
+ else
+ {
+ if(b2_config->length)
+ {
+ dbug(1,dprintf("B2-Config"));
+ if(llc[1]==X75_V42BIS){
+ if(api_parse(&b2_config->info[1], (word)b2_config->length, "bbbbwww", b2_config_parms))
+ {
+ return _WRONG_MESSAGE_FORMAT;
+ }
+ }
+ else {
+ if(api_parse(&b2_config->info[1], (word)b2_config->length, "bbbbs", b2_config_parms))
+ {
+ return _WRONG_MESSAGE_FORMAT;
+ }
+ }
+ /* if B2 Protocol is LAPD, b2_config structure is different */
+ if(llc[1]==6)
+ {
+ dlc[0] = 4;
+ if(b2_config->length>=1) dlc[2] = b2_config->info[1]; /* TEI */
+ else dlc[2] = 0x01;
+ if( (b2_config->length>=2) && (plci->B2_prot==12) )
+ {
+ SAPI = b2_config->info[2]; /* SAPI */
+ }
+ dlc[1] = SAPI;
+ if( (b2_config->length>=3) && (b2_config->info[3]==128) )
+ {
+ dlc[3] = 127; /* Mode */
+ }
+ else
+ {
+ dlc[3] = 7; /* Mode */
+ }
+
+ if(b2_config->length>=4) dlc[4] = b2_config->info[4]; /* Window */
+ else dlc[4] = 1;
+ dbug(1,dprintf("D-dlc[%d]=%x,%x,%x,%x", dlc[0], dlc[1], dlc[2], dlc[3], dlc[4]));
+ if(b2_config->length>5) return _B2_PARM_NOT_SUPPORTED;
+ }
+ else
+ {
+ dlc[0] = (byte)(b2_config_parms[4].length+6);
+ dlc[3] = b2_config->info[1];
+ dlc[4] = b2_config->info[2];
+ if(b2_config->info[3]!=8 && b2_config->info[3]!=128){
+ dbug(1,dprintf("1D-dlc= %x %x %x %x %x", dlc[0], dlc[1], dlc[2], dlc[3], dlc[4]));
+ return _B2_PARM_NOT_SUPPORTED;
+ }
+
+ dlc[5] = (byte)(b2_config->info[3]-1);
+ dlc[6] = b2_config->info[4];
+ if(dlc[6]>dlc[5]){
+ dbug(1,dprintf("2D-dlc= %x %x %x %x %x %x %x", dlc[0], dlc[1], dlc[2], dlc[3], dlc[4], dlc[5], dlc[6]));
+ return _B2_PARM_NOT_SUPPORTED;
+ }
+
+ if(llc[1]==X75_V42BIS) {
+ if (b2_config->length >= 10){
+ dlc[ 7] = 6;
+ dlc[ 8] = 0;
+ dlc[ 9] = b2_config_parms[4].info[0];
+ dlc[10] = b2_config_parms[4].info[1];
+ dlc[11] = b2_config_parms[5].info[0];
+ dlc[12] = b2_config_parms[5].info[1];
+ dlc[13] = b2_config_parms[6].info[0];
+ dlc[14] = b2_config_parms[6].info[1];
+ dlc[ 0] = 14;
+ dbug(1,dprintf("b2_config_parms[4].info[0] [1]: %x %x", b2_config_parms[4].info[0], b2_config_parms[4].info[1]));
+ dbug(1,dprintf("b2_config_parms[5].info[0] [1]: %x %x", b2_config_parms[5].info[0], b2_config_parms[5].info[1]));
+ dbug(1,dprintf("b2_config_parms[6].info[0] [1]: %x %x", b2_config_parms[6].info[0], b2_config_parms[6].info[1]));
+ }
+ else {
+ dlc[ 6] = 14;
+ }
+
+ }
+ else {
+ PUT_WORD(&dlc[7], (word)b2_config_parms[4].length);
+ for(i=0; i<b2_config_parms[4].length; i++)
+ dlc[11+i] = b2_config_parms[4].info[1+i];
+ }
+ }
+ }
+ }
+ add_p(plci, DLC, dlc);
+
+ b3_config = &bp_parms[5];
+ if(b3_config->length)
+ {
+ if(plci->B3_prot == 4
+ || plci->B3_prot == 5)
+ {
+ if(api_parse(&b3_config->info[1], (word)b3_config->length, "wwss", b3_config_parms))
+ {
+ return _WRONG_MESSAGE_FORMAT;
+ }
+ i = GET_WORD((byte *)(b3_config_parms[0].info));
+ ((T30_INFO *)&nlc[1])->resolution = (byte)(((i & 0x0001) ||
+ ((plci->B3_prot == 4) && (((byte)(GET_WORD((byte *)b3_config_parms[1].info))) != 5))) ? T30_RESOLUTION_R8_0770_OR_200 : 0);
+ ((T30_INFO *)&nlc[1])->data_format = (byte)(GET_WORD((byte *)b3_config_parms[1].info));
+ fax_control_bits = T30_CONTROL_BIT_ALL_FEATURES;
+ if ((((T30_INFO *)&nlc[1])->rate_div_2400 != 0) && (((T30_INFO *)&nlc[1])->rate_div_2400 <= 6))
+ fax_control_bits &= ~T30_CONTROL_BIT_ENABLE_V34FAX;
+ if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_FAX_PAPER_FORMATS)
+ {
+
+ if ((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[plci->appl->Id-1])
+ & (1L << PRIVATE_FAX_PAPER_FORMATS))
+ {
+ ((T30_INFO *)&nlc[1])->resolution |= T30_RESOLUTION_R8_1540 |
+ T30_RESOLUTION_R16_1540_OR_400 | T30_RESOLUTION_300_300 |
+ T30_RESOLUTION_INCH_BASED | T30_RESOLUTION_METRIC_BASED;
+ }
+
+ ((T30_INFO *)&nlc[1])->recording_properties =
+ T30_RECORDING_WIDTH_ISO_A3 |
+ (T30_RECORDING_LENGTH_UNLIMITED << 2) |
+ (T30_MIN_SCANLINE_TIME_00_00_00 << 4);
+ }
+ if(plci->B3_prot == 5)
+ {
+ if (i & 0x0002) /* Accept incoming fax-polling requests */
+ fax_control_bits |= T30_CONTROL_BIT_ACCEPT_POLLING;
+ if (i & 0x2000) /* Do not use MR compression */
+ fax_control_bits &= ~T30_CONTROL_BIT_ENABLE_2D_CODING;
+ if (i & 0x4000) /* Do not use MMR compression */
+ fax_control_bits &= ~T30_CONTROL_BIT_ENABLE_T6_CODING;
+ if (i & 0x8000) /* Do not use ECM */
+ fax_control_bits &= ~T30_CONTROL_BIT_ENABLE_ECM;
+ if (plci->fax_connect_info_length != 0)
+ {
+ ((T30_INFO *)&nlc[1])->resolution = ((T30_INFO *)plci->fax_connect_info_buffer)->resolution;
+ ((T30_INFO *)&nlc[1])->data_format = ((T30_INFO *)plci->fax_connect_info_buffer)->data_format;
+ ((T30_INFO *)&nlc[1])->recording_properties = ((T30_INFO *)plci->fax_connect_info_buffer)->recording_properties;
+ fax_control_bits |= GET_WORD(&((T30_INFO *)plci->fax_connect_info_buffer)->control_bits_low) &
+ (T30_CONTROL_BIT_REQUEST_POLLING | T30_CONTROL_BIT_MORE_DOCUMENTS);
+ }
+ }
+ /* copy station id to NLC */
+ for(i=0; i<20; i++)
+ {
+ if(i<b3_config_parms[2].length)
+ {
+ ((T30_INFO *)&nlc[1])->station_id[i] = ((byte *)b3_config_parms[2].info)[1+i];
+ }
+ else
+ {
+ ((T30_INFO *)&nlc[1])->station_id[i] = ' ';
+ }
+ }
+ ((T30_INFO *)&nlc[1])->station_id_len = 20;
+ /* copy head line to NLC */
+ if(b3_config_parms[3].length)
+ {
+
+ pos = (byte)(fax_head_line_time (&(((T30_INFO *)&nlc[1])->station_id[20])));
+ if (pos != 0)
+ {
+ if (CAPI_MAX_DATE_TIME_LENGTH + 2 + b3_config_parms[3].length > CAPI_MAX_HEAD_LINE_SPACE)
+ pos = 0;
+ else
+ {
+ ((T30_INFO *)&nlc[1])->station_id[20 + pos++] = ' ';
+ ((T30_INFO *)&nlc[1])->station_id[20 + pos++] = ' ';
+ len = (byte)b3_config_parms[2].length;
+ if (len > 20)
+ len = 20;
+ if (CAPI_MAX_DATE_TIME_LENGTH + 2 + len + 2 + b3_config_parms[3].length <= CAPI_MAX_HEAD_LINE_SPACE)
+ {
+ for (i = 0; i < len; i++)
+ ((T30_INFO *)&nlc[1])->station_id[20 + pos++] = ((byte *)b3_config_parms[2].info)[1+i];
+ ((T30_INFO *)&nlc[1])->station_id[20 + pos++] = ' ';
+ ((T30_INFO *)&nlc[1])->station_id[20 + pos++] = ' ';
+ }
+ }
+ }
+
+ len = (byte)b3_config_parms[3].length;
+ if (len > CAPI_MAX_HEAD_LINE_SPACE - pos)
+ len = (byte)(CAPI_MAX_HEAD_LINE_SPACE - pos);
+ ((T30_INFO *)&nlc[1])->head_line_len = (byte)(pos + len);
+ nlc[0] += (byte)(pos + len);
+ for (i = 0; i < len; i++)
+ ((T30_INFO *)&nlc[1])->station_id[20 + pos++] = ((byte *)b3_config_parms[3].info)[1+i];
+ }
+ else
+ ((T30_INFO *)&nlc[1])->head_line_len = 0;
+
+ plci->nsf_control_bits = 0;
+ if(plci->B3_prot == 5)
+ {
+ if ((plci->adapter->man_profile.private_options & (1L << PRIVATE_FAX_SUB_SEP_PWD))
+ && (GET_WORD((byte *)b3_config_parms[1].info) & 0x8000)) /* Private SUB/SEP/PWD enable */
+ {
+ plci->requested_options |= 1L << PRIVATE_FAX_SUB_SEP_PWD;
+ }
+ if ((plci->adapter->man_profile.private_options & (1L << PRIVATE_FAX_NONSTANDARD))
+ && (GET_WORD((byte *)b3_config_parms[1].info) & 0x4000)) /* Private non-standard facilities enable */
+ {
+ plci->requested_options |= 1L << PRIVATE_FAX_NONSTANDARD;
+ }
+ if ((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[plci->appl->Id-1])
+ & ((1L << PRIVATE_FAX_SUB_SEP_PWD) | (1L << PRIVATE_FAX_NONSTANDARD)))
+ {
+ if ((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[plci->appl->Id-1])
+ & (1L << PRIVATE_FAX_SUB_SEP_PWD))
+ {
+ fax_control_bits |= T30_CONTROL_BIT_ACCEPT_SUBADDRESS | T30_CONTROL_BIT_ACCEPT_PASSWORD;
+ if (fax_control_bits & T30_CONTROL_BIT_ACCEPT_POLLING)
+ fax_control_bits |= T30_CONTROL_BIT_ACCEPT_SEL_POLLING;
+ }
+ len = nlc[0];
+ pos = ((byte)(((T30_INFO *) 0)->station_id + 20));
+ if (pos < plci->fax_connect_info_length)
+ {
+ for (i = 1 + plci->fax_connect_info_buffer[pos]; i != 0; i--)
+ nlc[++len] = plci->fax_connect_info_buffer[pos++];
+ }
+ else
+ nlc[++len] = 0;
+ if (pos < plci->fax_connect_info_length)
+ {
+ for (i = 1 + plci->fax_connect_info_buffer[pos]; i != 0; i--)
+ nlc[++len] = plci->fax_connect_info_buffer[pos++];
+ }
+ else
+ nlc[++len] = 0;
+ if ((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[plci->appl->Id-1])
+ & (1L << PRIVATE_FAX_NONSTANDARD))
+ {
+ if ((pos < plci->fax_connect_info_length) && (plci->fax_connect_info_buffer[pos] != 0))
+ {
+ if ((plci->fax_connect_info_buffer[pos] >= 3) && (plci->fax_connect_info_buffer[pos+1] >= 2))
+ plci->nsf_control_bits = GET_WORD(&plci->fax_connect_info_buffer[pos+2]);
+ for (i = 1 + plci->fax_connect_info_buffer[pos]; i != 0; i--)
+ nlc[++len] = plci->fax_connect_info_buffer[pos++];
+ }
+ else
+ {
+ if(api_parse(&b3_config->info[1], (word)b3_config->length, "wwsss", b3_config_parms))
+ {
+ dbug(1,dprintf("non-standard facilities info missing or wrong format"));
+ nlc[++len] = 0;
+ }
+ else
+ {
+ if ((b3_config_parms[4].length >= 3) && (b3_config_parms[4].info[1] >= 2))
+ plci->nsf_control_bits = GET_WORD(&b3_config_parms[4].info[2]);
+ nlc[++len] = (byte)(b3_config_parms[4].length);
+ for (i = 0; i < b3_config_parms[4].length; i++)
+ nlc[++len] = b3_config_parms[4].info[1+i];
+ }
+ }
+ }
+ nlc[0] = len;
+ if ((plci->nsf_control_bits & T30_NSF_CONTROL_BIT_ENABLE_NSF)
+ && (plci->nsf_control_bits & T30_NSF_CONTROL_BIT_NEGOTIATE_RESP))
+ {
+ ((T30_INFO *)&nlc[1])->operating_mode = T30_OPERATING_MODE_CAPI_NEG;
+ }
+ }
+ }
+
+ PUT_WORD(&(((T30_INFO *)&nlc[1])->control_bits_low), fax_control_bits);
+ len = ((byte)(((T30_INFO *) 0)->station_id + 20));
+ for (i = 0; i < len; i++)
+ plci->fax_connect_info_buffer[i] = nlc[1+i];
+ ((T30_INFO *) plci->fax_connect_info_buffer)->head_line_len = 0;
+ i += ((T30_INFO *)&nlc[1])->head_line_len;
+ while (i < nlc[0])
+ plci->fax_connect_info_buffer[len++] = nlc[++i];
+ plci->fax_connect_info_length = len;
+ }
+ else
+ {
+ nlc[0] = 14;
+ if(b3_config->length!=16)
+ return _B3_PARM_NOT_SUPPORTED;
+ for(i=0; i<12; i++) nlc[1+i] = b3_config->info[1+i];
+ if(GET_WORD(&b3_config->info[13])!=8 && GET_WORD(&b3_config->info[13])!=128)
+ return _B3_PARM_NOT_SUPPORTED;
+ nlc[13] = b3_config->info[13];
+ if(GET_WORD(&b3_config->info[15])>=nlc[13])
+ return _B3_PARM_NOT_SUPPORTED;
+ nlc[14] = b3_config->info[15];
+ }
+ }
+ else
+ {
+ if (plci->B3_prot == 4
+ || plci->B3_prot == 5 /*T.30 - FAX*/ ) return _B3_PARM_NOT_SUPPORTED;
+ }
+ add_p(plci, NLC, nlc);
+ return 0;
+}
+
+/*----------------------------------------------------------------*/
+/* make the same as add_b23, but only for the modem related */
+/* L2 and L3 B-Chan protocol. */
+/* */
+/* Enabled L2 and L3 Configurations: */
+/* If L1 == Modem all negotiation */
+/* only L2 == Modem with full negotiation is allowed */
+/* If L1 == Modem async or sync */
+/* only L2 == Transparent is allowed */
+/* L3 == Modem or L3 == Transparent are allowed */
+/* B2 Configuration for modem: */
+/* word : enable/disable compression, bitoptions */
+/* B3 Configuration for modem: */
+/* empty */
+/*----------------------------------------------------------------*/
+static word add_modem_b23 (PLCI * plci, API_PARSE* bp_parms)
+{
+ static byte lli[12] = {1,1};
+ static byte llc[3] = {2,0,0};
+ static byte dlc[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+ API_PARSE mdm_config[2];
+ word i;
+ word b2_config = 0;
+
+ for(i=0;i<2;i++) mdm_config[i].length = 0;
+ for(i=0;i<sizeof(dlc);i++) dlc[i] = 0;
+
+ if (((GET_WORD(bp_parms[0].info) == B1_MODEM_ALL_NEGOTIATE)
+ && (GET_WORD(bp_parms[1].info) != B2_MODEM_EC_COMPRESSION))
+ || ((GET_WORD(bp_parms[0].info) != B1_MODEM_ALL_NEGOTIATE)
+ && (GET_WORD(bp_parms[1].info) != B2_TRANSPARENT)))
+ {
+ return (_B_STACK_NOT_SUPPORTED);
+ }
+ if ((GET_WORD(bp_parms[2].info) != B3_MODEM)
+ && (GET_WORD(bp_parms[2].info) != B3_TRANSPARENT))
+ {
+ return (_B_STACK_NOT_SUPPORTED);
+ }
+
+ plci->B2_prot = (byte) GET_WORD(bp_parms[1].info);
+ plci->B3_prot = (byte) GET_WORD(bp_parms[2].info);
+
+ if ((GET_WORD(bp_parms[1].info) == B2_MODEM_EC_COMPRESSION) && bp_parms[4].length)
+ {
+ if (api_parse (&bp_parms[4].info[1],
+ (word)bp_parms[4].length, "w",
+ mdm_config))
+ {
+ return (_WRONG_MESSAGE_FORMAT);
+ }
+ b2_config = GET_WORD(mdm_config[0].info);
+ }
+
+ /* OK, L2 is modem */
+
+ lli[0] = 1;
+ lli[1] = 1;
+ if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_XONOFF_FLOW_CONTROL)
+ lli[1] |= 2;
+ if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_OOB_CHANNEL)
+ lli[1] |= 4;
+
+ if ((lli[1] & 0x02) && (diva_xdi_extended_features & DIVA_CAPI_USE_CMA)) {
+ lli[1] |= 0x10;
+ if (plci->rx_dma_descriptor <= 0) {
+ plci->rx_dma_descriptor=diva_get_dma_descriptor(plci,&plci->rx_dma_magic);
+ if (plci->rx_dma_descriptor >= 0)
+ plci->rx_dma_descriptor++;
+ }
+ if (plci->rx_dma_descriptor > 0) {
+ lli[1] |= 0x40;
+ lli[0] = 6;
+ lli[2] = (byte)(plci->rx_dma_descriptor - 1);
+ lli[3] = (byte)plci->rx_dma_magic;
+ lli[4] = (byte)(plci->rx_dma_magic >> 8);
+ lli[5] = (byte)(plci->rx_dma_magic >> 16);
+ lli[6] = (byte)(plci->rx_dma_magic >> 24);
+ }
+ }
+
+ if (DIVA_CAPI_SUPPORTS_NO_CANCEL(plci->adapter)) {
+ lli[1] |= 0x20;
+ }
+
+ llc[1] = (plci->call_dir & (CALL_DIR_ORIGINATE | CALL_DIR_FORCE_OUTG_NL)) ?
+ /*V42*/ 10 : /*V42_IN*/ 9;
+ llc[2] = 4; /* pass L3 always transparent */
+ add_p(plci, LLI, lli);
+ add_p(plci, LLC, llc);
+ i = 1;
+ PUT_WORD (&dlc[i], plci->appl->MaxDataLength);
+ i += 2;
+ if (GET_WORD(bp_parms[1].info) == B2_MODEM_EC_COMPRESSION)
+ {
+ if (bp_parms[4].length)
+ {
+ dbug(1, dprintf("MDM b2_config=%02x", b2_config));
+ dlc[i++] = 3; /* Addr A */
+ dlc[i++] = 1; /* Addr B */
+ dlc[i++] = 7; /* modulo mode */
+ dlc[i++] = 7; /* window size */
+ dlc[i++] = 0; /* XID len Lo */
+ dlc[i++] = 0; /* XID len Hi */
+
+ if (b2_config & MDM_B2_DISABLE_V42bis)
+ {
+ dlc[i] |= DLC_MODEMPROT_DISABLE_V42_V42BIS;
+ }
+ if (b2_config & MDM_B2_DISABLE_MNP)
+ {
+ dlc[i] |= DLC_MODEMPROT_DISABLE_MNP_MNP5;
+ }
+ if (b2_config & MDM_B2_DISABLE_TRANS)
+ {
+ dlc[i] |= DLC_MODEMPROT_REQUIRE_PROTOCOL;
+ }
+ if (b2_config & MDM_B2_DISABLE_V42)
+ {
+ dlc[i] |= DLC_MODEMPROT_DISABLE_V42_DETECT;
+ }
+ if (b2_config & MDM_B2_DISABLE_COMP)
+ {
+ dlc[i] |= DLC_MODEMPROT_DISABLE_COMPRESSION;
+ }
+ i++;
+ }
+ }
+ else
+ {
+ dlc[i++] = 3; /* Addr A */
+ dlc[i++] = 1; /* Addr B */
+ dlc[i++] = 7; /* modulo mode */
+ dlc[i++] = 7; /* window size */
+ dlc[i++] = 0; /* XID len Lo */
+ dlc[i++] = 0; /* XID len Hi */
+ dlc[i++] = DLC_MODEMPROT_DISABLE_V42_V42BIS |
+ DLC_MODEMPROT_DISABLE_MNP_MNP5 |
+ DLC_MODEMPROT_DISABLE_V42_DETECT |
+ DLC_MODEMPROT_DISABLE_COMPRESSION;
+ }
+ dlc[0] = (byte)(i - 1);
+/* HexDump ("DLC", sizeof(dlc), &dlc[0]); */
+ add_p(plci, DLC, dlc);
+ return (0);
+}
+
+
+/*------------------------------------------------------------------*/
+/* send a request for the signaling entity */
+/*------------------------------------------------------------------*/
+
+void sig_req(PLCI * plci, byte req, byte Id)
+{
+ if(!plci) return;
+ if(plci->adapter->adapter_disabled) return;
+ dbug(1,dprintf("sig_req(%x)",req));
+ if (req == REMOVE)
+ plci->sig_remove_id = plci->Sig.Id;
+ if(plci->req_in==plci->req_in_start) {
+ plci->req_in +=2;
+ plci->RBuffer[plci->req_in++] = 0;
+ }
+ PUT_WORD(&plci->RBuffer[plci->req_in_start], plci->req_in-plci->req_in_start-2);
+ plci->RBuffer[plci->req_in++] = Id; /* sig/nl flag */
+ plci->RBuffer[plci->req_in++] = req; /* request */
+ plci->RBuffer[plci->req_in++] = 0; /* channel */
+ plci->req_in_start = plci->req_in;
+}
+
+/*------------------------------------------------------------------*/
+/* send a request for the network layer entity */
+/*------------------------------------------------------------------*/
+
+void nl_req_ncci(PLCI * plci, byte req, byte ncci)
+{
+ if(!plci) return;
+ if(plci->adapter->adapter_disabled) return;
+ dbug(1,dprintf("nl_req %02x %02x %02x", plci->Id, req, ncci));
+ if (req == REMOVE)
+ {
+ plci->nl_remove_id = plci->NL.Id;
+ ncci_remove (plci, 0, (byte)(ncci != 0));
+ ncci = 0;
+ }
+ if(plci->req_in==plci->req_in_start) {
+ plci->req_in +=2;
+ plci->RBuffer[plci->req_in++] = 0;
+ }
+ PUT_WORD(&plci->RBuffer[plci->req_in_start], plci->req_in-plci->req_in_start-2);
+ plci->RBuffer[plci->req_in++] = 1; /* sig/nl flag */
+ plci->RBuffer[plci->req_in++] = req; /* request */
+ plci->RBuffer[plci->req_in++] = plci->adapter->ncci_ch[ncci]; /* channel */
+ plci->req_in_start = plci->req_in;
+}
+
+void send_req(PLCI * plci)
+{
+ ENTITY * e;
+ word l;
+/* word i; */
+
+ if(!plci) return;
+ if(plci->adapter->adapter_disabled) return;
+ channel_xmit_xon (plci);
+
+ /* if nothing to do, return */
+ if(plci->req_in==plci->req_out) return;
+ dbug(1,dprintf("send_req(in=%d,out=%d)",plci->req_in,plci->req_out));
+
+ if(plci->nl_req || plci->sig_req) return;
+
+ l = GET_WORD(&plci->RBuffer[plci->req_out]);
+ plci->req_out += 2;
+ plci->XData[0].P = &plci->RBuffer[plci->req_out];
+ plci->req_out += l;
+ if(plci->RBuffer[plci->req_out]==1)
+ {
+ e = &plci->NL;
+ plci->req_out++;
+ e->Req = plci->nl_req = plci->RBuffer[plci->req_out++];
+ e->ReqCh = plci->RBuffer[plci->req_out++];
+ if(!(e->Id & 0x1f))
+ {
+ e->Id = NL_ID;
+ plci->RBuffer[plci->req_out-4] = CAI;
+ plci->RBuffer[plci->req_out-3] = 1;
+ plci->RBuffer[plci->req_out-2] = (plci->Sig.Id==0xff) ? 0 : plci->Sig.Id;
+ plci->RBuffer[plci->req_out-1] = 0;
+ l+=3;
+ plci->nl_global_req = plci->nl_req;
+ }
+ dbug(1,dprintf("%x:NLREQ(%x:%x:%x)",plci->adapter->Id,e->Id,e->Req,e->ReqCh));
+ }
+ else
+ {
+ e = &plci->Sig;
+ if(plci->RBuffer[plci->req_out])
+ e->Id = plci->RBuffer[plci->req_out];
+ plci->req_out++;
+ e->Req = plci->sig_req = plci->RBuffer[plci->req_out++];
+ e->ReqCh = plci->RBuffer[plci->req_out++];
+ if(!(e->Id & 0x1f))
+ plci->sig_global_req = plci->sig_req;
+ dbug(1,dprintf("%x:SIGREQ(%x:%x:%x)",plci->adapter->Id,e->Id,e->Req,e->ReqCh));
+ }
+ plci->XData[0].PLength = l;
+ e->X = plci->XData;
+ plci->adapter->request(e);
+ dbug(1,dprintf("send_ok"));
+}
+
+void send_data(PLCI * plci)
+{
+ DIVA_CAPI_ADAPTER * a;
+ DATA_B3_DESC * data;
+ NCCI *ncci_ptr;
+ word ncci;
+
+ if (!plci->nl_req && plci->ncci_ring_list)
+ {
+ a = plci->adapter;
+ ncci = plci->ncci_ring_list;
+ do
+ {
+ ncci = a->ncci_next[ncci];
+ ncci_ptr = &(a->ncci[ncci]);
+ if (!(a->ncci_ch[ncci]
+ && (a->ch_flow_control[a->ncci_ch[ncci]] & N_OK_FC_PENDING)))
+ {
+ if (ncci_ptr->data_pending)
+ {
+ if ((a->ncci_state[ncci] == CONNECTED)
+ || (a->ncci_state[ncci] == INC_ACT_PENDING)
+ || (plci->send_disc == ncci))
+ {
+ data = &(ncci_ptr->DBuffer[ncci_ptr->data_out]);
+ if ((plci->B2_prot == B2_V120_ASYNC)
+ || (plci->B2_prot == B2_V120_ASYNC_V42BIS)
+ || (plci->B2_prot == B2_V120_BIT_TRANSPARENT))
+ {
+ plci->NData[1].P = TransmitBufferGet (plci->appl, data->P);
+ plci->NData[1].PLength = data->Length;
+ if (data->Flags & 0x10)
+ plci->NData[0].P = v120_break_header;
+ else
+ plci->NData[0].P = v120_default_header;
+ plci->NData[0].PLength = 1 ;
+ plci->NL.XNum = 2;
+ plci->NL.Req = plci->nl_req = (byte)((data->Flags&0x07)<<4 |N_DATA);
+ }
+ else
+ {
+ plci->NData[0].P = TransmitBufferGet (plci->appl, data->P);
+ plci->NData[0].PLength = data->Length;
+ if (data->Flags & 0x10)
+ plci->NL.Req = plci->nl_req = (byte)N_UDATA;
+
+ else if ((plci->B3_prot == B3_RTP) && (data->Flags & 0x01))
+ plci->NL.Req = plci->nl_req = (byte)N_BDATA;
+
+ else
+ plci->NL.Req = plci->nl_req = (byte)((data->Flags&0x07)<<4 |N_DATA);
+ }
+ plci->NL.X = plci->NData;
+ plci->NL.ReqCh = a->ncci_ch[ncci];
+ dbug(1,dprintf("%x:DREQ(%x:%x)",a->Id,plci->NL.Id,plci->NL.Req));
+ plci->data_sent = true;
+ plci->data_sent_ptr = data->P;
+ a->request(&plci->NL);
+ }
+ else {
+ cleanup_ncci_data (plci, ncci);
+ }
+ }
+ else if (plci->send_disc == ncci)
+ {
+ /* dprintf("N_DISC"); */
+ plci->NData[0].PLength = 0;
+ plci->NL.ReqCh = a->ncci_ch[ncci];
+ plci->NL.Req = plci->nl_req = N_DISC;
+ a->request(&plci->NL);
+ plci->command = _DISCONNECT_B3_R;
+ plci->send_disc = 0;
+ }
+ }
+ } while (!plci->nl_req && (ncci != plci->ncci_ring_list));
+ plci->ncci_ring_list = ncci;
+ }
+}
+
+void listen_check(DIVA_CAPI_ADAPTER * a)
+{
+ word i,j;
+ PLCI * plci;
+ byte activnotifiedcalls = 0;
+
+ dbug(1,dprintf("listen_check(%d,%d)",a->listen_active,a->max_listen));
+ if (!remove_started && !a->adapter_disabled)
+ {
+ for(i=0;i<a->max_plci;i++)
+ {
+ plci = &(a->plci[i]);
+ if(plci->notifiedcall) activnotifiedcalls++;
+ }
+ dbug(1,dprintf("listen_check(%d)",activnotifiedcalls));
+
+ for(i=a->listen_active; i < ((word)(a->max_listen+activnotifiedcalls)); i++) {
+ if((j=get_plci(a))) {
+ a->listen_active++;
+ plci = &a->plci[j-1];
+ plci->State = LISTENING;
+
+ add_p(plci,OAD,"\x01\xfd");
+
+ add_p(plci,KEY,"\x04\x43\x41\x32\x30");
+
+ add_p(plci,CAI,"\x01\xc0");
+ add_p(plci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+ add_p(plci,LLI,"\x01\xc4"); /* support Dummy CR FAC + MWI + SpoofNotify */
+ add_p(plci,SHIFT|6,NULL);
+ add_p(plci,SIN,"\x02\x00\x00");
+ plci->internal_command = LISTEN_SIG_ASSIGN_PEND; /* do indicate_req if OK */
+ sig_req(plci,ASSIGN,DSIG_ID);
+ send_req(plci);
+ }
+ }
+ }
+}
+
+/*------------------------------------------------------------------*/
+/* functions for all parameters sent in INDs */
+/*------------------------------------------------------------------*/
+
+void IndParse(PLCI * plci, word * parms_id, byte ** parms, byte multiIEsize)
+{
+ word ploc; /* points to current location within packet */
+ byte w;
+ byte wlen;
+ byte codeset,lock;
+ byte * in;
+ word i;
+ word code;
+ word mIEindex = 0;
+ ploc = 0;
+ codeset = 0;
+ lock = 0;
+
+ in = plci->Sig.RBuffer->P;
+ for(i=0; i<parms_id[0]; i++) /* multiIE parms_id contains just the 1st */
+ { /* element but parms array is larger */
+ parms[i] = (byte *)"";
+ }
+ for(i=0; i<multiIEsize; i++)
+ {
+ parms[i] = (byte *)"";
+ }
+
+ while(ploc<plci->Sig.RBuffer->length-1) {
+
+ /* read information element id and length */
+ w = in[ploc];
+
+ if(w & 0x80) {
+/* w &=0xf0; removed, cannot detect congestion levels */
+/* upper 4 bit masked with w==SHIFT now */
+ wlen = 0;
+ }
+ else {
+ wlen = (byte)(in[ploc+1]+1);
+ }
+ /* check if length valid (not exceeding end of packet) */
+ if((ploc+wlen) > 270) return ;
+ if(lock & 0x80) lock &=0x7f;
+ else codeset = lock;
+
+ if((w&0xf0)==SHIFT) {
+ codeset = in[ploc];
+ if(!(codeset & 0x08)) lock = (byte)(codeset & 7);
+ codeset &=7;
+ lock |=0x80;
+ }
+ else {
+ if(w==ESC && wlen>=3) code = in[ploc+2] |0x800;
+ else code = w;
+ code |= (codeset<<8);
+
+ for(i=1; i<parms_id[0]+1 && parms_id[i]!=code; i++);
+
+ if(i<parms_id[0]+1) {
+ if(!multiIEsize) { /* with multiIEs use next field index, */
+ mIEindex = i-1; /* with normal IEs use same index like parms_id */
+ }
+
+ parms[mIEindex] = &in[ploc+1];
+ dbug(1,dprintf("mIE[%d]=0x%x",*parms[mIEindex],in[ploc]));
+ if(parms_id[i]==OAD
+ || parms_id[i]==CONN_NR
+ || parms_id[i]==CAD) {
+ if(in[ploc+2] &0x80) {
+ in[ploc+0] = (byte)(in[ploc+1]+1);
+ in[ploc+1] = (byte)(in[ploc+2] &0x7f);
+ in[ploc+2] = 0x80;
+ parms[mIEindex] = &in[ploc];
+ }
+ }
+ mIEindex++; /* effects multiIEs only */
+ }
+ }
+
+ ploc +=(wlen+1);
+ }
+ return ;
+}
+
+/*------------------------------------------------------------------*/
+/* try to match a cip from received BC and HLC */
+/*------------------------------------------------------------------*/
+
+byte ie_compare(byte * ie1, byte * ie2)
+{
+ word i;
+ if(!ie1 || ! ie2) return false;
+ if(!ie1[0]) return false;
+ for(i=0;i<(word)(ie1[0]+1);i++) if(ie1[i]!=ie2[i]) return false;
+ return true;
+}
+
+word find_cip(DIVA_CAPI_ADAPTER * a, byte * bc, byte * hlc)
+{
+ word i;
+ word j;
+
+ for(i=9;i && !ie_compare(bc,cip_bc[i][a->u_law]);i--);
+
+ for(j=16;j<29 &&
+ (!ie_compare(bc,cip_bc[j][a->u_law]) || !ie_compare(hlc,cip_hlc[j])); j++);
+ if(j==29) return i;
+ return j;
+}
+
+
+static byte AddInfo(byte **add_i,
+ byte **fty_i,
+ byte *esc_chi,
+ byte *facility)
+{
+ byte i;
+ byte j;
+ byte k;
+ byte flen;
+ byte len=0;
+ /* facility is a nested structure */
+ /* FTY can be more than once */
+
+ if (esc_chi[0] && !(esc_chi[esc_chi[0]] & 0x7f))
+ {
+ add_i[0] = (byte *)"\x02\x02\x00"; /* use neither b nor d channel */
+ }
+
+ else
+ {
+ add_i[0] = (byte *)"";
+ }
+ if(!fty_i[0][0])
+ {
+ add_i[3] = (byte *)"";
+ }
+ else
+ { /* facility array found */
+ for(i=0,j=1;i<MAX_MULTI_IE && fty_i[i][0];i++)
+ {
+ dbug(1,dprintf("AddIFac[%d]",fty_i[i][0]));
+ len += fty_i[i][0];
+ len += 2;
+ flen=fty_i[i][0];
+ facility[j++]=0x1c; /* copy fac IE */
+ for(k=0;k<=flen;k++,j++)
+ {
+ facility[j]=fty_i[i][k];
+/* dbug(1,dprintf("%x ",facility[j])); */
+ }
+ }
+ facility[0] = len;
+ add_i[3] = facility;
+ }
+/* dbug(1,dprintf("FacArrLen=%d ",len)); */
+ len = add_i[0][0]+add_i[1][0]+add_i[2][0]+add_i[3][0];
+ len += 4; /* calculate length of all */
+ return(len);
+}
+
+/*------------------------------------------------------------------*/
+/* voice and codec features */
+/*------------------------------------------------------------------*/
+
+void SetVoiceChannel(PLCI *plci, byte *chi, DIVA_CAPI_ADAPTER * a)
+{
+ byte voice_chi[] = "\x02\x18\x01";
+ byte channel;
+
+ channel = chi[chi[0]]&0x3;
+ dbug(1,dprintf("ExtDevON(Ch=0x%x)",channel));
+ voice_chi[2] = (channel) ? channel : 1;
+ add_p(plci,FTY,"\x02\x01\x07"); /* B On, default on 1 */
+ add_p(plci,ESC,voice_chi); /* Channel */
+ sig_req(plci,TEL_CTRL,0);
+ send_req(plci);
+ if(a->AdvSignalPLCI)
+ {
+ adv_voice_write_coefs (a->AdvSignalPLCI, ADV_VOICE_WRITE_ACTIVATION);
+ }
+}
+
+void VoiceChannelOff(PLCI *plci)
+{
+ dbug(1,dprintf("ExtDevOFF"));
+ add_p(plci,FTY,"\x02\x01\x08"); /* B Off */
+ sig_req(plci,TEL_CTRL,0);
+ send_req(plci);
+ if(plci->adapter->AdvSignalPLCI)
+ {
+ adv_voice_clear_config (plci->adapter->AdvSignalPLCI);
+ }
+}
+
+
+word AdvCodecSupport(DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, byte hook_listen)
+{
+ word j;
+ PLCI *splci;
+
+ /* check if hardware supports handset with hook states (adv.codec) */
+ /* or if just a on board codec is supported */
+ /* the advanced codec plci is just for internal use */
+
+ /* diva Pro with on-board codec: */
+ if(a->profile.Global_Options & HANDSET)
+ {
+ /* new call, but hook states are already signalled */
+ if(a->AdvCodecFLAG)
+ {
+ if(a->AdvSignalAppl!=appl || a->AdvSignalPLCI)
+ {
+ dbug(1,dprintf("AdvSigPlci=0x%x",a->AdvSignalPLCI));
+ return 0x2001; /* codec in use by another application */
+ }
+ if(plci!=NULL)
+ {
+ a->AdvSignalPLCI = plci;
+ plci->tel=ADV_VOICE;
+ }
+ return 0; /* adv codec still used */
+ }
+ if((j=get_plci(a)))
+ {
+ splci = &a->plci[j-1];
+ splci->tel = CODEC_PERMANENT;
+ /* hook_listen indicates if a facility_req with handset/hook support */
+ /* was sent. Otherwise if just a call on an external device was made */
+ /* the codec will be used but the hook info will be discarded (just */
+ /* the external controller is in use */
+ if(hook_listen) splci->State = ADVANCED_VOICE_SIG;
+ else
+ {
+ splci->State = ADVANCED_VOICE_NOSIG;
+ if(plci)
+ {
+ plci->spoofed_msg = SPOOFING_REQUIRED;
+ }
+ /* indicate D-ch connect if */
+ } /* codec is connected OK */
+ if(plci!=NULL)
+ {
+ a->AdvSignalPLCI = plci;
+ plci->tel=ADV_VOICE;
+ }
+ a->AdvSignalAppl = appl;
+ a->AdvCodecFLAG = true;
+ a->AdvCodecPLCI = splci;
+ add_p(splci,CAI,"\x01\x15");
+ add_p(splci,LLI,"\x01\x00");
+ add_p(splci,ESC,"\x02\x18\x00");
+ add_p(splci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+ splci->internal_command = PERM_COD_ASSIGN;
+ dbug(1,dprintf("Codec Assign"));
+ sig_req(splci,ASSIGN,DSIG_ID);
+ send_req(splci);
+ }
+ else
+ {
+ return 0x2001; /* wrong state, no more plcis */
+ }
+ }
+ else if(a->profile.Global_Options & ON_BOARD_CODEC)
+ {
+ if(hook_listen) return 0x300B; /* Facility not supported */
+ /* no hook with SCOM */
+ if(plci!=NULL) plci->tel = CODEC;
+ dbug(1,dprintf("S/SCOM codec"));
+ /* first time we use the scom-s codec we must shut down the internal */
+ /* handset application of the card. This can be done by an assign with */
+ /* a cai with the 0x80 bit set. Assign return code is 'out of resource'*/
+ if(!a->scom_appl_disable){
+ if((j=get_plci(a))) {
+ splci = &a->plci[j-1];
+ add_p(splci,CAI,"\x01\x80");
+ add_p(splci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+ sig_req(splci,ASSIGN,0xC0); /* 0xc0 is the TEL_ID */
+ send_req(splci);
+ a->scom_appl_disable = true;
+ }
+ else{
+ return 0x2001; /* wrong state, no more plcis */
+ }
+ }
+ }
+ else return 0x300B; /* Facility not supported */
+
+ return 0;
+}
+
+
+void CodecIdCheck(DIVA_CAPI_ADAPTER *a, PLCI *plci)
+{
+
+ dbug(1,dprintf("CodecIdCheck"));
+
+ if(a->AdvSignalPLCI == plci)
+ {
+ dbug(1,dprintf("PLCI owns codec"));
+ VoiceChannelOff(a->AdvCodecPLCI);
+ if(a->AdvCodecPLCI->State == ADVANCED_VOICE_NOSIG)
+ {
+ dbug(1,dprintf("remove temp codec PLCI"));
+ plci_remove(a->AdvCodecPLCI);
+ a->AdvCodecFLAG = 0;
+ a->AdvCodecPLCI = NULL;
+ a->AdvSignalAppl = NULL;
+ }
+ a->AdvSignalPLCI = NULL;
+ }
+}
+
+/* -------------------------------------------------------------------
+ Ask for physical address of card on PCI bus
+ ------------------------------------------------------------------- */
+static void diva_ask_for_xdi_sdram_bar (DIVA_CAPI_ADAPTER * a,
+ IDI_SYNC_REQ * preq) {
+ a->sdram_bar = 0;
+ if (diva_xdi_extended_features & DIVA_CAPI_XDI_PROVIDES_SDRAM_BAR) {
+ ENTITY * e = (ENTITY *)preq;
+
+ e->user[0] = a->Id - 1;
+ preq->xdi_sdram_bar.info.bar = 0;
+ preq->xdi_sdram_bar.Req = 0;
+ preq->xdi_sdram_bar.Rc = IDI_SYNC_REQ_XDI_GET_ADAPTER_SDRAM_BAR;
+
+ (*(a->request))(e);
+
+ a->sdram_bar = preq->xdi_sdram_bar.info.bar;
+ dbug(3,dprintf("A(%d) SDRAM BAR = %08x", a->Id, a->sdram_bar));
+ }
+}
+
+/* -------------------------------------------------------------------
+ Ask XDI about extended features
+ ------------------------------------------------------------------- */
+static void diva_get_extended_adapter_features (DIVA_CAPI_ADAPTER * a) {
+ IDI_SYNC_REQ * preq;
+ char buffer[ ((sizeof(preq->xdi_extended_features)+4) > sizeof(ENTITY)) ? (sizeof(preq->xdi_extended_features)+4) : sizeof(ENTITY)];
+
+ char features[4];
+ preq = (IDI_SYNC_REQ *)&buffer[0];
+
+ if (!diva_xdi_extended_features) {
+ ENTITY * e = (ENTITY *)preq;
+ diva_xdi_extended_features |= 0x80000000;
+
+ e->user[0] = a->Id - 1;
+ preq->xdi_extended_features.Req = 0;
+ preq->xdi_extended_features.Rc = IDI_SYNC_REQ_XDI_GET_EXTENDED_FEATURES;
+ preq->xdi_extended_features.info.buffer_length_in_bytes = sizeof(features);
+ preq->xdi_extended_features.info.features = &features[0];
+
+ (*(a->request))(e);
+
+ if (features[0] & DIVA_XDI_EXTENDED_FEATURES_VALID) {
+ /*
+ Check features located in the byte '0'
+ */
+ if (features[0] & DIVA_XDI_EXTENDED_FEATURE_CMA) {
+ diva_xdi_extended_features |= DIVA_CAPI_USE_CMA;
+ }
+ if (features[0] & DIVA_XDI_EXTENDED_FEATURE_RX_DMA) {
+ diva_xdi_extended_features |= DIVA_CAPI_XDI_PROVIDES_RX_DMA;
+ dbug(1,dprintf("XDI provides RxDMA"));
+ }
+ if (features[0] & DIVA_XDI_EXTENDED_FEATURE_SDRAM_BAR) {
+ diva_xdi_extended_features |= DIVA_CAPI_XDI_PROVIDES_SDRAM_BAR;
+ }
+ if (features[0] & DIVA_XDI_EXTENDED_FEATURE_NO_CANCEL_RC) {
+ diva_xdi_extended_features |= DIVA_CAPI_XDI_PROVIDES_NO_CANCEL;
+ dbug(3,dprintf("XDI provides NO_CANCEL_RC feature"));
+ }
+
+ }
+ }
+
+ diva_ask_for_xdi_sdram_bar (a, preq);
+}
+
+/*------------------------------------------------------------------*/
+/* automatic law */
+/*------------------------------------------------------------------*/
+/* called from OS specific part after init time to get the Law */
+/* a-law (Euro) and u-law (us,japan) use different BCs in the Setup message */
+void AutomaticLaw(DIVA_CAPI_ADAPTER *a)
+{
+ word j;
+ PLCI *splci;
+
+ if(a->automatic_law) {
+ return;
+ }
+ if((j=get_plci(a))) {
+ diva_get_extended_adapter_features (a);
+ splci = &a->plci[j-1];
+ a->automatic_lawPLCI = splci;
+ a->automatic_law = 1;
+ add_p(splci,CAI,"\x01\x80");
+ add_p(splci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+ splci->internal_command = USELAW_REQ;
+ splci->command = 0;
+ splci->number = 0;
+ sig_req(splci,ASSIGN,DSIG_ID);
+ send_req(splci);
+ }
+}
+
+/* called from OS specific part if an application sends an Capi20Release */
+word CapiRelease(word Id)
+{
+ word i, j, appls_found;
+ PLCI *plci;
+ APPL *this;
+ DIVA_CAPI_ADAPTER *a;
+
+ if (!Id)
+ {
+ dbug(0,dprintf("A: CapiRelease(Id==0)"));
+ return (_WRONG_APPL_ID);
+ }
+
+ this = &application[Id-1]; /* get application pointer */
+
+ for(i=0,appls_found=0; i<max_appl; i++)
+ {
+ if(application[i].Id) /* an application has been found */
+ {
+ appls_found++;
+ }
+ }
+
+ for(i=0; i<max_adapter; i++) /* scan all adapters... */
+ {
+ a = &adapter[i];
+ if (a->request)
+ {
+ a->Info_Mask[Id-1] = 0;
+ a->CIP_Mask[Id-1] = 0;
+ a->Notification_Mask[Id-1] = 0;
+ a->codec_listen[Id-1] = NULL;
+ a->requested_options_table[Id-1] = 0;
+ for(j=0; j<a->max_plci; j++) /* and all PLCIs connected */
+ { /* with this application */
+ plci = &a->plci[j];
+ if(plci->Id) /* if plci owns no application */
+ { /* it may be not jet connected */
+ if(plci->State==INC_CON_PENDING
+ || plci->State==INC_CON_ALERT)
+ {
+ if(test_c_ind_mask_bit (plci, (word)(Id-1)))
+ {
+ clear_c_ind_mask_bit (plci, (word)(Id-1));
+ if(c_ind_mask_empty (plci))
+ {
+ sig_req(plci,HANGUP,0);
+ send_req(plci);
+ plci->State = OUTG_DIS_PENDING;
+ }
+ }
+ }
+ if(test_c_ind_mask_bit (plci, (word)(Id-1)))
+ {
+ clear_c_ind_mask_bit (plci, (word)(Id-1));
+ if(c_ind_mask_empty (plci))
+ {
+ if(!plci->appl)
+ {
+ plci_remove(plci);
+ plci->State = IDLE;
+ }
+ }
+ }
+ if(plci->appl==this)
+ {
+ plci->appl = NULL;
+ plci_remove(plci);
+ plci->State = IDLE;
+ }
+ }
+ }
+ listen_check(a);
+
+ if(a->flag_dynamic_l1_down)
+ {
+ if(appls_found==1) /* last application does a capi release */
+ {
+ if((j=get_plci(a)))
+ {
+ plci = &a->plci[j-1];
+ plci->command = 0;
+ add_p(plci,OAD,"\x01\xfd");
+ add_p(plci,CAI,"\x01\x80");
+ add_p(plci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+ add_p(plci,SHIFT|6,NULL);
+ add_p(plci,SIN,"\x02\x00\x00");
+ plci->internal_command = REM_L1_SIG_ASSIGN_PEND;
+ sig_req(plci,ASSIGN,DSIG_ID);
+ add_p(plci,FTY,"\x02\xff\x06"); /* l1 down */
+ sig_req(plci,SIG_CTRL,0);
+ send_req(plci);
+ }
+ }
+ }
+ if(a->AdvSignalAppl==this)
+ {
+ this->NullCREnable = false;
+ if (a->AdvCodecPLCI)
+ {
+ plci_remove(a->AdvCodecPLCI);
+ a->AdvCodecPLCI->tel = 0;
+ a->AdvCodecPLCI->adv_nl = 0;
+ }
+ a->AdvSignalAppl = NULL;
+ a->AdvSignalPLCI = NULL;
+ a->AdvCodecFLAG = 0;
+ a->AdvCodecPLCI = NULL;
+ }
+ }
+ }
+
+ this->Id = 0;
+
+ return GOOD;
+}
+
+static word plci_remove_check(PLCI *plci)
+{
+ if(!plci) return true;
+ if(!plci->NL.Id && c_ind_mask_empty (plci))
+ {
+ if(plci->Sig.Id == 0xff)
+ plci->Sig.Id = 0;
+ if(!plci->Sig.Id)
+ {
+ dbug(1,dprintf("plci_remove_complete(%x)",plci->Id));
+ dbug(1,dprintf("tel=0x%x,Sig=0x%x",plci->tel,plci->Sig.Id));
+ if (plci->Id)
+ {
+ CodecIdCheck(plci->adapter, plci);
+ clear_b1_config (plci);
+ ncci_remove (plci, 0, false);
+ plci_free_msg_in_queue (plci);
+ channel_flow_control_remove (plci);
+ plci->Id = 0;
+ plci->State = IDLE;
+ plci->channels = 0;
+ plci->appl = NULL;
+ plci->notifiedcall = 0;
+ }
+ listen_check(plci->adapter);
+ return true;
+ }
+ }
+ return false;
+}
+
+
+/*------------------------------------------------------------------*/
+
+static byte plci_nl_busy (PLCI *plci)
+{
+ /* only applicable for non-multiplexed protocols */
+ return (plci->nl_req
+ || (plci->ncci_ring_list
+ && plci->adapter->ncci_ch[plci->ncci_ring_list]
+ && (plci->adapter->ch_flow_control[plci->adapter->ncci_ch[plci->ncci_ring_list]] & N_OK_FC_PENDING)));
+}
+
+
+/*------------------------------------------------------------------*/
+/* DTMF facilities */
+/*------------------------------------------------------------------*/
+
+
+static struct
+{
+ byte send_mask;
+ byte listen_mask;
+ byte character;
+ byte code;
+} dtmf_digit_map[] =
+{
+ { 0x01, 0x01, 0x23, DTMF_DIGIT_TONE_CODE_HASHMARK },
+ { 0x01, 0x01, 0x2a, DTMF_DIGIT_TONE_CODE_STAR },
+ { 0x01, 0x01, 0x30, DTMF_DIGIT_TONE_CODE_0 },
+ { 0x01, 0x01, 0x31, DTMF_DIGIT_TONE_CODE_1 },
+ { 0x01, 0x01, 0x32, DTMF_DIGIT_TONE_CODE_2 },
+ { 0x01, 0x01, 0x33, DTMF_DIGIT_TONE_CODE_3 },
+ { 0x01, 0x01, 0x34, DTMF_DIGIT_TONE_CODE_4 },
+ { 0x01, 0x01, 0x35, DTMF_DIGIT_TONE_CODE_5 },
+ { 0x01, 0x01, 0x36, DTMF_DIGIT_TONE_CODE_6 },
+ { 0x01, 0x01, 0x37, DTMF_DIGIT_TONE_CODE_7 },
+ { 0x01, 0x01, 0x38, DTMF_DIGIT_TONE_CODE_8 },
+ { 0x01, 0x01, 0x39, DTMF_DIGIT_TONE_CODE_9 },
+ { 0x01, 0x01, 0x41, DTMF_DIGIT_TONE_CODE_A },
+ { 0x01, 0x01, 0x42, DTMF_DIGIT_TONE_CODE_B },
+ { 0x01, 0x01, 0x43, DTMF_DIGIT_TONE_CODE_C },
+ { 0x01, 0x01, 0x44, DTMF_DIGIT_TONE_CODE_D },
+ { 0x01, 0x00, 0x61, DTMF_DIGIT_TONE_CODE_A },
+ { 0x01, 0x00, 0x62, DTMF_DIGIT_TONE_CODE_B },
+ { 0x01, 0x00, 0x63, DTMF_DIGIT_TONE_CODE_C },
+ { 0x01, 0x00, 0x64, DTMF_DIGIT_TONE_CODE_D },
+
+ { 0x04, 0x04, 0x80, DTMF_SIGNAL_NO_TONE },
+ { 0x00, 0x04, 0x81, DTMF_SIGNAL_UNIDENTIFIED_TONE },
+ { 0x04, 0x04, 0x82, DTMF_SIGNAL_DIAL_TONE },
+ { 0x04, 0x04, 0x83, DTMF_SIGNAL_PABX_INTERNAL_DIAL_TONE },
+ { 0x04, 0x04, 0x84, DTMF_SIGNAL_SPECIAL_DIAL_TONE },
+ { 0x04, 0x04, 0x85, DTMF_SIGNAL_SECOND_DIAL_TONE },
+ { 0x04, 0x04, 0x86, DTMF_SIGNAL_RINGING_TONE },
+ { 0x04, 0x04, 0x87, DTMF_SIGNAL_SPECIAL_RINGING_TONE },
+ { 0x04, 0x04, 0x88, DTMF_SIGNAL_BUSY_TONE },
+ { 0x04, 0x04, 0x89, DTMF_SIGNAL_CONGESTION_TONE },
+ { 0x04, 0x04, 0x8a, DTMF_SIGNAL_SPECIAL_INFORMATION_TONE },
+ { 0x04, 0x04, 0x8b, DTMF_SIGNAL_COMFORT_TONE },
+ { 0x04, 0x04, 0x8c, DTMF_SIGNAL_HOLD_TONE },
+ { 0x04, 0x04, 0x8d, DTMF_SIGNAL_RECORD_TONE },
+ { 0x04, 0x04, 0x8e, DTMF_SIGNAL_CALLER_WAITING_TONE },
+ { 0x04, 0x04, 0x8f, DTMF_SIGNAL_CALL_WAITING_TONE },
+ { 0x04, 0x04, 0x90, DTMF_SIGNAL_PAY_TONE },
+ { 0x04, 0x04, 0x91, DTMF_SIGNAL_POSITIVE_INDICATION_TONE },
+ { 0x04, 0x04, 0x92, DTMF_SIGNAL_NEGATIVE_INDICATION_TONE },
+ { 0x04, 0x04, 0x93, DTMF_SIGNAL_WARNING_TONE },
+ { 0x04, 0x04, 0x94, DTMF_SIGNAL_INTRUSION_TONE },
+ { 0x04, 0x04, 0x95, DTMF_SIGNAL_CALLING_CARD_SERVICE_TONE },
+ { 0x04, 0x04, 0x96, DTMF_SIGNAL_PAYPHONE_RECOGNITION_TONE },
+ { 0x04, 0x04, 0x97, DTMF_SIGNAL_CPE_ALERTING_SIGNAL },
+ { 0x04, 0x04, 0x98, DTMF_SIGNAL_OFF_HOOK_WARNING_TONE },
+ { 0x04, 0x04, 0xbf, DTMF_SIGNAL_INTERCEPT_TONE },
+ { 0x04, 0x04, 0xc0, DTMF_SIGNAL_MODEM_CALLING_TONE },
+ { 0x04, 0x04, 0xc1, DTMF_SIGNAL_FAX_CALLING_TONE },
+ { 0x04, 0x04, 0xc2, DTMF_SIGNAL_ANSWER_TONE },
+ { 0x04, 0x04, 0xc3, DTMF_SIGNAL_REVERSED_ANSWER_TONE },
+ { 0x04, 0x04, 0xc4, DTMF_SIGNAL_ANSAM_TONE },
+ { 0x04, 0x04, 0xc5, DTMF_SIGNAL_REVERSED_ANSAM_TONE },
+ { 0x04, 0x04, 0xc6, DTMF_SIGNAL_BELL103_ANSWER_TONE },
+ { 0x04, 0x04, 0xc7, DTMF_SIGNAL_FAX_FLAGS },
+ { 0x04, 0x04, 0xc8, DTMF_SIGNAL_G2_FAX_GROUP_ID },
+ { 0x00, 0x04, 0xc9, DTMF_SIGNAL_HUMAN_SPEECH },
+ { 0x04, 0x04, 0xca, DTMF_SIGNAL_ANSWERING_MACHINE_390 },
+ { 0x02, 0x02, 0xf1, DTMF_MF_DIGIT_TONE_CODE_1 },
+ { 0x02, 0x02, 0xf2, DTMF_MF_DIGIT_TONE_CODE_2 },
+ { 0x02, 0x02, 0xf3, DTMF_MF_DIGIT_TONE_CODE_3 },
+ { 0x02, 0x02, 0xf4, DTMF_MF_DIGIT_TONE_CODE_4 },
+ { 0x02, 0x02, 0xf5, DTMF_MF_DIGIT_TONE_CODE_5 },
+ { 0x02, 0x02, 0xf6, DTMF_MF_DIGIT_TONE_CODE_6 },
+ { 0x02, 0x02, 0xf7, DTMF_MF_DIGIT_TONE_CODE_7 },
+ { 0x02, 0x02, 0xf8, DTMF_MF_DIGIT_TONE_CODE_8 },
+ { 0x02, 0x02, 0xf9, DTMF_MF_DIGIT_TONE_CODE_9 },
+ { 0x02, 0x02, 0xfa, DTMF_MF_DIGIT_TONE_CODE_0 },
+ { 0x02, 0x02, 0xfb, DTMF_MF_DIGIT_TONE_CODE_K1 },
+ { 0x02, 0x02, 0xfc, DTMF_MF_DIGIT_TONE_CODE_K2 },
+ { 0x02, 0x02, 0xfd, DTMF_MF_DIGIT_TONE_CODE_KP },
+ { 0x02, 0x02, 0xfe, DTMF_MF_DIGIT_TONE_CODE_S1 },
+ { 0x02, 0x02, 0xff, DTMF_MF_DIGIT_TONE_CODE_ST },
+
+};
+
+#define DTMF_DIGIT_MAP_ENTRIES ARRAY_SIZE(dtmf_digit_map)
+
+
+static void dtmf_enable_receiver (PLCI *plci, byte enable_mask)
+{
+ word min_digit_duration, min_gap_duration;
+
+ dbug (1, dprintf ("[%06lx] %s,%d: dtmf_enable_receiver %02x",
+ (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+ (char *)(FILE_), __LINE__, enable_mask));
+
+ if (enable_mask != 0)
+ {
+ min_digit_duration = (plci->dtmf_rec_pulse_ms == 0) ? 40 : plci->dtmf_rec_pulse_ms;
+ min_gap_duration = (plci->dtmf_rec_pause_ms == 0) ? 40 : plci->dtmf_rec_pause_ms;
+ plci->internal_req_buffer[0] = DTMF_UDATA_REQUEST_ENABLE_RECEIVER;
+ PUT_WORD (&plci->internal_req_buffer[1], min_digit_duration);
+ PUT_WORD (&plci->internal_req_buffer[3], min_gap_duration);
+ plci->NData[0].PLength = 5;
+
+ PUT_WORD (&plci->internal_req_buffer[5], INTERNAL_IND_BUFFER_SIZE);
+ plci->NData[0].PLength += 2;
+ capidtmf_recv_enable (&(plci->capidtmf_state), min_digit_duration, min_gap_duration);
+
+ }
+ else
+ {
+ plci->internal_req_buffer[0] = DTMF_UDATA_REQUEST_DISABLE_RECEIVER;
+ plci->NData[0].PLength = 1;
+
+ capidtmf_recv_disable (&(plci->capidtmf_state));
+
+ }
+ plci->NData[0].P = plci->internal_req_buffer;
+ plci->NL.X = plci->NData;
+ plci->NL.ReqCh = 0;
+ plci->NL.Req = plci->nl_req = (byte) N_UDATA;
+ plci->adapter->request (&plci->NL);
+}
+
+
+static void dtmf_send_digits (PLCI *plci, byte *digit_buffer, word digit_count)
+{
+ word w, i;
+
+ dbug (1, dprintf ("[%06lx] %s,%d: dtmf_send_digits %d",
+ (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+ (char *)(FILE_), __LINE__, digit_count));
+
+ plci->internal_req_buffer[0] = DTMF_UDATA_REQUEST_SEND_DIGITS;
+ w = (plci->dtmf_send_pulse_ms == 0) ? 40 : plci->dtmf_send_pulse_ms;
+ PUT_WORD (&plci->internal_req_buffer[1], w);
+ w = (plci->dtmf_send_pause_ms == 0) ? 40 : plci->dtmf_send_pause_ms;
+ PUT_WORD (&plci->internal_req_buffer[3], w);
+ for (i = 0; i < digit_count; i++)
+ {
+ w = 0;
+ while ((w < DTMF_DIGIT_MAP_ENTRIES)
+ && (digit_buffer[i] != dtmf_digit_map[w].character))
+ {
+ w++;
+ }
+ plci->internal_req_buffer[5+i] = (w < DTMF_DIGIT_MAP_ENTRIES) ?
+ dtmf_digit_map[w].code : DTMF_DIGIT_TONE_CODE_STAR;
+ }
+ plci->NData[0].PLength = 5 + digit_count;
+ plci->NData[0].P = plci->internal_req_buffer;
+ plci->NL.X = plci->NData;
+ plci->NL.ReqCh = 0;
+ plci->NL.Req = plci->nl_req = (byte) N_UDATA;
+ plci->adapter->request (&plci->NL);
+}
+
+
+static void dtmf_rec_clear_config (PLCI *plci)
+{
+
+ dbug (1, dprintf ("[%06lx] %s,%d: dtmf_rec_clear_config",
+ (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+ (char *)(FILE_), __LINE__));
+
+ plci->dtmf_rec_active = 0;
+ plci->dtmf_rec_pulse_ms = 0;
+ plci->dtmf_rec_pause_ms = 0;
+
+ capidtmf_init (&(plci->capidtmf_state), plci->adapter->u_law);
+
+}
+
+
+static void dtmf_send_clear_config (PLCI *plci)
+{
+
+ dbug (1, dprintf ("[%06lx] %s,%d: dtmf_send_clear_config",
+ (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+ (char *)(FILE_), __LINE__));
+
+ plci->dtmf_send_requests = 0;
+ plci->dtmf_send_pulse_ms = 0;
+ plci->dtmf_send_pause_ms = 0;
+}
+
+
+static void dtmf_prepare_switch (dword Id, PLCI *plci)
+{
+
+ dbug (1, dprintf ("[%06lx] %s,%d: dtmf_prepare_switch",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+
+ while (plci->dtmf_send_requests != 0)
+ dtmf_confirmation (Id, plci);
+}
+
+
+static word dtmf_save_config (dword Id, PLCI *plci, byte Rc)
+{
+
+ dbug (1, dprintf ("[%06lx] %s,%d: dtmf_save_config %02x %d",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state));
+
+ return (GOOD);
+}
+
+
+static word dtmf_restore_config (dword Id, PLCI *plci, byte Rc)
+{
+ word Info;
+
+ dbug (1, dprintf ("[%06lx] %s,%d: dtmf_restore_config %02x %d",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state));
+
+ Info = GOOD;
+ if (plci->B1_facilities & B1_FACILITY_DTMFR)
+ {
+ switch (plci->adjust_b_state)
+ {
+ case ADJUST_B_RESTORE_DTMF_1:
+ plci->internal_command = plci->adjust_b_command;
+ if (plci_nl_busy (plci))
+ {
+ plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_1;
+ break;
+ }
+ dtmf_enable_receiver (plci, plci->dtmf_rec_active);
+ plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_2;
+ break;
+ case ADJUST_B_RESTORE_DTMF_2:
+ if ((Rc != OK) && (Rc != OK_FC))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Reenable DTMF receiver failed %02x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc));
+ Info = _WRONG_STATE;
+ break;
+ }
+ break;
+ }
+ }
+ return (Info);
+}
+
+
+static void dtmf_command (dword Id, PLCI *plci, byte Rc)
+{
+ word internal_command, Info;
+ byte mask;
+ byte result[4];
+
+ dbug (1, dprintf ("[%06lx] %s,%d: dtmf_command %02x %04x %04x %d %d %d %d",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command,
+ plci->dtmf_cmd, plci->dtmf_rec_pulse_ms, plci->dtmf_rec_pause_ms,
+ plci->dtmf_send_pulse_ms, plci->dtmf_send_pause_ms));
+
+ Info = GOOD;
+ result[0] = 2;
+ PUT_WORD (&result[1], DTMF_SUCCESS);
+ internal_command = plci->internal_command;
+ plci->internal_command = 0;
+ mask = 0x01;
+ switch (plci->dtmf_cmd)
+ {
+
+ case DTMF_LISTEN_TONE_START:
+ mask <<= 1;
+ case DTMF_LISTEN_MF_START:
+ mask <<= 1;
+
+ case DTMF_LISTEN_START:
+ switch (internal_command)
+ {
+ default:
+ adjust_b1_resource (Id, plci, NULL, (word)(plci->B1_facilities |
+ B1_FACILITY_DTMFR), DTMF_COMMAND_1);
+ case DTMF_COMMAND_1:
+ if (adjust_b_process (Id, plci, Rc) != GOOD)
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Load DTMF failed",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ Info = _FACILITY_NOT_SUPPORTED;
+ break;
+ }
+ if (plci->internal_command)
+ return;
+ case DTMF_COMMAND_2:
+ if (plci_nl_busy (plci))
+ {
+ plci->internal_command = DTMF_COMMAND_2;
+ return;
+ }
+ plci->internal_command = DTMF_COMMAND_3;
+ dtmf_enable_receiver (plci, (byte)(plci->dtmf_rec_active | mask));
+ return;
+ case DTMF_COMMAND_3:
+ if ((Rc != OK) && (Rc != OK_FC))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Enable DTMF receiver failed %02x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc));
+ Info = _FACILITY_NOT_SUPPORTED;
+ break;
+ }
+
+ plci->tone_last_indication_code = DTMF_SIGNAL_NO_TONE;
+
+ plci->dtmf_rec_active |= mask;
+ break;
+ }
+ break;
+
+
+ case DTMF_LISTEN_TONE_STOP:
+ mask <<= 1;
+ case DTMF_LISTEN_MF_STOP:
+ mask <<= 1;
+
+ case DTMF_LISTEN_STOP:
+ switch (internal_command)
+ {
+ default:
+ plci->dtmf_rec_active &= ~mask;
+ if (plci->dtmf_rec_active)
+ break;
+/*
+ case DTMF_COMMAND_1:
+ if (plci->dtmf_rec_active)
+ {
+ if (plci_nl_busy (plci))
+ {
+ plci->internal_command = DTMF_COMMAND_1;
+ return;
+ }
+ plci->dtmf_rec_active &= ~mask;
+ plci->internal_command = DTMF_COMMAND_2;
+ dtmf_enable_receiver (plci, false);
+ return;
+ }
+ Rc = OK;
+ case DTMF_COMMAND_2:
+ if ((Rc != OK) && (Rc != OK_FC))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Disable DTMF receiver failed %02x",
+ UnMapId (Id), (char far *)(FILE_), __LINE__, Rc));
+ Info = _FACILITY_NOT_SUPPORTED;
+ break;
+ }
+*/
+ adjust_b1_resource (Id, plci, NULL, (word)(plci->B1_facilities &
+ ~(B1_FACILITY_DTMFX | B1_FACILITY_DTMFR)), DTMF_COMMAND_3);
+ case DTMF_COMMAND_3:
+ if (adjust_b_process (Id, plci, Rc) != GOOD)
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Unload DTMF failed",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ Info = _FACILITY_NOT_SUPPORTED;
+ break;
+ }
+ if (plci->internal_command)
+ return;
+ break;
+ }
+ break;
+
+
+ case DTMF_SEND_TONE:
+ mask <<= 1;
+ case DTMF_SEND_MF:
+ mask <<= 1;
+
+ case DTMF_DIGITS_SEND:
+ switch (internal_command)
+ {
+ default:
+ adjust_b1_resource (Id, plci, NULL, (word)(plci->B1_facilities |
+ ((plci->dtmf_parameter_length != 0) ? B1_FACILITY_DTMFX | B1_FACILITY_DTMFR : B1_FACILITY_DTMFX)),
+ DTMF_COMMAND_1);
+ case DTMF_COMMAND_1:
+ if (adjust_b_process (Id, plci, Rc) != GOOD)
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Load DTMF failed",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ Info = _FACILITY_NOT_SUPPORTED;
+ break;
+ }
+ if (plci->internal_command)
+ return;
+ case DTMF_COMMAND_2:
+ if (plci_nl_busy (plci))
+ {
+ plci->internal_command = DTMF_COMMAND_2;
+ return;
+ }
+ plci->dtmf_msg_number_queue[(plci->dtmf_send_requests)++] = plci->number;
+ plci->internal_command = DTMF_COMMAND_3;
+ dtmf_send_digits (plci, &plci->saved_msg.parms[3].info[1], plci->saved_msg.parms[3].length);
+ return;
+ case DTMF_COMMAND_3:
+ if ((Rc != OK) && (Rc != OK_FC))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Send DTMF digits failed %02x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc));
+ if (plci->dtmf_send_requests != 0)
+ (plci->dtmf_send_requests)--;
+ Info = _FACILITY_NOT_SUPPORTED;
+ break;
+ }
+ return;
+ }
+ break;
+ }
+ sendf (plci->appl, _FACILITY_R | CONFIRM, Id & 0xffffL, plci->number,
+ "wws", Info, SELECTOR_DTMF, result);
+}
+
+
+static byte dtmf_request (dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, API_PARSE *msg)
+{
+ word Info;
+ word i, j;
+ byte mask;
+ API_PARSE dtmf_parms[5];
+ byte result[40];
+
+ dbug (1, dprintf ("[%06lx] %s,%d: dtmf_request",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+
+ Info = GOOD;
+ result[0] = 2;
+ PUT_WORD (&result[1], DTMF_SUCCESS);
+ if (!(a->profile.Global_Options & GL_DTMF_SUPPORTED))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Facility not supported",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ Info = _FACILITY_NOT_SUPPORTED;
+ }
+ else if (api_parse (&msg[1].info[1], msg[1].length, "w", dtmf_parms))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Wrong message format",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ Info = _WRONG_MESSAGE_FORMAT;
+ }
+
+ else if ((GET_WORD (dtmf_parms[0].info) == DTMF_GET_SUPPORTED_DETECT_CODES)
+ || (GET_WORD (dtmf_parms[0].info) == DTMF_GET_SUPPORTED_SEND_CODES))
+ {
+ if (!((a->requested_options_table[appl->Id-1])
+ & (1L << PRIVATE_DTMF_TONE)))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: DTMF unknown request %04x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, GET_WORD (dtmf_parms[0].info)));
+ PUT_WORD (&result[1], DTMF_UNKNOWN_REQUEST);
+ }
+ else
+ {
+ for (i = 0; i < 32; i++)
+ result[4 + i] = 0;
+ if (GET_WORD (dtmf_parms[0].info) == DTMF_GET_SUPPORTED_DETECT_CODES)
+ {
+ for (i = 0; i < DTMF_DIGIT_MAP_ENTRIES; i++)
+ {
+ if (dtmf_digit_map[i].listen_mask != 0)
+ result[4 + (dtmf_digit_map[i].character >> 3)] |= (1 << (dtmf_digit_map[i].character & 0x7));
+ }
+ }
+ else
+ {
+ for (i = 0; i < DTMF_DIGIT_MAP_ENTRIES; i++)
+ {
+ if (dtmf_digit_map[i].send_mask != 0)
+ result[4 + (dtmf_digit_map[i].character >> 3)] |= (1 << (dtmf_digit_map[i].character & 0x7));
+ }
+ }
+ result[0] = 3 + 32;
+ result[3] = 32;
+ }
+ }
+
+ else if (plci == NULL)
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Wrong PLCI",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ Info = _WRONG_IDENTIFIER;
+ }
+ else
+ {
+ if (!plci->State
+ || !plci->NL.Id || plci->nl_remove_id)
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Wrong state",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ Info = _WRONG_STATE;
+ }
+ else
+ {
+ plci->command = 0;
+ plci->dtmf_cmd = GET_WORD (dtmf_parms[0].info);
+ mask = 0x01;
+ switch (plci->dtmf_cmd)
+ {
+
+ case DTMF_LISTEN_TONE_START:
+ case DTMF_LISTEN_TONE_STOP:
+ mask <<= 1;
+ case DTMF_LISTEN_MF_START:
+ case DTMF_LISTEN_MF_STOP:
+ mask <<= 1;
+ if (!((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[appl->Id-1])
+ & (1L << PRIVATE_DTMF_TONE)))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: DTMF unknown request %04x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, GET_WORD (dtmf_parms[0].info)));
+ PUT_WORD (&result[1], DTMF_UNKNOWN_REQUEST);
+ break;
+ }
+
+ case DTMF_LISTEN_START:
+ case DTMF_LISTEN_STOP:
+ if (!(a->manufacturer_features & MANUFACTURER_FEATURE_HARDDTMF)
+ && !(a->manufacturer_features & MANUFACTURER_FEATURE_SOFTDTMF_RECEIVE))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Facility not supported",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ Info = _FACILITY_NOT_SUPPORTED;
+ break;
+ }
+ if (mask & DTMF_LISTEN_ACTIVE_FLAG)
+ {
+ if (api_parse (&msg[1].info[1], msg[1].length, "wwws", dtmf_parms))
+ {
+ plci->dtmf_rec_pulse_ms = 0;
+ plci->dtmf_rec_pause_ms = 0;
+ }
+ else
+ {
+ plci->dtmf_rec_pulse_ms = GET_WORD (dtmf_parms[1].info);
+ plci->dtmf_rec_pause_ms = GET_WORD (dtmf_parms[2].info);
+ }
+ }
+ start_internal_command (Id, plci, dtmf_command);
+ return (false);
+
+
+ case DTMF_SEND_TONE:
+ mask <<= 1;
+ case DTMF_SEND_MF:
+ mask <<= 1;
+ if (!((plci->requested_options_conn | plci->requested_options | plci->adapter->requested_options_table[appl->Id-1])
+ & (1L << PRIVATE_DTMF_TONE)))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: DTMF unknown request %04x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, GET_WORD (dtmf_parms[0].info)));
+ PUT_WORD (&result[1], DTMF_UNKNOWN_REQUEST);
+ break;
+ }
+
+ case DTMF_DIGITS_SEND:
+ if (api_parse (&msg[1].info[1], msg[1].length, "wwws", dtmf_parms))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Wrong message format",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ Info = _WRONG_MESSAGE_FORMAT;
+ break;
+ }
+ if (mask & DTMF_LISTEN_ACTIVE_FLAG)
+ {
+ plci->dtmf_send_pulse_ms = GET_WORD (dtmf_parms[1].info);
+ plci->dtmf_send_pause_ms = GET_WORD (dtmf_parms[2].info);
+ }
+ i = 0;
+ j = 0;
+ while ((i < dtmf_parms[3].length) && (j < DTMF_DIGIT_MAP_ENTRIES))
+ {
+ j = 0;
+ while ((j < DTMF_DIGIT_MAP_ENTRIES)
+ && ((dtmf_parms[3].info[i+1] != dtmf_digit_map[j].character)
+ || ((dtmf_digit_map[j].send_mask & mask) == 0)))
+ {
+ j++;
+ }
+ i++;
+ }
+ if (j == DTMF_DIGIT_MAP_ENTRIES)
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Incorrect DTMF digit %02x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, dtmf_parms[3].info[i]));
+ PUT_WORD (&result[1], DTMF_INCORRECT_DIGIT);
+ break;
+ }
+ if (plci->dtmf_send_requests >= ARRAY_SIZE(plci->dtmf_msg_number_queue))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: DTMF request overrun",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ Info = _WRONG_STATE;
+ break;
+ }
+ api_save_msg (dtmf_parms, "wwws", &plci->saved_msg);
+ start_internal_command (Id, plci, dtmf_command);
+ return (false);
+
+ default:
+ dbug (1, dprintf ("[%06lx] %s,%d: DTMF unknown request %04x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, plci->dtmf_cmd));
+ PUT_WORD (&result[1], DTMF_UNKNOWN_REQUEST);
+ }
+ }
+ }
+ sendf (appl, _FACILITY_R | CONFIRM, Id & 0xffffL, Number,
+ "wws", Info, SELECTOR_DTMF, result);
+ return (false);
+}
+
+
+static void dtmf_confirmation (dword Id, PLCI *plci)
+{
+ word Info;
+ word i;
+ byte result[4];
+
+ dbug (1, dprintf ("[%06lx] %s,%d: dtmf_confirmation",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+
+ Info = GOOD;
+ result[0] = 2;
+ PUT_WORD (&result[1], DTMF_SUCCESS);
+ if (plci->dtmf_send_requests != 0)
+ {
+ sendf (plci->appl, _FACILITY_R | CONFIRM, Id & 0xffffL, plci->dtmf_msg_number_queue[0],
+ "wws", GOOD, SELECTOR_DTMF, result);
+ (plci->dtmf_send_requests)--;
+ for (i = 0; i < plci->dtmf_send_requests; i++)
+ plci->dtmf_msg_number_queue[i] = plci->dtmf_msg_number_queue[i+1];
+ }
+}
+
+
+static void dtmf_indication (dword Id, PLCI *plci, byte *msg, word length)
+{
+ word i, j, n;
+
+ dbug (1, dprintf ("[%06lx] %s,%d: dtmf_indication",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+
+ n = 0;
+ for (i = 1; i < length; i++)
+ {
+ j = 0;
+ while ((j < DTMF_DIGIT_MAP_ENTRIES)
+ && ((msg[i] != dtmf_digit_map[j].code)
+ || ((dtmf_digit_map[j].listen_mask & plci->dtmf_rec_active) == 0)))
+ {
+ j++;
+ }
+ if (j < DTMF_DIGIT_MAP_ENTRIES)
+ {
+
+ if ((dtmf_digit_map[j].listen_mask & DTMF_TONE_LISTEN_ACTIVE_FLAG)
+ && (plci->tone_last_indication_code == DTMF_SIGNAL_NO_TONE)
+ && (dtmf_digit_map[j].character != DTMF_SIGNAL_UNIDENTIFIED_TONE))
+ {
+ if (n + 1 == i)
+ {
+ for (i = length; i > n + 1; i--)
+ msg[i] = msg[i - 1];
+ length++;
+ i++;
+ }
+ msg[++n] = DTMF_SIGNAL_UNIDENTIFIED_TONE;
+ }
+ plci->tone_last_indication_code = dtmf_digit_map[j].character;
+
+ msg[++n] = dtmf_digit_map[j].character;
+ }
+ }
+ if (n != 0)
+ {
+ msg[0] = (byte) n;
+ sendf (plci->appl, _FACILITY_I, Id & 0xffffL, 0, "wS", SELECTOR_DTMF, msg);
+ }
+}
+
+
+/*------------------------------------------------------------------*/
+/* DTMF parameters */
+/*------------------------------------------------------------------*/
+
+static void dtmf_parameter_write (PLCI *plci)
+{
+ word i;
+ byte parameter_buffer[DTMF_PARAMETER_BUFFER_SIZE + 2];
+
+ dbug (1, dprintf ("[%06lx] %s,%d: dtmf_parameter_write",
+ (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+ (char *)(FILE_), __LINE__));
+
+ parameter_buffer[0] = plci->dtmf_parameter_length + 1;
+ parameter_buffer[1] = DSP_CTRL_SET_DTMF_PARAMETERS;
+ for (i = 0; i < plci->dtmf_parameter_length; i++)
+ parameter_buffer[2+i] = plci->dtmf_parameter_buffer[i];
+ add_p (plci, FTY, parameter_buffer);
+ sig_req (plci, TEL_CTRL, 0);
+ send_req (plci);
+}
+
+
+static void dtmf_parameter_clear_config (PLCI *plci)
+{
+
+ dbug (1, dprintf ("[%06lx] %s,%d: dtmf_parameter_clear_config",
+ (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+ (char *)(FILE_), __LINE__));
+
+ plci->dtmf_parameter_length = 0;
+}
+
+
+static void dtmf_parameter_prepare_switch (dword Id, PLCI *plci)
+{
+
+ dbug (1, dprintf ("[%06lx] %s,%d: dtmf_parameter_prepare_switch",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+
+}
+
+
+static word dtmf_parameter_save_config (dword Id, PLCI *plci, byte Rc)
+{
+
+ dbug (1, dprintf ("[%06lx] %s,%d: dtmf_parameter_save_config %02x %d",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state));
+
+ return (GOOD);
+}
+
+
+static word dtmf_parameter_restore_config (dword Id, PLCI *plci, byte Rc)
+{
+ word Info;
+
+ dbug (1, dprintf ("[%06lx] %s,%d: dtmf_parameter_restore_config %02x %d",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state));
+
+ Info = GOOD;
+ if ((plci->B1_facilities & B1_FACILITY_DTMFR)
+ && (plci->dtmf_parameter_length != 0))
+ {
+ switch (plci->adjust_b_state)
+ {
+ case ADJUST_B_RESTORE_DTMF_PARAMETER_1:
+ plci->internal_command = plci->adjust_b_command;
+ if (plci->sig_req)
+ {
+ plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_PARAMETER_1;
+ break;
+ }
+ dtmf_parameter_write (plci);
+ plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_PARAMETER_2;
+ break;
+ case ADJUST_B_RESTORE_DTMF_PARAMETER_2:
+ if ((Rc != OK) && (Rc != OK_FC))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Restore DTMF parameters failed %02x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc));
+ Info = _WRONG_STATE;
+ break;
+ }
+ break;
+ }
+ }
+ return (Info);
+}
+
+
+/*------------------------------------------------------------------*/
+/* Line interconnect facilities */
+/*------------------------------------------------------------------*/
+
+
+LI_CONFIG *li_config_table;
+word li_total_channels;
+
+
+/*------------------------------------------------------------------*/
+/* translate a CHI information element to a channel number */
+/* returns 0xff - any channel */
+/* 0xfe - chi wrong coding */
+/* 0xfd - D-channel */
+/* 0x00 - no channel */
+/* else channel number / PRI: timeslot */
+/* if channels is provided we accept more than one channel. */
+/*------------------------------------------------------------------*/
+
+static byte chi_to_channel (byte *chi, dword *pchannelmap)
+{
+ int p;
+ int i;
+ dword map;
+ byte excl;
+ byte ofs;
+ byte ch;
+
+ if (pchannelmap) *pchannelmap = 0;
+ if(!chi[0]) return 0xff;
+ excl = 0;
+
+ if(chi[1] & 0x20) {
+ if(chi[0]==1 && chi[1]==0xac) return 0xfd; /* exclusive d-channel */
+ for(i=1; i<chi[0] && !(chi[i] &0x80); i++);
+ if(i==chi[0] || !(chi[i] &0x80)) return 0xfe;
+ if((chi[1] |0xc8)!=0xe9) return 0xfe;
+ if(chi[1] &0x08) excl = 0x40;
+
+ /* int. id present */
+ if(chi[1] &0x40) {
+ p=i+1;
+ for(i=p; i<chi[0] && !(chi[i] &0x80); i++);
+ if(i==chi[0] || !(chi[i] &0x80)) return 0xfe;
+ }
+
+ /* coding standard, Number/Map, Channel Type */
+ p=i+1;
+ for(i=p; i<chi[0] && !(chi[i] &0x80); i++);
+ if(i==chi[0] || !(chi[i] &0x80)) return 0xfe;
+ if((chi[p]|0xd0)!=0xd3) return 0xfe;
+
+ /* Number/Map */
+ if(chi[p] &0x10) {
+
+ /* map */
+ if((chi[0]-p)==4) ofs = 0;
+ else if((chi[0]-p)==3) ofs = 1;
+ else return 0xfe;
+ ch = 0;
+ map = 0;
+ for(i=0; i<4 && p<chi[0]; i++) {
+ p++;
+ ch += 8;
+ map <<= 8;
+ if(chi[p]) {
+ for (ch=0; !(chi[p] & (1 << ch)); ch++);
+ map |= chi[p];
+ }
+ }
+ ch += ofs;
+ map <<= ofs;
+ }
+ else {
+
+ /* number */
+ p=i+1;
+ ch = chi[p] &0x3f;
+ if(pchannelmap) {
+ if((byte)(chi[0]-p)>30) return 0xfe;
+ map = 0;
+ for(i=p; i<=chi[0]; i++) {
+ if ((chi[i] &0x7f) > 31) return 0xfe;
+ map |= (1L << (chi[i] &0x7f));
+ }
+ }
+ else {
+ if(p!=chi[0]) return 0xfe;
+ if (ch > 31) return 0xfe;
+ map = (1L << ch);
+ }
+ if(chi[p] &0x40) return 0xfe;
+ }
+ if (pchannelmap) *pchannelmap = map;
+ else if (map != ((dword)(1L << ch))) return 0xfe;
+ return (byte)(excl | ch);
+ }
+ else { /* not PRI */
+ for(i=1; i<chi[0] && !(chi[i] &0x80); i++);
+ if(i!=chi[0] || !(chi[i] &0x80)) return 0xfe;
+ if(chi[1] &0x08) excl = 0x40;
+
+ switch(chi[1] |0x98) {
+ case 0x98: return 0;
+ case 0x99:
+ if (pchannelmap) *pchannelmap = 2;
+ return excl |1;
+ case 0x9a:
+ if (pchannelmap) *pchannelmap = 4;
+ return excl |2;
+ case 0x9b: return 0xff;
+ case 0x9c: return 0xfd; /* d-ch */
+ default: return 0xfe;
+ }
+ }
+}
+
+
+static void mixer_set_bchannel_id_esc (PLCI *plci, byte bchannel_id)
+{
+ DIVA_CAPI_ADAPTER *a;
+ PLCI *splci;
+ byte old_id;
+
+ a = plci->adapter;
+ old_id = plci->li_bchannel_id;
+ if (a->li_pri)
+ {
+ if ((old_id != 0) && (li_config_table[a->li_base + (old_id - 1)].plci == plci))
+ li_config_table[a->li_base + (old_id - 1)].plci = NULL;
+ plci->li_bchannel_id = (bchannel_id & 0x1f) + 1;
+ if (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == NULL)
+ li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci = plci;
+ }
+ else
+ {
+ if (((bchannel_id & 0x03) == 1) || ((bchannel_id & 0x03) == 2))
+ {
+ if ((old_id != 0) && (li_config_table[a->li_base + (old_id - 1)].plci == plci))
+ li_config_table[a->li_base + (old_id - 1)].plci = NULL;
+ plci->li_bchannel_id = bchannel_id & 0x03;
+ if ((a->AdvSignalPLCI != NULL) && (a->AdvSignalPLCI != plci) && (a->AdvSignalPLCI->tel == ADV_VOICE))
+ {
+ splci = a->AdvSignalPLCI;
+ if (li_config_table[a->li_base + (2 - plci->li_bchannel_id)].plci == NULL)
+ {
+ if ((splci->li_bchannel_id != 0)
+ && (li_config_table[a->li_base + (splci->li_bchannel_id - 1)].plci == splci))
+ {
+ li_config_table[a->li_base + (splci->li_bchannel_id - 1)].plci = NULL;
+ }
+ splci->li_bchannel_id = 3 - plci->li_bchannel_id;
+ li_config_table[a->li_base + (2 - plci->li_bchannel_id)].plci = splci;
+ dbug (1, dprintf ("[%06lx] %s,%d: adv_voice_set_bchannel_id_esc %d",
+ (dword)((splci->Id << 8) | UnMapController (splci->adapter->Id)),
+ (char *)(FILE_), __LINE__, splci->li_bchannel_id));
+ }
+ }
+ if (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == NULL)
+ li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci = plci;
+ }
+ }
+ if ((old_id == 0) && (plci->li_bchannel_id != 0)
+ && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
+ {
+ mixer_clear_config (plci);
+ }
+ dbug (1, dprintf ("[%06lx] %s,%d: mixer_set_bchannel_id_esc %d %d",
+ (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+ (char *)(FILE_), __LINE__, bchannel_id, plci->li_bchannel_id));
+}
+
+
+static void mixer_set_bchannel_id (PLCI *plci, byte *chi)
+{
+ DIVA_CAPI_ADAPTER *a;
+ PLCI *splci;
+ byte ch, old_id;
+
+ a = plci->adapter;
+ old_id = plci->li_bchannel_id;
+ ch = chi_to_channel (chi, NULL);
+ if (!(ch & 0x80))
+ {
+ if (a->li_pri)
+ {
+ if ((old_id != 0) && (li_config_table[a->li_base + (old_id - 1)].plci == plci))
+ li_config_table[a->li_base + (old_id - 1)].plci = NULL;
+ plci->li_bchannel_id = (ch & 0x1f) + 1;
+ if (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == NULL)
+ li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci = plci;
+ }
+ else
+ {
+ if (((ch & 0x1f) == 1) || ((ch & 0x1f) == 2))
+ {
+ if ((old_id != 0) && (li_config_table[a->li_base + (old_id - 1)].plci == plci))
+ li_config_table[a->li_base + (old_id - 1)].plci = NULL;
+ plci->li_bchannel_id = ch & 0x1f;
+ if ((a->AdvSignalPLCI != NULL) && (a->AdvSignalPLCI != plci) && (a->AdvSignalPLCI->tel == ADV_VOICE))
+ {
+ splci = a->AdvSignalPLCI;
+ if (li_config_table[a->li_base + (2 - plci->li_bchannel_id)].plci == NULL)
+ {
+ if ((splci->li_bchannel_id != 0)
+ && (li_config_table[a->li_base + (splci->li_bchannel_id - 1)].plci == splci))
+ {
+ li_config_table[a->li_base + (splci->li_bchannel_id - 1)].plci = NULL;
+ }
+ splci->li_bchannel_id = 3 - plci->li_bchannel_id;
+ li_config_table[a->li_base + (2 - plci->li_bchannel_id)].plci = splci;
+ dbug (1, dprintf ("[%06lx] %s,%d: adv_voice_set_bchannel_id %d",
+ (dword)((splci->Id << 8) | UnMapController (splci->adapter->Id)),
+ (char *)(FILE_), __LINE__, splci->li_bchannel_id));
+ }
+ }
+ if (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == NULL)
+ li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci = plci;
+ }
+ }
+ }
+ if ((old_id == 0) && (plci->li_bchannel_id != 0)
+ && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
+ {
+ mixer_clear_config (plci);
+ }
+ dbug (1, dprintf ("[%06lx] %s,%d: mixer_set_bchannel_id %02x %d",
+ (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+ (char *)(FILE_), __LINE__, ch, plci->li_bchannel_id));
+}
+
+
+#define MIXER_MAX_DUMP_CHANNELS 34
+
+static void mixer_calculate_coefs (DIVA_CAPI_ADAPTER *a)
+{
+static char hex_digit_table[0x10] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
+ word n, i, j;
+ char *p;
+ char hex_line[2 * MIXER_MAX_DUMP_CHANNELS + MIXER_MAX_DUMP_CHANNELS / 8 + 4];
+
+ dbug (1, dprintf ("[%06lx] %s,%d: mixer_calculate_coefs",
+ (dword)(UnMapController (a->Id)), (char *)(FILE_), __LINE__));
+
+ for (i = 0; i < li_total_channels; i++)
+ {
+ li_config_table[i].channel &= LI_CHANNEL_ADDRESSES_SET;
+ if (li_config_table[i].chflags != 0)
+ li_config_table[i].channel |= LI_CHANNEL_INVOLVED;
+ else
+ {
+ for (j = 0; j < li_total_channels; j++)
+ {
+ if (((li_config_table[i].flag_table[j]) != 0)
+ || ((li_config_table[j].flag_table[i]) != 0))
+ {
+ li_config_table[i].channel |= LI_CHANNEL_INVOLVED;
+ }
+ if (((li_config_table[i].flag_table[j] & LI_FLAG_CONFERENCE) != 0)
+ || ((li_config_table[j].flag_table[i] & LI_FLAG_CONFERENCE) != 0))
+ {
+ li_config_table[i].channel |= LI_CHANNEL_CONFERENCE;
+ }
+ }
+ }
+ }
+ for (i = 0; i < li_total_channels; i++)
+ {
+ for (j = 0; j < li_total_channels; j++)
+ {
+ li_config_table[i].coef_table[j] &= ~(LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC);
+ if (li_config_table[i].flag_table[j] & LI_FLAG_CONFERENCE)
+ li_config_table[i].coef_table[j] |= LI_COEF_CH_CH;
+ }
+ }
+ for (n = 0; n < li_total_channels; n++)
+ {
+ if (li_config_table[n].channel & LI_CHANNEL_CONFERENCE)
+ {
+ for (i = 0; i < li_total_channels; i++)
+ {
+ if (li_config_table[i].channel & LI_CHANNEL_CONFERENCE)
+ {
+ for (j = 0; j < li_total_channels; j++)
+ {
+ li_config_table[i].coef_table[j] |=
+ li_config_table[i].coef_table[n] & li_config_table[n].coef_table[j];
+ }
+ }
+ }
+ }
+ }
+ for (i = 0; i < li_total_channels; i++)
+ {
+ if (li_config_table[i].channel & LI_CHANNEL_INVOLVED)
+ {
+ li_config_table[i].coef_table[i] &= ~LI_COEF_CH_CH;
+ for (j = 0; j < li_total_channels; j++)
+ {
+ if (li_config_table[i].coef_table[j] & LI_COEF_CH_CH)
+ li_config_table[i].flag_table[j] |= LI_FLAG_CONFERENCE;
+ }
+ if (li_config_table[i].flag_table[i] & LI_FLAG_CONFERENCE)
+ li_config_table[i].coef_table[i] |= LI_COEF_CH_CH;
+ }
+ }
+ for (i = 0; i < li_total_channels; i++)
+ {
+ if (li_config_table[i].channel & LI_CHANNEL_INVOLVED)
+ {
+ for (j = 0; j < li_total_channels; j++)
+ {
+ if (li_config_table[i].flag_table[j] & LI_FLAG_INTERCONNECT)
+ li_config_table[i].coef_table[j] |= LI_COEF_CH_CH;
+ if (li_config_table[i].flag_table[j] & LI_FLAG_MONITOR)
+ li_config_table[i].coef_table[j] |= LI_COEF_CH_PC;
+ if (li_config_table[i].flag_table[j] & LI_FLAG_MIX)
+ li_config_table[i].coef_table[j] |= LI_COEF_PC_CH;
+ if (li_config_table[i].flag_table[j] & LI_FLAG_PCCONNECT)
+ li_config_table[i].coef_table[j] |= LI_COEF_PC_PC;
+ }
+ if (li_config_table[i].chflags & LI_CHFLAG_MONITOR)
+ {
+ for (j = 0; j < li_total_channels; j++)
+ {
+ if (li_config_table[i].flag_table[j] & LI_FLAG_INTERCONNECT)
+ {
+ li_config_table[i].coef_table[j] |= LI_COEF_CH_PC;
+ if (li_config_table[j].chflags & LI_CHFLAG_MIX)
+ li_config_table[i].coef_table[j] |= LI_COEF_PC_CH | LI_COEF_PC_PC;
+ }
+ }
+ }
+ if (li_config_table[i].chflags & LI_CHFLAG_MIX)
+ {
+ for (j = 0; j < li_total_channels; j++)
+ {
+ if (li_config_table[j].flag_table[i] & LI_FLAG_INTERCONNECT)
+ li_config_table[j].coef_table[i] |= LI_COEF_PC_CH;
+ }
+ }
+ if (li_config_table[i].chflags & LI_CHFLAG_LOOP)
+ {
+ for (j = 0; j < li_total_channels; j++)
+ {
+ if (li_config_table[i].flag_table[j] & LI_FLAG_INTERCONNECT)
+ {
+ for (n = 0; n < li_total_channels; n++)
+ {
+ if (li_config_table[n].flag_table[i] & LI_FLAG_INTERCONNECT)
+ {
+ li_config_table[n].coef_table[j] |= LI_COEF_CH_CH;
+ if (li_config_table[j].chflags & LI_CHFLAG_MIX)
+ {
+ li_config_table[n].coef_table[j] |= LI_COEF_PC_CH;
+ if (li_config_table[n].chflags & LI_CHFLAG_MONITOR)
+ li_config_table[n].coef_table[j] |= LI_COEF_CH_PC | LI_COEF_PC_PC;
+ }
+ else if (li_config_table[n].chflags & LI_CHFLAG_MONITOR)
+ li_config_table[n].coef_table[j] |= LI_COEF_CH_PC;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ for (i = 0; i < li_total_channels; i++)
+ {
+ if (li_config_table[i].channel & LI_CHANNEL_INVOLVED)
+ {
+ if (li_config_table[i].chflags & (LI_CHFLAG_MONITOR | LI_CHFLAG_MIX | LI_CHFLAG_LOOP))
+ li_config_table[i].channel |= LI_CHANNEL_ACTIVE;
+ if (li_config_table[i].chflags & LI_CHFLAG_MONITOR)
+ li_config_table[i].channel |= LI_CHANNEL_RX_DATA;
+ if (li_config_table[i].chflags & LI_CHFLAG_MIX)
+ li_config_table[i].channel |= LI_CHANNEL_TX_DATA;
+ for (j = 0; j < li_total_channels; j++)
+ {
+ if ((li_config_table[i].flag_table[j] &
+ (LI_FLAG_INTERCONNECT | LI_FLAG_PCCONNECT | LI_FLAG_CONFERENCE | LI_FLAG_MONITOR))
+ || (li_config_table[j].flag_table[i] &
+ (LI_FLAG_INTERCONNECT | LI_FLAG_PCCONNECT | LI_FLAG_CONFERENCE | LI_FLAG_ANNOUNCEMENT | LI_FLAG_MIX)))
+ {
+ li_config_table[i].channel |= LI_CHANNEL_ACTIVE;
+ }
+ if (li_config_table[i].flag_table[j] & (LI_FLAG_PCCONNECT | LI_FLAG_MONITOR))
+ li_config_table[i].channel |= LI_CHANNEL_RX_DATA;
+ if (li_config_table[j].flag_table[i] & (LI_FLAG_PCCONNECT | LI_FLAG_ANNOUNCEMENT | LI_FLAG_MIX))
+ li_config_table[i].channel |= LI_CHANNEL_TX_DATA;
+ }
+ if (!(li_config_table[i].channel & LI_CHANNEL_ACTIVE))
+ {
+ li_config_table[i].coef_table[i] |= LI_COEF_PC_CH | LI_COEF_CH_PC;
+ li_config_table[i].channel |= LI_CHANNEL_TX_DATA | LI_CHANNEL_RX_DATA;
+ }
+ }
+ }
+ for (i = 0; i < li_total_channels; i++)
+ {
+ if (li_config_table[i].channel & LI_CHANNEL_INVOLVED)
+ {
+ j = 0;
+ while ((j < li_total_channels) && !(li_config_table[i].flag_table[j] & LI_FLAG_ANNOUNCEMENT))
+ j++;
+ if (j < li_total_channels)
+ {
+ for (j = 0; j < li_total_channels; j++)
+ {
+ li_config_table[i].coef_table[j] &= ~(LI_COEF_CH_CH | LI_COEF_PC_CH);
+ if (li_config_table[i].flag_table[j] & LI_FLAG_ANNOUNCEMENT)
+ li_config_table[i].coef_table[j] |= LI_COEF_PC_CH;
+ }
+ }
+ }
+ }
+ n = li_total_channels;
+ if (n > MIXER_MAX_DUMP_CHANNELS)
+ n = MIXER_MAX_DUMP_CHANNELS;
+ p = hex_line;
+ for (j = 0; j < n; j++)
+ {
+ if ((j & 0x7) == 0)
+ *(p++) = ' ';
+ *(p++) = hex_digit_table[li_config_table[j].curchnl >> 4];
+ *(p++) = hex_digit_table[li_config_table[j].curchnl & 0xf];
+ }
+ *p = '\0';
+ dbug (1, dprintf ("[%06lx] CURRENT %s",
+ (dword)(UnMapController (a->Id)), (char *) hex_line));
+ p = hex_line;
+ for (j = 0; j < n; j++)
+ {
+ if ((j & 0x7) == 0)
+ *(p++) = ' ';
+ *(p++) = hex_digit_table[li_config_table[j].channel >> 4];
+ *(p++) = hex_digit_table[li_config_table[j].channel & 0xf];
+ }
+ *p = '\0';
+ dbug (1, dprintf ("[%06lx] CHANNEL %s",
+ (dword)(UnMapController (a->Id)), (char *) hex_line));
+ p = hex_line;
+ for (j = 0; j < n; j++)
+ {
+ if ((j & 0x7) == 0)
+ *(p++) = ' ';
+ *(p++) = hex_digit_table[li_config_table[j].chflags >> 4];
+ *(p++) = hex_digit_table[li_config_table[j].chflags & 0xf];
+ }
+ *p = '\0';
+ dbug (1, dprintf ("[%06lx] CHFLAG %s",
+ (dword)(UnMapController (a->Id)), (char *) hex_line));
+ for (i = 0; i < n; i++)
+ {
+ p = hex_line;
+ for (j = 0; j < n; j++)
+ {
+ if ((j & 0x7) == 0)
+ *(p++) = ' ';
+ *(p++) = hex_digit_table[li_config_table[i].flag_table[j] >> 4];
+ *(p++) = hex_digit_table[li_config_table[i].flag_table[j] & 0xf];
+ }
+ *p = '\0';
+ dbug (1, dprintf ("[%06lx] FLAG[%02x]%s",
+ (dword)(UnMapController (a->Id)), i, (char *) hex_line));
+ }
+ for (i = 0; i < n; i++)
+ {
+ p = hex_line;
+ for (j = 0; j < n; j++)
+ {
+ if ((j & 0x7) == 0)
+ *(p++) = ' ';
+ *(p++) = hex_digit_table[li_config_table[i].coef_table[j] >> 4];
+ *(p++) = hex_digit_table[li_config_table[i].coef_table[j] & 0xf];
+ }
+ *p = '\0';
+ dbug (1, dprintf ("[%06lx] COEF[%02x]%s",
+ (dword)(UnMapController (a->Id)), i, (char *) hex_line));
+ }
+}
+
+
+static struct
+{
+ byte mask;
+ byte line_flags;
+} mixer_write_prog_pri[] =
+{
+ { LI_COEF_CH_CH, 0 },
+ { LI_COEF_CH_PC, MIXER_COEF_LINE_TO_PC_FLAG },
+ { LI_COEF_PC_CH, MIXER_COEF_LINE_FROM_PC_FLAG },
+ { LI_COEF_PC_PC, MIXER_COEF_LINE_TO_PC_FLAG | MIXER_COEF_LINE_FROM_PC_FLAG }
+};
+
+static struct
+{
+ byte from_ch;
+ byte to_ch;
+ byte mask;
+ byte xconnect_override;
+} mixer_write_prog_bri[] =
+{
+ { 0, 0, LI_COEF_CH_CH, 0x01 }, /* B to B */
+ { 1, 0, LI_COEF_CH_CH, 0x01 }, /* Alt B to B */
+ { 0, 0, LI_COEF_PC_CH, 0x80 }, /* PC to B */
+ { 1, 0, LI_COEF_PC_CH, 0x01 }, /* Alt PC to B */
+ { 2, 0, LI_COEF_CH_CH, 0x00 }, /* IC to B */
+ { 3, 0, LI_COEF_CH_CH, 0x00 }, /* Alt IC to B */
+ { 0, 0, LI_COEF_CH_PC, 0x80 }, /* B to PC */
+ { 1, 0, LI_COEF_CH_PC, 0x01 }, /* Alt B to PC */
+ { 0, 0, LI_COEF_PC_PC, 0x01 }, /* PC to PC */
+ { 1, 0, LI_COEF_PC_PC, 0x01 }, /* Alt PC to PC */
+ { 2, 0, LI_COEF_CH_PC, 0x00 }, /* IC to PC */
+ { 3, 0, LI_COEF_CH_PC, 0x00 }, /* Alt IC to PC */
+ { 0, 2, LI_COEF_CH_CH, 0x00 }, /* B to IC */
+ { 1, 2, LI_COEF_CH_CH, 0x00 }, /* Alt B to IC */
+ { 0, 2, LI_COEF_PC_CH, 0x00 }, /* PC to IC */
+ { 1, 2, LI_COEF_PC_CH, 0x00 }, /* Alt PC to IC */
+ { 2, 2, LI_COEF_CH_CH, 0x00 }, /* IC to IC */
+ { 3, 2, LI_COEF_CH_CH, 0x00 }, /* Alt IC to IC */
+ { 1, 1, LI_COEF_CH_CH, 0x01 }, /* Alt B to Alt B */
+ { 0, 1, LI_COEF_CH_CH, 0x01 }, /* B to Alt B */
+ { 1, 1, LI_COEF_PC_CH, 0x80 }, /* Alt PC to Alt B */
+ { 0, 1, LI_COEF_PC_CH, 0x01 }, /* PC to Alt B */
+ { 3, 1, LI_COEF_CH_CH, 0x00 }, /* Alt IC to Alt B */
+ { 2, 1, LI_COEF_CH_CH, 0x00 }, /* IC to Alt B */
+ { 1, 1, LI_COEF_CH_PC, 0x80 }, /* Alt B to Alt PC */
+ { 0, 1, LI_COEF_CH_PC, 0x01 }, /* B to Alt PC */
+ { 1, 1, LI_COEF_PC_PC, 0x01 }, /* Alt PC to Alt PC */
+ { 0, 1, LI_COEF_PC_PC, 0x01 }, /* PC to Alt PC */
+ { 3, 1, LI_COEF_CH_PC, 0x00 }, /* Alt IC to Alt PC */
+ { 2, 1, LI_COEF_CH_PC, 0x00 }, /* IC to Alt PC */
+ { 1, 3, LI_COEF_CH_CH, 0x00 }, /* Alt B to Alt IC */
+ { 0, 3, LI_COEF_CH_CH, 0x00 }, /* B to Alt IC */
+ { 1, 3, LI_COEF_PC_CH, 0x00 }, /* Alt PC to Alt IC */
+ { 0, 3, LI_COEF_PC_CH, 0x00 }, /* PC to Alt IC */
+ { 3, 3, LI_COEF_CH_CH, 0x00 }, /* Alt IC to Alt IC */
+ { 2, 3, LI_COEF_CH_CH, 0x00 } /* IC to Alt IC */
+};
+
+static byte mixer_swapped_index_bri[] =
+{
+ 18, /* B to B */
+ 19, /* Alt B to B */
+ 20, /* PC to B */
+ 21, /* Alt PC to B */
+ 22, /* IC to B */
+ 23, /* Alt IC to B */
+ 24, /* B to PC */
+ 25, /* Alt B to PC */
+ 26, /* PC to PC */
+ 27, /* Alt PC to PC */
+ 28, /* IC to PC */
+ 29, /* Alt IC to PC */
+ 30, /* B to IC */
+ 31, /* Alt B to IC */
+ 32, /* PC to IC */
+ 33, /* Alt PC to IC */
+ 34, /* IC to IC */
+ 35, /* Alt IC to IC */
+ 0, /* Alt B to Alt B */
+ 1, /* B to Alt B */
+ 2, /* Alt PC to Alt B */
+ 3, /* PC to Alt B */
+ 4, /* Alt IC to Alt B */
+ 5, /* IC to Alt B */
+ 6, /* Alt B to Alt PC */
+ 7, /* B to Alt PC */
+ 8, /* Alt PC to Alt PC */
+ 9, /* PC to Alt PC */
+ 10, /* Alt IC to Alt PC */
+ 11, /* IC to Alt PC */
+ 12, /* Alt B to Alt IC */
+ 13, /* B to Alt IC */
+ 14, /* Alt PC to Alt IC */
+ 15, /* PC to Alt IC */
+ 16, /* Alt IC to Alt IC */
+ 17 /* IC to Alt IC */
+};
+
+static struct
+{
+ byte mask;
+ byte from_pc;
+ byte to_pc;
+} xconnect_write_prog[] =
+{
+ { LI_COEF_CH_CH, false, false },
+ { LI_COEF_CH_PC, false, true },
+ { LI_COEF_PC_CH, true, false },
+ { LI_COEF_PC_PC, true, true }
+};
+
+
+static void xconnect_query_addresses (PLCI *plci)
+{
+ DIVA_CAPI_ADAPTER *a;
+ word w, ch;
+ byte *p;
+
+ dbug (1, dprintf ("[%06lx] %s,%d: xconnect_query_addresses",
+ (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+ (char *)(FILE_), __LINE__));
+
+ a = plci->adapter;
+ if (a->li_pri && ((plci->li_bchannel_id == 0)
+ || (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci != plci)))
+ {
+ dbug (1, dprintf ("[%06x] %s,%d: Channel id wiped out",
+ (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+ (char *)(FILE_), __LINE__));
+ return;
+ }
+ p = plci->internal_req_buffer;
+ ch = (a->li_pri) ? plci->li_bchannel_id - 1 : 0;
+ *(p++) = UDATA_REQUEST_XCONNECT_FROM;
+ w = ch;
+ *(p++) = (byte) w;
+ *(p++) = (byte)(w >> 8);
+ w = ch | XCONNECT_CHANNEL_PORT_PC;
+ *(p++) = (byte) w;
+ *(p++) = (byte)(w >> 8);
+ plci->NData[0].P = plci->internal_req_buffer;
+ plci->NData[0].PLength = p - plci->internal_req_buffer;
+ plci->NL.X = plci->NData;
+ plci->NL.ReqCh = 0;
+ plci->NL.Req = plci->nl_req = (byte) N_UDATA;
+ plci->adapter->request (&plci->NL);
+}
+
+
+static void xconnect_write_coefs (PLCI *plci, word internal_command)
+{
+
+ dbug (1, dprintf ("[%06lx] %s,%d: xconnect_write_coefs %04x",
+ (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+ (char *)(FILE_), __LINE__, internal_command));
+
+ plci->li_write_command = internal_command;
+ plci->li_write_channel = 0;
+}
+
+
+static byte xconnect_write_coefs_process (dword Id, PLCI *plci, byte Rc)
+{
+ DIVA_CAPI_ADAPTER *a;
+ word w, n, i, j, r, s, to_ch;
+ dword d;
+ byte *p;
+ struct xconnect_transfer_address_s *transfer_address;
+ byte ch_map[MIXER_CHANNELS_BRI];
+
+ dbug (1, dprintf ("[%06x] %s,%d: xconnect_write_coefs_process %02x %d",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc, plci->li_write_channel));
+
+ a = plci->adapter;
+ if ((plci->li_bchannel_id == 0)
+ || (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci != plci))
+ {
+ dbug (1, dprintf ("[%06x] %s,%d: Channel id wiped out",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ return (true);
+ }
+ i = a->li_base + (plci->li_bchannel_id - 1);
+ j = plci->li_write_channel;
+ p = plci->internal_req_buffer;
+ if (j != 0)
+ {
+ if ((Rc != OK) && (Rc != OK_FC))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: LI write coefs failed %02x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc));
+ return (false);
+ }
+ }
+ if (li_config_table[i].adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
+ {
+ r = 0;
+ s = 0;
+ if (j < li_total_channels)
+ {
+ if (li_config_table[i].channel & LI_CHANNEL_ADDRESSES_SET)
+ {
+ s = ((li_config_table[i].send_b.card_address.low | li_config_table[i].send_b.card_address.high) ?
+ (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_CH_PC | LI_COEF_PC_PC)) &
+ ((li_config_table[i].send_pc.card_address.low | li_config_table[i].send_pc.card_address.high) ?
+ (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_CH_CH | LI_COEF_PC_CH));
+ }
+ r = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4));
+ while ((j < li_total_channels)
+ && ((r == 0)
+ || (!(li_config_table[j].channel & LI_CHANNEL_ADDRESSES_SET))
+ || (!li_config_table[j].adapter->li_pri
+ && (j >= li_config_table[j].adapter->li_base + MIXER_BCHANNELS_BRI))
+ || (((li_config_table[j].send_b.card_address.low != li_config_table[i].send_b.card_address.low)
+ || (li_config_table[j].send_b.card_address.high != li_config_table[i].send_b.card_address.high))
+ && (!(a->manufacturer_features & MANUFACTURER_FEATURE_DMACONNECT)
+ || !(li_config_table[j].adapter->manufacturer_features & MANUFACTURER_FEATURE_DMACONNECT)))
+ || ((li_config_table[j].adapter->li_base != a->li_base)
+ && !(r & s &
+ ((li_config_table[j].send_b.card_address.low | li_config_table[j].send_b.card_address.high) ?
+ (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_PC_CH | LI_COEF_PC_PC)) &
+ ((li_config_table[j].send_pc.card_address.low | li_config_table[j].send_pc.card_address.high) ?
+ (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_CH_CH | LI_COEF_CH_PC))))))
+ {
+ j++;
+ if (j < li_total_channels)
+ r = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4));
+ }
+ }
+ if (j < li_total_channels)
+ {
+ plci->internal_command = plci->li_write_command;
+ if (plci_nl_busy (plci))
+ return (true);
+ to_ch = (a->li_pri) ? plci->li_bchannel_id - 1 : 0;
+ *(p++) = UDATA_REQUEST_XCONNECT_TO;
+ do
+ {
+ if (li_config_table[j].adapter->li_base != a->li_base)
+ {
+ r &= s &
+ ((li_config_table[j].send_b.card_address.low | li_config_table[j].send_b.card_address.high) ?
+ (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_PC_CH | LI_COEF_PC_PC)) &
+ ((li_config_table[j].send_pc.card_address.low | li_config_table[j].send_pc.card_address.high) ?
+ (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_CH_CH | LI_COEF_CH_PC));
+ }
+ n = 0;
+ do
+ {
+ if (r & xconnect_write_prog[n].mask)
+ {
+ if (xconnect_write_prog[n].from_pc)
+ transfer_address = &(li_config_table[j].send_pc);
+ else
+ transfer_address = &(li_config_table[j].send_b);
+ d = transfer_address->card_address.low;
+ *(p++) = (byte) d;
+ *(p++) = (byte)(d >> 8);
+ *(p++) = (byte)(d >> 16);
+ *(p++) = (byte)(d >> 24);
+ d = transfer_address->card_address.high;
+ *(p++) = (byte) d;
+ *(p++) = (byte)(d >> 8);
+ *(p++) = (byte)(d >> 16);
+ *(p++) = (byte)(d >> 24);
+ d = transfer_address->offset;
+ *(p++) = (byte) d;
+ *(p++) = (byte)(d >> 8);
+ *(p++) = (byte)(d >> 16);
+ *(p++) = (byte)(d >> 24);
+ w = xconnect_write_prog[n].to_pc ? to_ch | XCONNECT_CHANNEL_PORT_PC : to_ch;
+ *(p++) = (byte) w;
+ *(p++) = (byte)(w >> 8);
+ w = ((li_config_table[i].coef_table[j] & xconnect_write_prog[n].mask) == 0) ? 0x01 :
+ (li_config_table[i].adapter->u_law ?
+ (li_config_table[j].adapter->u_law ? 0x80 : 0x86) :
+ (li_config_table[j].adapter->u_law ? 0x7a : 0x80));
+ *(p++) = (byte) w;
+ *(p++) = (byte) 0;
+ li_config_table[i].coef_table[j] ^= xconnect_write_prog[n].mask << 4;
+ }
+ n++;
+ } while ((n < ARRAY_SIZE(xconnect_write_prog))
+ && ((p - plci->internal_req_buffer) + 16 < INTERNAL_REQ_BUFFER_SIZE));
+ if (n == ARRAY_SIZE(xconnect_write_prog))
+ {
+ do
+ {
+ j++;
+ if (j < li_total_channels)
+ r = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4));
+ } while ((j < li_total_channels)
+ && ((r == 0)
+ || (!(li_config_table[j].channel & LI_CHANNEL_ADDRESSES_SET))
+ || (!li_config_table[j].adapter->li_pri
+ && (j >= li_config_table[j].adapter->li_base + MIXER_BCHANNELS_BRI))
+ || (((li_config_table[j].send_b.card_address.low != li_config_table[i].send_b.card_address.low)
+ || (li_config_table[j].send_b.card_address.high != li_config_table[i].send_b.card_address.high))
+ && (!(a->manufacturer_features & MANUFACTURER_FEATURE_DMACONNECT)
+ || !(li_config_table[j].adapter->manufacturer_features & MANUFACTURER_FEATURE_DMACONNECT)))
+ || ((li_config_table[j].adapter->li_base != a->li_base)
+ && !(r & s &
+ ((li_config_table[j].send_b.card_address.low | li_config_table[j].send_b.card_address.high) ?
+ (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_PC_CH | LI_COEF_PC_PC)) &
+ ((li_config_table[j].send_pc.card_address.low | li_config_table[j].send_pc.card_address.high) ?
+ (LI_COEF_CH_CH | LI_COEF_CH_PC | LI_COEF_PC_CH | LI_COEF_PC_PC) : (LI_COEF_CH_CH | LI_COEF_CH_PC))))));
+ }
+ } while ((j < li_total_channels)
+ && ((p - plci->internal_req_buffer) + 16 < INTERNAL_REQ_BUFFER_SIZE));
+ }
+ else if (j == li_total_channels)
+ {
+ plci->internal_command = plci->li_write_command;
+ if (plci_nl_busy (plci))
+ return (true);
+ if (a->li_pri)
+ {
+ *(p++) = UDATA_REQUEST_SET_MIXER_COEFS_PRI_SYNC;
+ w = 0;
+ if (li_config_table[i].channel & LI_CHANNEL_TX_DATA)
+ w |= MIXER_FEATURE_ENABLE_TX_DATA;
+ if (li_config_table[i].channel & LI_CHANNEL_RX_DATA)
+ w |= MIXER_FEATURE_ENABLE_RX_DATA;
+ *(p++) = (byte) w;
+ *(p++) = (byte)(w >> 8);
+ }
+ else
+ {
+ *(p++) = UDATA_REQUEST_SET_MIXER_COEFS_BRI;
+ w = 0;
+ if ((plci->tel == ADV_VOICE) && (plci == a->AdvSignalPLCI)
+ && (ADV_VOICE_NEW_COEF_BASE + sizeof(word) <= a->adv_voice_coef_length))
+ {
+ w = GET_WORD (a->adv_voice_coef_buffer + ADV_VOICE_NEW_COEF_BASE);
+ }
+ if (li_config_table[i].channel & LI_CHANNEL_TX_DATA)
+ w |= MIXER_FEATURE_ENABLE_TX_DATA;
+ if (li_config_table[i].channel & LI_CHANNEL_RX_DATA)
+ w |= MIXER_FEATURE_ENABLE_RX_DATA;
+ *(p++) = (byte) w;
+ *(p++) = (byte)(w >> 8);
+ for (j = 0; j < sizeof(ch_map); j += 2)
+ {
+ if (plci->li_bchannel_id == 2)
+ {
+ ch_map[j] = (byte)(j+1);
+ ch_map[j+1] = (byte) j;
+ }
+ else
+ {
+ ch_map[j] = (byte) j;
+ ch_map[j+1] = (byte)(j+1);
+ }
+ }
+ for (n = 0; n < ARRAY_SIZE(mixer_write_prog_bri); n++)
+ {
+ i = a->li_base + ch_map[mixer_write_prog_bri[n].to_ch];
+ j = a->li_base + ch_map[mixer_write_prog_bri[n].from_ch];
+ if (li_config_table[i].channel & li_config_table[j].channel & LI_CHANNEL_INVOLVED)
+ {
+ *p = (mixer_write_prog_bri[n].xconnect_override != 0) ?
+ mixer_write_prog_bri[n].xconnect_override :
+ ((li_config_table[i].coef_table[j] & mixer_write_prog_bri[n].mask) ? 0x80 : 0x01);
+ if ((i >= a->li_base + MIXER_BCHANNELS_BRI) || (j >= a->li_base + MIXER_BCHANNELS_BRI))
+ {
+ w = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4));
+ li_config_table[i].coef_table[j] ^= (w & mixer_write_prog_bri[n].mask) << 4;
+ }
+ }
+ else
+ {
+ *p = 0x00;
+ if ((a->AdvSignalPLCI != NULL) && (a->AdvSignalPLCI->tel == ADV_VOICE))
+ {
+ w = (plci == a->AdvSignalPLCI) ? n : mixer_swapped_index_bri[n];
+ if (ADV_VOICE_NEW_COEF_BASE + sizeof(word) + w < a->adv_voice_coef_length)
+ *p = a->adv_voice_coef_buffer[ADV_VOICE_NEW_COEF_BASE + sizeof(word) + w];
+ }
+ }
+ p++;
+ }
+ }
+ j = li_total_channels + 1;
+ }
+ }
+ else
+ {
+ if (j <= li_total_channels)
+ {
+ plci->internal_command = plci->li_write_command;
+ if (plci_nl_busy (plci))
+ return (true);
+ if (j < a->li_base)
+ j = a->li_base;
+ if (a->li_pri)
+ {
+ *(p++) = UDATA_REQUEST_SET_MIXER_COEFS_PRI_SYNC;
+ w = 0;
+ if (li_config_table[i].channel & LI_CHANNEL_TX_DATA)
+ w |= MIXER_FEATURE_ENABLE_TX_DATA;
+ if (li_config_table[i].channel & LI_CHANNEL_RX_DATA)
+ w |= MIXER_FEATURE_ENABLE_RX_DATA;
+ *(p++) = (byte) w;
+ *(p++) = (byte)(w >> 8);
+ for (n = 0; n < ARRAY_SIZE(mixer_write_prog_pri); n++)
+ {
+ *(p++) = (byte)((plci->li_bchannel_id - 1) | mixer_write_prog_pri[n].line_flags);
+ for (j = a->li_base; j < a->li_base + MIXER_CHANNELS_PRI; j++)
+ {
+ w = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4));
+ if (w & mixer_write_prog_pri[n].mask)
+ {
+ *(p++) = (li_config_table[i].coef_table[j] & mixer_write_prog_pri[n].mask) ? 0x80 : 0x01;
+ li_config_table[i].coef_table[j] ^= mixer_write_prog_pri[n].mask << 4;
+ }
+ else
+ *(p++) = 0x00;
+ }
+ *(p++) = (byte)((plci->li_bchannel_id - 1) | MIXER_COEF_LINE_ROW_FLAG | mixer_write_prog_pri[n].line_flags);
+ for (j = a->li_base; j < a->li_base + MIXER_CHANNELS_PRI; j++)
+ {
+ w = ((li_config_table[j].coef_table[i] & 0xf) ^ (li_config_table[j].coef_table[i] >> 4));
+ if (w & mixer_write_prog_pri[n].mask)
+ {
+ *(p++) = (li_config_table[j].coef_table[i] & mixer_write_prog_pri[n].mask) ? 0x80 : 0x01;
+ li_config_table[j].coef_table[i] ^= mixer_write_prog_pri[n].mask << 4;
+ }
+ else
+ *(p++) = 0x00;
+ }
+ }
+ }
+ else
+ {
+ *(p++) = UDATA_REQUEST_SET_MIXER_COEFS_BRI;
+ w = 0;
+ if ((plci->tel == ADV_VOICE) && (plci == a->AdvSignalPLCI)
+ && (ADV_VOICE_NEW_COEF_BASE + sizeof(word) <= a->adv_voice_coef_length))
+ {
+ w = GET_WORD (a->adv_voice_coef_buffer + ADV_VOICE_NEW_COEF_BASE);
+ }
+ if (li_config_table[i].channel & LI_CHANNEL_TX_DATA)
+ w |= MIXER_FEATURE_ENABLE_TX_DATA;
+ if (li_config_table[i].channel & LI_CHANNEL_RX_DATA)
+ w |= MIXER_FEATURE_ENABLE_RX_DATA;
+ *(p++) = (byte) w;
+ *(p++) = (byte)(w >> 8);
+ for (j = 0; j < sizeof(ch_map); j += 2)
+ {
+ if (plci->li_bchannel_id == 2)
+ {
+ ch_map[j] = (byte)(j+1);
+ ch_map[j+1] = (byte) j;
+ }
+ else
+ {
+ ch_map[j] = (byte) j;
+ ch_map[j+1] = (byte)(j+1);
+ }
+ }
+ for (n = 0; n < ARRAY_SIZE(mixer_write_prog_bri); n++)
+ {
+ i = a->li_base + ch_map[mixer_write_prog_bri[n].to_ch];
+ j = a->li_base + ch_map[mixer_write_prog_bri[n].from_ch];
+ if (li_config_table[i].channel & li_config_table[j].channel & LI_CHANNEL_INVOLVED)
+ {
+ *p = ((li_config_table[i].coef_table[j] & mixer_write_prog_bri[n].mask) ? 0x80 : 0x01);
+ w = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4));
+ li_config_table[i].coef_table[j] ^= (w & mixer_write_prog_bri[n].mask) << 4;
+ }
+ else
+ {
+ *p = 0x00;
+ if ((a->AdvSignalPLCI != NULL) && (a->AdvSignalPLCI->tel == ADV_VOICE))
+ {
+ w = (plci == a->AdvSignalPLCI) ? n : mixer_swapped_index_bri[n];
+ if (ADV_VOICE_NEW_COEF_BASE + sizeof(word) + w < a->adv_voice_coef_length)
+ *p = a->adv_voice_coef_buffer[ADV_VOICE_NEW_COEF_BASE + sizeof(word) + w];
+ }
+ }
+ p++;
+ }
+ }
+ j = li_total_channels + 1;
+ }
+ }
+ plci->li_write_channel = j;
+ if (p != plci->internal_req_buffer)
+ {
+ plci->NData[0].P = plci->internal_req_buffer;
+ plci->NData[0].PLength = p - plci->internal_req_buffer;
+ plci->NL.X = plci->NData;
+ plci->NL.ReqCh = 0;
+ plci->NL.Req = plci->nl_req = (byte) N_UDATA;
+ plci->adapter->request (&plci->NL);
+ }
+ return (true);
+}
+
+
+static void mixer_notify_update (PLCI *plci, byte others)
+{
+ DIVA_CAPI_ADAPTER *a;
+ word i, w;
+ PLCI *notify_plci;
+ byte msg[sizeof(CAPI_MSG_HEADER) + 6];
+
+ dbug (1, dprintf ("[%06lx] %s,%d: mixer_notify_update %d",
+ (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+ (char *)(FILE_), __LINE__, others));
+
+ a = plci->adapter;
+ if (a->profile.Global_Options & GL_LINE_INTERCONNECT_SUPPORTED)
+ {
+ if (others)
+ plci->li_notify_update = true;
+ i = 0;
+ do
+ {
+ notify_plci = NULL;
+ if (others)
+ {
+ while ((i < li_total_channels) && (li_config_table[i].plci == NULL))
+ i++;
+ if (i < li_total_channels)
+ notify_plci = li_config_table[i++].plci;
+ }
+ else
+ {
+ if ((plci->li_bchannel_id != 0)
+ && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
+ {
+ notify_plci = plci;
+ }
+ }
+ if ((notify_plci != NULL)
+ && !notify_plci->li_notify_update
+ && (notify_plci->appl != NULL)
+ && (notify_plci->State)
+ && notify_plci->NL.Id && !notify_plci->nl_remove_id)
+ {
+ notify_plci->li_notify_update = true;
+ ((CAPI_MSG *) msg)->header.length = 18;
+ ((CAPI_MSG *) msg)->header.appl_id = notify_plci->appl->Id;
+ ((CAPI_MSG *) msg)->header.command = _FACILITY_R;
+ ((CAPI_MSG *) msg)->header.number = 0;
+ ((CAPI_MSG *) msg)->header.controller = notify_plci->adapter->Id;
+ ((CAPI_MSG *) msg)->header.plci = notify_plci->Id;
+ ((CAPI_MSG *) msg)->header.ncci = 0;
+ ((CAPI_MSG *) msg)->info.facility_req.Selector = SELECTOR_LINE_INTERCONNECT;
+ ((CAPI_MSG *) msg)->info.facility_req.structs[0] = 3;
+ PUT_WORD (&(((CAPI_MSG *) msg)->info.facility_req.structs[1]), LI_REQ_SILENT_UPDATE);
+ ((CAPI_MSG *) msg)->info.facility_req.structs[3] = 0;
+ w = api_put (notify_plci->appl, (CAPI_MSG *) msg);
+ if (w != _QUEUE_FULL)
+ {
+ if (w != 0)
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Interconnect notify failed %06x %d",
+ (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+ (char *)(FILE_), __LINE__,
+ (dword)((notify_plci->Id << 8) | UnMapController (notify_plci->adapter->Id)), w));
+ }
+ notify_plci->li_notify_update = false;
+ }
+ }
+ } while (others && (notify_plci != NULL));
+ if (others)
+ plci->li_notify_update = false;
+ }
+}
+
+
+static void mixer_clear_config (PLCI *plci)
+{
+ DIVA_CAPI_ADAPTER *a;
+ word i, j;
+
+ dbug (1, dprintf ("[%06lx] %s,%d: mixer_clear_config",
+ (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+ (char *)(FILE_), __LINE__));
+
+ plci->li_notify_update = false;
+ plci->li_plci_b_write_pos = 0;
+ plci->li_plci_b_read_pos = 0;
+ plci->li_plci_b_req_pos = 0;
+ a = plci->adapter;
+ if ((plci->li_bchannel_id != 0)
+ && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
+ {
+ i = a->li_base + (plci->li_bchannel_id - 1);
+ li_config_table[i].curchnl = 0;
+ li_config_table[i].channel = 0;
+ li_config_table[i].chflags = 0;
+ for (j = 0; j < li_total_channels; j++)
+ {
+ li_config_table[j].flag_table[i] = 0;
+ li_config_table[i].flag_table[j] = 0;
+ li_config_table[i].coef_table[j] = 0;
+ li_config_table[j].coef_table[i] = 0;
+ }
+ if (!a->li_pri)
+ {
+ li_config_table[i].coef_table[i] |= LI_COEF_CH_PC_SET | LI_COEF_PC_CH_SET;
+ if ((plci->tel == ADV_VOICE) && (plci == a->AdvSignalPLCI))
+ {
+ i = a->li_base + MIXER_IC_CHANNEL_BASE + (plci->li_bchannel_id - 1);
+ li_config_table[i].curchnl = 0;
+ li_config_table[i].channel = 0;
+ li_config_table[i].chflags = 0;
+ for (j = 0; j < li_total_channels; j++)
+ {
+ li_config_table[i].flag_table[j] = 0;
+ li_config_table[j].flag_table[i] = 0;
+ li_config_table[i].coef_table[j] = 0;
+ li_config_table[j].coef_table[i] = 0;
+ }
+ if (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC)
+ {
+ i = a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id);
+ li_config_table[i].curchnl = 0;
+ li_config_table[i].channel = 0;
+ li_config_table[i].chflags = 0;
+ for (j = 0; j < li_total_channels; j++)
+ {
+ li_config_table[i].flag_table[j] = 0;
+ li_config_table[j].flag_table[i] = 0;
+ li_config_table[i].coef_table[j] = 0;
+ li_config_table[j].coef_table[i] = 0;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+static void mixer_prepare_switch (dword Id, PLCI *plci)
+{
+
+ dbug (1, dprintf ("[%06lx] %s,%d: mixer_prepare_switch",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+
+ do
+ {
+ mixer_indication_coefs_set (Id, plci);
+ } while (plci->li_plci_b_read_pos != plci->li_plci_b_req_pos);
+}
+
+
+static word mixer_save_config (dword Id, PLCI *plci, byte Rc)
+{
+ DIVA_CAPI_ADAPTER *a;
+ word i, j;
+
+ dbug (1, dprintf ("[%06lx] %s,%d: mixer_save_config %02x %d",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state));
+
+ a = plci->adapter;
+ if ((plci->li_bchannel_id != 0)
+ && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
+ {
+ i = a->li_base + (plci->li_bchannel_id - 1);
+ for (j = 0; j < li_total_channels; j++)
+ {
+ li_config_table[i].coef_table[j] &= 0xf;
+ li_config_table[j].coef_table[i] &= 0xf;
+ }
+ if (!a->li_pri)
+ li_config_table[i].coef_table[i] |= LI_COEF_CH_PC_SET | LI_COEF_PC_CH_SET;
+ }
+ return (GOOD);
+}
+
+
+static word mixer_restore_config (dword Id, PLCI *plci, byte Rc)
+{
+ DIVA_CAPI_ADAPTER *a;
+ word Info;
+
+ dbug (1, dprintf ("[%06lx] %s,%d: mixer_restore_config %02x %d",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state));
+
+ Info = GOOD;
+ a = plci->adapter;
+ if ((plci->B1_facilities & B1_FACILITY_MIXER)
+ && (plci->li_bchannel_id != 0)
+ && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
+ {
+ switch (plci->adjust_b_state)
+ {
+ case ADJUST_B_RESTORE_MIXER_1:
+ if (a->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
+ {
+ plci->internal_command = plci->adjust_b_command;
+ if (plci_nl_busy (plci))
+ {
+ plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_1;
+ break;
+ }
+ xconnect_query_addresses (plci);
+ plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_2;
+ break;
+ }
+ plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_5;
+ Rc = OK;
+ case ADJUST_B_RESTORE_MIXER_2:
+ case ADJUST_B_RESTORE_MIXER_3:
+ case ADJUST_B_RESTORE_MIXER_4:
+ if ((Rc != OK) && (Rc != OK_FC) && (Rc != 0))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Adjust B query addresses failed %02x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc));
+ Info = _WRONG_STATE;
+ break;
+ }
+ if (Rc == OK)
+ {
+ if (plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_2)
+ plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_3;
+ else if (plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_4)
+ plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_5;
+ }
+ else if (Rc == 0)
+ {
+ if (plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_2)
+ plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_4;
+ else if (plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_3)
+ plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_5;
+ }
+ if (plci->adjust_b_state != ADJUST_B_RESTORE_MIXER_5)
+ {
+ plci->internal_command = plci->adjust_b_command;
+ break;
+ }
+ case ADJUST_B_RESTORE_MIXER_5:
+ xconnect_write_coefs (plci, plci->adjust_b_command);
+ plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_6;
+ Rc = OK;
+ case ADJUST_B_RESTORE_MIXER_6:
+ if (!xconnect_write_coefs_process (Id, plci, Rc))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Write mixer coefs failed",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ Info = _FACILITY_NOT_SUPPORTED;
+ break;
+ }
+ if (plci->internal_command)
+ break;
+ plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_7;
+ case ADJUST_B_RESTORE_MIXER_7:
+ break;
+ }
+ }
+ return (Info);
+}
+
+
+static void mixer_command (dword Id, PLCI *plci, byte Rc)
+{
+ DIVA_CAPI_ADAPTER *a;
+ word i, internal_command, Info;
+
+ dbug (1, dprintf ("[%06lx] %s,%d: mixer_command %02x %04x %04x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command,
+ plci->li_cmd));
+
+ Info = GOOD;
+ a = plci->adapter;
+ internal_command = plci->internal_command;
+ plci->internal_command = 0;
+ switch (plci->li_cmd)
+ {
+ case LI_REQ_CONNECT:
+ case LI_REQ_DISCONNECT:
+ case LI_REQ_SILENT_UPDATE:
+ switch (internal_command)
+ {
+ default:
+ if (plci->li_channel_bits & LI_CHANNEL_INVOLVED)
+ {
+ adjust_b1_resource (Id, plci, NULL, (word)(plci->B1_facilities |
+ B1_FACILITY_MIXER), MIXER_COMMAND_1);
+ }
+ case MIXER_COMMAND_1:
+ if (plci->li_channel_bits & LI_CHANNEL_INVOLVED)
+ {
+ if (adjust_b_process (Id, plci, Rc) != GOOD)
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Load mixer failed",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ Info = _FACILITY_NOT_SUPPORTED;
+ break;
+ }
+ if (plci->internal_command)
+ return;
+ }
+ plci->li_plci_b_req_pos = plci->li_plci_b_write_pos;
+ if ((plci->li_channel_bits & LI_CHANNEL_INVOLVED)
+ || ((get_b1_facilities (plci, plci->B1_resource) & B1_FACILITY_MIXER)
+ && (add_b1_facilities (plci, plci->B1_resource, (word)(plci->B1_facilities &
+ ~B1_FACILITY_MIXER)) == plci->B1_resource)))
+ {
+ xconnect_write_coefs (plci, MIXER_COMMAND_2);
+ }
+ else
+ {
+ do
+ {
+ mixer_indication_coefs_set (Id, plci);
+ } while (plci->li_plci_b_read_pos != plci->li_plci_b_req_pos);
+ }
+ case MIXER_COMMAND_2:
+ if ((plci->li_channel_bits & LI_CHANNEL_INVOLVED)
+ || ((get_b1_facilities (plci, plci->B1_resource) & B1_FACILITY_MIXER)
+ && (add_b1_facilities (plci, plci->B1_resource, (word)(plci->B1_facilities &
+ ~B1_FACILITY_MIXER)) == plci->B1_resource)))
+ {
+ if (!xconnect_write_coefs_process (Id, plci, Rc))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Write mixer coefs failed",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ if (plci->li_plci_b_write_pos != plci->li_plci_b_req_pos)
+ {
+ do
+ {
+ plci->li_plci_b_write_pos = (plci->li_plci_b_write_pos == 0) ?
+ LI_PLCI_B_QUEUE_ENTRIES-1 : plci->li_plci_b_write_pos - 1;
+ i = (plci->li_plci_b_write_pos == 0) ?
+ LI_PLCI_B_QUEUE_ENTRIES-1 : plci->li_plci_b_write_pos - 1;
+ } while ((plci->li_plci_b_write_pos != plci->li_plci_b_req_pos)
+ && !(plci->li_plci_b_queue[i] & LI_PLCI_B_LAST_FLAG));
+ }
+ Info = _FACILITY_NOT_SUPPORTED;
+ break;
+ }
+ if (plci->internal_command)
+ return;
+ }
+ if (!(plci->li_channel_bits & LI_CHANNEL_INVOLVED))
+ {
+ adjust_b1_resource (Id, plci, NULL, (word)(plci->B1_facilities &
+ ~B1_FACILITY_MIXER), MIXER_COMMAND_3);
+ }
+ case MIXER_COMMAND_3:
+ if (!(plci->li_channel_bits & LI_CHANNEL_INVOLVED))
+ {
+ if (adjust_b_process (Id, plci, Rc) != GOOD)
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Unload mixer failed",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ Info = _FACILITY_NOT_SUPPORTED;
+ break;
+ }
+ if (plci->internal_command)
+ return;
+ }
+ break;
+ }
+ break;
+ }
+ if ((plci->li_bchannel_id == 0)
+ || (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci != plci))
+ {
+ dbug (1, dprintf ("[%06x] %s,%d: Channel id wiped out %d",
+ UnMapId (Id), (char *)(FILE_), __LINE__, (int)(plci->li_bchannel_id)));
+ }
+ else
+ {
+ i = a->li_base + (plci->li_bchannel_id - 1);
+ li_config_table[i].curchnl = plci->li_channel_bits;
+ if (!a->li_pri && (plci->tel == ADV_VOICE) && (plci == a->AdvSignalPLCI))
+ {
+ i = a->li_base + MIXER_IC_CHANNEL_BASE + (plci->li_bchannel_id - 1);
+ li_config_table[i].curchnl = plci->li_channel_bits;
+ if (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC)
+ {
+ i = a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id);
+ li_config_table[i].curchnl = plci->li_channel_bits;
+ }
+ }
+ }
+}
+
+
+static void li_update_connect (dword Id, DIVA_CAPI_ADAPTER *a, PLCI *plci,
+ dword plci_b_id, byte connect, dword li_flags)
+{
+ word i, ch_a, ch_a_v, ch_a_s, ch_b, ch_b_v, ch_b_s;
+ PLCI *plci_b;
+ DIVA_CAPI_ADAPTER *a_b;
+
+ a_b = &(adapter[MapController ((byte)(plci_b_id & 0x7f)) - 1]);
+ plci_b = &(a_b->plci[((plci_b_id >> 8) & 0xff) - 1]);
+ ch_a = a->li_base + (plci->li_bchannel_id - 1);
+ if (!a->li_pri && (plci->tel == ADV_VOICE)
+ && (plci == a->AdvSignalPLCI) && (Id & EXT_CONTROLLER))
+ {
+ ch_a_v = ch_a + MIXER_IC_CHANNEL_BASE;
+ ch_a_s = (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC) ?
+ a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id) : ch_a_v;
+ }
+ else
+ {
+ ch_a_v = ch_a;
+ ch_a_s = ch_a;
+ }
+ ch_b = a_b->li_base + (plci_b->li_bchannel_id - 1);
+ if (!a_b->li_pri && (plci_b->tel == ADV_VOICE)
+ && (plci_b == a_b->AdvSignalPLCI) && (plci_b_id & EXT_CONTROLLER))
+ {
+ ch_b_v = ch_b + MIXER_IC_CHANNEL_BASE;
+ ch_b_s = (a_b->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC) ?
+ a_b->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci_b->li_bchannel_id) : ch_b_v;
+ }
+ else
+ {
+ ch_b_v = ch_b;
+ ch_b_s = ch_b;
+ }
+ if (connect)
+ {
+ li_config_table[ch_a].flag_table[ch_a_v] &= ~LI_FLAG_MONITOR;
+ li_config_table[ch_a].flag_table[ch_a_s] &= ~LI_FLAG_MONITOR;
+ li_config_table[ch_a_v].flag_table[ch_a] &= ~(LI_FLAG_ANNOUNCEMENT | LI_FLAG_MIX);
+ li_config_table[ch_a_s].flag_table[ch_a] &= ~(LI_FLAG_ANNOUNCEMENT | LI_FLAG_MIX);
+ }
+ li_config_table[ch_a].flag_table[ch_b_v] &= ~LI_FLAG_MONITOR;
+ li_config_table[ch_a].flag_table[ch_b_s] &= ~LI_FLAG_MONITOR;
+ li_config_table[ch_b_v].flag_table[ch_a] &= ~(LI_FLAG_ANNOUNCEMENT | LI_FLAG_MIX);
+ li_config_table[ch_b_s].flag_table[ch_a] &= ~(LI_FLAG_ANNOUNCEMENT | LI_FLAG_MIX);
+ if (ch_a_v == ch_b_v)
+ {
+ li_config_table[ch_a_v].flag_table[ch_b_v] &= ~LI_FLAG_CONFERENCE;
+ li_config_table[ch_a_s].flag_table[ch_b_s] &= ~LI_FLAG_CONFERENCE;
+ }
+ else
+ {
+ if (li_config_table[ch_a_v].flag_table[ch_b_v] & LI_FLAG_CONFERENCE)
+ {
+ for (i = 0; i < li_total_channels; i++)
+ {
+ if (i != ch_a_v)
+ li_config_table[ch_a_v].flag_table[i] &= ~LI_FLAG_CONFERENCE;
+ }
+ }
+ if (li_config_table[ch_a_s].flag_table[ch_b_v] & LI_FLAG_CONFERENCE)
+ {
+ for (i = 0; i < li_total_channels; i++)
+ {
+ if (i != ch_a_s)
+ li_config_table[ch_a_s].flag_table[i] &= ~LI_FLAG_CONFERENCE;
+ }
+ }
+ if (li_config_table[ch_b_v].flag_table[ch_a_v] & LI_FLAG_CONFERENCE)
+ {
+ for (i = 0; i < li_total_channels; i++)
+ {
+ if (i != ch_a_v)
+ li_config_table[i].flag_table[ch_a_v] &= ~LI_FLAG_CONFERENCE;
+ }
+ }
+ if (li_config_table[ch_b_v].flag_table[ch_a_s] & LI_FLAG_CONFERENCE)
+ {
+ for (i = 0; i < li_total_channels; i++)
+ {
+ if (i != ch_a_s)
+ li_config_table[i].flag_table[ch_a_s] &= ~LI_FLAG_CONFERENCE;
+ }
+ }
+ }
+ if (li_flags & LI_FLAG_CONFERENCE_A_B)
+ {
+ li_config_table[ch_b_v].flag_table[ch_a_v] |= LI_FLAG_CONFERENCE;
+ li_config_table[ch_b_s].flag_table[ch_a_v] |= LI_FLAG_CONFERENCE;
+ li_config_table[ch_b_v].flag_table[ch_a_s] |= LI_FLAG_CONFERENCE;
+ li_config_table[ch_b_s].flag_table[ch_a_s] |= LI_FLAG_CONFERENCE;
+ }
+ if (li_flags & LI_FLAG_CONFERENCE_B_A)
+ {
+ li_config_table[ch_a_v].flag_table[ch_b_v] |= LI_FLAG_CONFERENCE;
+ li_config_table[ch_a_v].flag_table[ch_b_s] |= LI_FLAG_CONFERENCE;
+ li_config_table[ch_a_s].flag_table[ch_b_v] |= LI_FLAG_CONFERENCE;
+ li_config_table[ch_a_s].flag_table[ch_b_s] |= LI_FLAG_CONFERENCE;
+ }
+ if (li_flags & LI_FLAG_MONITOR_A)
+ {
+ li_config_table[ch_a].flag_table[ch_a_v] |= LI_FLAG_MONITOR;
+ li_config_table[ch_a].flag_table[ch_a_s] |= LI_FLAG_MONITOR;
+ }
+ if (li_flags & LI_FLAG_MONITOR_B)
+ {
+ li_config_table[ch_a].flag_table[ch_b_v] |= LI_FLAG_MONITOR;
+ li_config_table[ch_a].flag_table[ch_b_s] |= LI_FLAG_MONITOR;
+ }
+ if (li_flags & LI_FLAG_ANNOUNCEMENT_A)
+ {
+ li_config_table[ch_a_v].flag_table[ch_a] |= LI_FLAG_ANNOUNCEMENT;
+ li_config_table[ch_a_s].flag_table[ch_a] |= LI_FLAG_ANNOUNCEMENT;
+ }
+ if (li_flags & LI_FLAG_ANNOUNCEMENT_B)
+ {
+ li_config_table[ch_b_v].flag_table[ch_a] |= LI_FLAG_ANNOUNCEMENT;
+ li_config_table[ch_b_s].flag_table[ch_a] |= LI_FLAG_ANNOUNCEMENT;
+ }
+ if (li_flags & LI_FLAG_MIX_A)
+ {
+ li_config_table[ch_a_v].flag_table[ch_a] |= LI_FLAG_MIX;
+ li_config_table[ch_a_s].flag_table[ch_a] |= LI_FLAG_MIX;
+ }
+ if (li_flags & LI_FLAG_MIX_B)
+ {
+ li_config_table[ch_b_v].flag_table[ch_a] |= LI_FLAG_MIX;
+ li_config_table[ch_b_s].flag_table[ch_a] |= LI_FLAG_MIX;
+ }
+ if (ch_a_v != ch_a_s)
+ {
+ li_config_table[ch_a_v].flag_table[ch_a_s] |= LI_FLAG_CONFERENCE;
+ li_config_table[ch_a_s].flag_table[ch_a_v] |= LI_FLAG_CONFERENCE;
+ }
+ if (ch_b_v != ch_b_s)
+ {
+ li_config_table[ch_b_v].flag_table[ch_b_s] |= LI_FLAG_CONFERENCE;
+ li_config_table[ch_b_s].flag_table[ch_b_v] |= LI_FLAG_CONFERENCE;
+ }
+}
+
+
+static void li2_update_connect (dword Id, DIVA_CAPI_ADAPTER *a, PLCI *plci,
+ dword plci_b_id, byte connect, dword li_flags)
+{
+ word ch_a, ch_a_v, ch_a_s, ch_b, ch_b_v, ch_b_s;
+ PLCI *plci_b;
+ DIVA_CAPI_ADAPTER *a_b;
+
+ a_b = &(adapter[MapController ((byte)(plci_b_id & 0x7f)) - 1]);
+ plci_b = &(a_b->plci[((plci_b_id >> 8) & 0xff) - 1]);
+ ch_a = a->li_base + (plci->li_bchannel_id - 1);
+ if (!a->li_pri && (plci->tel == ADV_VOICE)
+ && (plci == a->AdvSignalPLCI) && (Id & EXT_CONTROLLER))
+ {
+ ch_a_v = ch_a + MIXER_IC_CHANNEL_BASE;
+ ch_a_s = (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC) ?
+ a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id) : ch_a_v;
+ }
+ else
+ {
+ ch_a_v = ch_a;
+ ch_a_s = ch_a;
+ }
+ ch_b = a_b->li_base + (plci_b->li_bchannel_id - 1);
+ if (!a_b->li_pri && (plci_b->tel == ADV_VOICE)
+ && (plci_b == a_b->AdvSignalPLCI) && (plci_b_id & EXT_CONTROLLER))
+ {
+ ch_b_v = ch_b + MIXER_IC_CHANNEL_BASE;
+ ch_b_s = (a_b->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC) ?
+ a_b->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci_b->li_bchannel_id) : ch_b_v;
+ }
+ else
+ {
+ ch_b_v = ch_b;
+ ch_b_s = ch_b;
+ }
+ if (connect)
+ {
+ li_config_table[ch_b].flag_table[ch_b_v] &= ~LI_FLAG_MONITOR;
+ li_config_table[ch_b].flag_table[ch_b_s] &= ~LI_FLAG_MONITOR;
+ li_config_table[ch_b_v].flag_table[ch_b] &= ~LI_FLAG_MIX;
+ li_config_table[ch_b_s].flag_table[ch_b] &= ~LI_FLAG_MIX;
+ li_config_table[ch_b].flag_table[ch_b] &= ~LI_FLAG_PCCONNECT;
+ li_config_table[ch_b].chflags &= ~(LI_CHFLAG_MONITOR | LI_CHFLAG_MIX | LI_CHFLAG_LOOP);
+ }
+ li_config_table[ch_b_v].flag_table[ch_a_v] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE);
+ li_config_table[ch_b_s].flag_table[ch_a_v] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE);
+ li_config_table[ch_b_v].flag_table[ch_a_s] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE);
+ li_config_table[ch_b_s].flag_table[ch_a_s] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE);
+ li_config_table[ch_a_v].flag_table[ch_b_v] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE);
+ li_config_table[ch_a_v].flag_table[ch_b_s] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE);
+ li_config_table[ch_a_s].flag_table[ch_b_v] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE);
+ li_config_table[ch_a_s].flag_table[ch_b_s] &= ~(LI_FLAG_INTERCONNECT | LI_FLAG_CONFERENCE);
+ if (li_flags & LI2_FLAG_INTERCONNECT_A_B)
+ {
+ li_config_table[ch_b_v].flag_table[ch_a_v] |= LI_FLAG_INTERCONNECT;
+ li_config_table[ch_b_s].flag_table[ch_a_v] |= LI_FLAG_INTERCONNECT;
+ li_config_table[ch_b_v].flag_table[ch_a_s] |= LI_FLAG_INTERCONNECT;
+ li_config_table[ch_b_s].flag_table[ch_a_s] |= LI_FLAG_INTERCONNECT;
+ }
+ if (li_flags & LI2_FLAG_INTERCONNECT_B_A)
+ {
+ li_config_table[ch_a_v].flag_table[ch_b_v] |= LI_FLAG_INTERCONNECT;
+ li_config_table[ch_a_v].flag_table[ch_b_s] |= LI_FLAG_INTERCONNECT;
+ li_config_table[ch_a_s].flag_table[ch_b_v] |= LI_FLAG_INTERCONNECT;
+ li_config_table[ch_a_s].flag_table[ch_b_s] |= LI_FLAG_INTERCONNECT;
+ }
+ if (li_flags & LI2_FLAG_MONITOR_B)
+ {
+ li_config_table[ch_b].flag_table[ch_b_v] |= LI_FLAG_MONITOR;
+ li_config_table[ch_b].flag_table[ch_b_s] |= LI_FLAG_MONITOR;
+ }
+ if (li_flags & LI2_FLAG_MIX_B)
+ {
+ li_config_table[ch_b_v].flag_table[ch_b] |= LI_FLAG_MIX;
+ li_config_table[ch_b_s].flag_table[ch_b] |= LI_FLAG_MIX;
+ }
+ if (li_flags & LI2_FLAG_MONITOR_X)
+ li_config_table[ch_b].chflags |= LI_CHFLAG_MONITOR;
+ if (li_flags & LI2_FLAG_MIX_X)
+ li_config_table[ch_b].chflags |= LI_CHFLAG_MIX;
+ if (li_flags & LI2_FLAG_LOOP_B)
+ {
+ li_config_table[ch_b_v].flag_table[ch_b_v] |= LI_FLAG_INTERCONNECT;
+ li_config_table[ch_b_s].flag_table[ch_b_v] |= LI_FLAG_INTERCONNECT;
+ li_config_table[ch_b_v].flag_table[ch_b_s] |= LI_FLAG_INTERCONNECT;
+ li_config_table[ch_b_s].flag_table[ch_b_s] |= LI_FLAG_INTERCONNECT;
+ }
+ if (li_flags & LI2_FLAG_LOOP_PC)
+ li_config_table[ch_b].flag_table[ch_b] |= LI_FLAG_PCCONNECT;
+ if (li_flags & LI2_FLAG_LOOP_X)
+ li_config_table[ch_b].chflags |= LI_CHFLAG_LOOP;
+ if (li_flags & LI2_FLAG_PCCONNECT_A_B)
+ li_config_table[ch_b_s].flag_table[ch_a_s] |= LI_FLAG_PCCONNECT;
+ if (li_flags & LI2_FLAG_PCCONNECT_B_A)
+ li_config_table[ch_a_s].flag_table[ch_b_s] |= LI_FLAG_PCCONNECT;
+ if (ch_a_v != ch_a_s)
+ {
+ li_config_table[ch_a_v].flag_table[ch_a_s] |= LI_FLAG_CONFERENCE;
+ li_config_table[ch_a_s].flag_table[ch_a_v] |= LI_FLAG_CONFERENCE;
+ }
+ if (ch_b_v != ch_b_s)
+ {
+ li_config_table[ch_b_v].flag_table[ch_b_s] |= LI_FLAG_CONFERENCE;
+ li_config_table[ch_b_s].flag_table[ch_b_v] |= LI_FLAG_CONFERENCE;
+ }
+}
+
+
+static word li_check_main_plci (dword Id, PLCI *plci)
+{
+ if (plci == NULL)
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Wrong PLCI",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ return (_WRONG_IDENTIFIER);
+ }
+ if (!plci->State
+ || !plci->NL.Id || plci->nl_remove_id
+ || (plci->li_bchannel_id == 0))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Wrong state",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ return (_WRONG_STATE);
+ }
+ li_config_table[plci->adapter->li_base + (plci->li_bchannel_id - 1)].plci = plci;
+ return (GOOD);
+}
+
+
+static PLCI *li_check_plci_b (dword Id, PLCI *plci,
+ dword plci_b_id, word plci_b_write_pos, byte *p_result)
+{
+ byte ctlr_b;
+ PLCI *plci_b;
+
+ if (((plci->li_plci_b_read_pos > plci_b_write_pos) ? plci->li_plci_b_read_pos :
+ LI_PLCI_B_QUEUE_ENTRIES + plci->li_plci_b_read_pos) - plci_b_write_pos - 1 < 2)
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: LI request overrun",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ PUT_WORD (p_result, _REQUEST_NOT_ALLOWED_IN_THIS_STATE);
+ return (NULL);
+ }
+ ctlr_b = 0;
+ if ((plci_b_id & 0x7f) != 0)
+ {
+ ctlr_b = MapController ((byte)(plci_b_id & 0x7f));
+ if ((ctlr_b > max_adapter) || ((ctlr_b != 0) && (adapter[ctlr_b - 1].request == NULL)))
+ ctlr_b = 0;
+ }
+ if ((ctlr_b == 0)
+ || (((plci_b_id >> 8) & 0xff) == 0)
+ || (((plci_b_id >> 8) & 0xff) > adapter[ctlr_b - 1].max_plci))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: LI invalid second PLCI %08lx",
+ UnMapId (Id), (char *)(FILE_), __LINE__, plci_b_id));
+ PUT_WORD (p_result, _WRONG_IDENTIFIER);
+ return (NULL);
+ }
+ plci_b = &(adapter[ctlr_b - 1].plci[((plci_b_id >> 8) & 0xff) - 1]);
+ if (!plci_b->State
+ || !plci_b->NL.Id || plci_b->nl_remove_id
+ || (plci_b->li_bchannel_id == 0))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: LI peer in wrong state %08lx",
+ UnMapId (Id), (char *)(FILE_), __LINE__, plci_b_id));
+ PUT_WORD (p_result, _REQUEST_NOT_ALLOWED_IN_THIS_STATE);
+ return (NULL);
+ }
+ li_config_table[plci_b->adapter->li_base + (plci_b->li_bchannel_id - 1)].plci = plci_b;
+ if (((byte)(plci_b_id & ~EXT_CONTROLLER)) !=
+ ((byte)(UnMapController (plci->adapter->Id) & ~EXT_CONTROLLER))
+ && (!(plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
+ || !(plci_b->adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: LI not on same ctrl %08lx",
+ UnMapId (Id), (char *)(FILE_), __LINE__, plci_b_id));
+ PUT_WORD (p_result, _WRONG_IDENTIFIER);
+ return (NULL);
+ }
+ if (!(get_b1_facilities (plci_b, add_b1_facilities (plci_b, plci_b->B1_resource,
+ (word)(plci_b->B1_facilities | B1_FACILITY_MIXER))) & B1_FACILITY_MIXER))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Interconnect peer cannot mix %d",
+ UnMapId (Id), (char *)(FILE_), __LINE__, plci_b->B1_resource));
+ PUT_WORD (p_result, _REQUEST_NOT_ALLOWED_IN_THIS_STATE);
+ return (NULL);
+ }
+ return (plci_b);
+}
+
+
+static PLCI *li2_check_plci_b (dword Id, PLCI *plci,
+ dword plci_b_id, word plci_b_write_pos, byte *p_result)
+{
+ byte ctlr_b;
+ PLCI *plci_b;
+
+ if (((plci->li_plci_b_read_pos > plci_b_write_pos) ? plci->li_plci_b_read_pos :
+ LI_PLCI_B_QUEUE_ENTRIES + plci->li_plci_b_read_pos) - plci_b_write_pos - 1 < 2)
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: LI request overrun",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ PUT_WORD (p_result, _WRONG_STATE);
+ return (NULL);
+ }
+ ctlr_b = 0;
+ if ((plci_b_id & 0x7f) != 0)
+ {
+ ctlr_b = MapController ((byte)(plci_b_id & 0x7f));
+ if ((ctlr_b > max_adapter) || ((ctlr_b != 0) && (adapter[ctlr_b - 1].request == NULL)))
+ ctlr_b = 0;
+ }
+ if ((ctlr_b == 0)
+ || (((plci_b_id >> 8) & 0xff) == 0)
+ || (((plci_b_id >> 8) & 0xff) > adapter[ctlr_b - 1].max_plci))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: LI invalid second PLCI %08lx",
+ UnMapId (Id), (char *)(FILE_), __LINE__, plci_b_id));
+ PUT_WORD (p_result, _WRONG_IDENTIFIER);
+ return (NULL);
+ }
+ plci_b = &(adapter[ctlr_b - 1].plci[((plci_b_id >> 8) & 0xff) - 1]);
+ if (!plci_b->State
+ || !plci_b->NL.Id || plci_b->nl_remove_id
+ || (plci_b->li_bchannel_id == 0)
+ || (li_config_table[plci_b->adapter->li_base + (plci_b->li_bchannel_id - 1)].plci != plci_b))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: LI peer in wrong state %08lx",
+ UnMapId (Id), (char *)(FILE_), __LINE__, plci_b_id));
+ PUT_WORD (p_result, _WRONG_STATE);
+ return (NULL);
+ }
+ if (((byte)(plci_b_id & ~EXT_CONTROLLER)) !=
+ ((byte)(UnMapController (plci->adapter->Id) & ~EXT_CONTROLLER))
+ && (!(plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
+ || !(plci_b->adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: LI not on same ctrl %08lx",
+ UnMapId (Id), (char *)(FILE_), __LINE__, plci_b_id));
+ PUT_WORD (p_result, _WRONG_IDENTIFIER);
+ return (NULL);
+ }
+ if (!(get_b1_facilities (plci_b, add_b1_facilities (plci_b, plci_b->B1_resource,
+ (word)(plci_b->B1_facilities | B1_FACILITY_MIXER))) & B1_FACILITY_MIXER))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Interconnect peer cannot mix %d",
+ UnMapId (Id), (char *)(FILE_), __LINE__, plci_b->B1_resource));
+ PUT_WORD (p_result, _WRONG_STATE);
+ return (NULL);
+ }
+ return (plci_b);
+}
+
+
+static byte mixer_request (dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, API_PARSE *msg)
+{
+ word Info;
+ word i;
+ dword d, li_flags, plci_b_id;
+ PLCI *plci_b;
+ API_PARSE li_parms[3];
+ API_PARSE li_req_parms[3];
+ API_PARSE li_participant_struct[2];
+ API_PARSE li_participant_parms[3];
+ word participant_parms_pos;
+ byte result_buffer[32];
+ byte *result;
+ word result_pos;
+ word plci_b_write_pos;
+
+ dbug (1, dprintf ("[%06lx] %s,%d: mixer_request",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+
+ Info = GOOD;
+ result = result_buffer;
+ result_buffer[0] = 0;
+ if (!(a->profile.Global_Options & GL_LINE_INTERCONNECT_SUPPORTED))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Facility not supported",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ Info = _FACILITY_NOT_SUPPORTED;
+ }
+ else if (api_parse (&msg[1].info[1], msg[1].length, "ws", li_parms))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Wrong message format",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ Info = _WRONG_MESSAGE_FORMAT;
+ }
+ else
+ {
+ result_buffer[0] = 3;
+ PUT_WORD (&result_buffer[1], GET_WORD (li_parms[0].info));
+ result_buffer[3] = 0;
+ switch (GET_WORD (li_parms[0].info))
+ {
+ case LI_GET_SUPPORTED_SERVICES:
+ if (appl->appl_flags & APPL_FLAG_OLD_LI_SPEC)
+ {
+ result_buffer[0] = 17;
+ result_buffer[3] = 14;
+ PUT_WORD (&result_buffer[4], GOOD);
+ d = 0;
+ if (a->manufacturer_features & MANUFACTURER_FEATURE_MIXER_CH_CH)
+ d |= LI_CONFERENCING_SUPPORTED;
+ if (a->manufacturer_features & MANUFACTURER_FEATURE_MIXER_CH_PC)
+ d |= LI_MONITORING_SUPPORTED;
+ if (a->manufacturer_features & MANUFACTURER_FEATURE_MIXER_PC_CH)
+ d |= LI_ANNOUNCEMENTS_SUPPORTED | LI_MIXING_SUPPORTED;
+ if (a->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
+ d |= LI_CROSS_CONTROLLER_SUPPORTED;
+ PUT_DWORD (&result_buffer[6], d);
+ if (a->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
+ {
+ d = 0;
+ for (i = 0; i < li_total_channels; i++)
+ {
+ if ((li_config_table[i].adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
+ && (li_config_table[i].adapter->li_pri
+ || (i < li_config_table[i].adapter->li_base + MIXER_BCHANNELS_BRI)))
+ {
+ d++;
+ }
+ }
+ }
+ else
+ {
+ d = a->li_pri ? a->li_channels : MIXER_BCHANNELS_BRI;
+ }
+ PUT_DWORD (&result_buffer[10], d / 2);
+ PUT_DWORD (&result_buffer[14], d);
+ }
+ else
+ {
+ result_buffer[0] = 25;
+ result_buffer[3] = 22;
+ PUT_WORD (&result_buffer[4], GOOD);
+ d = LI2_ASYMMETRIC_SUPPORTED | LI2_B_LOOPING_SUPPORTED | LI2_X_LOOPING_SUPPORTED;
+ if (a->manufacturer_features & MANUFACTURER_FEATURE_MIXER_CH_PC)
+ d |= LI2_MONITORING_SUPPORTED | LI2_REMOTE_MONITORING_SUPPORTED;
+ if (a->manufacturer_features & MANUFACTURER_FEATURE_MIXER_PC_CH)
+ d |= LI2_MIXING_SUPPORTED | LI2_REMOTE_MIXING_SUPPORTED;
+ if (a->manufacturer_features & MANUFACTURER_FEATURE_MIXER_PC_PC)
+ d |= LI2_PC_LOOPING_SUPPORTED;
+ if (a->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
+ d |= LI2_CROSS_CONTROLLER_SUPPORTED;
+ PUT_DWORD (&result_buffer[6], d);
+ d = a->li_pri ? a->li_channels : MIXER_BCHANNELS_BRI;
+ PUT_DWORD (&result_buffer[10], d / 2);
+ PUT_DWORD (&result_buffer[14], d - 1);
+ if (a->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
+ {
+ d = 0;
+ for (i = 0; i < li_total_channels; i++)
+ {
+ if ((li_config_table[i].adapter->manufacturer_features & MANUFACTURER_FEATURE_XCONNECT)
+ && (li_config_table[i].adapter->li_pri
+ || (i < li_config_table[i].adapter->li_base + MIXER_BCHANNELS_BRI)))
+ {
+ d++;
+ }
+ }
+ }
+ PUT_DWORD (&result_buffer[18], d / 2);
+ PUT_DWORD (&result_buffer[22], d - 1);
+ }
+ break;
+
+ case LI_REQ_CONNECT:
+ if (li_parms[1].length == 8)
+ {
+ appl->appl_flags |= APPL_FLAG_OLD_LI_SPEC;
+ if (api_parse (&li_parms[1].info[1], li_parms[1].length, "dd", li_req_parms))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Wrong message format",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ Info = _WRONG_MESSAGE_FORMAT;
+ break;
+ }
+ plci_b_id = GET_DWORD (li_req_parms[0].info) & 0xffff;
+ li_flags = GET_DWORD (li_req_parms[1].info);
+ Info = li_check_main_plci (Id, plci);
+ result_buffer[0] = 9;
+ result_buffer[3] = 6;
+ PUT_DWORD (&result_buffer[4], plci_b_id);
+ PUT_WORD (&result_buffer[8], GOOD);
+ if (Info != GOOD)
+ break;
+ result = plci->saved_msg.info;
+ for (i = 0; i <= result_buffer[0]; i++)
+ result[i] = result_buffer[i];
+ plci_b_write_pos = plci->li_plci_b_write_pos;
+ plci_b = li_check_plci_b (Id, plci, plci_b_id, plci_b_write_pos, &result[8]);
+ if (plci_b == NULL)
+ break;
+ li_update_connect (Id, a, plci, plci_b_id, true, li_flags);
+ plci->li_plci_b_queue[plci_b_write_pos] = plci_b_id | LI_PLCI_B_LAST_FLAG;
+ plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES-1) ? 0 : plci_b_write_pos + 1;
+ plci->li_plci_b_write_pos = plci_b_write_pos;
+ }
+ else
+ {
+ appl->appl_flags &= ~APPL_FLAG_OLD_LI_SPEC;
+ if (api_parse (&li_parms[1].info[1], li_parms[1].length, "ds", li_req_parms))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Wrong message format",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ Info = _WRONG_MESSAGE_FORMAT;
+ break;
+ }
+ li_flags = GET_DWORD (li_req_parms[0].info) & ~(LI2_FLAG_INTERCONNECT_A_B | LI2_FLAG_INTERCONNECT_B_A);
+ Info = li_check_main_plci (Id, plci);
+ result_buffer[0] = 7;
+ result_buffer[3] = 4;
+ PUT_WORD (&result_buffer[4], Info);
+ result_buffer[6] = 0;
+ if (Info != GOOD)
+ break;
+ result = plci->saved_msg.info;
+ for (i = 0; i <= result_buffer[0]; i++)
+ result[i] = result_buffer[i];
+ plci_b_write_pos = plci->li_plci_b_write_pos;
+ participant_parms_pos = 0;
+ result_pos = 7;
+ li2_update_connect (Id, a, plci, UnMapId (Id), true, li_flags);
+ while (participant_parms_pos < li_req_parms[1].length)
+ {
+ result[result_pos] = 6;
+ result_pos += 7;
+ PUT_DWORD (&result[result_pos - 6], 0);
+ PUT_WORD (&result[result_pos - 2], GOOD);
+ if (api_parse (&li_req_parms[1].info[1 + participant_parms_pos],
+ (word)(li_parms[1].length - participant_parms_pos), "s", li_participant_struct))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Wrong message format",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ PUT_WORD (&result[result_pos - 2], _WRONG_MESSAGE_FORMAT);
+ break;
+ }
+ if (api_parse (&li_participant_struct[0].info[1],
+ li_participant_struct[0].length, "dd", li_participant_parms))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Wrong message format",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ PUT_WORD (&result[result_pos - 2], _WRONG_MESSAGE_FORMAT);
+ break;
+ }
+ plci_b_id = GET_DWORD (li_participant_parms[0].info) & 0xffff;
+ li_flags = GET_DWORD (li_participant_parms[1].info);
+ PUT_DWORD (&result[result_pos - 6], plci_b_id);
+ if (sizeof(result) - result_pos < 7)
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: LI result overrun",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ PUT_WORD (&result[result_pos - 2], _WRONG_STATE);
+ break;
+ }
+ plci_b = li2_check_plci_b (Id, plci, plci_b_id, plci_b_write_pos, &result[result_pos - 2]);
+ if (plci_b != NULL)
+ {
+ li2_update_connect (Id, a, plci, plci_b_id, true, li_flags);
+ plci->li_plci_b_queue[plci_b_write_pos] = plci_b_id |
+ ((li_flags & (LI2_FLAG_INTERCONNECT_A_B | LI2_FLAG_INTERCONNECT_B_A |
+ LI2_FLAG_PCCONNECT_A_B | LI2_FLAG_PCCONNECT_B_A)) ? 0 : LI_PLCI_B_DISC_FLAG);
+ plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES-1) ? 0 : plci_b_write_pos + 1;
+ }
+ participant_parms_pos = (word)((&li_participant_struct[0].info[1 + li_participant_struct[0].length]) -
+ (&li_req_parms[1].info[1]));
+ }
+ result[0] = (byte)(result_pos - 1);
+ result[3] = (byte)(result_pos - 4);
+ result[6] = (byte)(result_pos - 7);
+ i = (plci_b_write_pos == 0) ? LI_PLCI_B_QUEUE_ENTRIES-1 : plci_b_write_pos - 1;
+ if ((plci_b_write_pos == plci->li_plci_b_read_pos)
+ || (plci->li_plci_b_queue[i] & LI_PLCI_B_LAST_FLAG))
+ {
+ plci->li_plci_b_queue[plci_b_write_pos] = LI_PLCI_B_SKIP_FLAG | LI_PLCI_B_LAST_FLAG;
+ plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES-1) ? 0 : plci_b_write_pos + 1;
+ }
+ else
+ plci->li_plci_b_queue[i] |= LI_PLCI_B_LAST_FLAG;
+ plci->li_plci_b_write_pos = plci_b_write_pos;
+ }
+ mixer_calculate_coefs (a);
+ plci->li_channel_bits = li_config_table[a->li_base + (plci->li_bchannel_id - 1)].channel;
+ mixer_notify_update (plci, true);
+ sendf (appl, _FACILITY_R | CONFIRM, Id & 0xffffL, Number,
+ "wwS", Info, SELECTOR_LINE_INTERCONNECT, result);
+ plci->command = 0;
+ plci->li_cmd = GET_WORD (li_parms[0].info);
+ start_internal_command (Id, plci, mixer_command);
+ return (false);
+
+ case LI_REQ_DISCONNECT:
+ if (li_parms[1].length == 4)
+ {
+ appl->appl_flags |= APPL_FLAG_OLD_LI_SPEC;
+ if (api_parse (&li_parms[1].info[1], li_parms[1].length, "d", li_req_parms))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Wrong message format",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ Info = _WRONG_MESSAGE_FORMAT;
+ break;
+ }
+ plci_b_id = GET_DWORD (li_req_parms[0].info) & 0xffff;
+ Info = li_check_main_plci (Id, plci);
+ result_buffer[0] = 9;
+ result_buffer[3] = 6;
+ PUT_DWORD (&result_buffer[4], GET_DWORD (li_req_parms[0].info));
+ PUT_WORD (&result_buffer[8], GOOD);
+ if (Info != GOOD)
+ break;
+ result = plci->saved_msg.info;
+ for (i = 0; i <= result_buffer[0]; i++)
+ result[i] = result_buffer[i];
+ plci_b_write_pos = plci->li_plci_b_write_pos;
+ plci_b = li_check_plci_b (Id, plci, plci_b_id, plci_b_write_pos, &result[8]);
+ if (plci_b == NULL)
+ break;
+ li_update_connect (Id, a, plci, plci_b_id, false, 0);
+ plci->li_plci_b_queue[plci_b_write_pos] = plci_b_id | LI_PLCI_B_DISC_FLAG | LI_PLCI_B_LAST_FLAG;
+ plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES-1) ? 0 : plci_b_write_pos + 1;
+ plci->li_plci_b_write_pos = plci_b_write_pos;
+ }
+ else
+ {
+ appl->appl_flags &= ~APPL_FLAG_OLD_LI_SPEC;
+ if (api_parse (&li_parms[1].info[1], li_parms[1].length, "s", li_req_parms))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Wrong message format",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ Info = _WRONG_MESSAGE_FORMAT;
+ break;
+ }
+ Info = li_check_main_plci (Id, plci);
+ result_buffer[0] = 7;
+ result_buffer[3] = 4;
+ PUT_WORD (&result_buffer[4], Info);
+ result_buffer[6] = 0;
+ if (Info != GOOD)
+ break;
+ result = plci->saved_msg.info;
+ for (i = 0; i <= result_buffer[0]; i++)
+ result[i] = result_buffer[i];
+ plci_b_write_pos = plci->li_plci_b_write_pos;
+ participant_parms_pos = 0;
+ result_pos = 7;
+ while (participant_parms_pos < li_req_parms[0].length)
+ {
+ result[result_pos] = 6;
+ result_pos += 7;
+ PUT_DWORD (&result[result_pos - 6], 0);
+ PUT_WORD (&result[result_pos - 2], GOOD);
+ if (api_parse (&li_req_parms[0].info[1 + participant_parms_pos],
+ (word)(li_parms[1].length - participant_parms_pos), "s", li_participant_struct))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Wrong message format",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ PUT_WORD (&result[result_pos - 2], _WRONG_MESSAGE_FORMAT);
+ break;
+ }
+ if (api_parse (&li_participant_struct[0].info[1],
+ li_participant_struct[0].length, "d", li_participant_parms))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Wrong message format",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ PUT_WORD (&result[result_pos - 2], _WRONG_MESSAGE_FORMAT);
+ break;
+ }
+ plci_b_id = GET_DWORD (li_participant_parms[0].info) & 0xffff;
+ PUT_DWORD (&result[result_pos - 6], plci_b_id);
+ if (sizeof(result) - result_pos < 7)
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: LI result overrun",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ PUT_WORD (&result[result_pos - 2], _WRONG_STATE);
+ break;
+ }
+ plci_b = li2_check_plci_b (Id, plci, plci_b_id, plci_b_write_pos, &result[result_pos - 2]);
+ if (plci_b != NULL)
+ {
+ li2_update_connect (Id, a, plci, plci_b_id, false, 0);
+ plci->li_plci_b_queue[plci_b_write_pos] = plci_b_id | LI_PLCI_B_DISC_FLAG;
+ plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES-1) ? 0 : plci_b_write_pos + 1;
+ }
+ participant_parms_pos = (word)((&li_participant_struct[0].info[1 + li_participant_struct[0].length]) -
+ (&li_req_parms[0].info[1]));
+ }
+ result[0] = (byte)(result_pos - 1);
+ result[3] = (byte)(result_pos - 4);
+ result[6] = (byte)(result_pos - 7);
+ i = (plci_b_write_pos == 0) ? LI_PLCI_B_QUEUE_ENTRIES-1 : plci_b_write_pos - 1;
+ if ((plci_b_write_pos == plci->li_plci_b_read_pos)
+ || (plci->li_plci_b_queue[i] & LI_PLCI_B_LAST_FLAG))
+ {
+ plci->li_plci_b_queue[plci_b_write_pos] = LI_PLCI_B_SKIP_FLAG | LI_PLCI_B_LAST_FLAG;
+ plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES-1) ? 0 : plci_b_write_pos + 1;
+ }
+ else
+ plci->li_plci_b_queue[i] |= LI_PLCI_B_LAST_FLAG;
+ plci->li_plci_b_write_pos = plci_b_write_pos;
+ }
+ mixer_calculate_coefs (a);
+ plci->li_channel_bits = li_config_table[a->li_base + (plci->li_bchannel_id - 1)].channel;
+ mixer_notify_update (plci, true);
+ sendf (appl, _FACILITY_R | CONFIRM, Id & 0xffffL, Number,
+ "wwS", Info, SELECTOR_LINE_INTERCONNECT, result);
+ plci->command = 0;
+ plci->li_cmd = GET_WORD (li_parms[0].info);
+ start_internal_command (Id, plci, mixer_command);
+ return (false);
+
+ case LI_REQ_SILENT_UPDATE:
+ if (!plci || !plci->State
+ || !plci->NL.Id || plci->nl_remove_id
+ || (plci->li_bchannel_id == 0)
+ || (li_config_table[plci->adapter->li_base + (plci->li_bchannel_id - 1)].plci != plci))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Wrong state",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ return (false);
+ }
+ plci_b_write_pos = plci->li_plci_b_write_pos;
+ if (((plci->li_plci_b_read_pos > plci_b_write_pos) ? plci->li_plci_b_read_pos :
+ LI_PLCI_B_QUEUE_ENTRIES + plci->li_plci_b_read_pos) - plci_b_write_pos - 1 < 2)
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: LI request overrun",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ return (false);
+ }
+ i = (plci_b_write_pos == 0) ? LI_PLCI_B_QUEUE_ENTRIES-1 : plci_b_write_pos - 1;
+ if ((plci_b_write_pos == plci->li_plci_b_read_pos)
+ || (plci->li_plci_b_queue[i] & LI_PLCI_B_LAST_FLAG))
+ {
+ plci->li_plci_b_queue[plci_b_write_pos] = LI_PLCI_B_SKIP_FLAG | LI_PLCI_B_LAST_FLAG;
+ plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES-1) ? 0 : plci_b_write_pos + 1;
+ }
+ else
+ plci->li_plci_b_queue[i] |= LI_PLCI_B_LAST_FLAG;
+ plci->li_plci_b_write_pos = plci_b_write_pos;
+ plci->li_channel_bits = li_config_table[a->li_base + (plci->li_bchannel_id - 1)].channel;
+ plci->command = 0;
+ plci->li_cmd = GET_WORD (li_parms[0].info);
+ start_internal_command (Id, plci, mixer_command);
+ return (false);
+
+ default:
+ dbug (1, dprintf ("[%06lx] %s,%d: LI unknown request %04x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, GET_WORD (li_parms[0].info)));
+ Info = _FACILITY_NOT_SUPPORTED;
+ }
+ }
+ sendf (appl, _FACILITY_R | CONFIRM, Id & 0xffffL, Number,
+ "wwS", Info, SELECTOR_LINE_INTERCONNECT, result);
+ return (false);
+}
+
+
+static void mixer_indication_coefs_set (dword Id, PLCI *plci)
+{
+ dword d;
+ DIVA_CAPI_ADAPTER *a;
+ byte result[12];
+
+ dbug (1, dprintf ("[%06lx] %s,%d: mixer_indication_coefs_set",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+
+ a = plci->adapter;
+ if (plci->li_plci_b_read_pos != plci->li_plci_b_req_pos)
+ {
+ do
+ {
+ d = plci->li_plci_b_queue[plci->li_plci_b_read_pos];
+ if (!(d & LI_PLCI_B_SKIP_FLAG))
+ {
+ if (plci->appl->appl_flags & APPL_FLAG_OLD_LI_SPEC)
+ {
+ if (d & LI_PLCI_B_DISC_FLAG)
+ {
+ result[0] = 5;
+ PUT_WORD (&result[1], LI_IND_DISCONNECT);
+ result[3] = 2;
+ PUT_WORD (&result[4], _LI_USER_INITIATED);
+ }
+ else
+ {
+ result[0] = 7;
+ PUT_WORD (&result[1], LI_IND_CONNECT_ACTIVE);
+ result[3] = 4;
+ PUT_DWORD (&result[4], d & ~LI_PLCI_B_FLAG_MASK);
+ }
+ }
+ else
+ {
+ if (d & LI_PLCI_B_DISC_FLAG)
+ {
+ result[0] = 9;
+ PUT_WORD (&result[1], LI_IND_DISCONNECT);
+ result[3] = 6;
+ PUT_DWORD (&result[4], d & ~LI_PLCI_B_FLAG_MASK);
+ PUT_WORD (&result[8], _LI_USER_INITIATED);
+ }
+ else
+ {
+ result[0] = 7;
+ PUT_WORD (&result[1], LI_IND_CONNECT_ACTIVE);
+ result[3] = 4;
+ PUT_DWORD (&result[4], d & ~LI_PLCI_B_FLAG_MASK);
+ }
+ }
+ sendf (plci->appl, _FACILITY_I, Id & 0xffffL, 0,
+ "ws", SELECTOR_LINE_INTERCONNECT, result);
+ }
+ plci->li_plci_b_read_pos = (plci->li_plci_b_read_pos == LI_PLCI_B_QUEUE_ENTRIES-1) ?
+ 0 : plci->li_plci_b_read_pos + 1;
+ } while (!(d & LI_PLCI_B_LAST_FLAG) && (plci->li_plci_b_read_pos != plci->li_plci_b_req_pos));
+ }
+}
+
+
+static void mixer_indication_xconnect_from (dword Id, PLCI *plci, byte *msg, word length)
+{
+ word i, j, ch;
+ struct xconnect_transfer_address_s s, *p;
+ DIVA_CAPI_ADAPTER *a;
+
+ dbug (1, dprintf ("[%06lx] %s,%d: mixer_indication_xconnect_from %d",
+ UnMapId (Id), (char *)(FILE_), __LINE__, (int) length));
+
+ a = plci->adapter;
+ i = 1;
+ for (i = 1; i < length; i += 16)
+ {
+ s.card_address.low = msg[i] | (msg[i+1] << 8) | (((dword)(msg[i+2])) << 16) | (((dword)(msg[i+3])) << 24);
+ s.card_address.high = msg[i+4] | (msg[i+5] << 8) | (((dword)(msg[i+6])) << 16) | (((dword)(msg[i+7])) << 24);
+ s.offset = msg[i+8] | (msg[i+9] << 8) | (((dword)(msg[i+10])) << 16) | (((dword)(msg[i+11])) << 24);
+ ch = msg[i+12] | (msg[i+13] << 8);
+ j = ch & XCONNECT_CHANNEL_NUMBER_MASK;
+ if (!a->li_pri && (plci->li_bchannel_id == 2))
+ j = 1 - j;
+ j += a->li_base;
+ if (ch & XCONNECT_CHANNEL_PORT_PC)
+ p = &(li_config_table[j].send_pc);
+ else
+ p = &(li_config_table[j].send_b);
+ p->card_address.low = s.card_address.low;
+ p->card_address.high = s.card_address.high;
+ p->offset = s.offset;
+ li_config_table[j].channel |= LI_CHANNEL_ADDRESSES_SET;
+ }
+ if (plci->internal_command_queue[0]
+ && ((plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_2)
+ || (plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_3)
+ || (plci->adjust_b_state == ADJUST_B_RESTORE_MIXER_4)))
+ {
+ (*(plci->internal_command_queue[0]))(Id, plci, 0);
+ if (!plci->internal_command)
+ next_internal_command (Id, plci);
+ }
+ mixer_notify_update (plci, true);
+}
+
+
+static void mixer_indication_xconnect_to (dword Id, PLCI *plci, byte *msg, word length)
+{
+
+ dbug (1, dprintf ("[%06lx] %s,%d: mixer_indication_xconnect_to %d",
+ UnMapId (Id), (char *)(FILE_), __LINE__, (int) length));
+
+}
+
+
+static byte mixer_notify_source_removed (PLCI *plci, dword plci_b_id)
+{
+ word plci_b_write_pos;
+
+ plci_b_write_pos = plci->li_plci_b_write_pos;
+ if (((plci->li_plci_b_read_pos > plci_b_write_pos) ? plci->li_plci_b_read_pos :
+ LI_PLCI_B_QUEUE_ENTRIES + plci->li_plci_b_read_pos) - plci_b_write_pos - 1 < 1)
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: LI request overrun",
+ (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+ (char *)(FILE_), __LINE__));
+ return (false);
+ }
+ plci->li_plci_b_queue[plci_b_write_pos] = plci_b_id | LI_PLCI_B_DISC_FLAG;
+ plci_b_write_pos = (plci_b_write_pos == LI_PLCI_B_QUEUE_ENTRIES-1) ? 0 : plci_b_write_pos + 1;
+ plci->li_plci_b_write_pos = plci_b_write_pos;
+ return (true);
+}
+
+
+static void mixer_remove (PLCI *plci)
+{
+ DIVA_CAPI_ADAPTER *a;
+ PLCI *notify_plci;
+ dword plci_b_id;
+ word i, j;
+
+ dbug (1, dprintf ("[%06lx] %s,%d: mixer_remove",
+ (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+ (char *)(FILE_), __LINE__));
+
+ a = plci->adapter;
+ plci_b_id = (plci->Id << 8) | UnMapController (plci->adapter->Id);
+ if (a->profile.Global_Options & GL_LINE_INTERCONNECT_SUPPORTED)
+ {
+ if ((plci->li_bchannel_id != 0)
+ && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
+ {
+ i = a->li_base + (plci->li_bchannel_id - 1);
+ if ((li_config_table[i].curchnl | li_config_table[i].channel) & LI_CHANNEL_INVOLVED)
+ {
+ for (j = 0; j < li_total_channels; j++)
+ {
+ if ((li_config_table[i].flag_table[j] & LI_FLAG_INTERCONNECT)
+ || (li_config_table[j].flag_table[i] & LI_FLAG_INTERCONNECT))
+ {
+ notify_plci = li_config_table[j].plci;
+ if ((notify_plci != NULL)
+ && (notify_plci != plci)
+ && (notify_plci->appl != NULL)
+ && !(notify_plci->appl->appl_flags & APPL_FLAG_OLD_LI_SPEC)
+ && (notify_plci->State)
+ && notify_plci->NL.Id && !notify_plci->nl_remove_id)
+ {
+ mixer_notify_source_removed (notify_plci, plci_b_id);
+ }
+ }
+ }
+ mixer_clear_config (plci);
+ mixer_calculate_coefs (a);
+ mixer_notify_update (plci, true);
+ }
+ li_config_table[i].plci = NULL;
+ plci->li_bchannel_id = 0;
+ }
+ }
+}
+
+
+/*------------------------------------------------------------------*/
+/* Echo canceller facilities */
+/*------------------------------------------------------------------*/
+
+
+static void ec_write_parameters (PLCI *plci)
+{
+ word w;
+ byte parameter_buffer[6];
+
+ dbug (1, dprintf ("[%06lx] %s,%d: ec_write_parameters",
+ (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+ (char *)(FILE_), __LINE__));
+
+ parameter_buffer[0] = 5;
+ parameter_buffer[1] = DSP_CTRL_SET_LEC_PARAMETERS;
+ PUT_WORD (&parameter_buffer[2], plci->ec_idi_options);
+ plci->ec_idi_options &= ~LEC_RESET_COEFFICIENTS;
+ w = (plci->ec_tail_length == 0) ? 128 : plci->ec_tail_length;
+ PUT_WORD (&parameter_buffer[4], w);
+ add_p (plci, FTY, parameter_buffer);
+ sig_req (plci, TEL_CTRL, 0);
+ send_req (plci);
+}
+
+
+static void ec_clear_config (PLCI *plci)
+{
+
+ dbug (1, dprintf ("[%06lx] %s,%d: ec_clear_config",
+ (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+ (char *)(FILE_), __LINE__));
+
+ plci->ec_idi_options = LEC_ENABLE_ECHO_CANCELLER |
+ LEC_MANUAL_DISABLE | LEC_ENABLE_NONLINEAR_PROCESSING;
+ plci->ec_tail_length = 0;
+}
+
+
+static void ec_prepare_switch (dword Id, PLCI *plci)
+{
+
+ dbug (1, dprintf ("[%06lx] %s,%d: ec_prepare_switch",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+
+}
+
+
+static word ec_save_config (dword Id, PLCI *plci, byte Rc)
+{
+
+ dbug (1, dprintf ("[%06lx] %s,%d: ec_save_config %02x %d",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state));
+
+ return (GOOD);
+}
+
+
+static word ec_restore_config (dword Id, PLCI *plci, byte Rc)
+{
+ word Info;
+
+ dbug (1, dprintf ("[%06lx] %s,%d: ec_restore_config %02x %d",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state));
+
+ Info = GOOD;
+ if (plci->B1_facilities & B1_FACILITY_EC)
+ {
+ switch (plci->adjust_b_state)
+ {
+ case ADJUST_B_RESTORE_EC_1:
+ plci->internal_command = plci->adjust_b_command;
+ if (plci->sig_req)
+ {
+ plci->adjust_b_state = ADJUST_B_RESTORE_EC_1;
+ break;
+ }
+ ec_write_parameters (plci);
+ plci->adjust_b_state = ADJUST_B_RESTORE_EC_2;
+ break;
+ case ADJUST_B_RESTORE_EC_2:
+ if ((Rc != OK) && (Rc != OK_FC))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Restore EC failed %02x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc));
+ Info = _WRONG_STATE;
+ break;
+ }
+ break;
+ }
+ }
+ return (Info);
+}
+
+
+static void ec_command (dword Id, PLCI *plci, byte Rc)
+{
+ word internal_command, Info;
+ byte result[8];
+
+ dbug (1, dprintf ("[%06lx] %s,%d: ec_command %02x %04x %04x %04x %d",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command,
+ plci->ec_cmd, plci->ec_idi_options, plci->ec_tail_length));
+
+ Info = GOOD;
+ if (plci->appl->appl_flags & APPL_FLAG_PRIV_EC_SPEC)
+ {
+ result[0] = 2;
+ PUT_WORD (&result[1], EC_SUCCESS);
+ }
+ else
+ {
+ result[0] = 5;
+ PUT_WORD (&result[1], plci->ec_cmd);
+ result[3] = 2;
+ PUT_WORD (&result[4], GOOD);
+ }
+ internal_command = plci->internal_command;
+ plci->internal_command = 0;
+ switch (plci->ec_cmd)
+ {
+ case EC_ENABLE_OPERATION:
+ case EC_FREEZE_COEFFICIENTS:
+ case EC_RESUME_COEFFICIENT_UPDATE:
+ case EC_RESET_COEFFICIENTS:
+ switch (internal_command)
+ {
+ default:
+ adjust_b1_resource (Id, plci, NULL, (word)(plci->B1_facilities |
+ B1_FACILITY_EC), EC_COMMAND_1);
+ case EC_COMMAND_1:
+ if (adjust_b_process (Id, plci, Rc) != GOOD)
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Load EC failed",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ Info = _FACILITY_NOT_SUPPORTED;
+ break;
+ }
+ if (plci->internal_command)
+ return;
+ case EC_COMMAND_2:
+ if (plci->sig_req)
+ {
+ plci->internal_command = EC_COMMAND_2;
+ return;
+ }
+ plci->internal_command = EC_COMMAND_3;
+ ec_write_parameters (plci);
+ return;
+ case EC_COMMAND_3:
+ if ((Rc != OK) && (Rc != OK_FC))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Enable EC failed %02x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc));
+ Info = _FACILITY_NOT_SUPPORTED;
+ break;
+ }
+ break;
+ }
+ break;
+
+ case EC_DISABLE_OPERATION:
+ switch (internal_command)
+ {
+ default:
+ case EC_COMMAND_1:
+ if (plci->B1_facilities & B1_FACILITY_EC)
+ {
+ if (plci->sig_req)
+ {
+ plci->internal_command = EC_COMMAND_1;
+ return;
+ }
+ plci->internal_command = EC_COMMAND_2;
+ ec_write_parameters (plci);
+ return;
+ }
+ Rc = OK;
+ case EC_COMMAND_2:
+ if ((Rc != OK) && (Rc != OK_FC))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Disable EC failed %02x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc));
+ Info = _FACILITY_NOT_SUPPORTED;
+ break;
+ }
+ adjust_b1_resource (Id, plci, NULL, (word)(plci->B1_facilities &
+ ~B1_FACILITY_EC), EC_COMMAND_3);
+ case EC_COMMAND_3:
+ if (adjust_b_process (Id, plci, Rc) != GOOD)
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Unload EC failed",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ Info = _FACILITY_NOT_SUPPORTED;
+ break;
+ }
+ if (plci->internal_command)
+ return;
+ break;
+ }
+ break;
+ }
+ sendf (plci->appl, _FACILITY_R | CONFIRM, Id & 0xffffL, plci->number,
+ "wws", Info, (plci->appl->appl_flags & APPL_FLAG_PRIV_EC_SPEC) ?
+ PRIV_SELECTOR_ECHO_CANCELLER : SELECTOR_ECHO_CANCELLER, result);
+}
+
+
+static byte ec_request (dword Id, word Number, DIVA_CAPI_ADAPTER *a, PLCI *plci, APPL *appl, API_PARSE *msg)
+{
+ word Info;
+ word opt;
+ API_PARSE ec_parms[3];
+ byte result[16];
+
+ dbug (1, dprintf ("[%06lx] %s,%d: ec_request",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+
+ Info = GOOD;
+ result[0] = 0;
+ if (!(a->man_profile.private_options & (1L << PRIVATE_ECHO_CANCELLER)))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Facility not supported",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ Info = _FACILITY_NOT_SUPPORTED;
+ }
+ else
+ {
+ if (appl->appl_flags & APPL_FLAG_PRIV_EC_SPEC)
+ {
+ if (api_parse (&msg[1].info[1], msg[1].length, "w", ec_parms))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Wrong message format",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ Info = _WRONG_MESSAGE_FORMAT;
+ }
+ else
+ {
+ if (plci == NULL)
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Wrong PLCI",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ Info = _WRONG_IDENTIFIER;
+ }
+ else if (!plci->State || !plci->NL.Id || plci->nl_remove_id)
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Wrong state",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ Info = _WRONG_STATE;
+ }
+ else
+ {
+ plci->command = 0;
+ plci->ec_cmd = GET_WORD (ec_parms[0].info);
+ plci->ec_idi_options &= ~(LEC_MANUAL_DISABLE | LEC_RESET_COEFFICIENTS);
+ result[0] = 2;
+ PUT_WORD (&result[1], EC_SUCCESS);
+ if (msg[1].length >= 4)
+ {
+ opt = GET_WORD (&ec_parms[0].info[2]);
+ plci->ec_idi_options &= ~(LEC_ENABLE_NONLINEAR_PROCESSING |
+ LEC_ENABLE_2100HZ_DETECTOR | LEC_REQUIRE_2100HZ_REVERSALS);
+ if (!(opt & EC_DISABLE_NON_LINEAR_PROCESSING))
+ plci->ec_idi_options |= LEC_ENABLE_NONLINEAR_PROCESSING;
+ if (opt & EC_DETECT_DISABLE_TONE)
+ plci->ec_idi_options |= LEC_ENABLE_2100HZ_DETECTOR;
+ if (!(opt & EC_DO_NOT_REQUIRE_REVERSALS))
+ plci->ec_idi_options |= LEC_REQUIRE_2100HZ_REVERSALS;
+ if (msg[1].length >= 6)
+ {
+ plci->ec_tail_length = GET_WORD (&ec_parms[0].info[4]);
+ }
+ }
+ switch (plci->ec_cmd)
+ {
+ case EC_ENABLE_OPERATION:
+ plci->ec_idi_options &= ~LEC_FREEZE_COEFFICIENTS;
+ start_internal_command (Id, plci, ec_command);
+ return (false);
+
+ case EC_DISABLE_OPERATION:
+ plci->ec_idi_options = LEC_ENABLE_ECHO_CANCELLER |
+ LEC_MANUAL_DISABLE | LEC_ENABLE_NONLINEAR_PROCESSING |
+ LEC_RESET_COEFFICIENTS;
+ start_internal_command (Id, plci, ec_command);
+ return (false);
+
+ case EC_FREEZE_COEFFICIENTS:
+ plci->ec_idi_options |= LEC_FREEZE_COEFFICIENTS;
+ start_internal_command (Id, plci, ec_command);
+ return (false);
+
+ case EC_RESUME_COEFFICIENT_UPDATE:
+ plci->ec_idi_options &= ~LEC_FREEZE_COEFFICIENTS;
+ start_internal_command (Id, plci, ec_command);
+ return (false);
+
+ case EC_RESET_COEFFICIENTS:
+ plci->ec_idi_options |= LEC_RESET_COEFFICIENTS;
+ start_internal_command (Id, plci, ec_command);
+ return (false);
+
+ default:
+ dbug (1, dprintf ("[%06lx] %s,%d: EC unknown request %04x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, plci->ec_cmd));
+ PUT_WORD (&result[1], EC_UNSUPPORTED_OPERATION);
+ }
+ }
+ }
+ }
+ else
+ {
+ if (api_parse (&msg[1].info[1], msg[1].length, "ws", ec_parms))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Wrong message format",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ Info = _WRONG_MESSAGE_FORMAT;
+ }
+ else
+ {
+ if (GET_WORD (ec_parms[0].info) == EC_GET_SUPPORTED_SERVICES)
+ {
+ result[0] = 11;
+ PUT_WORD (&result[1], EC_GET_SUPPORTED_SERVICES);
+ result[3] = 8;
+ PUT_WORD (&result[4], GOOD);
+ PUT_WORD (&result[6], 0x0007);
+ PUT_WORD (&result[8], LEC_MAX_SUPPORTED_TAIL_LENGTH);
+ PUT_WORD (&result[10], 0);
+ }
+ else if (plci == NULL)
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Wrong PLCI",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ Info = _WRONG_IDENTIFIER;
+ }
+ else if (!plci->State || !plci->NL.Id || plci->nl_remove_id)
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Wrong state",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ Info = _WRONG_STATE;
+ }
+ else
+ {
+ plci->command = 0;
+ plci->ec_cmd = GET_WORD (ec_parms[0].info);
+ plci->ec_idi_options &= ~(LEC_MANUAL_DISABLE | LEC_RESET_COEFFICIENTS);
+ result[0] = 5;
+ PUT_WORD (&result[1], plci->ec_cmd);
+ result[3] = 2;
+ PUT_WORD (&result[4], GOOD);
+ plci->ec_idi_options &= ~(LEC_ENABLE_NONLINEAR_PROCESSING |
+ LEC_ENABLE_2100HZ_DETECTOR | LEC_REQUIRE_2100HZ_REVERSALS);
+ plci->ec_tail_length = 0;
+ if (ec_parms[1].length >= 2)
+ {
+ opt = GET_WORD (&ec_parms[1].info[1]);
+ if (opt & EC_ENABLE_NON_LINEAR_PROCESSING)
+ plci->ec_idi_options |= LEC_ENABLE_NONLINEAR_PROCESSING;
+ if (opt & EC_DETECT_DISABLE_TONE)
+ plci->ec_idi_options |= LEC_ENABLE_2100HZ_DETECTOR;
+ if (!(opt & EC_DO_NOT_REQUIRE_REVERSALS))
+ plci->ec_idi_options |= LEC_REQUIRE_2100HZ_REVERSALS;
+ if (ec_parms[1].length >= 4)
+ {
+ plci->ec_tail_length = GET_WORD (&ec_parms[1].info[3]);
+ }
+ }
+ switch (plci->ec_cmd)
+ {
+ case EC_ENABLE_OPERATION:
+ plci->ec_idi_options &= ~LEC_FREEZE_COEFFICIENTS;
+ start_internal_command (Id, plci, ec_command);
+ return (false);
+
+ case EC_DISABLE_OPERATION:
+ plci->ec_idi_options = LEC_ENABLE_ECHO_CANCELLER |
+ LEC_MANUAL_DISABLE | LEC_ENABLE_NONLINEAR_PROCESSING |
+ LEC_RESET_COEFFICIENTS;
+ start_internal_command (Id, plci, ec_command);
+ return (false);
+
+ default:
+ dbug (1, dprintf ("[%06lx] %s,%d: EC unknown request %04x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, plci->ec_cmd));
+ PUT_WORD (&result[4], _FACILITY_SPECIFIC_FUNCTION_NOT_SUPP);
+ }
+ }
+ }
+ }
+ }
+ sendf (appl, _FACILITY_R | CONFIRM, Id & 0xffffL, Number,
+ "wws", Info, (appl->appl_flags & APPL_FLAG_PRIV_EC_SPEC) ?
+ PRIV_SELECTOR_ECHO_CANCELLER : SELECTOR_ECHO_CANCELLER, result);
+ return (false);
+}
+
+
+static void ec_indication (dword Id, PLCI *plci, byte *msg, word length)
+{
+ byte result[8];
+
+ dbug (1, dprintf ("[%06lx] %s,%d: ec_indication",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+
+ if (!(plci->ec_idi_options & LEC_MANUAL_DISABLE))
+ {
+ if (plci->appl->appl_flags & APPL_FLAG_PRIV_EC_SPEC)
+ {
+ result[0] = 2;
+ PUT_WORD (&result[1], 0);
+ switch (msg[1])
+ {
+ case LEC_DISABLE_TYPE_CONTIGNUOUS_2100HZ:
+ PUT_WORD (&result[1], EC_BYPASS_DUE_TO_CONTINUOUS_2100HZ);
+ break;
+ case LEC_DISABLE_TYPE_REVERSED_2100HZ:
+ PUT_WORD (&result[1], EC_BYPASS_DUE_TO_REVERSED_2100HZ);
+ break;
+ case LEC_DISABLE_RELEASED:
+ PUT_WORD (&result[1], EC_BYPASS_RELEASED);
+ break;
+ }
+ }
+ else
+ {
+ result[0] = 5;
+ PUT_WORD (&result[1], EC_BYPASS_INDICATION);
+ result[3] = 2;
+ PUT_WORD (&result[4], 0);
+ switch (msg[1])
+ {
+ case LEC_DISABLE_TYPE_CONTIGNUOUS_2100HZ:
+ PUT_WORD (&result[4], EC_BYPASS_DUE_TO_CONTINUOUS_2100HZ);
+ break;
+ case LEC_DISABLE_TYPE_REVERSED_2100HZ:
+ PUT_WORD (&result[4], EC_BYPASS_DUE_TO_REVERSED_2100HZ);
+ break;
+ case LEC_DISABLE_RELEASED:
+ PUT_WORD (&result[4], EC_BYPASS_RELEASED);
+ break;
+ }
+ }
+ sendf (plci->appl, _FACILITY_I, Id & 0xffffL, 0, "ws", (plci->appl->appl_flags & APPL_FLAG_PRIV_EC_SPEC) ?
+ PRIV_SELECTOR_ECHO_CANCELLER : SELECTOR_ECHO_CANCELLER, result);
+ }
+}
+
+
+
+/*------------------------------------------------------------------*/
+/* Advanced voice */
+/*------------------------------------------------------------------*/
+
+static void adv_voice_write_coefs (PLCI *plci, word write_command)
+{
+ DIVA_CAPI_ADAPTER *a;
+ word i;
+ byte *p;
+
+ word w, n, j, k;
+ byte ch_map[MIXER_CHANNELS_BRI];
+
+ byte coef_buffer[ADV_VOICE_COEF_BUFFER_SIZE + 2];
+
+ dbug (1, dprintf ("[%06lx] %s,%d: adv_voice_write_coefs %d",
+ (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+ (char *)(FILE_), __LINE__, write_command));
+
+ a = plci->adapter;
+ p = coef_buffer + 1;
+ *(p++) = DSP_CTRL_OLD_SET_MIXER_COEFFICIENTS;
+ i = 0;
+ while (i + sizeof(word) <= a->adv_voice_coef_length)
+ {
+ PUT_WORD (p, GET_WORD (a->adv_voice_coef_buffer + i));
+ p += 2;
+ i += 2;
+ }
+ while (i < ADV_VOICE_OLD_COEF_COUNT * sizeof(word))
+ {
+ PUT_WORD (p, 0x8000);
+ p += 2;
+ i += 2;
+ }
+
+ if (!a->li_pri && (plci->li_bchannel_id == 0))
+ {
+ if ((li_config_table[a->li_base].plci == NULL) && (li_config_table[a->li_base + 1].plci != NULL))
+ {
+ plci->li_bchannel_id = 1;
+ li_config_table[a->li_base].plci = plci;
+ dbug (1, dprintf ("[%06lx] %s,%d: adv_voice_set_bchannel_id %d",
+ (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+ (char *)(FILE_), __LINE__, plci->li_bchannel_id));
+ }
+ else if ((li_config_table[a->li_base].plci != NULL) && (li_config_table[a->li_base + 1].plci == NULL))
+ {
+ plci->li_bchannel_id = 2;
+ li_config_table[a->li_base + 1].plci = plci;
+ dbug (1, dprintf ("[%06lx] %s,%d: adv_voice_set_bchannel_id %d",
+ (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+ (char *)(FILE_), __LINE__, plci->li_bchannel_id));
+ }
+ }
+ if (!a->li_pri && (plci->li_bchannel_id != 0)
+ && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
+ {
+ i = a->li_base + (plci->li_bchannel_id - 1);
+ switch (write_command)
+ {
+ case ADV_VOICE_WRITE_ACTIVATION:
+ j = a->li_base + MIXER_IC_CHANNEL_BASE + (plci->li_bchannel_id - 1);
+ k = a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id);
+ if (!(plci->B1_facilities & B1_FACILITY_MIXER))
+ {
+ li_config_table[j].flag_table[i] |= LI_FLAG_CONFERENCE | LI_FLAG_MIX;
+ li_config_table[i].flag_table[j] |= LI_FLAG_CONFERENCE | LI_FLAG_MONITOR;
+ }
+ if (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC)
+ {
+ li_config_table[k].flag_table[i] |= LI_FLAG_CONFERENCE | LI_FLAG_MIX;
+ li_config_table[i].flag_table[k] |= LI_FLAG_CONFERENCE | LI_FLAG_MONITOR;
+ li_config_table[k].flag_table[j] |= LI_FLAG_CONFERENCE;
+ li_config_table[j].flag_table[k] |= LI_FLAG_CONFERENCE;
+ }
+ mixer_calculate_coefs (a);
+ li_config_table[i].curchnl = li_config_table[i].channel;
+ li_config_table[j].curchnl = li_config_table[j].channel;
+ if (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC)
+ li_config_table[k].curchnl = li_config_table[k].channel;
+ break;
+
+ case ADV_VOICE_WRITE_DEACTIVATION:
+ for (j = 0; j < li_total_channels; j++)
+ {
+ li_config_table[i].flag_table[j] = 0;
+ li_config_table[j].flag_table[i] = 0;
+ }
+ k = a->li_base + MIXER_IC_CHANNEL_BASE + (plci->li_bchannel_id - 1);
+ for (j = 0; j < li_total_channels; j++)
+ {
+ li_config_table[k].flag_table[j] = 0;
+ li_config_table[j].flag_table[k] = 0;
+ }
+ if (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC)
+ {
+ k = a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id);
+ for (j = 0; j < li_total_channels; j++)
+ {
+ li_config_table[k].flag_table[j] = 0;
+ li_config_table[j].flag_table[k] = 0;
+ }
+ }
+ mixer_calculate_coefs (a);
+ break;
+ }
+ if (plci->B1_facilities & B1_FACILITY_MIXER)
+ {
+ w = 0;
+ if (ADV_VOICE_NEW_COEF_BASE + sizeof(word) <= a->adv_voice_coef_length)
+ w = GET_WORD (a->adv_voice_coef_buffer + ADV_VOICE_NEW_COEF_BASE);
+ if (li_config_table[i].channel & LI_CHANNEL_TX_DATA)
+ w |= MIXER_FEATURE_ENABLE_TX_DATA;
+ if (li_config_table[i].channel & LI_CHANNEL_RX_DATA)
+ w |= MIXER_FEATURE_ENABLE_RX_DATA;
+ *(p++) = (byte) w;
+ *(p++) = (byte)(w >> 8);
+ for (j = 0; j < sizeof(ch_map); j += 2)
+ {
+ ch_map[j] = (byte)(j + (plci->li_bchannel_id - 1));
+ ch_map[j+1] = (byte)(j + (2 - plci->li_bchannel_id));
+ }
+ for (n = 0; n < ARRAY_SIZE(mixer_write_prog_bri); n++)
+ {
+ i = a->li_base + ch_map[mixer_write_prog_bri[n].to_ch];
+ j = a->li_base + ch_map[mixer_write_prog_bri[n].from_ch];
+ if (li_config_table[i].channel & li_config_table[j].channel & LI_CHANNEL_INVOLVED)
+ {
+ *(p++) = ((li_config_table[i].coef_table[j] & mixer_write_prog_bri[n].mask) ? 0x80 : 0x01);
+ w = ((li_config_table[i].coef_table[j] & 0xf) ^ (li_config_table[i].coef_table[j] >> 4));
+ li_config_table[i].coef_table[j] ^= (w & mixer_write_prog_bri[n].mask) << 4;
+ }
+ else
+ {
+ *(p++) = (ADV_VOICE_NEW_COEF_BASE + sizeof(word) + n < a->adv_voice_coef_length) ?
+ a->adv_voice_coef_buffer[ADV_VOICE_NEW_COEF_BASE + sizeof(word) + n] : 0x00;
+ }
+ }
+ }
+ else
+ {
+ for (i = ADV_VOICE_NEW_COEF_BASE; i < a->adv_voice_coef_length; i++)
+ *(p++) = a->adv_voice_coef_buffer[i];
+ }
+ }
+ else
+
+ {
+ for (i = ADV_VOICE_NEW_COEF_BASE; i < a->adv_voice_coef_length; i++)
+ *(p++) = a->adv_voice_coef_buffer[i];
+ }
+ coef_buffer[0] = (p - coef_buffer) - 1;
+ add_p (plci, FTY, coef_buffer);
+ sig_req (plci, TEL_CTRL, 0);
+ send_req (plci);
+}
+
+
+static void adv_voice_clear_config (PLCI *plci)
+{
+ DIVA_CAPI_ADAPTER *a;
+
+ word i, j;
+
+
+ dbug (1, dprintf ("[%06lx] %s,%d: adv_voice_clear_config",
+ (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+ (char *)(FILE_), __LINE__));
+
+ a = plci->adapter;
+ if ((plci->tel == ADV_VOICE) && (plci == a->AdvSignalPLCI))
+ {
+ a->adv_voice_coef_length = 0;
+
+ if (!a->li_pri && (plci->li_bchannel_id != 0)
+ && (li_config_table[a->li_base + (plci->li_bchannel_id - 1)].plci == plci))
+ {
+ i = a->li_base + (plci->li_bchannel_id - 1);
+ li_config_table[i].curchnl = 0;
+ li_config_table[i].channel = 0;
+ li_config_table[i].chflags = 0;
+ for (j = 0; j < li_total_channels; j++)
+ {
+ li_config_table[i].flag_table[j] = 0;
+ li_config_table[j].flag_table[i] = 0;
+ li_config_table[i].coef_table[j] = 0;
+ li_config_table[j].coef_table[i] = 0;
+ }
+ li_config_table[i].coef_table[i] |= LI_COEF_CH_PC_SET | LI_COEF_PC_CH_SET;
+ i = a->li_base + MIXER_IC_CHANNEL_BASE + (plci->li_bchannel_id - 1);
+ li_config_table[i].curchnl = 0;
+ li_config_table[i].channel = 0;
+ li_config_table[i].chflags = 0;
+ for (j = 0; j < li_total_channels; j++)
+ {
+ li_config_table[i].flag_table[j] = 0;
+ li_config_table[j].flag_table[i] = 0;
+ li_config_table[i].coef_table[j] = 0;
+ li_config_table[j].coef_table[i] = 0;
+ }
+ if (a->manufacturer_features & MANUFACTURER_FEATURE_SLAVE_CODEC)
+ {
+ i = a->li_base + MIXER_IC_CHANNEL_BASE + (2 - plci->li_bchannel_id);
+ li_config_table[i].curchnl = 0;
+ li_config_table[i].channel = 0;
+ li_config_table[i].chflags = 0;
+ for (j = 0; j < li_total_channels; j++)
+ {
+ li_config_table[i].flag_table[j] = 0;
+ li_config_table[j].flag_table[i] = 0;
+ li_config_table[i].coef_table[j] = 0;
+ li_config_table[j].coef_table[i] = 0;
+ }
+ }
+ }
+
+ }
+}
+
+
+static void adv_voice_prepare_switch (dword Id, PLCI *plci)
+{
+
+ dbug (1, dprintf ("[%06lx] %s,%d: adv_voice_prepare_switch",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+
+}
+
+
+static word adv_voice_save_config (dword Id, PLCI *plci, byte Rc)
+{
+
+ dbug (1, dprintf ("[%06lx] %s,%d: adv_voice_save_config %02x %d",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state));
+
+ return (GOOD);
+}
+
+
+static word adv_voice_restore_config (dword Id, PLCI *plci, byte Rc)
+{
+ DIVA_CAPI_ADAPTER *a;
+ word Info;
+
+ dbug (1, dprintf ("[%06lx] %s,%d: adv_voice_restore_config %02x %d",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state));
+
+ Info = GOOD;
+ a = plci->adapter;
+ if ((plci->B1_facilities & B1_FACILITY_VOICE)
+ && (plci->tel == ADV_VOICE) && (plci == a->AdvSignalPLCI))
+ {
+ switch (plci->adjust_b_state)
+ {
+ case ADJUST_B_RESTORE_VOICE_1:
+ plci->internal_command = plci->adjust_b_command;
+ if (plci->sig_req)
+ {
+ plci->adjust_b_state = ADJUST_B_RESTORE_VOICE_1;
+ break;
+ }
+ adv_voice_write_coefs (plci, ADV_VOICE_WRITE_UPDATE);
+ plci->adjust_b_state = ADJUST_B_RESTORE_VOICE_2;
+ break;
+ case ADJUST_B_RESTORE_VOICE_2:
+ if ((Rc != OK) && (Rc != OK_FC))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Restore voice config failed %02x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc));
+ Info = _WRONG_STATE;
+ break;
+ }
+ break;
+ }
+ }
+ return (Info);
+}
+
+
+
+
+/*------------------------------------------------------------------*/
+/* B1 resource switching */
+/*------------------------------------------------------------------*/
+
+static byte b1_facilities_table[] =
+{
+ 0x00, /* 0 No bchannel resources */
+ 0x00, /* 1 Codec (automatic law) */
+ 0x00, /* 2 Codec (A-law) */
+ 0x00, /* 3 Codec (y-law) */
+ 0x00, /* 4 HDLC for X.21 */
+ 0x00, /* 5 HDLC */
+ 0x00, /* 6 External Device 0 */
+ 0x00, /* 7 External Device 1 */
+ 0x00, /* 8 HDLC 56k */
+ 0x00, /* 9 Transparent */
+ 0x00, /* 10 Loopback to network */
+ 0x00, /* 11 Test pattern to net */
+ 0x00, /* 12 Rate adaptation sync */
+ 0x00, /* 13 Rate adaptation async */
+ 0x00, /* 14 R-Interface */
+ 0x00, /* 15 HDLC 128k leased line */
+ 0x00, /* 16 FAX */
+ 0x00, /* 17 Modem async */
+ 0x00, /* 18 Modem sync HDLC */
+ 0x00, /* 19 V.110 async HDLC */
+ 0x12, /* 20 Adv voice (Trans,mixer) */
+ 0x00, /* 21 Codec connected to IC */
+ 0x0c, /* 22 Trans,DTMF */
+ 0x1e, /* 23 Trans,DTMF+mixer */
+ 0x1f, /* 24 Trans,DTMF+mixer+local */
+ 0x13, /* 25 Trans,mixer+local */
+ 0x12, /* 26 HDLC,mixer */
+ 0x12, /* 27 HDLC 56k,mixer */
+ 0x2c, /* 28 Trans,LEC+DTMF */
+ 0x3e, /* 29 Trans,LEC+DTMF+mixer */
+ 0x3f, /* 30 Trans,LEC+DTMF+mixer+local */
+ 0x2c, /* 31 RTP,LEC+DTMF */
+ 0x3e, /* 32 RTP,LEC+DTMF+mixer */
+ 0x3f, /* 33 RTP,LEC+DTMF+mixer+local */
+ 0x00, /* 34 Signaling task */
+ 0x00, /* 35 PIAFS */
+ 0x0c, /* 36 Trans,DTMF+TONE */
+ 0x1e, /* 37 Trans,DTMF+TONE+mixer */
+ 0x1f /* 38 Trans,DTMF+TONE+mixer+local*/
+};
+
+
+static word get_b1_facilities (PLCI * plci, byte b1_resource)
+{
+ word b1_facilities;
+
+ b1_facilities = b1_facilities_table[b1_resource];
+ if ((b1_resource == 9) || (b1_resource == 20) || (b1_resource == 25))
+ {
+
+ if (!(((plci->requested_options_conn | plci->requested_options) & (1L << PRIVATE_DTMF_TONE))
+ || (plci->appl && (plci->adapter->requested_options_table[plci->appl->Id-1] & (1L << PRIVATE_DTMF_TONE)))))
+
+ {
+ if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_SOFTDTMF_SEND)
+ b1_facilities |= B1_FACILITY_DTMFX;
+ if (plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_SOFTDTMF_RECEIVE)
+ b1_facilities |= B1_FACILITY_DTMFR;
+ }
+ }
+ if ((b1_resource == 17) || (b1_resource == 18))
+ {
+ if (plci->adapter->manufacturer_features & (MANUFACTURER_FEATURE_V18 | MANUFACTURER_FEATURE_VOWN))
+ b1_facilities |= B1_FACILITY_DTMFX | B1_FACILITY_DTMFR;
+ }
+/*
+ dbug (1, dprintf ("[%06lx] %s,%d: get_b1_facilities %d %04x",
+ (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+ (char far *)(FILE_), __LINE__, b1_resource, b1_facilites));
+*/
+ return (b1_facilities);
+}
+
+
+static byte add_b1_facilities (PLCI * plci, byte b1_resource, word b1_facilities)
+{
+ byte b;
+
+ switch (b1_resource)
+ {
+ case 5:
+ case 26:
+ if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE))
+ b = 26;
+ else
+ b = 5;
+ break;
+
+ case 8:
+ case 27:
+ if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE))
+ b = 27;
+ else
+ b = 8;
+ break;
+
+ case 9:
+ case 20:
+ case 22:
+ case 23:
+ case 24:
+ case 25:
+ case 28:
+ case 29:
+ case 30:
+ case 36:
+ case 37:
+ case 38:
+ if (b1_facilities & B1_FACILITY_EC)
+ {
+ if (b1_facilities & B1_FACILITY_LOCAL)
+ b = 30;
+ else if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE))
+ b = 29;
+ else
+ b = 28;
+ }
+
+ else if ((b1_facilities & (B1_FACILITY_DTMFX | B1_FACILITY_DTMFR | B1_FACILITY_MIXER))
+ && (((plci->requested_options_conn | plci->requested_options) & (1L << PRIVATE_DTMF_TONE))
+ || (plci->appl && (plci->adapter->requested_options_table[plci->appl->Id-1] & (1L << PRIVATE_DTMF_TONE)))))
+ {
+ if (b1_facilities & B1_FACILITY_LOCAL)
+ b = 38;
+ else if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE))
+ b = 37;
+ else
+ b = 36;
+ }
+
+ else if (((plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_HARDDTMF)
+ && !(plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_SOFTDTMF_RECEIVE))
+ || ((b1_facilities & B1_FACILITY_DTMFR)
+ && ((b1_facilities & B1_FACILITY_MIXER)
+ || !(plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_SOFTDTMF_RECEIVE)))
+ || ((b1_facilities & B1_FACILITY_DTMFX)
+ && ((b1_facilities & B1_FACILITY_MIXER)
+ || !(plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_SOFTDTMF_SEND))))
+ {
+ if (b1_facilities & B1_FACILITY_LOCAL)
+ b = 24;
+ else if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE))
+ b = 23;
+ else
+ b = 22;
+ }
+ else
+ {
+ if (b1_facilities & B1_FACILITY_LOCAL)
+ b = 25;
+ else if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE))
+ b = 20;
+ else
+ b = 9;
+ }
+ break;
+
+ case 31:
+ case 32:
+ case 33:
+ if (b1_facilities & B1_FACILITY_LOCAL)
+ b = 33;
+ else if (b1_facilities & (B1_FACILITY_MIXER | B1_FACILITY_VOICE))
+ b = 32;
+ else
+ b = 31;
+ break;
+
+ default:
+ b = b1_resource;
+ }
+ dbug (1, dprintf ("[%06lx] %s,%d: add_b1_facilities %d %04x %d %04x",
+ (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+ (char *)(FILE_), __LINE__,
+ b1_resource, b1_facilities, b, get_b1_facilities (plci, b)));
+ return (b);
+}
+
+
+static void adjust_b1_facilities (PLCI *plci, byte new_b1_resource, word new_b1_facilities)
+{
+ word removed_facilities;
+
+ dbug (1, dprintf ("[%06lx] %s,%d: adjust_b1_facilities %d %04x %04x",
+ (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+ (char *)(FILE_), __LINE__, new_b1_resource, new_b1_facilities,
+ new_b1_facilities & get_b1_facilities (plci, new_b1_resource)));
+
+ new_b1_facilities &= get_b1_facilities (plci, new_b1_resource);
+ removed_facilities = plci->B1_facilities & ~new_b1_facilities;
+
+ if (removed_facilities & B1_FACILITY_EC)
+ ec_clear_config (plci);
+
+
+ if (removed_facilities & B1_FACILITY_DTMFR)
+ {
+ dtmf_rec_clear_config (plci);
+ dtmf_parameter_clear_config (plci);
+ }
+ if (removed_facilities & B1_FACILITY_DTMFX)
+ dtmf_send_clear_config (plci);
+
+
+ if (removed_facilities & B1_FACILITY_MIXER)
+ mixer_clear_config (plci);
+
+ if (removed_facilities & B1_FACILITY_VOICE)
+ adv_voice_clear_config (plci);
+ plci->B1_facilities = new_b1_facilities;
+}
+
+
+static void adjust_b_clear (PLCI *plci)
+{
+
+ dbug (1, dprintf ("[%06lx] %s,%d: adjust_b_clear",
+ (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+ (char *)(FILE_), __LINE__));
+
+ plci->adjust_b_restore = false;
+}
+
+
+static word adjust_b_process (dword Id, PLCI *plci, byte Rc)
+{
+ word Info;
+ byte b1_resource;
+ NCCI * ncci_ptr;
+ API_PARSE bp[2];
+
+ dbug (1, dprintf ("[%06lx] %s,%d: adjust_b_process %02x %d",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc, plci->adjust_b_state));
+
+ Info = GOOD;
+ switch (plci->adjust_b_state)
+ {
+ case ADJUST_B_START:
+ if ((plci->adjust_b_parms_msg == NULL)
+ && (plci->adjust_b_mode & ADJUST_B_MODE_SWITCH_L1)
+ && ((plci->adjust_b_mode & ~(ADJUST_B_MODE_SAVE | ADJUST_B_MODE_SWITCH_L1 |
+ ADJUST_B_MODE_NO_RESOURCE | ADJUST_B_MODE_RESTORE)) == 0))
+ {
+ b1_resource = (plci->adjust_b_mode == ADJUST_B_MODE_NO_RESOURCE) ?
+ 0 : add_b1_facilities (plci, plci->B1_resource, plci->adjust_b_facilities);
+ if (b1_resource == plci->B1_resource)
+ {
+ adjust_b1_facilities (plci, b1_resource, plci->adjust_b_facilities);
+ break;
+ }
+ if (plci->adjust_b_facilities & ~get_b1_facilities (plci, b1_resource))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Adjust B nonsupported facilities %d %d %04x",
+ UnMapId (Id), (char *)(FILE_), __LINE__,
+ plci->B1_resource, b1_resource, plci->adjust_b_facilities));
+ Info = _WRONG_STATE;
+ break;
+ }
+ }
+ if (plci->adjust_b_mode & ADJUST_B_MODE_SAVE)
+ {
+
+ mixer_prepare_switch (Id, plci);
+
+
+ dtmf_prepare_switch (Id, plci);
+ dtmf_parameter_prepare_switch (Id, plci);
+
+
+ ec_prepare_switch (Id, plci);
+
+ adv_voice_prepare_switch (Id, plci);
+ }
+ plci->adjust_b_state = ADJUST_B_SAVE_MIXER_1;
+ Rc = OK;
+ case ADJUST_B_SAVE_MIXER_1:
+ if (plci->adjust_b_mode & ADJUST_B_MODE_SAVE)
+ {
+
+ Info = mixer_save_config (Id, plci, Rc);
+ if ((Info != GOOD) || plci->internal_command)
+ break;
+
+ }
+ plci->adjust_b_state = ADJUST_B_SAVE_DTMF_1;
+ Rc = OK;
+ case ADJUST_B_SAVE_DTMF_1:
+ if (plci->adjust_b_mode & ADJUST_B_MODE_SAVE)
+ {
+
+ Info = dtmf_save_config (Id, plci, Rc);
+ if ((Info != GOOD) || plci->internal_command)
+ break;
+
+ }
+ plci->adjust_b_state = ADJUST_B_REMOVE_L23_1;
+ case ADJUST_B_REMOVE_L23_1:
+ if ((plci->adjust_b_mode & ADJUST_B_MODE_REMOVE_L23)
+ && plci->NL.Id && !plci->nl_remove_id)
+ {
+ plci->internal_command = plci->adjust_b_command;
+ if (plci->adjust_b_ncci != 0)
+ {
+ ncci_ptr = &(plci->adapter->ncci[plci->adjust_b_ncci]);
+ while (ncci_ptr->data_pending)
+ {
+ plci->data_sent_ptr = ncci_ptr->DBuffer[ncci_ptr->data_out].P;
+ data_rc (plci, plci->adapter->ncci_ch[plci->adjust_b_ncci]);
+ }
+ while (ncci_ptr->data_ack_pending)
+ data_ack (plci, plci->adapter->ncci_ch[plci->adjust_b_ncci]);
+ }
+ nl_req_ncci (plci, REMOVE,
+ (byte)((plci->adjust_b_mode & ADJUST_B_MODE_CONNECT) ? plci->adjust_b_ncci : 0));
+ send_req (plci);
+ plci->adjust_b_state = ADJUST_B_REMOVE_L23_2;
+ break;
+ }
+ plci->adjust_b_state = ADJUST_B_REMOVE_L23_2;
+ Rc = OK;
+ case ADJUST_B_REMOVE_L23_2:
+ if ((Rc != OK) && (Rc != OK_FC))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Adjust B remove failed %02x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc));
+ Info = _WRONG_STATE;
+ break;
+ }
+ if (plci->adjust_b_mode & ADJUST_B_MODE_REMOVE_L23)
+ {
+ if (plci_nl_busy (plci))
+ {
+ plci->internal_command = plci->adjust_b_command;
+ break;
+ }
+ }
+ plci->adjust_b_state = ADJUST_B_SAVE_EC_1;
+ Rc = OK;
+ case ADJUST_B_SAVE_EC_1:
+ if (plci->adjust_b_mode & ADJUST_B_MODE_SAVE)
+ {
+
+ Info = ec_save_config (Id, plci, Rc);
+ if ((Info != GOOD) || plci->internal_command)
+ break;
+
+ }
+ plci->adjust_b_state = ADJUST_B_SAVE_DTMF_PARAMETER_1;
+ Rc = OK;
+ case ADJUST_B_SAVE_DTMF_PARAMETER_1:
+ if (plci->adjust_b_mode & ADJUST_B_MODE_SAVE)
+ {
+
+ Info = dtmf_parameter_save_config (Id, plci, Rc);
+ if ((Info != GOOD) || plci->internal_command)
+ break;
+
+ }
+ plci->adjust_b_state = ADJUST_B_SAVE_VOICE_1;
+ Rc = OK;
+ case ADJUST_B_SAVE_VOICE_1:
+ if (plci->adjust_b_mode & ADJUST_B_MODE_SAVE)
+ {
+ Info = adv_voice_save_config (Id, plci, Rc);
+ if ((Info != GOOD) || plci->internal_command)
+ break;
+ }
+ plci->adjust_b_state = ADJUST_B_SWITCH_L1_1;
+ case ADJUST_B_SWITCH_L1_1:
+ if (plci->adjust_b_mode & ADJUST_B_MODE_SWITCH_L1)
+ {
+ if (plci->sig_req)
+ {
+ plci->internal_command = plci->adjust_b_command;
+ break;
+ }
+ if (plci->adjust_b_parms_msg != NULL)
+ api_load_msg (plci->adjust_b_parms_msg, bp);
+ else
+ api_load_msg (&plci->B_protocol, bp);
+ Info = add_b1 (plci, bp,
+ (word)((plci->adjust_b_mode & ADJUST_B_MODE_NO_RESOURCE) ? 2 : 0),
+ plci->adjust_b_facilities);
+ if (Info != GOOD)
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Adjust B invalid L1 parameters %d %04x",
+ UnMapId (Id), (char *)(FILE_), __LINE__,
+ plci->B1_resource, plci->adjust_b_facilities));
+ break;
+ }
+ plci->internal_command = plci->adjust_b_command;
+ sig_req (plci, RESOURCES, 0);
+ send_req (plci);
+ plci->adjust_b_state = ADJUST_B_SWITCH_L1_2;
+ break;
+ }
+ plci->adjust_b_state = ADJUST_B_SWITCH_L1_2;
+ Rc = OK;
+ case ADJUST_B_SWITCH_L1_2:
+ if ((Rc != OK) && (Rc != OK_FC))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Adjust B switch failed %02x %d %04x",
+ UnMapId (Id), (char *)(FILE_), __LINE__,
+ Rc, plci->B1_resource, plci->adjust_b_facilities));
+ Info = _WRONG_STATE;
+ break;
+ }
+ plci->adjust_b_state = ADJUST_B_RESTORE_VOICE_1;
+ Rc = OK;
+ case ADJUST_B_RESTORE_VOICE_1:
+ case ADJUST_B_RESTORE_VOICE_2:
+ if (plci->adjust_b_mode & ADJUST_B_MODE_RESTORE)
+ {
+ Info = adv_voice_restore_config (Id, plci, Rc);
+ if ((Info != GOOD) || plci->internal_command)
+ break;
+ }
+ plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_PARAMETER_1;
+ Rc = OK;
+ case ADJUST_B_RESTORE_DTMF_PARAMETER_1:
+ case ADJUST_B_RESTORE_DTMF_PARAMETER_2:
+ if (plci->adjust_b_mode & ADJUST_B_MODE_RESTORE)
+ {
+
+ Info = dtmf_parameter_restore_config (Id, plci, Rc);
+ if ((Info != GOOD) || plci->internal_command)
+ break;
+
+ }
+ plci->adjust_b_state = ADJUST_B_RESTORE_EC_1;
+ Rc = OK;
+ case ADJUST_B_RESTORE_EC_1:
+ case ADJUST_B_RESTORE_EC_2:
+ if (plci->adjust_b_mode & ADJUST_B_MODE_RESTORE)
+ {
+
+ Info = ec_restore_config (Id, plci, Rc);
+ if ((Info != GOOD) || plci->internal_command)
+ break;
+
+ }
+ plci->adjust_b_state = ADJUST_B_ASSIGN_L23_1;
+ case ADJUST_B_ASSIGN_L23_1:
+ if (plci->adjust_b_mode & ADJUST_B_MODE_ASSIGN_L23)
+ {
+ if (plci_nl_busy (plci))
+ {
+ plci->internal_command = plci->adjust_b_command;
+ break;
+ }
+ if (plci->adjust_b_mode & ADJUST_B_MODE_CONNECT)
+ plci->call_dir |= CALL_DIR_FORCE_OUTG_NL;
+ if (plci->adjust_b_parms_msg != NULL)
+ api_load_msg (plci->adjust_b_parms_msg, bp);
+ else
+ api_load_msg (&plci->B_protocol, bp);
+ Info = add_b23 (plci, bp);
+ if (Info != GOOD)
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Adjust B invalid L23 parameters %04x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Info));
+ break;
+ }
+ plci->internal_command = plci->adjust_b_command;
+ nl_req_ncci (plci, ASSIGN, 0);
+ send_req (plci);
+ plci->adjust_b_state = ADJUST_B_ASSIGN_L23_2;
+ break;
+ }
+ plci->adjust_b_state = ADJUST_B_ASSIGN_L23_2;
+ Rc = ASSIGN_OK;
+ case ADJUST_B_ASSIGN_L23_2:
+ if ((Rc != OK) && (Rc != OK_FC) && (Rc != ASSIGN_OK))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Adjust B assign failed %02x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc));
+ Info = _WRONG_STATE;
+ break;
+ }
+ if (plci->adjust_b_mode & ADJUST_B_MODE_ASSIGN_L23)
+ {
+ if (Rc != ASSIGN_OK)
+ {
+ plci->internal_command = plci->adjust_b_command;
+ break;
+ }
+ }
+ if (plci->adjust_b_mode & ADJUST_B_MODE_USER_CONNECT)
+ {
+ plci->adjust_b_restore = true;
+ break;
+ }
+ plci->adjust_b_state = ADJUST_B_CONNECT_1;
+ case ADJUST_B_CONNECT_1:
+ if (plci->adjust_b_mode & ADJUST_B_MODE_CONNECT)
+ {
+ plci->internal_command = plci->adjust_b_command;
+ if (plci_nl_busy (plci))
+ break;
+ nl_req_ncci (plci, N_CONNECT, 0);
+ send_req (plci);
+ plci->adjust_b_state = ADJUST_B_CONNECT_2;
+ break;
+ }
+ plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_1;
+ Rc = OK;
+ case ADJUST_B_CONNECT_2:
+ case ADJUST_B_CONNECT_3:
+ case ADJUST_B_CONNECT_4:
+ if ((Rc != OK) && (Rc != OK_FC) && (Rc != 0))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Adjust B connect failed %02x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc));
+ Info = _WRONG_STATE;
+ break;
+ }
+ if (Rc == OK)
+ {
+ if (plci->adjust_b_mode & ADJUST_B_MODE_CONNECT)
+ {
+ get_ncci (plci, (byte)(Id >> 16), plci->adjust_b_ncci);
+ Id = (Id & 0xffff) | (((dword)(plci->adjust_b_ncci)) << 16);
+ }
+ if (plci->adjust_b_state == ADJUST_B_CONNECT_2)
+ plci->adjust_b_state = ADJUST_B_CONNECT_3;
+ else if (plci->adjust_b_state == ADJUST_B_CONNECT_4)
+ plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_1;
+ }
+ else if (Rc == 0)
+ {
+ if (plci->adjust_b_state == ADJUST_B_CONNECT_2)
+ plci->adjust_b_state = ADJUST_B_CONNECT_4;
+ else if (plci->adjust_b_state == ADJUST_B_CONNECT_3)
+ plci->adjust_b_state = ADJUST_B_RESTORE_DTMF_1;
+ }
+ if (plci->adjust_b_state != ADJUST_B_RESTORE_DTMF_1)
+ {
+ plci->internal_command = plci->adjust_b_command;
+ break;
+ }
+ Rc = OK;
+ case ADJUST_B_RESTORE_DTMF_1:
+ case ADJUST_B_RESTORE_DTMF_2:
+ if (plci->adjust_b_mode & ADJUST_B_MODE_RESTORE)
+ {
+
+ Info = dtmf_restore_config (Id, plci, Rc);
+ if ((Info != GOOD) || plci->internal_command)
+ break;
+
+ }
+ plci->adjust_b_state = ADJUST_B_RESTORE_MIXER_1;
+ Rc = OK;
+ case ADJUST_B_RESTORE_MIXER_1:
+ case ADJUST_B_RESTORE_MIXER_2:
+ case ADJUST_B_RESTORE_MIXER_3:
+ case ADJUST_B_RESTORE_MIXER_4:
+ case ADJUST_B_RESTORE_MIXER_5:
+ case ADJUST_B_RESTORE_MIXER_6:
+ case ADJUST_B_RESTORE_MIXER_7:
+ if (plci->adjust_b_mode & ADJUST_B_MODE_RESTORE)
+ {
+
+ Info = mixer_restore_config (Id, plci, Rc);
+ if ((Info != GOOD) || plci->internal_command)
+ break;
+
+ }
+ plci->adjust_b_state = ADJUST_B_END;
+ case ADJUST_B_END:
+ break;
+ }
+ return (Info);
+}
+
+
+static void adjust_b1_resource (dword Id, PLCI *plci, API_SAVE *bp_msg, word b1_facilities, word internal_command)
+{
+
+ dbug (1, dprintf ("[%06lx] %s,%d: adjust_b1_resource %d %04x",
+ UnMapId (Id), (char *)(FILE_), __LINE__,
+ plci->B1_resource, b1_facilities));
+
+ plci->adjust_b_parms_msg = bp_msg;
+ plci->adjust_b_facilities = b1_facilities;
+ plci->adjust_b_command = internal_command;
+ plci->adjust_b_ncci = (word)(Id >> 16);
+ if ((bp_msg == NULL) && (plci->B1_resource == 0))
+ plci->adjust_b_mode = ADJUST_B_MODE_SAVE | ADJUST_B_MODE_NO_RESOURCE | ADJUST_B_MODE_SWITCH_L1;
+ else
+ plci->adjust_b_mode = ADJUST_B_MODE_SAVE | ADJUST_B_MODE_SWITCH_L1 | ADJUST_B_MODE_RESTORE;
+ plci->adjust_b_state = ADJUST_B_START;
+ dbug (1, dprintf ("[%06lx] %s,%d: Adjust B1 resource %d %04x...",
+ UnMapId (Id), (char *)(FILE_), __LINE__,
+ plci->B1_resource, b1_facilities));
+}
+
+
+static void adjust_b_restore (dword Id, PLCI *plci, byte Rc)
+{
+ word internal_command;
+
+ dbug (1, dprintf ("[%06lx] %s,%d: adjust_b_restore %02x %04x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));
+
+ internal_command = plci->internal_command;
+ plci->internal_command = 0;
+ switch (internal_command)
+ {
+ default:
+ plci->command = 0;
+ if (plci->req_in != 0)
+ {
+ plci->internal_command = ADJUST_B_RESTORE_1;
+ break;
+ }
+ Rc = OK;
+ case ADJUST_B_RESTORE_1:
+ if ((Rc != OK) && (Rc != OK_FC))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Adjust B enqueued failed %02x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc));
+ }
+ plci->adjust_b_parms_msg = NULL;
+ plci->adjust_b_facilities = plci->B1_facilities;
+ plci->adjust_b_command = ADJUST_B_RESTORE_2;
+ plci->adjust_b_ncci = (word)(Id >> 16);
+ plci->adjust_b_mode = ADJUST_B_MODE_RESTORE;
+ plci->adjust_b_state = ADJUST_B_START;
+ dbug (1, dprintf ("[%06lx] %s,%d: Adjust B restore...",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ case ADJUST_B_RESTORE_2:
+ if (adjust_b_process (Id, plci, Rc) != GOOD)
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Adjust B restore failed",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ }
+ if (plci->internal_command)
+ break;
+ break;
+ }
+}
+
+
+static void reset_b3_command (dword Id, PLCI *plci, byte Rc)
+{
+ word Info;
+ word internal_command;
+
+ dbug (1, dprintf ("[%06lx] %s,%d: reset_b3_command %02x %04x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));
+
+ Info = GOOD;
+ internal_command = plci->internal_command;
+ plci->internal_command = 0;
+ switch (internal_command)
+ {
+ default:
+ plci->command = 0;
+ plci->adjust_b_parms_msg = NULL;
+ plci->adjust_b_facilities = plci->B1_facilities;
+ plci->adjust_b_command = RESET_B3_COMMAND_1;
+ plci->adjust_b_ncci = (word)(Id >> 16);
+ plci->adjust_b_mode = ADJUST_B_MODE_REMOVE_L23 | ADJUST_B_MODE_ASSIGN_L23 | ADJUST_B_MODE_CONNECT;
+ plci->adjust_b_state = ADJUST_B_START;
+ dbug (1, dprintf ("[%06lx] %s,%d: Reset B3...",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ case RESET_B3_COMMAND_1:
+ Info = adjust_b_process (Id, plci, Rc);
+ if (Info != GOOD)
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Reset failed",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ break;
+ }
+ if (plci->internal_command)
+ return;
+ break;
+ }
+/* sendf (plci->appl, _RESET_B3_R | CONFIRM, Id, plci->number, "w", Info);*/
+ sendf(plci->appl,_RESET_B3_I,Id,0,"s","");
+}
+
+
+static void select_b_command (dword Id, PLCI *plci, byte Rc)
+{
+ word Info;
+ word internal_command;
+ byte esc_chi[3];
+
+ dbug (1, dprintf ("[%06lx] %s,%d: select_b_command %02x %04x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));
+
+ Info = GOOD;
+ internal_command = plci->internal_command;
+ plci->internal_command = 0;
+ switch (internal_command)
+ {
+ default:
+ plci->command = 0;
+ plci->adjust_b_parms_msg = &plci->saved_msg;
+ if ((plci->tel == ADV_VOICE) && (plci == plci->adapter->AdvSignalPLCI))
+ plci->adjust_b_facilities = plci->B1_facilities | B1_FACILITY_VOICE;
+ else
+ plci->adjust_b_facilities = plci->B1_facilities & ~B1_FACILITY_VOICE;
+ plci->adjust_b_command = SELECT_B_COMMAND_1;
+ plci->adjust_b_ncci = (word)(Id >> 16);
+ if (plci->saved_msg.parms[0].length == 0)
+ {
+ plci->adjust_b_mode = ADJUST_B_MODE_SAVE | ADJUST_B_MODE_REMOVE_L23 | ADJUST_B_MODE_SWITCH_L1 |
+ ADJUST_B_MODE_NO_RESOURCE;
+ }
+ else
+ {
+ plci->adjust_b_mode = ADJUST_B_MODE_SAVE | ADJUST_B_MODE_REMOVE_L23 | ADJUST_B_MODE_SWITCH_L1 |
+ ADJUST_B_MODE_ASSIGN_L23 | ADJUST_B_MODE_USER_CONNECT | ADJUST_B_MODE_RESTORE;
+ }
+ plci->adjust_b_state = ADJUST_B_START;
+ dbug (1, dprintf ("[%06lx] %s,%d: Select B protocol...",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ case SELECT_B_COMMAND_1:
+ Info = adjust_b_process (Id, plci, Rc);
+ if (Info != GOOD)
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: Select B protocol failed",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ break;
+ }
+ if (plci->internal_command)
+ return;
+ if (plci->tel == ADV_VOICE)
+ {
+ esc_chi[0] = 0x02;
+ esc_chi[1] = 0x18;
+ esc_chi[2] = plci->b_channel;
+ SetVoiceChannel (plci->adapter->AdvCodecPLCI, esc_chi, plci->adapter);
+ }
+ break;
+ }
+ sendf (plci->appl, _SELECT_B_REQ | CONFIRM, Id, plci->number, "w", Info);
+}
+
+
+static void fax_connect_ack_command (dword Id, PLCI *plci, byte Rc)
+{
+ word Info;
+ word internal_command;
+
+ dbug (1, dprintf ("[%06lx] %s,%d: fax_connect_ack_command %02x %04x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));
+
+ Info = GOOD;
+ internal_command = plci->internal_command;
+ plci->internal_command = 0;
+ switch (internal_command)
+ {
+ default:
+ plci->command = 0;
+ case FAX_CONNECT_ACK_COMMAND_1:
+ if (plci_nl_busy (plci))
+ {
+ plci->internal_command = FAX_CONNECT_ACK_COMMAND_1;
+ return;
+ }
+ plci->internal_command = FAX_CONNECT_ACK_COMMAND_2;
+ plci->NData[0].P = plci->fax_connect_info_buffer;
+ plci->NData[0].PLength = plci->fax_connect_info_length;
+ plci->NL.X = plci->NData;
+ plci->NL.ReqCh = 0;
+ plci->NL.Req = plci->nl_req = (byte) N_CONNECT_ACK;
+ plci->adapter->request (&plci->NL);
+ return;
+ case FAX_CONNECT_ACK_COMMAND_2:
+ if ((Rc != OK) && (Rc != OK_FC))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: FAX issue CONNECT ACK failed %02x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc));
+ break;
+ }
+ }
+ if ((plci->ncpi_state & NCPI_VALID_CONNECT_B3_ACT)
+ && !(plci->ncpi_state & NCPI_CONNECT_B3_ACT_SENT))
+ {
+ if (plci->B3_prot == 4)
+ sendf(plci->appl,_CONNECT_B3_ACTIVE_I,Id,0,"s","");
+ else
+ sendf(plci->appl,_CONNECT_B3_ACTIVE_I,Id,0,"S",plci->ncpi_buffer);
+ plci->ncpi_state |= NCPI_CONNECT_B3_ACT_SENT;
+ }
+}
+
+
+static void fax_edata_ack_command (dword Id, PLCI *plci, byte Rc)
+{
+ word Info;
+ word internal_command;
+
+ dbug (1, dprintf ("[%06lx] %s,%d: fax_edata_ack_command %02x %04x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));
+
+ Info = GOOD;
+ internal_command = plci->internal_command;
+ plci->internal_command = 0;
+ switch (internal_command)
+ {
+ default:
+ plci->command = 0;
+ case FAX_EDATA_ACK_COMMAND_1:
+ if (plci_nl_busy (plci))
+ {
+ plci->internal_command = FAX_EDATA_ACK_COMMAND_1;
+ return;
+ }
+ plci->internal_command = FAX_EDATA_ACK_COMMAND_2;
+ plci->NData[0].P = plci->fax_connect_info_buffer;
+ plci->NData[0].PLength = plci->fax_edata_ack_length;
+ plci->NL.X = plci->NData;
+ plci->NL.ReqCh = 0;
+ plci->NL.Req = plci->nl_req = (byte) N_EDATA;
+ plci->adapter->request (&plci->NL);
+ return;
+ case FAX_EDATA_ACK_COMMAND_2:
+ if ((Rc != OK) && (Rc != OK_FC))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: FAX issue EDATA ACK failed %02x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc));
+ break;
+ }
+ }
+}
+
+
+static void fax_connect_info_command (dword Id, PLCI *plci, byte Rc)
+{
+ word Info;
+ word internal_command;
+
+ dbug (1, dprintf ("[%06lx] %s,%d: fax_connect_info_command %02x %04x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));
+
+ Info = GOOD;
+ internal_command = plci->internal_command;
+ plci->internal_command = 0;
+ switch (internal_command)
+ {
+ default:
+ plci->command = 0;
+ case FAX_CONNECT_INFO_COMMAND_1:
+ if (plci_nl_busy (plci))
+ {
+ plci->internal_command = FAX_CONNECT_INFO_COMMAND_1;
+ return;
+ }
+ plci->internal_command = FAX_CONNECT_INFO_COMMAND_2;
+ plci->NData[0].P = plci->fax_connect_info_buffer;
+ plci->NData[0].PLength = plci->fax_connect_info_length;
+ plci->NL.X = plci->NData;
+ plci->NL.ReqCh = 0;
+ plci->NL.Req = plci->nl_req = (byte) N_EDATA;
+ plci->adapter->request (&plci->NL);
+ return;
+ case FAX_CONNECT_INFO_COMMAND_2:
+ if ((Rc != OK) && (Rc != OK_FC))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: FAX setting connect info failed %02x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc));
+ Info = _WRONG_STATE;
+ break;
+ }
+ if (plci_nl_busy (plci))
+ {
+ plci->internal_command = FAX_CONNECT_INFO_COMMAND_2;
+ return;
+ }
+ plci->command = _CONNECT_B3_R;
+ nl_req_ncci (plci, N_CONNECT, 0);
+ send_req (plci);
+ return;
+ }
+ sendf (plci->appl, _CONNECT_B3_R | CONFIRM, Id, plci->number, "w", Info);
+}
+
+
+static void fax_adjust_b23_command (dword Id, PLCI *plci, byte Rc)
+{
+ word Info;
+ word internal_command;
+
+ dbug (1, dprintf ("[%06lx] %s,%d: fax_adjust_b23_command %02x %04x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));
+
+ Info = GOOD;
+ internal_command = plci->internal_command;
+ plci->internal_command = 0;
+ switch (internal_command)
+ {
+ default:
+ plci->command = 0;
+ plci->adjust_b_parms_msg = NULL;
+ plci->adjust_b_facilities = plci->B1_facilities;
+ plci->adjust_b_command = FAX_ADJUST_B23_COMMAND_1;
+ plci->adjust_b_ncci = (word)(Id >> 16);
+ plci->adjust_b_mode = ADJUST_B_MODE_REMOVE_L23 | ADJUST_B_MODE_ASSIGN_L23;
+ plci->adjust_b_state = ADJUST_B_START;
+ dbug (1, dprintf ("[%06lx] %s,%d: FAX adjust B23...",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ case FAX_ADJUST_B23_COMMAND_1:
+ Info = adjust_b_process (Id, plci, Rc);
+ if (Info != GOOD)
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: FAX adjust failed",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ break;
+ }
+ if (plci->internal_command)
+ return;
+ case FAX_ADJUST_B23_COMMAND_2:
+ if (plci_nl_busy (plci))
+ {
+ plci->internal_command = FAX_ADJUST_B23_COMMAND_2;
+ return;
+ }
+ plci->command = _CONNECT_B3_R;
+ nl_req_ncci (plci, N_CONNECT, 0);
+ send_req (plci);
+ return;
+ }
+ sendf (plci->appl, _CONNECT_B3_R | CONFIRM, Id, plci->number, "w", Info);
+}
+
+
+static void fax_disconnect_command (dword Id, PLCI *plci, byte Rc)
+{
+ word internal_command;
+
+ dbug (1, dprintf ("[%06lx] %s,%d: fax_disconnect_command %02x %04x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));
+
+ internal_command = plci->internal_command;
+ plci->internal_command = 0;
+ switch (internal_command)
+ {
+ default:
+ plci->command = 0;
+ plci->internal_command = FAX_DISCONNECT_COMMAND_1;
+ return;
+ case FAX_DISCONNECT_COMMAND_1:
+ case FAX_DISCONNECT_COMMAND_2:
+ case FAX_DISCONNECT_COMMAND_3:
+ if ((Rc != OK) && (Rc != OK_FC) && (Rc != 0))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: FAX disconnect EDATA failed %02x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc));
+ break;
+ }
+ if (Rc == OK)
+ {
+ if ((internal_command == FAX_DISCONNECT_COMMAND_1)
+ || (internal_command == FAX_DISCONNECT_COMMAND_2))
+ {
+ plci->internal_command = FAX_DISCONNECT_COMMAND_2;
+ }
+ }
+ else if (Rc == 0)
+ {
+ if (internal_command == FAX_DISCONNECT_COMMAND_1)
+ plci->internal_command = FAX_DISCONNECT_COMMAND_3;
+ }
+ return;
+ }
+}
+
+
+
+static void rtp_connect_b3_req_command (dword Id, PLCI *plci, byte Rc)
+{
+ word Info;
+ word internal_command;
+
+ dbug (1, dprintf ("[%06lx] %s,%d: rtp_connect_b3_req_command %02x %04x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));
+
+ Info = GOOD;
+ internal_command = plci->internal_command;
+ plci->internal_command = 0;
+ switch (internal_command)
+ {
+ default:
+ plci->command = 0;
+ case RTP_CONNECT_B3_REQ_COMMAND_1:
+ if (plci_nl_busy (plci))
+ {
+ plci->internal_command = RTP_CONNECT_B3_REQ_COMMAND_1;
+ return;
+ }
+ plci->internal_command = RTP_CONNECT_B3_REQ_COMMAND_2;
+ nl_req_ncci (plci, N_CONNECT, 0);
+ send_req (plci);
+ return;
+ case RTP_CONNECT_B3_REQ_COMMAND_2:
+ if ((Rc != OK) && (Rc != OK_FC))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: RTP setting connect info failed %02x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc));
+ Info = _WRONG_STATE;
+ break;
+ }
+ if (plci_nl_busy (plci))
+ {
+ plci->internal_command = RTP_CONNECT_B3_REQ_COMMAND_2;
+ return;
+ }
+ plci->internal_command = RTP_CONNECT_B3_REQ_COMMAND_3;
+ plci->NData[0].PLength = plci->internal_req_buffer[0];
+ plci->NData[0].P = plci->internal_req_buffer + 1;
+ plci->NL.X = plci->NData;
+ plci->NL.ReqCh = 0;
+ plci->NL.Req = plci->nl_req = (byte) N_UDATA;
+ plci->adapter->request (&plci->NL);
+ break;
+ case RTP_CONNECT_B3_REQ_COMMAND_3:
+ return;
+ }
+ sendf (plci->appl, _CONNECT_B3_R | CONFIRM, Id, plci->number, "w", Info);
+}
+
+
+static void rtp_connect_b3_res_command (dword Id, PLCI *plci, byte Rc)
+{
+ word Info;
+ word internal_command;
+
+ dbug (1, dprintf ("[%06lx] %s,%d: rtp_connect_b3_res_command %02x %04x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));
+
+ Info = GOOD;
+ internal_command = plci->internal_command;
+ plci->internal_command = 0;
+ switch (internal_command)
+ {
+ default:
+ plci->command = 0;
+ case RTP_CONNECT_B3_RES_COMMAND_1:
+ if (plci_nl_busy (plci))
+ {
+ plci->internal_command = RTP_CONNECT_B3_RES_COMMAND_1;
+ return;
+ }
+ plci->internal_command = RTP_CONNECT_B3_RES_COMMAND_2;
+ nl_req_ncci (plci, N_CONNECT_ACK, (byte)(Id >> 16));
+ send_req (plci);
+ return;
+ case RTP_CONNECT_B3_RES_COMMAND_2:
+ if ((Rc != OK) && (Rc != OK_FC))
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: RTP setting connect resp info failed %02x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc));
+ Info = _WRONG_STATE;
+ break;
+ }
+ if (plci_nl_busy (plci))
+ {
+ plci->internal_command = RTP_CONNECT_B3_RES_COMMAND_2;
+ return;
+ }
+ sendf (plci->appl, _CONNECT_B3_ACTIVE_I, Id, 0, "s", "");
+ plci->internal_command = RTP_CONNECT_B3_RES_COMMAND_3;
+ plci->NData[0].PLength = plci->internal_req_buffer[0];
+ plci->NData[0].P = plci->internal_req_buffer + 1;
+ plci->NL.X = plci->NData;
+ plci->NL.ReqCh = 0;
+ plci->NL.Req = plci->nl_req = (byte) N_UDATA;
+ plci->adapter->request (&plci->NL);
+ return;
+ case RTP_CONNECT_B3_RES_COMMAND_3:
+ return;
+ }
+}
+
+
+
+static void hold_save_command (dword Id, PLCI *plci, byte Rc)
+{
+ byte SS_Ind[] = "\x05\x02\x00\x02\x00\x00"; /* Hold_Ind struct*/
+ word Info;
+ word internal_command;
+
+ dbug (1, dprintf ("[%06lx] %s,%d: hold_save_command %02x %04x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));
+
+ Info = GOOD;
+ internal_command = plci->internal_command;
+ plci->internal_command = 0;
+ switch (internal_command)
+ {
+ default:
+ if (!plci->NL.Id)
+ break;
+ plci->command = 0;
+ plci->adjust_b_parms_msg = NULL;
+ plci->adjust_b_facilities = plci->B1_facilities;
+ plci->adjust_b_command = HOLD_SAVE_COMMAND_1;
+ plci->adjust_b_ncci = (word)(Id >> 16);
+ plci->adjust_b_mode = ADJUST_B_MODE_SAVE | ADJUST_B_MODE_REMOVE_L23;
+ plci->adjust_b_state = ADJUST_B_START;
+ dbug (1, dprintf ("[%06lx] %s,%d: HOLD save...",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ case HOLD_SAVE_COMMAND_1:
+ Info = adjust_b_process (Id, plci, Rc);
+ if (Info != GOOD)
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: HOLD save failed",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ break;
+ }
+ if (plci->internal_command)
+ return;
+ }
+ sendf (plci->appl, _FACILITY_I, Id & 0xffffL, 0, "ws", 3, SS_Ind);
+}
+
+
+static void retrieve_restore_command (dword Id, PLCI *plci, byte Rc)
+{
+ byte SS_Ind[] = "\x05\x03\x00\x02\x00\x00"; /* Retrieve_Ind struct*/
+ word Info;
+ word internal_command;
+
+ dbug (1, dprintf ("[%06lx] %s,%d: retrieve_restore_command %02x %04x",
+ UnMapId (Id), (char *)(FILE_), __LINE__, Rc, plci->internal_command));
+
+ Info = GOOD;
+ internal_command = plci->internal_command;
+ plci->internal_command = 0;
+ switch (internal_command)
+ {
+ default:
+ plci->command = 0;
+ plci->adjust_b_parms_msg = NULL;
+ plci->adjust_b_facilities = plci->B1_facilities;
+ plci->adjust_b_command = RETRIEVE_RESTORE_COMMAND_1;
+ plci->adjust_b_ncci = (word)(Id >> 16);
+ plci->adjust_b_mode = ADJUST_B_MODE_ASSIGN_L23 | ADJUST_B_MODE_USER_CONNECT | ADJUST_B_MODE_RESTORE;
+ plci->adjust_b_state = ADJUST_B_START;
+ dbug (1, dprintf ("[%06lx] %s,%d: RETRIEVE restore...",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ case RETRIEVE_RESTORE_COMMAND_1:
+ Info = adjust_b_process (Id, plci, Rc);
+ if (Info != GOOD)
+ {
+ dbug (1, dprintf ("[%06lx] %s,%d: RETRIEVE restore failed",
+ UnMapId (Id), (char *)(FILE_), __LINE__));
+ break;
+ }
+ if (plci->internal_command)
+ return;
+ }
+ sendf (plci->appl, _FACILITY_I, Id & 0xffffL, 0, "ws", 3, SS_Ind);
+}
+
+
+static void init_b1_config (PLCI *plci)
+{
+
+ dbug (1, dprintf ("[%06lx] %s,%d: init_b1_config",
+ (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+ (char *)(FILE_), __LINE__));
+
+ plci->B1_resource = 0;
+ plci->B1_facilities = 0;
+
+ plci->li_bchannel_id = 0;
+ mixer_clear_config (plci);
+
+
+ ec_clear_config (plci);
+
+
+ dtmf_rec_clear_config (plci);
+ dtmf_send_clear_config (plci);
+ dtmf_parameter_clear_config (plci);
+
+ adv_voice_clear_config (plci);
+ adjust_b_clear (plci);
+}
+
+
+static void clear_b1_config (PLCI *plci)
+{
+
+ dbug (1, dprintf ("[%06lx] %s,%d: clear_b1_config",
+ (dword)((plci->Id << 8) | UnMapController (plci->adapter->Id)),
+ (char *)(FILE_), __LINE__));
+
+ adv_voice_clear_config (plci);
+ adjust_b_clear (plci);
+
+ ec_clear_config (plci);
+
+
+ dtmf_rec_clear_config (plci);
+ dtmf_send_clear_config (plci);
+ dtmf_parameter_clear_config (plci);
+
+
+ if ((plci->li_bchannel_id != 0)
+ && (li_config_table[plci->adapter->li_base + (plci->li_bchannel_id - 1)].plci == plci))
+ {
+ mixer_clear_config (plci);
+ li_config_table[plci->adapter->li_base + (plci->li_bchannel_id - 1)].plci = NULL;
+ plci->li_bchannel_id = 0;
+ }
+
+ plci->B1_resource = 0;
+ plci->B1_facilities = 0;
+}
+
+
+/* -----------------------------------------------------------------
+ XON protocol local helpers
+ ----------------------------------------------------------------- */
+static void channel_flow_control_remove (PLCI * plci) {
+ DIVA_CAPI_ADAPTER * a = plci->adapter;
+ word i;
+ for(i=1;i<MAX_NL_CHANNEL+1;i++) {
+ if (a->ch_flow_plci[i] == plci->Id) {
+ a->ch_flow_plci[i] = 0;
+ a->ch_flow_control[i] = 0;
+ }
+ }
+}
+
+static void channel_x_on (PLCI * plci, byte ch) {
+ DIVA_CAPI_ADAPTER * a = plci->adapter;
+ if (a->ch_flow_control[ch] & N_XON_SENT) {
+ a->ch_flow_control[ch] &= ~N_XON_SENT;
+ }
+}
+
+static void channel_x_off (PLCI * plci, byte ch, byte flag) {
+ DIVA_CAPI_ADAPTER * a = plci->adapter;
+ if ((a->ch_flow_control[ch] & N_RX_FLOW_CONTROL_MASK) == 0) {
+ a->ch_flow_control[ch] |= (N_CH_XOFF | flag);
+ a->ch_flow_plci[ch] = plci->Id;
+ a->ch_flow_control_pending++;
+ }
+}
+
+static void channel_request_xon (PLCI * plci, byte ch) {
+ DIVA_CAPI_ADAPTER * a = plci->adapter;
+
+ if (a->ch_flow_control[ch] & N_CH_XOFF) {
+ a->ch_flow_control[ch] |= N_XON_REQ;
+ a->ch_flow_control[ch] &= ~N_CH_XOFF;
+ a->ch_flow_control[ch] &= ~N_XON_CONNECT_IND;
+ }
+}
+
+static void channel_xmit_extended_xon (PLCI * plci) {
+ DIVA_CAPI_ADAPTER * a;
+ int max_ch = ARRAY_SIZE(a->ch_flow_control);
+ int i, one_requested = 0;
+
+ if ((!plci) || (!plci->Id) || ((a = plci->adapter) == NULL)) {
+ return;
+ }
+
+ for (i = 0; i < max_ch; i++) {
+ if ((a->ch_flow_control[i] & N_CH_XOFF) &&
+ (a->ch_flow_control[i] & N_XON_CONNECT_IND) &&
+ (plci->Id == a->ch_flow_plci[i])) {
+ channel_request_xon (plci, (byte)i);
+ one_requested = 1;
+ }
+ }
+
+ if (one_requested) {
+ channel_xmit_xon (plci);
+ }
+}
+
+/*
+ Try to xmit next X_ON
+ */
+static int find_channel_with_pending_x_on (DIVA_CAPI_ADAPTER * a, PLCI * plci) {
+ int max_ch = ARRAY_SIZE(a->ch_flow_control);
+ int i;
+
+ if (!(plci->adapter->manufacturer_features & MANUFACTURER_FEATURE_XONOFF_FLOW_CONTROL)) {
+ return (0);
+ }
+
+ if (a->last_flow_control_ch >= max_ch) {
+ a->last_flow_control_ch = 1;
+ }
+ for (i=a->last_flow_control_ch; i < max_ch; i++) {
+ if ((a->ch_flow_control[i] & N_XON_REQ) &&
+ (plci->Id == a->ch_flow_plci[i])) {
+ a->last_flow_control_ch = i+1;
+ return (i);
+ }
+ }
+
+ for (i = 1; i < a->last_flow_control_ch; i++) {
+ if ((a->ch_flow_control[i] & N_XON_REQ) &&
+ (plci->Id == a->ch_flow_plci[i])) {
+ a->last_flow_control_ch = i+1;
+ return (i);
+ }
+ }
+
+ return (0);
+}
+
+static void channel_xmit_xon (PLCI * plci) {
+ DIVA_CAPI_ADAPTER * a = plci->adapter;
+ byte ch;
+
+ if (plci->nl_req || !plci->NL.Id || plci->nl_remove_id) {
+ return;
+ }
+ if ((ch = (byte)find_channel_with_pending_x_on (a, plci)) == 0) {
+ return;
+ }
+ a->ch_flow_control[ch] &= ~N_XON_REQ;
+ a->ch_flow_control[ch] |= N_XON_SENT;
+
+ plci->NL.Req = plci->nl_req = (byte)N_XON;
+ plci->NL.ReqCh = ch;
+ plci->NL.X = plci->NData;
+ plci->NL.XNum = 1;
+ plci->NData[0].P = &plci->RBuffer[0];
+ plci->NData[0].PLength = 0;
+
+ plci->adapter->request(&plci->NL);
+}
+
+static int channel_can_xon (PLCI * plci, byte ch) {
+ APPL * APPLptr;
+ DIVA_CAPI_ADAPTER * a;
+ word NCCIcode;
+ dword count;
+ word Num;
+ word i;
+
+ APPLptr = plci->appl;
+ a = plci->adapter;
+
+ if (!APPLptr)
+ return (0);
+
+ NCCIcode = a->ch_ncci[ch] | (((word) a->Id) << 8);
+
+ /* count all buffers within the Application pool */
+ /* belonging to the same NCCI. XON if a first is */
+ /* used. */
+ count = 0;
+ Num = 0xffff;
+ for(i=0; i<APPLptr->MaxBuffer; i++) {
+ if(NCCIcode==APPLptr->DataNCCI[i]) count++;
+ if(!APPLptr->DataNCCI[i] && Num==0xffff) Num = i;
+ }
+ if ((count > 2) || (Num == 0xffff)) {
+ return (0);
+ }
+ return (1);
+}
+
+
+/*------------------------------------------------------------------*/
+
+static word CPN_filter_ok(byte *cpn,DIVA_CAPI_ADAPTER * a,word offset)
+{
+ return 1;
+}
+
+
+
+/**********************************************************************************/
+/* function groups the listening applications according to the CIP mask and the */
+/* Info_Mask. Each group gets just one Connect_Ind. Some application manufacturer */
+/* are not multi-instance capable, so they start e.g. 30 applications what causes */
+/* big problems on application level (one call, 30 Connect_Ind, ect). The */
+/* function must be enabled by setting "a->group_optimization_enabled" from the */
+/* OS specific part (per adapter). */
+/**********************************************************************************/
+static void group_optimization(DIVA_CAPI_ADAPTER * a, PLCI * plci)
+{
+ word i,j,k,busy,group_found;
+ dword info_mask_group[MAX_CIP_TYPES];
+ dword cip_mask_group[MAX_CIP_TYPES];
+ word appl_number_group_type[MAX_APPL];
+ PLCI *auxplci;
+
+ set_group_ind_mask (plci); /* all APPLs within this inc. call are allowed to dial in */
+
+ if(!a->group_optimization_enabled)
+ {
+ dbug(1,dprintf("No group optimization"));
+ return;
+ }
+
+ dbug(1,dprintf("Group optimization = 0x%x...", a->group_optimization_enabled));
+
+ for(i=0;i<MAX_CIP_TYPES;i++)
+ {
+ info_mask_group[i] = 0;
+ cip_mask_group [i] = 0;
+ }
+ for(i=0;i<MAX_APPL;i++)
+ {
+ appl_number_group_type[i] = 0;
+ }
+ for(i=0; i<max_appl; i++) /* check if any multi instance capable application is present */
+ { /* group_optimization set to 1 means not to optimize multi-instance capable applications (default) */
+ if(application[i].Id && (application[i].MaxNCCI) > 1 && (a->CIP_Mask[i]) && (a->group_optimization_enabled ==1) )
+ {
+ dbug(1,dprintf("Multi-Instance capable, no optimization required"));
+ return; /* allow good application unfiltered access */
+ }
+ }
+ for(i=0; i<max_appl; i++) /* Build CIP Groups */
+ {
+ if(application[i].Id && a->CIP_Mask[i] )
+ {
+ for(k=0,busy=false; k<a->max_plci; k++)
+ {
+ if(a->plci[k].Id)
+ {
+ auxplci = &a->plci[k];
+ if(auxplci->appl == &application[i]) /* application has a busy PLCI */
+ {
+ busy = true;
+ dbug(1,dprintf("Appl 0x%x is busy",i+1));
+ }
+ else if(test_c_ind_mask_bit (auxplci, i)) /* application has an incoming call pending */
+ {
+ busy = true;
+ dbug(1,dprintf("Appl 0x%x has inc. call pending",i+1));
+ }
+ }
+ }
+
+ for(j=0,group_found=0; j<=(MAX_CIP_TYPES) && !busy &&!group_found; j++) /* build groups with free applications only */
+ {
+ if(j==MAX_CIP_TYPES) /* all groups are in use but group still not found */
+ { /* the MAX_CIP_TYPES group enables all calls because of field overflow */
+ appl_number_group_type[i] = MAX_CIP_TYPES;
+ group_found=true;
+ dbug(1,dprintf("Field overflow appl 0x%x",i+1));
+ }
+ else if( (info_mask_group[j]==a->CIP_Mask[i]) && (cip_mask_group[j]==a->Info_Mask[i]) )
+ { /* is group already present ? */
+ appl_number_group_type[i] = j|0x80; /* store the group number for each application */
+ group_found=true;
+ dbug(1,dprintf("Group 0x%x found with appl 0x%x, CIP=0x%lx",appl_number_group_type[i],i+1,info_mask_group[j]));
+ }
+ else if(!info_mask_group[j])
+ { /* establish a new group */
+ appl_number_group_type[i] = j|0x80; /* store the group number for each application */
+ info_mask_group[j] = a->CIP_Mask[i]; /* store the new CIP mask for the new group */
+ cip_mask_group[j] = a->Info_Mask[i]; /* store the new Info_Mask for this new group */
+ group_found=true;
+ dbug(1,dprintf("New Group 0x%x established with appl 0x%x, CIP=0x%lx",appl_number_group_type[i],i+1,info_mask_group[j]));
+ }
+ }
+ }
+ }
+
+ for(i=0; i<max_appl; i++) /* Build group_optimization_mask_table */
+ {
+ if(appl_number_group_type[i]) /* application is free, has listens and is member of a group */
+ {
+ if(appl_number_group_type[i] == MAX_CIP_TYPES)
+ {
+ dbug(1,dprintf("OverflowGroup 0x%x, valid appl = 0x%x, call enabled",appl_number_group_type[i],i+1));
+ }
+ else
+ {
+ dbug(1,dprintf("Group 0x%x, valid appl = 0x%x",appl_number_group_type[i],i+1));
+ for(j=i+1; j<max_appl; j++) /* search other group members and mark them as busy */
+ {
+ if(appl_number_group_type[i] == appl_number_group_type[j])
+ {
+ dbug(1,dprintf("Appl 0x%x is member of group 0x%x, no call",j+1,appl_number_group_type[j]));
+ clear_group_ind_mask_bit (plci, j); /* disable call on other group members */
+ appl_number_group_type[j] = 0; /* remove disabled group member from group list */
+ }
+ }
+ }
+ }
+ else /* application should not get a call */
+ {
+ clear_group_ind_mask_bit (plci, i);
+ }
+ }
+
+}
+
+
+
+/* OS notifies the driver about a application Capi_Register */
+word CapiRegister(word id)
+{
+ word i,j,appls_found;
+
+ PLCI *plci;
+ DIVA_CAPI_ADAPTER *a;
+
+ for(i=0,appls_found=0; i<max_appl; i++)
+ {
+ if( application[i].Id && (application[i].Id!=id) )
+ {
+ appls_found++; /* an application has been found */
+ }
+ }
+
+ if(appls_found) return true;
+ for(i=0; i<max_adapter; i++) /* scan all adapters... */
+ {
+ a = &adapter[i];
+ if(a->request)
+ {
+ if(a->flag_dynamic_l1_down) /* remove adapter from L1 tristate (Huntgroup) */
+ {
+ if(!appls_found) /* first application does a capi register */
+ {
+ if((j=get_plci(a))) /* activate L1 of all adapters */
+ {
+ plci = &a->plci[j-1];
+ plci->command = 0;
+ add_p(plci,OAD,"\x01\xfd");
+ add_p(plci,CAI,"\x01\x80");
+ add_p(plci,UID,"\x06\x43\x61\x70\x69\x32\x30");
+ add_p(plci,SHIFT|6,NULL);
+ add_p(plci,SIN,"\x02\x00\x00");
+ plci->internal_command = START_L1_SIG_ASSIGN_PEND;
+ sig_req(plci,ASSIGN,DSIG_ID);
+ add_p(plci,FTY,"\x02\xff\x07"); /* l1 start */
+ sig_req(plci,SIG_CTRL,0);
+ send_req(plci);
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+/*------------------------------------------------------------------*/
+
+/* Functions for virtual Switching e.g. Transfer by join, Conference */
+
+static void VSwitchReqInd(PLCI *plci, dword Id, byte **parms)
+{
+ word i;
+ /* Format of vswitch_t:
+ 0 byte length
+ 1 byte VSWITCHIE
+ 2 byte VSWITCH_REQ/VSWITCH_IND
+ 3 byte reserved
+ 4 word VSwitchcommand
+ 6 word returnerror
+ 8... Params
+ */
+ if(!plci ||
+ !plci->appl ||
+ !plci->State ||
+ plci->Sig.Ind==NCR_FACILITY
+ )
+ return;
+
+ for(i=0;i<MAX_MULTI_IE;i++)
+ {
+ if(!parms[i][0]) continue;
+ if(parms[i][0]<7)
+ {
+ parms[i][0]=0; /* kill it */
+ continue;
+ }
+ dbug(1,dprintf("VSwitchReqInd(%d)",parms[i][4]));
+ switch(parms[i][4])
+ {
+ case VSJOIN:
+ if(!plci->relatedPTYPLCI ||
+ (plci->ptyState!=S_ECT && plci->relatedPTYPLCI->ptyState!=S_ECT))
+ { /* Error */
+ break;
+ }
+ /* remember all necessary informations */
+ if(parms[i][0]!=11 || parms[i][8]!=3) /* Length Test */
+ {
+ break;
+ }
+ if(parms[i][2]==VSWITCH_IND && parms[i][9]==1)
+ { /* first indication after ECT-Request on Consultation Call */
+ plci->vswitchstate=parms[i][9];
+ parms[i][9]=2; /* State */
+ /* now ask first Call to join */
+ }
+ else if(parms[i][2]==VSWITCH_REQ && parms[i][9]==3)
+ { /* Answer of VSWITCH_REQ from first Call */
+ plci->vswitchstate=parms[i][9];
+ /* tell consultation call to join
+ and the protocol capabilities of the first call */
+ }
+ else
+ { /* Error */
+ break;
+ }
+ plci->vsprot=parms[i][10]; /* protocol */
+ plci->vsprotdialect=parms[i][11]; /* protocoldialect */
+ /* send join request to related PLCI */
+ parms[i][1]=VSWITCHIE;
+ parms[i][2]=VSWITCH_REQ;
+
+ plci->relatedPTYPLCI->command = 0;
+ plci->relatedPTYPLCI->internal_command = VSWITCH_REQ_PEND;
+ add_p(plci->relatedPTYPLCI,ESC,&parms[i][0]);
+ sig_req(plci->relatedPTYPLCI,VSWITCH_REQ,0);
+ send_req(plci->relatedPTYPLCI);
+ break;
+ case VSTRANSPORT:
+ default:
+ if(plci->relatedPTYPLCI &&
+ plci->vswitchstate==3 &&
+ plci->relatedPTYPLCI->vswitchstate==3)
+ {
+ add_p(plci->relatedPTYPLCI,ESC,&parms[i][0]);
+ sig_req(plci->relatedPTYPLCI,VSWITCH_REQ,0);
+ send_req(plci->relatedPTYPLCI);
+ }
+ break;
+ }
+ parms[i][0]=0; /* kill it */
+ }
+}
+
+
+/*------------------------------------------------------------------*/
+
+static int diva_get_dma_descriptor (PLCI *plci, dword *dma_magic) {
+ ENTITY e;
+ IDI_SYNC_REQ* pReq = (IDI_SYNC_REQ*)&e;
+
+ if (!(diva_xdi_extended_features & DIVA_CAPI_XDI_PROVIDES_RX_DMA)) {
+ return (-1);
+ }
+
+ pReq->xdi_dma_descriptor_operation.Req = 0;
+ pReq->xdi_dma_descriptor_operation.Rc = IDI_SYNC_REQ_DMA_DESCRIPTOR_OPERATION;
+
+ pReq->xdi_dma_descriptor_operation.info.operation = IDI_SYNC_REQ_DMA_DESCRIPTOR_ALLOC;
+ pReq->xdi_dma_descriptor_operation.info.descriptor_number = -1;
+ pReq->xdi_dma_descriptor_operation.info.descriptor_address = NULL;
+ pReq->xdi_dma_descriptor_operation.info.descriptor_magic = 0;
+
+ e.user[0] = plci->adapter->Id - 1;
+ plci->adapter->request((ENTITY*)pReq);
+
+ if (!pReq->xdi_dma_descriptor_operation.info.operation &&
+ (pReq->xdi_dma_descriptor_operation.info.descriptor_number >= 0) &&
+ pReq->xdi_dma_descriptor_operation.info.descriptor_magic) {
+ *dma_magic = pReq->xdi_dma_descriptor_operation.info.descriptor_magic;
+ dbug(3,dprintf("dma_alloc, a:%d (%d-%08x)",
+ plci->adapter->Id,
+ pReq->xdi_dma_descriptor_operation.info.descriptor_number,
+ *dma_magic));
+ return (pReq->xdi_dma_descriptor_operation.info.descriptor_number);
+ } else {
+ dbug(1,dprintf("dma_alloc failed"));
+ return (-1);
+ }
+}
+
+static void diva_free_dma_descriptor (PLCI *plci, int nr) {
+ ENTITY e;
+ IDI_SYNC_REQ* pReq = (IDI_SYNC_REQ*)&e;
+
+ if (nr < 0) {
+ return;
+ }
+
+ pReq->xdi_dma_descriptor_operation.Req = 0;
+ pReq->xdi_dma_descriptor_operation.Rc = IDI_SYNC_REQ_DMA_DESCRIPTOR_OPERATION;
+
+ pReq->xdi_dma_descriptor_operation.info.operation = IDI_SYNC_REQ_DMA_DESCRIPTOR_FREE;
+ pReq->xdi_dma_descriptor_operation.info.descriptor_number = nr;
+ pReq->xdi_dma_descriptor_operation.info.descriptor_address = NULL;
+ pReq->xdi_dma_descriptor_operation.info.descriptor_magic = 0;
+
+ e.user[0] = plci->adapter->Id - 1;
+ plci->adapter->request((ENTITY*)pReq);
+
+ if (!pReq->xdi_dma_descriptor_operation.info.operation) {
+ dbug(1,dprintf("dma_free(%d)", nr));
+ } else {
+ dbug(1,dprintf("dma_free failed (%d)", nr));
+ }
+}
+
+/*------------------------------------------------------------------*/
diff --git a/drivers/isdn/hardware/eicon/mi_pc.h b/drivers/isdn/hardware/eicon/mi_pc.h
new file mode 100644
index 0000000..a861dac
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/mi_pc.h
@@ -0,0 +1,204 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+/*----------------------------------------------------------------------------
+// MAESTRA ISA PnP */
+#define BRI_MEMORY_BASE 0x1f700000
+#define BRI_MEMORY_SIZE 0x00100000 /* 1MB on the BRI */
+#define BRI_SHARED_RAM_SIZE 0x00010000 /* 64k shared RAM */
+#define BRI_RAY_TAYLOR_DSP_CODE_SIZE 0x00020000 /* max 128k DSP-Code (Ray Taylor's code) */
+#define BRI_ORG_MAX_DSP_CODE_SIZE 0x00050000 /* max 320k DSP-Code (Telindus) */
+#define BRI_V90D_MAX_DSP_CODE_SIZE 0x00060000 /* max 384k DSP-Code if V.90D included */
+#define BRI_CACHED_ADDR(x) (((x) & 0x1fffffffL) | 0x80000000L)
+#define BRI_UNCACHED_ADDR(x) (((x) & 0x1fffffffL) | 0xa0000000L)
+#define ADDR 4
+#define ADDRH 6
+#define DATA 0
+#define RESET 7
+#define DEFAULT_ADDRESS 0x240
+#define DEFAULT_IRQ 3
+#define M_PCI_ADDR 0x04 /* MAESTRA BRI PCI */
+#define M_PCI_ADDRH 0x0c /* MAESTRA BRI PCI */
+#define M_PCI_DATA 0x00 /* MAESTRA BRI PCI */
+#define M_PCI_RESET 0x10 /* MAESTRA BRI PCI */
+/*----------------------------------------------------------------------------
+// MAESTRA PRI PCI */
+#define MP_IRQ_RESET 0xc18 /* offset of isr in the CONFIG memory bar */
+#define MP_IRQ_RESET_VAL 0xfe /* value to clear an interrupt */
+#define MP_MEMORY_SIZE 0x00400000 /* 4MB on standard PRI */
+#define MP2_MEMORY_SIZE 0x00800000 /* 8MB on PRI Rev. 2 */
+#define MP_SHARED_RAM_OFFSET 0x00001000 /* offset of shared RAM base in the DRAM memory bar */
+#define MP_SHARED_RAM_SIZE 0x00010000 /* 64k shared RAM */
+#define MP_PROTOCOL_OFFSET (MP_SHARED_RAM_OFFSET + MP_SHARED_RAM_SIZE)
+#define MP_RAY_TAYLOR_DSP_CODE_SIZE 0x00040000 /* max 256k DSP-Code (Ray Taylor's code) */
+#define MP_ORG_MAX_DSP_CODE_SIZE 0x00060000 /* max 384k DSP-Code (Telindus) */
+#define MP_V90D_MAX_DSP_CODE_SIZE 0x00070000 /* max 448k DSP-Code if V.90D included) */
+#define MP_VOIP_MAX_DSP_CODE_SIZE 0x00090000 /* max 576k DSP-Code if voice over IP included */
+#define MP_CACHED_ADDR(x) (((x) & 0x1fffffffL) | 0x80000000L)
+#define MP_UNCACHED_ADDR(x) (((x) & 0x1fffffffL) | 0xa0000000L)
+#define MP_RESET 0x20 /* offset of RESET register in the DEVICES memory bar */
+/* RESET register bits */
+#define _MP_S2M_RESET 0x10 /* active lo */
+#define _MP_LED2 0x08 /* 1 = on */
+#define _MP_LED1 0x04 /* 1 = on */
+#define _MP_DSP_RESET 0x02 /* active lo */
+#define _MP_RISC_RESET 0x81 /* active hi, bit 7 for compatibility with old boards */
+/* CPU exception context structure in MP shared ram after trap */
+typedef struct mp_xcptcontext_s MP_XCPTC;
+struct mp_xcptcontext_s {
+ dword sr;
+ dword cr;
+ dword epc;
+ dword vaddr;
+ dword regs[32];
+ dword mdlo;
+ dword mdhi;
+ dword reseverd;
+ dword xclass;
+};
+/* boot interface structure for PRI */
+struct mp_load {
+ dword volatile cmd;
+ dword volatile addr;
+ dword volatile len;
+ dword volatile err;
+ dword volatile live;
+ dword volatile res1[0x1b];
+ dword volatile TrapId; /* has value 0x999999XX on a CPU trap */
+ dword volatile res2[0x03];
+ MP_XCPTC volatile xcpt; /* contains register dump */
+ dword volatile rest[((0x1020>>2)-6) - 0x1b - 1 - 0x03 - (sizeof(MP_XCPTC)>>2)];
+ dword volatile signature;
+ dword data[60000]; /* real interface description */
+};
+/*----------------------------------------------------------------------------*/
+/* SERVER 4BRI (Quattro PCI) */
+#define MQ_BOARD_REG_OFFSET 0x800000 /* PC relative On board registers offset */
+#define MQ_BREG_RISC 0x1200 /* RISC Reset ect */
+#define MQ_RISC_COLD_RESET_MASK 0x0001 /* RISC Cold reset */
+#define MQ_RISC_WARM_RESET_MASK 0x0002 /* RISC Warm reset */
+#define MQ_BREG_IRQ_TEST 0x0608 /* Interrupt request, no CPU interaction */
+#define MQ_IRQ_REQ_ON 0x1
+#define MQ_IRQ_REQ_OFF 0x0
+#define MQ_BOARD_DSP_OFFSET 0xa00000 /* PC relative On board DSP regs offset */
+#define MQ_DSP1_ADDR_OFFSET 0x0008 /* Addr register offset DSP 1 subboard 1 */
+#define MQ_DSP2_ADDR_OFFSET 0x0208 /* Addr register offset DSP 2 subboard 1 */
+#define MQ_DSP1_DATA_OFFSET 0x0000 /* Data register offset DSP 1 subboard 1 */
+#define MQ_DSP2_DATA_OFFSET 0x0200 /* Data register offset DSP 2 subboard 1 */
+#define MQ_DSP_JUNK_OFFSET 0x0400 /* DSP Data/Addr regs subboard offset */
+#define MQ_ISAC_DSP_RESET 0x0028 /* ISAC and DSP reset address offset */
+#define MQ_BOARD_ISAC_DSP_RESET 0x800028 /* ISAC and DSP reset address offset */
+#define MQ_INSTANCE_COUNT 4 /* 4BRI consists of four instances */
+#define MQ_MEMORY_SIZE 0x00400000 /* 4MB on standard 4BRI */
+#define MQ_CTRL_SIZE 0x00002000 /* 8K memory mapped registers */
+#define MQ_SHARED_RAM_SIZE 0x00010000 /* 64k shared RAM */
+#define MQ_ORG_MAX_DSP_CODE_SIZE 0x00050000 /* max 320k DSP-Code (Telindus) */
+#define MQ_V90D_MAX_DSP_CODE_SIZE 0x00060000 /* max 384K DSP-Code if V.90D included */
+#define MQ_VOIP_MAX_DSP_CODE_SIZE 0x00028000 /* max 4*160k = 640K DSP-Code if voice over IP included */
+#define MQ_CACHED_ADDR(x) (((x) & 0x1fffffffL) | 0x80000000L)
+#define MQ_UNCACHED_ADDR(x) (((x) & 0x1fffffffL) | 0xa0000000L)
+/*--------------------------------------------------------------------------------------------*/
+/* Additional definitions reflecting the different address map of the SERVER 4BRI V2 */
+#define MQ2_BREG_RISC 0x0200 /* RISC Reset ect */
+#define MQ2_BREG_IRQ_TEST 0x0400 /* Interrupt request, no CPU interaction */
+#define MQ2_BOARD_DSP_OFFSET 0x800000 /* PC relative On board DSP regs offset */
+#define MQ2_DSP1_DATA_OFFSET 0x1800 /* Data register offset DSP 1 subboard 1 */
+#define MQ2_DSP1_ADDR_OFFSET 0x1808 /* Addr register offset DSP 1 subboard 1 */
+#define MQ2_DSP2_DATA_OFFSET 0x1810 /* Data register offset DSP 2 subboard 1 */
+#define MQ2_DSP2_ADDR_OFFSET 0x1818 /* Addr register offset DSP 2 subboard 1 */
+#define MQ2_DSP_JUNK_OFFSET 0x1000 /* DSP Data/Addr regs subboard offset */
+#define MQ2_ISAC_DSP_RESET 0x0000 /* ISAC and DSP reset address offset */
+#define MQ2_BOARD_ISAC_DSP_RESET 0x800000 /* ISAC and DSP reset address offset */
+#define MQ2_IPACX_CONFIG 0x0300 /* IPACX Configuration TE(0)/NT(1) */
+#define MQ2_BOARD_IPACX_CONFIG 0x800300 /* "" */
+#define MQ2_MEMORY_SIZE 0x01000000 /* 16MB code/data memory */
+#define MQ2_CTRL_SIZE 0x00008000 /* 32K memory mapped registers */
+/*----------------------------------------------------------------------------*/
+/* SERVER BRI 2M/2F as derived from 4BRI V2 */
+#define BRI2_MEMORY_SIZE 0x00800000 /* 8MB code/data memory */
+#define BRI2_PROTOCOL_MEMORY_SIZE (MQ2_MEMORY_SIZE >> 2) /* same as one 4BRI Rev.2 task */
+#define BRI2_CTRL_SIZE 0x00008000 /* 32K memory mapped registers */
+#define M_INSTANCE_COUNT 1 /* BRI consists of one instance */
+/*
+ * Some useful constants for proper initialization of the GT6401x
+ */
+#define ID_REG 0x0000 /*Pci reg-contain the Dev&Ven ID of the card*/
+#define RAS0_BASEREG 0x0010 /*Ras0 register - contain the base addr Ras0*/
+#define RAS2_BASEREG 0x0014
+#define CS_BASEREG 0x0018
+#define BOOT_BASEREG 0x001c
+#define GTREGS_BASEREG 0x0024 /*GTRegsBase reg-contain the base addr where*/
+ /*the GT64010 internal regs where mapped */
+/*
+ * GT64010 internal registers
+ */
+ /* DRAM device coding */
+#define LOW_RAS0_DREG 0x0400 /*Ras0 low decode address*/
+#define HI_RAS0_DREG 0x0404 /*Ras0 high decode address*/
+#define LOW_RAS1_DREG 0x0408 /*Ras1 low decode address*/
+#define HI_RAS1_DREG 0x040c /*Ras1 high decode address*/
+#define LOW_RAS2_DREG 0x0410 /*Ras2 low decode address*/
+#define HI_RAS2_DREG 0x0414 /*Ras2 high decode address*/
+#define LOW_RAS3_DREG 0x0418 /*Ras3 low decode address*/
+#define HI_RAS3_DREG 0x041c /*Ras3 high decode address*/
+ /* I/O CS device coding */
+#define LOW_CS0_DREG 0x0420 /* CS0* low decode register */
+#define HI_CS0_DREG 0x0424 /* CS0* high decode register */
+#define LOW_CS1_DREG 0x0428 /* CS1* low decode register */
+#define HI_CS1_DREG 0x042c /* CS1* high decode register */
+#define LOW_CS2_DREG 0x0430 /* CS2* low decode register */
+#define HI_CS2_DREG 0x0434 /* CS2* high decode register */
+#define LOW_CS3_DREG 0x0438 /* CS3* low decode register */
+#define HI_CS3_DREG 0x043c /* CS3* high decode register */
+ /* Boot PROM device coding */
+#define LOW_BOOTCS_DREG 0x0440 /* Boot CS low decode register */
+#define HI_BOOTCS_DREG 0x0444 /* Boot CS High decode register */
+ /* DRAM group coding (for CPU) */
+#define LO_RAS10_GREG 0x0008 /*Ras1..0 group low decode address*/
+#define HI_RAS10_GREG 0x0010 /*Ras1..0 group high decode address*/
+#define LO_RAS32_GREG 0x0018 /*Ras3..2 group low decode address */
+#define HI_RAS32_GREG 0x0020 /*Ras3..2 group high decode address */
+ /* I/O CS group coding for (CPU) */
+#define LO_CS20_GREG 0x0028 /* CS2..0 group low decode register */
+#define HI_CS20_GREG 0x0030 /* CS2..0 group high decode register */
+#define LO_CS3B_GREG 0x0038 /* CS3 & PROM group low decode register */
+#define HI_CS3B_GREG 0x0040 /* CS3 & PROM group high decode register */
+ /* Galileo specific PCI config. */
+#define PCI_TIMEOUT_RET 0x0c04 /* Time Out and retry register */
+#define RAS10_BANKSIZE 0x0c08 /* RAS 1..0 group PCI bank size */
+#define RAS32_BANKSIZE 0x0c0c /* RAS 3..2 group PCI bank size */
+#define CS20_BANKSIZE 0x0c10 /* CS 2..0 group PCI bank size */
+#define CS3B_BANKSIZE 0x0c14 /* CS 3 & Boot group PCI bank size */
+#define DRAM_SIZE 0x0001 /*Dram size in mega bytes*/
+#define PROM_SIZE 0x08000 /*Prom size in bytes*/
+/*--------------------------------------------------------------------------*/
+#define OFFS_DIVA_INIT_TASK_COUNT 0x68
+#define OFFS_DSP_CODE_BASE_ADDR 0x6c
+#define OFFS_XLOG_BUF_ADDR 0x70
+#define OFFS_XLOG_COUNT_ADDR 0x74
+#define OFFS_XLOG_OUT_ADDR 0x78
+#define OFFS_PROTOCOL_END_ADDR 0x7c
+#define OFFS_PROTOCOL_ID_STRING 0x80
+/*--------------------------------------------------------------------------*/
diff --git a/drivers/isdn/hardware/eicon/mntfunc.c b/drivers/isdn/hardware/eicon/mntfunc.c
new file mode 100644
index 0000000..a564b75
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/mntfunc.c
@@ -0,0 +1,370 @@
+/* $Id: mntfunc.c,v 1.19.6.4 2005/01/31 12:22:20 armin Exp $
+ *
+ * Driver for Eicon DIVA Server ISDN cards.
+ * Maint module
+ *
+ * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
+ * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+
+#include "platform.h"
+#include "di_defs.h"
+#include "divasync.h"
+#include "debug_if.h"
+
+extern char *DRIVERRELEASE_MNT;
+
+#define DBG_MINIMUM (DL_LOG + DL_FTL + DL_ERR)
+#define DBG_DEFAULT (DBG_MINIMUM + DL_XLOG + DL_REG)
+
+extern void DIVA_DIDD_Read(void *, int);
+
+static dword notify_handle;
+static DESCRIPTOR DAdapter;
+static DESCRIPTOR MAdapter;
+static DESCRIPTOR MaintDescriptor =
+ { IDI_DIMAINT, 0, 0, (IDI_CALL) diva_maint_prtComp };
+
+extern int diva_os_copy_to_user(void *os_handle, void __user *dst,
+ const void *src, int length);
+extern int diva_os_copy_from_user(void *os_handle, void *dst,
+ const void __user *src, int length);
+
+static void no_printf(unsigned char *x, ...)
+{
+ /* dummy debug function */
+}
+
+#include "debuglib.c"
+
+/*
+ * DIDD callback function
+ */
+static void *didd_callback(void *context, DESCRIPTOR * adapter,
+ int removal)
+{
+ if (adapter->type == IDI_DADAPTER) {
+ DBG_ERR(("cb: Change in DAdapter ? Oops ?."));
+ } else if (adapter->type == IDI_DIMAINT) {
+ if (removal) {
+ DbgDeregister();
+ memset(&MAdapter, 0, sizeof(MAdapter));
+ dprintf = no_printf;
+ } else {
+ memcpy(&MAdapter, adapter, sizeof(MAdapter));
+ dprintf = (DIVA_DI_PRINTF) MAdapter.request;
+ DbgRegister("MAINT", DRIVERRELEASE_MNT, DBG_DEFAULT);
+ }
+ } else if ((adapter->type > 0) && (adapter->type < 16)) {
+ if (removal) {
+ diva_mnt_remove_xdi_adapter(adapter);
+ } else {
+ diva_mnt_add_xdi_adapter(adapter);
+ }
+ }
+ return (NULL);
+}
+
+/*
+ * connect to didd
+ */
+static int DIVA_INIT_FUNCTION connect_didd(void)
+{
+ int x = 0;
+ int dadapter = 0;
+ IDI_SYNC_REQ req;
+ DESCRIPTOR DIDD_Table[MAX_DESCRIPTORS];
+
+ DIVA_DIDD_Read(DIDD_Table, sizeof(DIDD_Table));
+
+ for (x = 0; x < MAX_DESCRIPTORS; x++) {
+ if (DIDD_Table[x].type == IDI_DADAPTER) { /* DADAPTER found */
+ dadapter = 1;
+ memcpy(&DAdapter, &DIDD_Table[x], sizeof(DAdapter));
+ req.didd_notify.e.Req = 0;
+ req.didd_notify.e.Rc =
+ IDI_SYNC_REQ_DIDD_REGISTER_ADAPTER_NOTIFY;
+ req.didd_notify.info.callback = (void *)didd_callback;
+ req.didd_notify.info.context = NULL;
+ DAdapter.request((ENTITY *) & req);
+ if (req.didd_notify.e.Rc != 0xff)
+ return (0);
+ notify_handle = req.didd_notify.info.handle;
+ /* Register MAINT (me) */
+ req.didd_add_adapter.e.Req = 0;
+ req.didd_add_adapter.e.Rc =
+ IDI_SYNC_REQ_DIDD_ADD_ADAPTER;
+ req.didd_add_adapter.info.descriptor =
+ (void *) &MaintDescriptor;
+ DAdapter.request((ENTITY *) & req);
+ if (req.didd_add_adapter.e.Rc != 0xff)
+ return (0);
+ } else if ((DIDD_Table[x].type > 0)
+ && (DIDD_Table[x].type < 16)) {
+ diva_mnt_add_xdi_adapter(&DIDD_Table[x]);
+ }
+ }
+ return (dadapter);
+}
+
+/*
+ * disconnect from didd
+ */
+static void DIVA_EXIT_FUNCTION disconnect_didd(void)
+{
+ IDI_SYNC_REQ req;
+
+ req.didd_notify.e.Req = 0;
+ req.didd_notify.e.Rc = IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER_NOTIFY;
+ req.didd_notify.info.handle = notify_handle;
+ DAdapter.request((ENTITY *) & req);
+
+ req.didd_remove_adapter.e.Req = 0;
+ req.didd_remove_adapter.e.Rc = IDI_SYNC_REQ_DIDD_REMOVE_ADAPTER;
+ req.didd_remove_adapter.info.p_request =
+ (IDI_CALL) MaintDescriptor.request;
+ DAdapter.request((ENTITY *) & req);
+}
+
+/*
+ * read/write maint
+ */
+int maint_read_write(void __user *buf, int count)
+{
+ byte data[128];
+ dword cmd, id, mask;
+ int ret = 0;
+
+ if (count < (3 * sizeof(dword)))
+ return (-EFAULT);
+
+ if (diva_os_copy_from_user(NULL, (void *) &data[0],
+ buf, 3 * sizeof(dword))) {
+ return (-EFAULT);
+ }
+
+ cmd = *(dword *) & data[0]; /* command */
+ id = *(dword *) & data[4]; /* driver id */
+ mask = *(dword *) & data[8]; /* mask or size */
+
+ switch (cmd) {
+ case DITRACE_CMD_GET_DRIVER_INFO:
+ if ((ret = diva_get_driver_info(id, data, sizeof(data))) > 0) {
+ if ((count < ret) || diva_os_copy_to_user
+ (NULL, buf, (void *) &data[0], ret))
+ ret = -EFAULT;
+ } else {
+ ret = -EINVAL;
+ }
+ break;
+
+ case DITRACE_READ_DRIVER_DBG_MASK:
+ if ((ret = diva_get_driver_dbg_mask(id, (byte *) data)) > 0) {
+ if ((count < ret) || diva_os_copy_to_user
+ (NULL, buf, (void *) &data[0], ret))
+ ret = -EFAULT;
+ } else {
+ ret = -ENODEV;
+ }
+ break;
+
+ case DITRACE_WRITE_DRIVER_DBG_MASK:
+ if ((ret = diva_set_driver_dbg_mask(id, mask)) <= 0) {
+ ret = -ENODEV;
+ }
+ break;
+
+ /*
+ Filter commands will ignore the ID due to fact that filtering affects
+ the B- channel and Audio Tap trace levels only. Also MAINT driver will
+ select the right trace ID by itself
+ */
+ case DITRACE_WRITE_SELECTIVE_TRACE_FILTER:
+ if (!mask) {
+ ret = diva_set_trace_filter (1, "*");
+ } else if (mask < sizeof(data)) {
+ if (diva_os_copy_from_user(NULL, data, (char __user *)buf+12, mask)) {
+ ret = -EFAULT;
+ } else {
+ ret = diva_set_trace_filter ((int)mask, data);
+ }
+ } else {
+ ret = -EINVAL;
+ }
+ break;
+
+ case DITRACE_READ_SELECTIVE_TRACE_FILTER:
+ if ((ret = diva_get_trace_filter (sizeof(data), data)) > 0) {
+ if (diva_os_copy_to_user (NULL, buf, data, ret))
+ ret = -EFAULT;
+ } else {
+ ret = -ENODEV;
+ }
+ break;
+
+ case DITRACE_READ_TRACE_ENTRY:{
+ diva_os_spin_lock_magic_t old_irql;
+ word size;
+ diva_dbg_entry_head_t *pmsg;
+ byte *pbuf;
+
+ if (!(pbuf = diva_os_malloc(0, mask))) {
+ return (-ENOMEM);
+ }
+
+ for(;;) {
+ if (!(pmsg =
+ diva_maint_get_message(&size, &old_irql))) {
+ break;
+ }
+ if (size > mask) {
+ diva_maint_ack_message(0, &old_irql);
+ ret = -EINVAL;
+ break;
+ }
+ ret = size;
+ memcpy(pbuf, pmsg, size);
+ diva_maint_ack_message(1, &old_irql);
+ if ((count < size) ||
+ diva_os_copy_to_user (NULL, buf, (void *) pbuf, size))
+ ret = -EFAULT;
+ break;
+ }
+ diva_os_free(0, pbuf);
+ }
+ break;
+
+ case DITRACE_READ_TRACE_ENTRYS:{
+ diva_os_spin_lock_magic_t old_irql;
+ word size;
+ diva_dbg_entry_head_t *pmsg;
+ byte *pbuf = NULL;
+ int written = 0;
+
+ if (mask < 4096) {
+ ret = -EINVAL;
+ break;
+ }
+ if (!(pbuf = diva_os_malloc(0, mask))) {
+ return (-ENOMEM);
+ }
+
+ for (;;) {
+ if (!(pmsg =
+ diva_maint_get_message(&size, &old_irql))) {
+ break;
+ }
+ if ((size + 8) > mask) {
+ diva_maint_ack_message(0, &old_irql);
+ break;
+ }
+ /*
+ Write entry length
+ */
+ pbuf[written++] = (byte) size;
+ pbuf[written++] = (byte) (size >> 8);
+ pbuf[written++] = 0;
+ pbuf[written++] = 0;
+ /*
+ Write message
+ */
+ memcpy(&pbuf[written], pmsg, size);
+ diva_maint_ack_message(1, &old_irql);
+ written += size;
+ mask -= (size + 4);
+ }
+ pbuf[written++] = 0;
+ pbuf[written++] = 0;
+ pbuf[written++] = 0;
+ pbuf[written++] = 0;
+
+ if ((count < written) || diva_os_copy_to_user(NULL, buf, (void *) pbuf, written)) {
+ ret = -EFAULT;
+ } else {
+ ret = written;
+ }
+ diva_os_free(0, pbuf);
+ }
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+ return (ret);
+}
+
+/*
+ * init
+ */
+int DIVA_INIT_FUNCTION mntfunc_init(int *buffer_length, void **buffer,
+ unsigned long diva_dbg_mem)
+{
+ if (*buffer_length < 64) {
+ *buffer_length = 64;
+ }
+ if (*buffer_length > 512) {
+ *buffer_length = 512;
+ }
+ *buffer_length *= 1024;
+
+ if (diva_dbg_mem) {
+ *buffer = (void *) diva_dbg_mem;
+ } else {
+ while ((*buffer_length >= (64 * 1024))
+ &&
+ (!(*buffer = diva_os_malloc (0, *buffer_length)))) {
+ *buffer_length -= 1024;
+ }
+
+ if (!*buffer) {
+ DBG_ERR(("init: Can not alloc trace buffer"));
+ return (0);
+ }
+ }
+
+ if (diva_maint_init(*buffer, *buffer_length, (diva_dbg_mem == 0))) {
+ if (!diva_dbg_mem) {
+ diva_os_free (0, *buffer);
+ }
+ DBG_ERR(("init: maint init failed"));
+ return (0);
+ }
+
+ if (!connect_didd()) {
+ DBG_ERR(("init: failed to connect to DIDD."));
+ diva_maint_finit();
+ if (!diva_dbg_mem) {
+ diva_os_free (0, *buffer);
+ }
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * exit
+ */
+void DIVA_EXIT_FUNCTION mntfunc_finit(void)
+{
+ void *buffer;
+ int i = 100;
+
+ DbgDeregister();
+
+ while (diva_mnt_shutdown_xdi_adapters() && i--) {
+ diva_os_sleep(10);
+ }
+
+ disconnect_didd();
+
+ if ((buffer = diva_maint_finit())) {
+ diva_os_free (0, buffer);
+ }
+
+ memset(&MAdapter, 0, sizeof(MAdapter));
+ dprintf = no_printf;
+}
diff --git a/drivers/isdn/hardware/eicon/os_4bri.c b/drivers/isdn/hardware/eicon/os_4bri.c
new file mode 100644
index 0000000..7b4ec3f
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/os_4bri.c
@@ -0,0 +1,1132 @@
+/* $Id: os_4bri.c,v 1.28.4.4 2005/02/11 19:40:25 armin Exp $ */
+
+#include "platform.h"
+#include "debuglib.h"
+#include "cardtype.h"
+#include "pc.h"
+#include "pr_pc.h"
+#include "di_defs.h"
+#include "dsp_defs.h"
+#include "di.h"
+#include "io.h"
+
+#include "xdi_msg.h"
+#include "xdi_adapter.h"
+#include "os_4bri.h"
+#include "diva_pci.h"
+#include "mi_pc.h"
+#include "dsrv4bri.h"
+#include "helpers.h"
+
+static void *diva_xdiLoadFileFile = NULL;
+static dword diva_xdiLoadFileLength = 0;
+
+/*
+** IMPORTS
+*/
+extern void prepare_qBri_functions(PISDN_ADAPTER IoAdapter);
+extern void prepare_qBri2_functions(PISDN_ADAPTER IoAdapter);
+extern void diva_xdi_display_adapter_features(int card);
+extern void diva_add_slave_adapter(diva_os_xdi_adapter_t * a);
+
+extern int qBri_FPGA_download(PISDN_ADAPTER IoAdapter);
+extern void start_qBri_hardware(PISDN_ADAPTER IoAdapter);
+
+extern int diva_card_read_xlog(diva_os_xdi_adapter_t * a);
+
+/*
+** LOCALS
+*/
+static unsigned long _4bri_bar_length[4] = {
+ 0x100,
+ 0x100, /* I/O */
+ MQ_MEMORY_SIZE,
+ 0x2000
+};
+static unsigned long _4bri_v2_bar_length[4] = {
+ 0x100,
+ 0x100, /* I/O */
+ MQ2_MEMORY_SIZE,
+ 0x10000
+};
+static unsigned long _4bri_v2_bri_bar_length[4] = {
+ 0x100,
+ 0x100, /* I/O */
+ BRI2_MEMORY_SIZE,
+ 0x10000
+};
+
+
+static int diva_4bri_cleanup_adapter(diva_os_xdi_adapter_t * a);
+static int _4bri_get_serial_number(diva_os_xdi_adapter_t * a);
+static int diva_4bri_cmd_card_proc(struct _diva_os_xdi_adapter *a,
+ diva_xdi_um_cfg_cmd_t * cmd,
+ int length);
+static int diva_4bri_cleanup_slave_adapters(diva_os_xdi_adapter_t * a);
+static int diva_4bri_write_fpga_image(diva_os_xdi_adapter_t * a,
+ byte * data, dword length);
+static int diva_4bri_reset_adapter(PISDN_ADAPTER IoAdapter);
+static int diva_4bri_write_sdram_block(PISDN_ADAPTER IoAdapter,
+ dword address,
+ const byte * data,
+ dword length, dword limit);
+static int diva_4bri_start_adapter(PISDN_ADAPTER IoAdapter,
+ dword start_address, dword features);
+static int check_qBri_interrupt(PISDN_ADAPTER IoAdapter);
+static int diva_4bri_stop_adapter(diva_os_xdi_adapter_t * a);
+
+static int _4bri_is_rev_2_card(int card_ordinal)
+{
+ switch (card_ordinal) {
+ case CARDTYPE_DIVASRV_Q_8M_V2_PCI:
+ case CARDTYPE_DIVASRV_VOICE_Q_8M_V2_PCI:
+ case CARDTYPE_DIVASRV_B_2M_V2_PCI:
+ case CARDTYPE_DIVASRV_B_2F_PCI:
+ case CARDTYPE_DIVASRV_VOICE_B_2M_V2_PCI:
+ return (1);
+ }
+ return (0);
+}
+
+static int _4bri_is_rev_2_bri_card(int card_ordinal)
+{
+ switch (card_ordinal) {
+ case CARDTYPE_DIVASRV_B_2M_V2_PCI:
+ case CARDTYPE_DIVASRV_B_2F_PCI:
+ case CARDTYPE_DIVASRV_VOICE_B_2M_V2_PCI:
+ return (1);
+ }
+ return (0);
+}
+
+static void diva_4bri_set_addresses(diva_os_xdi_adapter_t *a)
+{
+ dword offset = a->resources.pci.qoffset;
+ dword c_offset = offset * a->xdi_adapter.ControllerNumber;
+
+ a->resources.pci.mem_type_id[MEM_TYPE_RAM] = 2;
+ a->resources.pci.mem_type_id[MEM_TYPE_ADDRESS] = 2;
+ a->resources.pci.mem_type_id[MEM_TYPE_CONTROL] = 2;
+ a->resources.pci.mem_type_id[MEM_TYPE_RESET] = 0;
+ a->resources.pci.mem_type_id[MEM_TYPE_CTLREG] = 3;
+ a->resources.pci.mem_type_id[MEM_TYPE_PROM] = 0;
+
+ /*
+ Set up hardware related pointers
+ */
+ a->xdi_adapter.Address = a->resources.pci.addr[2]; /* BAR2 SDRAM */
+ a->xdi_adapter.Address += c_offset;
+
+ a->xdi_adapter.Control = a->resources.pci.addr[2]; /* BAR2 SDRAM */
+
+ a->xdi_adapter.ram = a->resources.pci.addr[2]; /* BAR2 SDRAM */
+ a->xdi_adapter.ram += c_offset + (offset - MQ_SHARED_RAM_SIZE);
+
+ a->xdi_adapter.reset = a->resources.pci.addr[0]; /* BAR0 CONFIG */
+ /*
+ ctlReg contains the register address for the MIPS CPU reset control
+ */
+ a->xdi_adapter.ctlReg = a->resources.pci.addr[3]; /* BAR3 CNTRL */
+ /*
+ prom contains the register address for FPGA and EEPROM programming
+ */
+ a->xdi_adapter.prom = &a->xdi_adapter.reset[0x6E];
+}
+
+/*
+** BAR0 - MEM - 0x100 - CONFIG MEM
+** BAR1 - I/O - 0x100 - UNUSED
+** BAR2 - MEM - MQ_MEMORY_SIZE (MQ2_MEMORY_SIZE on Rev.2) - SDRAM
+** BAR3 - MEM - 0x2000 (0x10000 on Rev.2) - CNTRL
+**
+** Called by master adapter, that will initialize and add slave adapters
+*/
+int diva_4bri_init_card(diva_os_xdi_adapter_t * a)
+{
+ int bar, i;
+ byte __iomem *p;
+ PADAPTER_LIST_ENTRY quadro_list;
+ diva_os_xdi_adapter_t *diva_current;
+ diva_os_xdi_adapter_t *adapter_list[4];
+ PISDN_ADAPTER Slave;
+ unsigned long bar_length[sizeof(_4bri_bar_length) /
+ sizeof(_4bri_bar_length[0])];
+ int v2 = _4bri_is_rev_2_card(a->CardOrdinal);
+ int tasks = _4bri_is_rev_2_bri_card(a->CardOrdinal) ? 1 : MQ_INSTANCE_COUNT;
+ int factor = (tasks == 1) ? 1 : 2;
+
+ if (v2) {
+ if (_4bri_is_rev_2_bri_card(a->CardOrdinal)) {
+ memcpy(bar_length, _4bri_v2_bri_bar_length,
+ sizeof(bar_length));
+ } else {
+ memcpy(bar_length, _4bri_v2_bar_length,
+ sizeof(bar_length));
+ }
+ } else {
+ memcpy(bar_length, _4bri_bar_length, sizeof(bar_length));
+ }
+ DBG_TRC(("SDRAM_LENGTH=%08x, tasks=%d, factor=%d",
+ bar_length[2], tasks, factor))
+
+ /*
+ Get Serial Number
+ The serial number of 4BRI is accessible in accordance with PCI spec
+ via command register located in configuration space, also we do not
+ have to map any BAR before we can access it
+ */
+ if (!_4bri_get_serial_number(a)) {
+ DBG_ERR(("A: 4BRI can't get Serial Number"))
+ diva_4bri_cleanup_adapter(a);
+ return (-1);
+ }
+
+ /*
+ Set properties
+ */
+ a->xdi_adapter.Properties = CardProperties[a->CardOrdinal];
+ DBG_LOG(("Load %s, SN:%ld, bus:%02x, func:%02x",
+ a->xdi_adapter.Properties.Name,
+ a->xdi_adapter.serialNo,
+ a->resources.pci.bus, a->resources.pci.func))
+
+ /*
+ First initialization step: get and check hardware resoures.
+ Do not map resources and do not access card at this step
+ */
+ for (bar = 0; bar < 4; bar++) {
+ a->resources.pci.bar[bar] =
+ divasa_get_pci_bar(a->resources.pci.bus,
+ a->resources.pci.func, bar,
+ a->resources.pci.hdev);
+ if (!a->resources.pci.bar[bar]
+ || (a->resources.pci.bar[bar] == 0xFFFFFFF0)) {
+ DBG_ERR(
+ ("A: invalid bar[%d]=%08x", bar,
+ a->resources.pci.bar[bar]))
+ return (-1);
+ }
+ }
+ a->resources.pci.irq =
+ (byte) divasa_get_pci_irq(a->resources.pci.bus,
+ a->resources.pci.func,
+ a->resources.pci.hdev);
+ if (!a->resources.pci.irq) {
+ DBG_ERR(("A: invalid irq"));
+ return (-1);
+ }
+
+ a->xdi_adapter.sdram_bar = a->resources.pci.bar[2];
+
+ /*
+ Map all MEMORY BAR's
+ */
+ for (bar = 0; bar < 4; bar++) {
+ if (bar != 1) { /* ignore I/O */
+ a->resources.pci.addr[bar] =
+ divasa_remap_pci_bar(a, bar, a->resources.pci.bar[bar],
+ bar_length[bar]);
+ if (!a->resources.pci.addr[bar]) {
+ DBG_ERR(("A: 4BRI: can't map bar[%d]", bar))
+ diva_4bri_cleanup_adapter(a);
+ return (-1);
+ }
+ }
+ }
+
+ /*
+ Register I/O port
+ */
+ sprintf(&a->port_name[0], "DIVA 4BRI %ld", (long) a->xdi_adapter.serialNo);
+
+ if (diva_os_register_io_port(a, 1, a->resources.pci.bar[1],
+ bar_length[1], &a->port_name[0], 1)) {
+ DBG_ERR(("A: 4BRI: can't register bar[1]"))
+ diva_4bri_cleanup_adapter(a);
+ return (-1);
+ }
+
+ a->resources.pci.addr[1] =
+ (void *) (unsigned long) a->resources.pci.bar[1];
+
+ /*
+ Set cleanup pointer for base adapter only, so slave adapter
+ will be unable to get cleanup
+ */
+ a->interface.cleanup_adapter_proc = diva_4bri_cleanup_adapter;
+
+ /*
+ Create slave adapters
+ */
+ if (tasks > 1) {
+ if (!(a->slave_adapters[0] =
+ (diva_os_xdi_adapter_t *) diva_os_malloc(0, sizeof(*a))))
+ {
+ diva_4bri_cleanup_adapter(a);
+ return (-1);
+ }
+ if (!(a->slave_adapters[1] =
+ (diva_os_xdi_adapter_t *) diva_os_malloc(0, sizeof(*a))))
+ {
+ diva_os_free(0, a->slave_adapters[0]);
+ a->slave_adapters[0] = NULL;
+ diva_4bri_cleanup_adapter(a);
+ return (-1);
+ }
+ if (!(a->slave_adapters[2] =
+ (diva_os_xdi_adapter_t *) diva_os_malloc(0, sizeof(*a))))
+ {
+ diva_os_free(0, a->slave_adapters[0]);
+ diva_os_free(0, a->slave_adapters[1]);
+ a->slave_adapters[0] = NULL;
+ a->slave_adapters[1] = NULL;
+ diva_4bri_cleanup_adapter(a);
+ return (-1);
+ }
+ memset(a->slave_adapters[0], 0x00, sizeof(*a));
+ memset(a->slave_adapters[1], 0x00, sizeof(*a));
+ memset(a->slave_adapters[2], 0x00, sizeof(*a));
+ }
+
+ adapter_list[0] = a;
+ adapter_list[1] = a->slave_adapters[0];
+ adapter_list[2] = a->slave_adapters[1];
+ adapter_list[3] = a->slave_adapters[2];
+
+ /*
+ Allocate slave list
+ */
+ quadro_list =
+ (PADAPTER_LIST_ENTRY) diva_os_malloc(0, sizeof(*quadro_list));
+ if (!(a->slave_list = quadro_list)) {
+ for (i = 0; i < (tasks - 1); i++) {
+ diva_os_free(0, a->slave_adapters[i]);
+ a->slave_adapters[i] = NULL;
+ }
+ diva_4bri_cleanup_adapter(a);
+ return (-1);
+ }
+ memset(quadro_list, 0x00, sizeof(*quadro_list));
+
+ /*
+ Set interfaces
+ */
+ a->xdi_adapter.QuadroList = quadro_list;
+ for (i = 0; i < tasks; i++) {
+ adapter_list[i]->xdi_adapter.ControllerNumber = i;
+ adapter_list[i]->xdi_adapter.tasks = tasks;
+ quadro_list->QuadroAdapter[i] =
+ &adapter_list[i]->xdi_adapter;
+ }
+
+ for (i = 0; i < tasks; i++) {
+ diva_current = adapter_list[i];
+
+ diva_current->dsp_mask = 0x00000003;
+
+ diva_current->xdi_adapter.a.io =
+ &diva_current->xdi_adapter;
+ diva_current->xdi_adapter.DIRequest = request;
+ diva_current->interface.cmd_proc = diva_4bri_cmd_card_proc;
+ diva_current->xdi_adapter.Properties =
+ CardProperties[a->CardOrdinal];
+ diva_current->CardOrdinal = a->CardOrdinal;
+
+ diva_current->xdi_adapter.Channels =
+ CardProperties[a->CardOrdinal].Channels;
+ diva_current->xdi_adapter.e_max =
+ CardProperties[a->CardOrdinal].E_info;
+ diva_current->xdi_adapter.e_tbl =
+ diva_os_malloc(0,
+ diva_current->xdi_adapter.e_max *
+ sizeof(E_INFO));
+
+ if (!diva_current->xdi_adapter.e_tbl) {
+ diva_4bri_cleanup_slave_adapters(a);
+ diva_4bri_cleanup_adapter(a);
+ for (i = 1; i < (tasks - 1); i++) {
+ diva_os_free(0, adapter_list[i]);
+ }
+ return (-1);
+ }
+ memset(diva_current->xdi_adapter.e_tbl, 0x00,
+ diva_current->xdi_adapter.e_max * sizeof(E_INFO));
+
+ if (diva_os_initialize_spin_lock(&diva_current->xdi_adapter.isr_spin_lock, "isr")) {
+ diva_4bri_cleanup_slave_adapters(a);
+ diva_4bri_cleanup_adapter(a);
+ for (i = 1; i < (tasks - 1); i++) {
+ diva_os_free(0, adapter_list[i]);
+ }
+ return (-1);
+ }
+ if (diva_os_initialize_spin_lock(&diva_current->xdi_adapter.data_spin_lock, "data")) {
+ diva_4bri_cleanup_slave_adapters(a);
+ diva_4bri_cleanup_adapter(a);
+ for (i = 1; i < (tasks - 1); i++) {
+ diva_os_free(0, adapter_list[i]);
+ }
+ return (-1);
+ }
+
+ strcpy(diva_current->xdi_adapter.req_soft_isr. dpc_thread_name, "kdivas4brid");
+
+ if (diva_os_initialize_soft_isr (&diva_current->xdi_adapter.req_soft_isr, DIDpcRoutine,
+ &diva_current->xdi_adapter)) {
+ diva_4bri_cleanup_slave_adapters(a);
+ diva_4bri_cleanup_adapter(a);
+ for (i = 1; i < (tasks - 1); i++) {
+ diva_os_free(0, adapter_list[i]);
+ }
+ return (-1);
+ }
+
+ /*
+ Do not initialize second DPC - only one thread will be created
+ */
+ diva_current->xdi_adapter.isr_soft_isr.object =
+ diva_current->xdi_adapter.req_soft_isr.object;
+ }
+
+ if (v2) {
+ prepare_qBri2_functions(&a->xdi_adapter);
+ } else {
+ prepare_qBri_functions(&a->xdi_adapter);
+ }
+
+ for (i = 0; i < tasks; i++) {
+ diva_current = adapter_list[i];
+ if (i)
+ memcpy(&diva_current->resources, &a->resources, sizeof(divas_card_resources_t));
+ diva_current->resources.pci.qoffset = (a->xdi_adapter.MemorySize >> factor);
+ }
+
+ /*
+ Set up hardware related pointers
+ */
+ a->xdi_adapter.cfg = (void *) (unsigned long) a->resources.pci.bar[0]; /* BAR0 CONFIG */
+ a->xdi_adapter.port = (void *) (unsigned long) a->resources.pci.bar[1]; /* BAR1 */
+ a->xdi_adapter.ctlReg = (void *) (unsigned long) a->resources.pci.bar[3]; /* BAR3 CNTRL */
+
+ for (i = 0; i < tasks; i++) {
+ diva_current = adapter_list[i];
+ diva_4bri_set_addresses(diva_current);
+ Slave = a->xdi_adapter.QuadroList->QuadroAdapter[i];
+ Slave->MultiMaster = &a->xdi_adapter;
+ Slave->sdram_bar = a->xdi_adapter.sdram_bar;
+ if (i) {
+ Slave->serialNo = ((dword) (Slave->ControllerNumber << 24)) |
+ a->xdi_adapter.serialNo;
+ Slave->cardType = a->xdi_adapter.cardType;
+ }
+ }
+
+ /*
+ reset contains the base address for the PLX 9054 register set
+ */
+ p = DIVA_OS_MEM_ATTACH_RESET(&a->xdi_adapter);
+ WRITE_BYTE(&p[PLX9054_INTCSR], 0x00); /* disable PCI interrupts */
+ DIVA_OS_MEM_DETACH_RESET(&a->xdi_adapter, p);
+
+ /*
+ Set IRQ handler
+ */
+ a->xdi_adapter.irq_info.irq_nr = a->resources.pci.irq;
+ sprintf(a->xdi_adapter.irq_info.irq_name, "DIVA 4BRI %ld",
+ (long) a->xdi_adapter.serialNo);
+
+ if (diva_os_register_irq(a, a->xdi_adapter.irq_info.irq_nr,
+ a->xdi_adapter.irq_info.irq_name)) {
+ diva_4bri_cleanup_slave_adapters(a);
+ diva_4bri_cleanup_adapter(a);
+ for (i = 1; i < (tasks - 1); i++) {
+ diva_os_free(0, adapter_list[i]);
+ }
+ return (-1);
+ }
+
+ a->xdi_adapter.irq_info.registered = 1;
+
+ /*
+ Add three slave adapters
+ */
+ if (tasks > 1) {
+ diva_add_slave_adapter(adapter_list[1]);
+ diva_add_slave_adapter(adapter_list[2]);
+ diva_add_slave_adapter(adapter_list[3]);
+ }
+
+ diva_log_info("%s IRQ:%d SerNo:%d", a->xdi_adapter.Properties.Name,
+ a->resources.pci.irq, a->xdi_adapter.serialNo);
+
+ return (0);
+}
+
+/*
+** Cleanup function will be called for master adapter only
+** this is guaranteed by design: cleanup callback is set
+** by master adapter only
+*/
+static int diva_4bri_cleanup_adapter(diva_os_xdi_adapter_t * a)
+{
+ int bar;
+
+ /*
+ Stop adapter if running
+ */
+ if (a->xdi_adapter.Initialized) {
+ diva_4bri_stop_adapter(a);
+ }
+
+ /*
+ Remove IRQ handler
+ */
+ if (a->xdi_adapter.irq_info.registered) {
+ diva_os_remove_irq(a, a->xdi_adapter.irq_info.irq_nr);
+ }
+ a->xdi_adapter.irq_info.registered = 0;
+
+ /*
+ Free DPC's and spin locks on all adapters
+ */
+ diva_4bri_cleanup_slave_adapters(a);
+
+ /*
+ Unmap all BARS
+ */
+ for (bar = 0; bar < 4; bar++) {
+ if (bar != 1) {
+ if (a->resources.pci.bar[bar]
+ && a->resources.pci.addr[bar]) {
+ divasa_unmap_pci_bar(a->resources.pci.addr[bar]);
+ a->resources.pci.bar[bar] = 0;
+ a->resources.pci.addr[bar] = NULL;
+ }
+ }
+ }
+
+ /*
+ Unregister I/O
+ */
+ if (a->resources.pci.bar[1] && a->resources.pci.addr[1]) {
+ diva_os_register_io_port(a, 0, a->resources.pci.bar[1],
+ _4bri_is_rev_2_card(a->
+ CardOrdinal) ?
+ _4bri_v2_bar_length[1] :
+ _4bri_bar_length[1],
+ &a->port_name[0], 1);
+ a->resources.pci.bar[1] = 0;
+ a->resources.pci.addr[1] = NULL;
+ }
+
+ if (a->slave_list) {
+ diva_os_free(0, a->slave_list);
+ a->slave_list = NULL;
+ }
+
+ return (0);
+}
+
+static int _4bri_get_serial_number(diva_os_xdi_adapter_t * a)
+{
+ dword data[64];
+ dword serNo;
+ word addr, status, i, j;
+ byte Bus, Slot;
+ void *hdev;
+
+ Bus = a->resources.pci.bus;
+ Slot = a->resources.pci.func;
+ hdev = a->resources.pci.hdev;
+
+ for (i = 0; i < 64; ++i) {
+ addr = i * 4;
+ for (j = 0; j < 5; ++j) {
+ PCIwrite(Bus, Slot, 0x4E, &addr, sizeof(addr),
+ hdev);
+ diva_os_wait(1);
+ PCIread(Bus, Slot, 0x4E, &status, sizeof(status),
+ hdev);
+ if (status & 0x8000)
+ break;
+ }
+ if (j >= 5) {
+ DBG_ERR(("EEPROM[%d] read failed (0x%x)", i * 4, addr))
+ return (0);
+ }
+ PCIread(Bus, Slot, 0x50, &data[i], sizeof(data[i]), hdev);
+ }
+ DBG_BLK(((char *) &data[0], sizeof(data)))
+
+ serNo = data[32];
+ if (serNo == 0 || serNo == 0xffffffff)
+ serNo = data[63];
+
+ if (!serNo) {
+ DBG_LOG(("W: Serial Number == 0, create one serial number"));
+ serNo = a->resources.pci.bar[1] & 0xffff0000;
+ serNo |= a->resources.pci.bus << 8;
+ serNo |= a->resources.pci.func;
+ }
+
+ a->xdi_adapter.serialNo = serNo;
+
+ DBG_REG(("Serial No. : %ld", a->xdi_adapter.serialNo))
+
+ return (serNo);
+}
+
+/*
+** Release resources of slave adapters
+*/
+static int diva_4bri_cleanup_slave_adapters(diva_os_xdi_adapter_t * a)
+{
+ diva_os_xdi_adapter_t *adapter_list[4];
+ diva_os_xdi_adapter_t *diva_current;
+ int i;
+
+ adapter_list[0] = a;
+ adapter_list[1] = a->slave_adapters[0];
+ adapter_list[2] = a->slave_adapters[1];
+ adapter_list[3] = a->slave_adapters[2];
+
+ for (i = 0; i < a->xdi_adapter.tasks; i++) {
+ diva_current = adapter_list[i];
+ if (diva_current) {
+ diva_os_destroy_spin_lock(&diva_current->
+ xdi_adapter.
+ isr_spin_lock, "unload");
+ diva_os_destroy_spin_lock(&diva_current->
+ xdi_adapter.
+ data_spin_lock,
+ "unload");
+
+ diva_os_cancel_soft_isr(&diva_current->xdi_adapter.
+ req_soft_isr);
+ diva_os_cancel_soft_isr(&diva_current->xdi_adapter.
+ isr_soft_isr);
+
+ diva_os_remove_soft_isr(&diva_current->xdi_adapter.
+ req_soft_isr);
+ diva_current->xdi_adapter.isr_soft_isr.object = NULL;
+
+ if (diva_current->xdi_adapter.e_tbl) {
+ diva_os_free(0,
+ diva_current->xdi_adapter.
+ e_tbl);
+ }
+ diva_current->xdi_adapter.e_tbl = NULL;
+ diva_current->xdi_adapter.e_max = 0;
+ diva_current->xdi_adapter.e_count = 0;
+ }
+ }
+
+ return (0);
+}
+
+static int
+diva_4bri_cmd_card_proc(struct _diva_os_xdi_adapter *a,
+ diva_xdi_um_cfg_cmd_t * cmd, int length)
+{
+ int ret = -1;
+
+ if (cmd->adapter != a->controller) {
+ DBG_ERR(("A: 4bri_cmd, invalid controller=%d != %d",
+ cmd->adapter, a->controller))
+ return (-1);
+ }
+
+ switch (cmd->command) {
+ case DIVA_XDI_UM_CMD_GET_CARD_ORDINAL:
+ a->xdi_mbox.data_length = sizeof(dword);
+ a->xdi_mbox.data =
+ diva_os_malloc(0, a->xdi_mbox.data_length);
+ if (a->xdi_mbox.data) {
+ *(dword *) a->xdi_mbox.data =
+ (dword) a->CardOrdinal;
+ a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
+ ret = 0;
+ }
+ break;
+
+ case DIVA_XDI_UM_CMD_GET_SERIAL_NR:
+ a->xdi_mbox.data_length = sizeof(dword);
+ a->xdi_mbox.data =
+ diva_os_malloc(0, a->xdi_mbox.data_length);
+ if (a->xdi_mbox.data) {
+ *(dword *) a->xdi_mbox.data =
+ (dword) a->xdi_adapter.serialNo;
+ a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
+ ret = 0;
+ }
+ break;
+
+ case DIVA_XDI_UM_CMD_GET_PCI_HW_CONFIG:
+ if (!a->xdi_adapter.ControllerNumber) {
+ /*
+ Only master adapter can access hardware config
+ */
+ a->xdi_mbox.data_length = sizeof(dword) * 9;
+ a->xdi_mbox.data =
+ diva_os_malloc(0, a->xdi_mbox.data_length);
+ if (a->xdi_mbox.data) {
+ int i;
+ dword *data = (dword *) a->xdi_mbox.data;
+
+ for (i = 0; i < 8; i++) {
+ *data++ = a->resources.pci.bar[i];
+ }
+ *data++ = (dword) a->resources.pci.irq;
+ a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
+ ret = 0;
+ }
+ }
+ break;
+
+ case DIVA_XDI_UM_CMD_GET_CARD_STATE:
+ if (!a->xdi_adapter.ControllerNumber) {
+ a->xdi_mbox.data_length = sizeof(dword);
+ a->xdi_mbox.data =
+ diva_os_malloc(0, a->xdi_mbox.data_length);
+ if (a->xdi_mbox.data) {
+ dword *data = (dword *) a->xdi_mbox.data;
+ if (!a->xdi_adapter.ram
+ || !a->xdi_adapter.reset
+ || !a->xdi_adapter.cfg) {
+ *data = 3;
+ } else if (a->xdi_adapter.trapped) {
+ *data = 2;
+ } else if (a->xdi_adapter.Initialized) {
+ *data = 1;
+ } else {
+ *data = 0;
+ }
+ a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
+ ret = 0;
+ }
+ }
+ break;
+
+ case DIVA_XDI_UM_CMD_WRITE_FPGA:
+ if (!a->xdi_adapter.ControllerNumber) {
+ ret =
+ diva_4bri_write_fpga_image(a,
+ (byte *) & cmd[1],
+ cmd->command_data.
+ write_fpga.
+ image_length);
+ }
+ break;
+
+ case DIVA_XDI_UM_CMD_RESET_ADAPTER:
+ if (!a->xdi_adapter.ControllerNumber) {
+ ret = diva_4bri_reset_adapter(&a->xdi_adapter);
+ }
+ break;
+
+ case DIVA_XDI_UM_CMD_WRITE_SDRAM_BLOCK:
+ if (!a->xdi_adapter.ControllerNumber) {
+ ret = diva_4bri_write_sdram_block(&a->xdi_adapter,
+ cmd->
+ command_data.
+ write_sdram.
+ offset,
+ (byte *) &
+ cmd[1],
+ cmd->
+ command_data.
+ write_sdram.
+ length,
+ a->xdi_adapter.
+ MemorySize);
+ }
+ break;
+
+ case DIVA_XDI_UM_CMD_START_ADAPTER:
+ if (!a->xdi_adapter.ControllerNumber) {
+ ret = diva_4bri_start_adapter(&a->xdi_adapter,
+ cmd->command_data.
+ start.offset,
+ cmd->command_data.
+ start.features);
+ }
+ break;
+
+ case DIVA_XDI_UM_CMD_SET_PROTOCOL_FEATURES:
+ if (!a->xdi_adapter.ControllerNumber) {
+ a->xdi_adapter.features =
+ cmd->command_data.features.features;
+ a->xdi_adapter.a.protocol_capabilities =
+ a->xdi_adapter.features;
+ DBG_TRC(("Set raw protocol features (%08x)",
+ a->xdi_adapter.features))
+ ret = 0;
+ }
+ break;
+
+ case DIVA_XDI_UM_CMD_STOP_ADAPTER:
+ if (!a->xdi_adapter.ControllerNumber) {
+ ret = diva_4bri_stop_adapter(a);
+ }
+ break;
+
+ case DIVA_XDI_UM_CMD_READ_XLOG_ENTRY:
+ ret = diva_card_read_xlog(a);
+ break;
+
+ case DIVA_XDI_UM_CMD_READ_SDRAM:
+ if (!a->xdi_adapter.ControllerNumber
+ && a->xdi_adapter.Address) {
+ if (
+ (a->xdi_mbox.data_length =
+ cmd->command_data.read_sdram.length)) {
+ if (
+ (a->xdi_mbox.data_length +
+ cmd->command_data.read_sdram.offset) <
+ a->xdi_adapter.MemorySize) {
+ a->xdi_mbox.data =
+ diva_os_malloc(0,
+ a->xdi_mbox.
+ data_length);
+ if (a->xdi_mbox.data) {
+ byte __iomem *p = DIVA_OS_MEM_ATTACH_ADDRESS(&a->xdi_adapter);
+ byte __iomem *src = p;
+ byte *dst = a->xdi_mbox.data;
+ dword len = a->xdi_mbox.data_length;
+
+ src += cmd->command_data.read_sdram.offset;
+
+ while (len--) {
+ *dst++ = READ_BYTE(src++);
+ }
+ DIVA_OS_MEM_DETACH_ADDRESS(&a->xdi_adapter, p);
+ a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
+ ret = 0;
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ DBG_ERR(("A: A(%d) invalid cmd=%d", a->controller,
+ cmd->command))
+ }
+
+ return (ret);
+}
+
+void *xdiLoadFile(char *FileName, dword *FileLength,
+ unsigned long lim)
+{
+ void *ret = diva_xdiLoadFileFile;
+
+ if (FileLength) {
+ *FileLength = diva_xdiLoadFileLength;
+ }
+ diva_xdiLoadFileFile = NULL;
+ diva_xdiLoadFileLength = 0;
+
+ return (ret);
+}
+
+void diva_os_set_qBri_functions(PISDN_ADAPTER IoAdapter)
+{
+}
+
+void diva_os_set_qBri2_functions(PISDN_ADAPTER IoAdapter)
+{
+}
+
+static int
+diva_4bri_write_fpga_image(diva_os_xdi_adapter_t * a, byte * data,
+ dword length)
+{
+ int ret;
+
+ diva_xdiLoadFileFile = data;
+ diva_xdiLoadFileLength = length;
+
+ ret = qBri_FPGA_download(&a->xdi_adapter);
+
+ diva_xdiLoadFileFile = NULL;
+ diva_xdiLoadFileLength = 0;
+
+ return (ret ? 0 : -1);
+}
+
+static int diva_4bri_reset_adapter(PISDN_ADAPTER IoAdapter)
+{
+ PISDN_ADAPTER Slave;
+ int i;
+
+ if (!IoAdapter->Address || !IoAdapter->reset) {
+ return (-1);
+ }
+ if (IoAdapter->Initialized) {
+ DBG_ERR(("A: A(%d) can't reset 4BRI adapter - please stop first",
+ IoAdapter->ANum))
+ return (-1);
+ }
+
+ /*
+ Forget all entities on all adapters
+ */
+ for (i = 0; ((i < IoAdapter->tasks) && IoAdapter->QuadroList); i++) {
+ Slave = IoAdapter->QuadroList->QuadroAdapter[i];
+ Slave->e_count = 0;
+ if (Slave->e_tbl) {
+ memset(Slave->e_tbl, 0x00,
+ Slave->e_max * sizeof(E_INFO));
+ }
+ Slave->head = 0;
+ Slave->tail = 0;
+ Slave->assign = 0;
+ Slave->trapped = 0;
+
+ memset(&Slave->a.IdTable[0], 0x00,
+ sizeof(Slave->a.IdTable));
+ memset(&Slave->a.IdTypeTable[0], 0x00,
+ sizeof(Slave->a.IdTypeTable));
+ memset(&Slave->a.FlowControlIdTable[0], 0x00,
+ sizeof(Slave->a.FlowControlIdTable));
+ memset(&Slave->a.FlowControlSkipTable[0], 0x00,
+ sizeof(Slave->a.FlowControlSkipTable));
+ memset(&Slave->a.misc_flags_table[0], 0x00,
+ sizeof(Slave->a.misc_flags_table));
+ memset(&Slave->a.rx_stream[0], 0x00,
+ sizeof(Slave->a.rx_stream));
+ memset(&Slave->a.tx_stream[0], 0x00,
+ sizeof(Slave->a.tx_stream));
+ memset(&Slave->a.tx_pos[0], 0x00, sizeof(Slave->a.tx_pos));
+ memset(&Slave->a.rx_pos[0], 0x00, sizeof(Slave->a.rx_pos));
+ }
+
+ return (0);
+}
+
+
+static int
+diva_4bri_write_sdram_block(PISDN_ADAPTER IoAdapter,
+ dword address,
+ const byte * data, dword length, dword limit)
+{
+ byte __iomem *p = DIVA_OS_MEM_ATTACH_ADDRESS(IoAdapter);
+ byte __iomem *mem = p;
+
+ if (((address + length) >= limit) || !mem) {
+ DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, p);
+ DBG_ERR(("A: A(%d) write 4BRI address=0x%08lx",
+ IoAdapter->ANum, address + length))
+ return (-1);
+ }
+ mem += address;
+
+ while (length--) {
+ WRITE_BYTE(mem++, *data++);
+ }
+
+ DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, p);
+ return (0);
+}
+
+static int
+diva_4bri_start_adapter(PISDN_ADAPTER IoAdapter,
+ dword start_address, dword features)
+{
+ volatile word __iomem *signature;
+ int started = 0;
+ int i;
+ byte __iomem *p;
+
+ /*
+ start adapter
+ */
+ start_qBri_hardware(IoAdapter);
+
+ p = DIVA_OS_MEM_ATTACH_RAM(IoAdapter);
+ /*
+ wait for signature in shared memory (max. 3 seconds)
+ */
+ signature = (volatile word __iomem *) (&p[0x1E]);
+
+ for (i = 0; i < 300; ++i) {
+ diva_os_wait(10);
+ if (READ_WORD(&signature[0]) == 0x4447) {
+ DBG_TRC(("Protocol startup time %d.%02d seconds",
+ (i / 100), (i % 100)))
+ started = 1;
+ break;
+ }
+ }
+
+ for (i = 1; i < IoAdapter->tasks; i++) {
+ IoAdapter->QuadroList->QuadroAdapter[i]->features =
+ IoAdapter->features;
+ IoAdapter->QuadroList->QuadroAdapter[i]->a.
+ protocol_capabilities = IoAdapter->features;
+ }
+
+ if (!started) {
+ DBG_FTL(("%s: Adapter selftest failed, signature=%04x",
+ IoAdapter->Properties.Name,
+ READ_WORD(&signature[0])))
+ DIVA_OS_MEM_DETACH_RAM(IoAdapter, p);
+ (*(IoAdapter->trapFnc)) (IoAdapter);
+ IoAdapter->stop(IoAdapter);
+ return (-1);
+ }
+ DIVA_OS_MEM_DETACH_RAM(IoAdapter, p);
+
+ for (i = 0; i < IoAdapter->tasks; i++) {
+ IoAdapter->QuadroList->QuadroAdapter[i]->Initialized = 1;
+ IoAdapter->QuadroList->QuadroAdapter[i]->IrqCount = 0;
+ }
+
+ if (check_qBri_interrupt(IoAdapter)) {
+ DBG_ERR(("A: A(%d) interrupt test failed",
+ IoAdapter->ANum))
+ for (i = 0; i < IoAdapter->tasks; i++) {
+ IoAdapter->QuadroList->QuadroAdapter[i]->Initialized = 0;
+ }
+ IoAdapter->stop(IoAdapter);
+ return (-1);
+ }
+
+ IoAdapter->Properties.Features = (word) features;
+ diva_xdi_display_adapter_features(IoAdapter->ANum);
+
+ for (i = 0; i < IoAdapter->tasks; i++) {
+ DBG_LOG(("A(%d) %s adapter successfull started",
+ IoAdapter->QuadroList->QuadroAdapter[i]->ANum,
+ (IoAdapter->tasks == 1) ? "BRI 2.0" : "4BRI"))
+ diva_xdi_didd_register_adapter(IoAdapter->QuadroList->QuadroAdapter[i]->ANum);
+ IoAdapter->QuadroList->QuadroAdapter[i]->Properties.Features = (word) features;
+ }
+
+ return (0);
+}
+
+static int check_qBri_interrupt(PISDN_ADAPTER IoAdapter)
+{
+#ifdef SUPPORT_INTERRUPT_TEST_ON_4BRI
+ int i;
+ ADAPTER *a = &IoAdapter->a;
+ byte __iomem *p;
+
+ IoAdapter->IrqCount = 0;
+
+ if (IoAdapter->ControllerNumber > 0)
+ return (-1);
+
+ p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter);
+ WRITE_BYTE(&p[PLX9054_INTCSR], PLX9054_INT_ENABLE);
+ DIVA_OS_MEM_DETACH_RESET(IoAdapter, p);
+ /*
+ interrupt test
+ */
+ a->ReadyInt = 1;
+ a->ram_out(a, &PR_RAM->ReadyInt, 1);
+
+ for (i = 100; !IoAdapter->IrqCount && (i-- > 0); diva_os_wait(10));
+
+ return ((IoAdapter->IrqCount > 0) ? 0 : -1);
+#else
+ dword volatile __iomem *qBriIrq;
+ byte __iomem *p;
+ /*
+ Reset on-board interrupt register
+ */
+ IoAdapter->IrqCount = 0;
+ p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
+ qBriIrq = (dword volatile __iomem *) (&p[_4bri_is_rev_2_card
+ (IoAdapter->
+ cardType) ? (MQ2_BREG_IRQ_TEST)
+ : (MQ_BREG_IRQ_TEST)]);
+
+ WRITE_DWORD(qBriIrq, MQ_IRQ_REQ_OFF);
+ DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p);
+
+ p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter);
+ WRITE_BYTE(&p[PLX9054_INTCSR], PLX9054_INT_ENABLE);
+ DIVA_OS_MEM_DETACH_RESET(IoAdapter, p);
+
+ diva_os_wait(100);
+
+ return (0);
+#endif /* SUPPORT_INTERRUPT_TEST_ON_4BRI */
+}
+
+static void diva_4bri_clear_interrupts(diva_os_xdi_adapter_t * a)
+{
+ PISDN_ADAPTER IoAdapter = &a->xdi_adapter;
+
+ /*
+ clear any pending interrupt
+ */
+ IoAdapter->disIrq(IoAdapter);
+
+ IoAdapter->tst_irq(&IoAdapter->a);
+ IoAdapter->clr_irq(&IoAdapter->a);
+ IoAdapter->tst_irq(&IoAdapter->a);
+
+ /*
+ kill pending dpcs
+ */
+ diva_os_cancel_soft_isr(&IoAdapter->req_soft_isr);
+ diva_os_cancel_soft_isr(&IoAdapter->isr_soft_isr);
+}
+
+static int diva_4bri_stop_adapter(diva_os_xdi_adapter_t * a)
+{
+ PISDN_ADAPTER IoAdapter = &a->xdi_adapter;
+ int i;
+
+ if (!IoAdapter->ram) {
+ return (-1);
+ }
+
+ if (!IoAdapter->Initialized) {
+ DBG_ERR(("A: A(%d) can't stop PRI adapter - not running",
+ IoAdapter->ANum))
+ return (-1); /* nothing to stop */
+ }
+
+ for (i = 0; i < IoAdapter->tasks; i++) {
+ IoAdapter->QuadroList->QuadroAdapter[i]->Initialized = 0;
+ }
+
+ /*
+ Disconnect Adapters from DIDD
+ */
+ for (i = 0; i < IoAdapter->tasks; i++) {
+ diva_xdi_didd_remove_adapter(IoAdapter->QuadroList->QuadroAdapter[i]->ANum);
+ }
+
+ i = 100;
+
+ /*
+ Stop interrupts
+ */
+ a->clear_interrupts_proc = diva_4bri_clear_interrupts;
+ IoAdapter->a.ReadyInt = 1;
+ IoAdapter->a.ram_inc(&IoAdapter->a, &PR_RAM->ReadyInt);
+ do {
+ diva_os_sleep(10);
+ } while (i-- && a->clear_interrupts_proc);
+
+ if (a->clear_interrupts_proc) {
+ diva_4bri_clear_interrupts(a);
+ a->clear_interrupts_proc = NULL;
+ DBG_ERR(("A: A(%d) no final interrupt from 4BRI adapter",
+ IoAdapter->ANum))
+ }
+ IoAdapter->a.ReadyInt = 0;
+
+ /*
+ Stop and reset adapter
+ */
+ IoAdapter->stop(IoAdapter);
+
+ return (0);
+}
diff --git a/drivers/isdn/hardware/eicon/os_4bri.h b/drivers/isdn/hardware/eicon/os_4bri.h
new file mode 100644
index 0000000..665f0af
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/os_4bri.h
@@ -0,0 +1,8 @@
+/* $Id: os_4bri.h,v 1.1.2.2 2001/02/08 12:25:44 armin Exp $ */
+
+#ifndef __DIVA_OS_4_BRI_H__
+#define __DIVA_OS_4_BRI_H__
+
+int diva_4bri_init_card(diva_os_xdi_adapter_t * a);
+
+#endif
diff --git a/drivers/isdn/hardware/eicon/os_bri.c b/drivers/isdn/hardware/eicon/os_bri.c
new file mode 100644
index 0000000..f31bba5
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/os_bri.c
@@ -0,0 +1,814 @@
+/* $Id: os_bri.c,v 1.21 2004/03/21 17:26:01 armin Exp $ */
+
+#include "platform.h"
+#include "debuglib.h"
+#include "cardtype.h"
+#include "pc.h"
+#include "pr_pc.h"
+#include "di_defs.h"
+#include "dsp_defs.h"
+#include "di.h"
+#include "io.h"
+
+#include "xdi_msg.h"
+#include "xdi_adapter.h"
+#include "os_bri.h"
+#include "diva_pci.h"
+#include "mi_pc.h"
+#include "pc_maint.h"
+#include "dsrv_bri.h"
+
+/*
+** IMPORTS
+*/
+extern void prepare_maestra_functions(PISDN_ADAPTER IoAdapter);
+extern void diva_xdi_display_adapter_features(int card);
+extern int diva_card_read_xlog(diva_os_xdi_adapter_t * a);
+
+/*
+** LOCALS
+*/
+static int bri_bar_length[3] = {
+ 0x80,
+ 0x80,
+ 0x20
+};
+static int diva_bri_cleanup_adapter(diva_os_xdi_adapter_t * a);
+static dword diva_bri_get_serial_number(diva_os_xdi_adapter_t * a);
+static int diva_bri_cmd_card_proc(struct _diva_os_xdi_adapter *a,
+ diva_xdi_um_cfg_cmd_t * cmd, int length);
+static int diva_bri_reregister_io(diva_os_xdi_adapter_t * a);
+static int diva_bri_reset_adapter(PISDN_ADAPTER IoAdapter);
+static int diva_bri_write_sdram_block(PISDN_ADAPTER IoAdapter,
+ dword address,
+ const byte * data, dword length);
+static int diva_bri_start_adapter(PISDN_ADAPTER IoAdapter,
+ dword start_address, dword features);
+static int diva_bri_stop_adapter(diva_os_xdi_adapter_t * a);
+
+static void diva_bri_set_addresses(diva_os_xdi_adapter_t * a)
+{
+ a->resources.pci.mem_type_id[MEM_TYPE_RAM] = 0;
+ a->resources.pci.mem_type_id[MEM_TYPE_CFG] = 1;
+ a->resources.pci.mem_type_id[MEM_TYPE_ADDRESS] = 2;
+ a->resources.pci.mem_type_id[MEM_TYPE_RESET] = 1;
+ a->resources.pci.mem_type_id[MEM_TYPE_PORT] = 2;
+ a->resources.pci.mem_type_id[MEM_TYPE_CTLREG] = 2;
+
+ a->xdi_adapter.ram = a->resources.pci.addr[0];
+ a->xdi_adapter.cfg = a->resources.pci.addr[1];
+ a->xdi_adapter.Address = a->resources.pci.addr[2];
+
+ a->xdi_adapter.reset = a->xdi_adapter.cfg;
+ a->xdi_adapter.port = a->xdi_adapter.Address;
+
+ a->xdi_adapter.ctlReg = a->xdi_adapter.port + M_PCI_RESET;
+
+ a->xdi_adapter.reset += 0x4C; /* PLX 9050 !! */
+}
+
+/*
+** BAR0 - MEM Addr - 0x80 - NOT USED
+** BAR1 - I/O Addr - 0x80
+** BAR2 - I/O Addr - 0x20
+*/
+int diva_bri_init_card(diva_os_xdi_adapter_t * a)
+{
+ int bar;
+ dword bar2 = 0, bar2_length = 0xffffffff;
+ word cmd = 0, cmd_org;
+ byte Bus, Slot;
+ void *hdev;
+ byte __iomem *p;
+
+ /*
+ Set properties
+ */
+ a->xdi_adapter.Properties = CardProperties[a->CardOrdinal];
+ DBG_LOG(("Load %s", a->xdi_adapter.Properties.Name))
+
+ /*
+ Get resources
+ */
+ for (bar = 0; bar < 3; bar++) {
+ a->resources.pci.bar[bar] =
+ divasa_get_pci_bar(a->resources.pci.bus,
+ a->resources.pci.func, bar,
+ a->resources.pci.hdev);
+ if (!a->resources.pci.bar[bar]) {
+ DBG_ERR(("A: can't get BAR[%d]", bar))
+ return (-1);
+ }
+ }
+
+ a->resources.pci.irq =
+ (byte) divasa_get_pci_irq(a->resources.pci.bus,
+ a->resources.pci.func,
+ a->resources.pci.hdev);
+ if (!a->resources.pci.irq) {
+ DBG_ERR(("A: invalid irq"));
+ return (-1);
+ }
+
+ /*
+ Get length of I/O bar 2 - it is different by older
+ EEPROM version
+ */
+ Bus = a->resources.pci.bus;
+ Slot = a->resources.pci.func;
+ hdev = a->resources.pci.hdev;
+
+ /*
+ Get plain original values of the BAR2 CDM registers
+ */
+ PCIread(Bus, Slot, 0x18, &bar2, sizeof(bar2), hdev);
+ PCIread(Bus, Slot, 0x04, &cmd_org, sizeof(cmd_org), hdev);
+ /*
+ Disable device and get BAR2 length
+ */
+ PCIwrite(Bus, Slot, 0x04, &cmd, sizeof(cmd), hdev);
+ PCIwrite(Bus, Slot, 0x18, &bar2_length, sizeof(bar2_length), hdev);
+ PCIread(Bus, Slot, 0x18, &bar2_length, sizeof(bar2_length), hdev);
+ /*
+ Restore BAR2 and CMD registers
+ */
+ PCIwrite(Bus, Slot, 0x18, &bar2, sizeof(bar2), hdev);
+ PCIwrite(Bus, Slot, 0x04, &cmd_org, sizeof(cmd_org), hdev);
+
+ /*
+ Calculate BAR2 length
+ */
+ bar2_length = (~(bar2_length & ~7)) + 1;
+ DBG_LOG(("BAR[2] length=%lx", bar2_length))
+
+ /*
+ Map and register resources
+ */
+ if (!(a->resources.pci.addr[0] =
+ divasa_remap_pci_bar(a, 0, a->resources.pci.bar[0],
+ bri_bar_length[0]))) {
+ DBG_ERR(("A: BRI, can't map BAR[0]"))
+ diva_bri_cleanup_adapter(a);
+ return (-1);
+ }
+
+ sprintf(&a->port_name[0], "BRI %02x:%02x",
+ a->resources.pci.bus, a->resources.pci.func);
+
+ if (diva_os_register_io_port(a, 1, a->resources.pci.bar[1],
+ bri_bar_length[1], &a->port_name[0], 1)) {
+ DBG_ERR(("A: BRI, can't register BAR[1]"))
+ diva_bri_cleanup_adapter(a);
+ return (-1);
+ }
+ a->resources.pci.addr[1] = (void *) (unsigned long) a->resources.pci.bar[1];
+ a->resources.pci.length[1] = bri_bar_length[1];
+
+ if (diva_os_register_io_port(a, 1, a->resources.pci.bar[2],
+ bar2_length, &a->port_name[0], 2)) {
+ DBG_ERR(("A: BRI, can't register BAR[2]"))
+ diva_bri_cleanup_adapter(a);
+ return (-1);
+ }
+ a->resources.pci.addr[2] = (void *) (unsigned long) a->resources.pci.bar[2];
+ a->resources.pci.length[2] = bar2_length;
+
+ /*
+ Set all memory areas
+ */
+ diva_bri_set_addresses(a);
+
+ /*
+ Get Serial Number
+ */
+ a->xdi_adapter.serialNo = diva_bri_get_serial_number(a);
+
+ /*
+ Register I/O ports with correct name now
+ */
+ if (diva_bri_reregister_io(a)) {
+ diva_bri_cleanup_adapter(a);
+ return (-1);
+ }
+
+ /*
+ Initialize OS dependent objects
+ */
+ if (diva_os_initialize_spin_lock
+ (&a->xdi_adapter.isr_spin_lock, "isr")) {
+ diva_bri_cleanup_adapter(a);
+ return (-1);
+ }
+ if (diva_os_initialize_spin_lock
+ (&a->xdi_adapter.data_spin_lock, "data")) {
+ diva_bri_cleanup_adapter(a);
+ return (-1);
+ }
+
+ strcpy(a->xdi_adapter.req_soft_isr.dpc_thread_name, "kdivasbrid");
+
+ if (diva_os_initialize_soft_isr(&a->xdi_adapter.req_soft_isr,
+ DIDpcRoutine, &a->xdi_adapter)) {
+ diva_bri_cleanup_adapter(a);
+ return (-1);
+ }
+ /*
+ Do not initialize second DPC - only one thread will be created
+ */
+ a->xdi_adapter.isr_soft_isr.object = a->xdi_adapter.req_soft_isr.object;
+
+ /*
+ Create entity table
+ */
+ a->xdi_adapter.Channels = CardProperties[a->CardOrdinal].Channels;
+ a->xdi_adapter.e_max = CardProperties[a->CardOrdinal].E_info;
+ a->xdi_adapter.e_tbl = diva_os_malloc(0, a->xdi_adapter.e_max * sizeof(E_INFO));
+ if (!a->xdi_adapter.e_tbl) {
+ diva_bri_cleanup_adapter(a);
+ return (-1);
+ }
+ memset(a->xdi_adapter.e_tbl, 0x00, a->xdi_adapter.e_max * sizeof(E_INFO));
+
+ /*
+ Set up interface
+ */
+ a->xdi_adapter.a.io = &a->xdi_adapter;
+ a->xdi_adapter.DIRequest = request;
+ a->interface.cleanup_adapter_proc = diva_bri_cleanup_adapter;
+ a->interface.cmd_proc = diva_bri_cmd_card_proc;
+
+ p = DIVA_OS_MEM_ATTACH_RESET(&a->xdi_adapter);
+ outpp(p, 0x41);
+ DIVA_OS_MEM_DETACH_RESET(&a->xdi_adapter, p);
+
+ prepare_maestra_functions(&a->xdi_adapter);
+
+ a->dsp_mask = 0x00000003;
+
+ /*
+ Set IRQ handler
+ */
+ a->xdi_adapter.irq_info.irq_nr = a->resources.pci.irq;
+ sprintf(a->xdi_adapter.irq_info.irq_name, "DIVA BRI %ld",
+ (long) a->xdi_adapter.serialNo);
+ if (diva_os_register_irq(a, a->xdi_adapter.irq_info.irq_nr,
+ a->xdi_adapter.irq_info.irq_name)) {
+ diva_bri_cleanup_adapter(a);
+ return (-1);
+ }
+ a->xdi_adapter.irq_info.registered = 1;
+
+ diva_log_info("%s IRQ:%d SerNo:%d", a->xdi_adapter.Properties.Name,
+ a->resources.pci.irq, a->xdi_adapter.serialNo);
+
+ return (0);
+}
+
+
+static int diva_bri_cleanup_adapter(diva_os_xdi_adapter_t * a)
+{
+ int i;
+
+ if (a->xdi_adapter.Initialized) {
+ diva_bri_stop_adapter(a);
+ }
+
+ /*
+ Remove ISR Handler
+ */
+ if (a->xdi_adapter.irq_info.registered) {
+ diva_os_remove_irq(a, a->xdi_adapter.irq_info.irq_nr);
+ }
+ a->xdi_adapter.irq_info.registered = 0;
+
+ if (a->resources.pci.addr[0] && a->resources.pci.bar[0]) {
+ divasa_unmap_pci_bar(a->resources.pci.addr[0]);
+ a->resources.pci.addr[0] = NULL;
+ a->resources.pci.bar[0] = 0;
+ }
+
+ for (i = 1; i < 3; i++) {
+ if (a->resources.pci.addr[i] && a->resources.pci.bar[i]) {
+ diva_os_register_io_port(a, 0,
+ a->resources.pci.bar[i],
+ a->resources.pci.
+ length[i],
+ &a->port_name[0], i);
+ a->resources.pci.addr[i] = NULL;
+ a->resources.pci.bar[i] = 0;
+ }
+ }
+
+ /*
+ Free OS objects
+ */
+ diva_os_cancel_soft_isr(&a->xdi_adapter.req_soft_isr);
+ diva_os_cancel_soft_isr(&a->xdi_adapter.isr_soft_isr);
+
+ diva_os_remove_soft_isr(&a->xdi_adapter.req_soft_isr);
+ a->xdi_adapter.isr_soft_isr.object = NULL;
+
+ diva_os_destroy_spin_lock(&a->xdi_adapter.isr_spin_lock, "rm");
+ diva_os_destroy_spin_lock(&a->xdi_adapter.data_spin_lock, "rm");
+
+ /*
+ Free memory
+ */
+ if (a->xdi_adapter.e_tbl) {
+ diva_os_free(0, a->xdi_adapter.e_tbl);
+ a->xdi_adapter.e_tbl = NULL;
+ }
+
+ return (0);
+}
+
+void diva_os_prepare_maestra_functions(PISDN_ADAPTER IoAdapter)
+{
+}
+
+/*
+** Get serial number
+*/
+static dword diva_bri_get_serial_number(diva_os_xdi_adapter_t * a)
+{
+ dword serNo = 0;
+ byte __iomem *confIO;
+ word serHi, serLo;
+ word __iomem *confMem;
+
+ confIO = DIVA_OS_MEM_ATTACH_CFG(&a->xdi_adapter);
+ serHi = (word) (inppw(&confIO[0x22]) & 0x0FFF);
+ serLo = (word) (inppw(&confIO[0x26]) & 0x0FFF);
+ serNo = ((dword) serHi << 16) | (dword) serLo;
+ DIVA_OS_MEM_DETACH_CFG(&a->xdi_adapter, confIO);
+
+ if ((serNo == 0) || (serNo == 0xFFFFFFFF)) {
+ DBG_FTL(("W: BRI use BAR[0] to get card serial number"))
+
+ confMem = (word __iomem *)DIVA_OS_MEM_ATTACH_RAM(&a->xdi_adapter);
+ serHi = (word) (READ_WORD(&confMem[0x11]) & 0x0FFF);
+ serLo = (word) (READ_WORD(&confMem[0x13]) & 0x0FFF);
+ serNo = (((dword) serHi) << 16) | ((dword) serLo);
+ DIVA_OS_MEM_DETACH_RAM(&a->xdi_adapter, confMem);
+ }
+
+ DBG_LOG(("Serial Number=%ld", serNo))
+
+ return (serNo);
+}
+
+/*
+** Unregister I/O and register it with new name,
+** based on Serial Number
+*/
+static int diva_bri_reregister_io(diva_os_xdi_adapter_t * a)
+{
+ int i;
+
+ for (i = 1; i < 3; i++) {
+ diva_os_register_io_port(a, 0, a->resources.pci.bar[i],
+ a->resources.pci.length[i],
+ &a->port_name[0], i);
+ a->resources.pci.addr[i] = NULL;
+ }
+
+ sprintf(a->port_name, "DIVA BRI %ld",
+ (long) a->xdi_adapter.serialNo);
+
+ for (i = 1; i < 3; i++) {
+ if (diva_os_register_io_port(a, 1, a->resources.pci.bar[i],
+ a->resources.pci.length[i],
+ &a->port_name[0], i)) {
+ DBG_ERR(("A: failed to reregister BAR[%d]", i))
+ return (-1);
+ }
+ a->resources.pci.addr[i] =
+ (void *) (unsigned long) a->resources.pci.bar[i];
+ }
+
+ return (0);
+}
+
+/*
+** Process command from user mode
+*/
+static int
+diva_bri_cmd_card_proc(struct _diva_os_xdi_adapter *a,
+ diva_xdi_um_cfg_cmd_t * cmd, int length)
+{
+ int ret = -1;
+
+ if (cmd->adapter != a->controller) {
+ DBG_ERR(("A: pri_cmd, invalid controller=%d != %d",
+ cmd->adapter, a->controller))
+ return (-1);
+ }
+
+ switch (cmd->command) {
+ case DIVA_XDI_UM_CMD_GET_CARD_ORDINAL:
+ a->xdi_mbox.data_length = sizeof(dword);
+ a->xdi_mbox.data =
+ diva_os_malloc(0, a->xdi_mbox.data_length);
+ if (a->xdi_mbox.data) {
+ *(dword *) a->xdi_mbox.data =
+ (dword) a->CardOrdinal;
+ a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
+ ret = 0;
+ }
+ break;
+
+ case DIVA_XDI_UM_CMD_GET_SERIAL_NR:
+ a->xdi_mbox.data_length = sizeof(dword);
+ a->xdi_mbox.data =
+ diva_os_malloc(0, a->xdi_mbox.data_length);
+ if (a->xdi_mbox.data) {
+ *(dword *) a->xdi_mbox.data =
+ (dword) a->xdi_adapter.serialNo;
+ a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
+ ret = 0;
+ }
+ break;
+
+ case DIVA_XDI_UM_CMD_GET_PCI_HW_CONFIG:
+ a->xdi_mbox.data_length = sizeof(dword) * 9;
+ a->xdi_mbox.data =
+ diva_os_malloc(0, a->xdi_mbox.data_length);
+ if (a->xdi_mbox.data) {
+ int i;
+ dword *data = (dword *) a->xdi_mbox.data;
+
+ for (i = 0; i < 8; i++) {
+ *data++ = a->resources.pci.bar[i];
+ }
+ *data++ = (dword) a->resources.pci.irq;
+ a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
+ ret = 0;
+ }
+ break;
+
+ case DIVA_XDI_UM_CMD_GET_CARD_STATE:
+ a->xdi_mbox.data_length = sizeof(dword);
+ a->xdi_mbox.data =
+ diva_os_malloc(0, a->xdi_mbox.data_length);
+ if (a->xdi_mbox.data) {
+ dword *data = (dword *) a->xdi_mbox.data;
+ if (!a->xdi_adapter.port) {
+ *data = 3;
+ } else if (a->xdi_adapter.trapped) {
+ *data = 2;
+ } else if (a->xdi_adapter.Initialized) {
+ *data = 1;
+ } else {
+ *data = 0;
+ }
+ a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
+ ret = 0;
+ }
+ break;
+
+ case DIVA_XDI_UM_CMD_RESET_ADAPTER:
+ ret = diva_bri_reset_adapter(&a->xdi_adapter);
+ break;
+
+ case DIVA_XDI_UM_CMD_WRITE_SDRAM_BLOCK:
+ ret = diva_bri_write_sdram_block(&a->xdi_adapter,
+ cmd->command_data.
+ write_sdram.offset,
+ (byte *) & cmd[1],
+ cmd->command_data.
+ write_sdram.length);
+ break;
+
+ case DIVA_XDI_UM_CMD_START_ADAPTER:
+ ret = diva_bri_start_adapter(&a->xdi_adapter,
+ cmd->command_data.start.
+ offset,
+ cmd->command_data.start.
+ features);
+ break;
+
+ case DIVA_XDI_UM_CMD_SET_PROTOCOL_FEATURES:
+ a->xdi_adapter.features =
+ cmd->command_data.features.features;
+ a->xdi_adapter.a.protocol_capabilities =
+ a->xdi_adapter.features;
+ DBG_TRC(
+ ("Set raw protocol features (%08x)",
+ a->xdi_adapter.features)) ret = 0;
+ break;
+
+ case DIVA_XDI_UM_CMD_STOP_ADAPTER:
+ ret = diva_bri_stop_adapter(a);
+ break;
+
+ case DIVA_XDI_UM_CMD_READ_XLOG_ENTRY:
+ ret = diva_card_read_xlog(a);
+ break;
+
+ default:
+ DBG_ERR(
+ ("A: A(%d) invalid cmd=%d", a->controller,
+ cmd->command))}
+
+ return (ret);
+}
+
+static int diva_bri_reset_adapter(PISDN_ADAPTER IoAdapter)
+{
+ byte __iomem *addrHi, *addrLo, *ioaddr;
+ dword i;
+ byte __iomem *Port;
+
+ if (!IoAdapter->port) {
+ return (-1);
+ }
+ if (IoAdapter->Initialized) {
+ DBG_ERR(("A: A(%d) can't reset BRI adapter - please stop first",
+ IoAdapter->ANum)) return (-1);
+ }
+ (*(IoAdapter->rstFnc)) (IoAdapter);
+ diva_os_wait(100);
+ Port = DIVA_OS_MEM_ATTACH_PORT(IoAdapter);
+ addrHi = Port +
+ ((IoAdapter->Properties.Bus == BUS_PCI) ? M_PCI_ADDRH : ADDRH);
+ addrLo = Port + ADDR;
+ ioaddr = Port + DATA;
+ /*
+ recover
+ */
+ outpp(addrHi, (byte) 0);
+ outppw(addrLo, (word) 0);
+ outppw(ioaddr, (word) 0);
+ /*
+ clear shared memory
+ */
+ outpp(addrHi,
+ (byte) (
+ (IoAdapter->MemoryBase + IoAdapter->MemorySize -
+ BRI_SHARED_RAM_SIZE) >> 16));
+ outppw(addrLo, 0);
+ for (i = 0; i < 0x8000; outppw(ioaddr, 0), ++i);
+ diva_os_wait(100);
+
+ /*
+ clear signature
+ */
+ outpp(addrHi,
+ (byte) (
+ (IoAdapter->MemoryBase + IoAdapter->MemorySize -
+ BRI_SHARED_RAM_SIZE) >> 16));
+ outppw(addrLo, 0x1e);
+ outpp(ioaddr, 0);
+ outpp(ioaddr, 0);
+
+ outpp(addrHi, (byte) 0);
+ outppw(addrLo, (word) 0);
+ outppw(ioaddr, (word) 0);
+
+ DIVA_OS_MEM_DETACH_PORT(IoAdapter, Port);
+
+ /*
+ Forget all outstanding entities
+ */
+ IoAdapter->e_count = 0;
+ if (IoAdapter->e_tbl) {
+ memset(IoAdapter->e_tbl, 0x00,
+ IoAdapter->e_max * sizeof(E_INFO));
+ }
+ IoAdapter->head = 0;
+ IoAdapter->tail = 0;
+ IoAdapter->assign = 0;
+ IoAdapter->trapped = 0;
+
+ memset(&IoAdapter->a.IdTable[0], 0x00,
+ sizeof(IoAdapter->a.IdTable));
+ memset(&IoAdapter->a.IdTypeTable[0], 0x00,
+ sizeof(IoAdapter->a.IdTypeTable));
+ memset(&IoAdapter->a.FlowControlIdTable[0], 0x00,
+ sizeof(IoAdapter->a.FlowControlIdTable));
+ memset(&IoAdapter->a.FlowControlSkipTable[0], 0x00,
+ sizeof(IoAdapter->a.FlowControlSkipTable));
+ memset(&IoAdapter->a.misc_flags_table[0], 0x00,
+ sizeof(IoAdapter->a.misc_flags_table));
+ memset(&IoAdapter->a.rx_stream[0], 0x00,
+ sizeof(IoAdapter->a.rx_stream));
+ memset(&IoAdapter->a.tx_stream[0], 0x00,
+ sizeof(IoAdapter->a.tx_stream));
+ memset(&IoAdapter->a.tx_pos[0], 0x00, sizeof(IoAdapter->a.tx_pos));
+ memset(&IoAdapter->a.rx_pos[0], 0x00, sizeof(IoAdapter->a.rx_pos));
+
+ return (0);
+}
+
+static int
+diva_bri_write_sdram_block(PISDN_ADAPTER IoAdapter,
+ dword address, const byte * data, dword length)
+{
+ byte __iomem *addrHi, *addrLo, *ioaddr;
+ byte __iomem *Port;
+
+ if (!IoAdapter->port) {
+ return (-1);
+ }
+
+ Port = DIVA_OS_MEM_ATTACH_PORT(IoAdapter);
+ addrHi = Port +
+ ((IoAdapter->Properties.Bus == BUS_PCI) ? M_PCI_ADDRH : ADDRH);
+ addrLo = Port + ADDR;
+ ioaddr = Port + DATA;
+
+ while (length--) {
+ outpp(addrHi, (word) (address >> 16));
+ outppw(addrLo, (word) (address & 0x0000ffff));
+ outpp(ioaddr, *data++);
+ address++;
+ }
+
+ DIVA_OS_MEM_DETACH_PORT(IoAdapter, Port);
+ return (0);
+}
+
+static int
+diva_bri_start_adapter(PISDN_ADAPTER IoAdapter,
+ dword start_address, dword features)
+{
+ byte __iomem *Port;
+ dword i, test;
+ byte __iomem *addrHi, *addrLo, *ioaddr;
+ int started = 0;
+ ADAPTER *a = &IoAdapter->a;
+
+ if (IoAdapter->Initialized) {
+ DBG_ERR(
+ ("A: A(%d) bri_start_adapter, adapter already running",
+ IoAdapter->ANum)) return (-1);
+ }
+ if (!IoAdapter->port) {
+ DBG_ERR(("A: A(%d) bri_start_adapter, adapter not mapped",
+ IoAdapter->ANum)) return (-1);
+ }
+
+ sprintf(IoAdapter->Name, "A(%d)", (int) IoAdapter->ANum);
+ DBG_LOG(("A(%d) start BRI", IoAdapter->ANum))
+
+ Port = DIVA_OS_MEM_ATTACH_PORT(IoAdapter);
+ addrHi = Port +
+ ((IoAdapter->Properties.Bus == BUS_PCI) ? M_PCI_ADDRH : ADDRH);
+ addrLo = Port + ADDR;
+ ioaddr = Port + DATA;
+
+ outpp(addrHi,
+ (byte) (
+ (IoAdapter->MemoryBase + IoAdapter->MemorySize -
+ BRI_SHARED_RAM_SIZE) >> 16));
+ outppw(addrLo, 0x1e);
+ outppw(ioaddr, 0x00);
+ DIVA_OS_MEM_DETACH_PORT(IoAdapter, Port);
+
+ /*
+ start the protocol code
+ */
+ Port = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
+ outpp(Port, 0x08);
+ DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, Port);
+
+ Port = DIVA_OS_MEM_ATTACH_PORT(IoAdapter);
+ addrHi = Port +
+ ((IoAdapter->Properties.Bus == BUS_PCI) ? M_PCI_ADDRH : ADDRH);
+ addrLo = Port + ADDR;
+ ioaddr = Port + DATA;
+ /*
+ wait for signature (max. 3 seconds)
+ */
+ for (i = 0; i < 300; ++i) {
+ diva_os_wait(10);
+ outpp(addrHi,
+ (byte) (
+ (IoAdapter->MemoryBase +
+ IoAdapter->MemorySize -
+ BRI_SHARED_RAM_SIZE) >> 16));
+ outppw(addrLo, 0x1e);
+ test = (dword) inppw(ioaddr);
+ if (test == 0x4447) {
+ DBG_LOG(
+ ("Protocol startup time %d.%02d seconds",
+ (i / 100), (i % 100)))
+ started = 1;
+ break;
+ }
+ }
+ DIVA_OS_MEM_DETACH_PORT(IoAdapter, Port);
+
+ if (!started) {
+ DBG_FTL(("A: A(%d) %s: Adapter selftest failed 0x%04X",
+ IoAdapter->ANum, IoAdapter->Properties.Name,
+ test))
+ (*(IoAdapter->trapFnc)) (IoAdapter);
+ return (-1);
+ }
+
+ IoAdapter->Initialized = 1;
+
+ /*
+ Check Interrupt
+ */
+ IoAdapter->IrqCount = 0;
+ a->ReadyInt = 1;
+
+ if (IoAdapter->reset) {
+ Port = DIVA_OS_MEM_ATTACH_RESET(IoAdapter);
+ outpp(Port, 0x41);
+ DIVA_OS_MEM_DETACH_RESET(IoAdapter, Port);
+ }
+
+ a->ram_out(a, &PR_RAM->ReadyInt, 1);
+ for (i = 0; ((!IoAdapter->IrqCount) && (i < 100)); i++) {
+ diva_os_wait(10);
+ }
+ if (!IoAdapter->IrqCount) {
+ DBG_ERR(
+ ("A: A(%d) interrupt test failed",
+ IoAdapter->ANum))
+ IoAdapter->Initialized = 0;
+ IoAdapter->stop(IoAdapter);
+ return (-1);
+ }
+
+ IoAdapter->Properties.Features = (word) features;
+ diva_xdi_display_adapter_features(IoAdapter->ANum);
+ DBG_LOG(("A(%d) BRI adapter successfull started", IoAdapter->ANum))
+ /*
+ Register with DIDD
+ */
+ diva_xdi_didd_register_adapter(IoAdapter->ANum);
+
+ return (0);
+}
+
+static void diva_bri_clear_interrupts(diva_os_xdi_adapter_t * a)
+{
+ PISDN_ADAPTER IoAdapter = &a->xdi_adapter;
+
+ /*
+ clear any pending interrupt
+ */
+ IoAdapter->disIrq(IoAdapter);
+
+ IoAdapter->tst_irq(&IoAdapter->a);
+ IoAdapter->clr_irq(&IoAdapter->a);
+ IoAdapter->tst_irq(&IoAdapter->a);
+
+ /*
+ kill pending dpcs
+ */
+ diva_os_cancel_soft_isr(&IoAdapter->req_soft_isr);
+ diva_os_cancel_soft_isr(&IoAdapter->isr_soft_isr);
+}
+
+/*
+** Stop card
+*/
+static int diva_bri_stop_adapter(diva_os_xdi_adapter_t * a)
+{
+ PISDN_ADAPTER IoAdapter = &a->xdi_adapter;
+ int i = 100;
+
+ if (!IoAdapter->port) {
+ return (-1);
+ }
+ if (!IoAdapter->Initialized) {
+ DBG_ERR(("A: A(%d) can't stop BRI adapter - not running",
+ IoAdapter->ANum))
+ return (-1); /* nothing to stop */
+ }
+ IoAdapter->Initialized = 0;
+
+ /*
+ Disconnect Adapter from DIDD
+ */
+ diva_xdi_didd_remove_adapter(IoAdapter->ANum);
+
+ /*
+ Stop interrupts
+ */
+ a->clear_interrupts_proc = diva_bri_clear_interrupts;
+ IoAdapter->a.ReadyInt = 1;
+ IoAdapter->a.ram_inc(&IoAdapter->a, &PR_RAM->ReadyInt);
+ do {
+ diva_os_sleep(10);
+ } while (i-- && a->clear_interrupts_proc);
+ if (a->clear_interrupts_proc) {
+ diva_bri_clear_interrupts(a);
+ a->clear_interrupts_proc = NULL;
+ DBG_ERR(("A: A(%d) no final interrupt from BRI adapter",
+ IoAdapter->ANum))
+ }
+ IoAdapter->a.ReadyInt = 0;
+
+ /*
+ Stop and reset adapter
+ */
+ IoAdapter->stop(IoAdapter);
+
+ return (0);
+}
diff --git a/drivers/isdn/hardware/eicon/os_bri.h b/drivers/isdn/hardware/eicon/os_bri.h
new file mode 100644
index 0000000..a54f0ce
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/os_bri.h
@@ -0,0 +1,8 @@
+/* $Id: os_bri.h,v 1.1.2.2 2001/02/08 12:25:44 armin Exp $ */
+
+#ifndef __DIVA_OS_BRI_REV_1_H__
+#define __DIVA_OS_BRI_REV_1_H__
+
+int diva_bri_init_card(diva_os_xdi_adapter_t * a);
+
+#endif
diff --git a/drivers/isdn/hardware/eicon/os_capi.h b/drivers/isdn/hardware/eicon/os_capi.h
new file mode 100644
index 0000000..726f915
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/os_capi.h
@@ -0,0 +1,21 @@
+/* $Id: os_capi.h,v 1.7 2003/04/12 21:40:49 schindler Exp $
+ *
+ * ISDN interface module for Eicon active cards DIVA.
+ * CAPI Interface OS include files
+ *
+ * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
+ * Copyright 2000-2003 Cytronics & Melware (info@melware.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#ifndef __OS_CAPI_H__
+#define __OS_CAPI_H__
+
+#include <linux/capi.h>
+#include <linux/kernelcapi.h>
+#include <linux/isdn/capiutil.h>
+#include <linux/isdn/capilli.h>
+
+#endif /* __OS_CAPI_H__ */
diff --git a/drivers/isdn/hardware/eicon/os_pri.c b/drivers/isdn/hardware/eicon/os_pri.c
new file mode 100644
index 0000000..9033565
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/os_pri.c
@@ -0,0 +1,1052 @@
+/* $Id: os_pri.c,v 1.32 2004/03/21 17:26:01 armin Exp $ */
+
+#include "platform.h"
+#include "debuglib.h"
+#include "cardtype.h"
+#include "pc.h"
+#include "pr_pc.h"
+#include "di_defs.h"
+#include "dsp_defs.h"
+#include "di.h"
+#include "io.h"
+
+#include "xdi_msg.h"
+#include "xdi_adapter.h"
+#include "os_pri.h"
+#include "diva_pci.h"
+#include "mi_pc.h"
+#include "pc_maint.h"
+#include "dsp_tst.h"
+#include "diva_dma.h"
+#include "dsrv_pri.h"
+
+/* --------------------------------------------------------------------------
+ OS Dependent part of XDI driver for DIVA PRI Adapter
+
+ DSP detection/validation by Anthony Booth (Eicon Networks, www.eicon.com)
+-------------------------------------------------------------------------- */
+
+#define DIVA_PRI_NO_PCI_BIOS_WORKAROUND 1
+
+extern int diva_card_read_xlog(diva_os_xdi_adapter_t * a);
+
+/*
+** IMPORTS
+*/
+extern void prepare_pri_functions(PISDN_ADAPTER IoAdapter);
+extern void prepare_pri2_functions(PISDN_ADAPTER IoAdapter);
+extern void diva_xdi_display_adapter_features(int card);
+
+static int diva_pri_cleanup_adapter(diva_os_xdi_adapter_t * a);
+static int diva_pri_cmd_card_proc(struct _diva_os_xdi_adapter *a,
+ diva_xdi_um_cfg_cmd_t * cmd, int length);
+static int pri_get_serial_number(diva_os_xdi_adapter_t * a);
+static int diva_pri_stop_adapter(diva_os_xdi_adapter_t * a);
+static dword diva_pri_detect_dsps(diva_os_xdi_adapter_t * a);
+
+/*
+** Check card revision
+*/
+static int pri_is_rev_2_card(int card_ordinal)
+{
+ switch (card_ordinal) {
+ case CARDTYPE_DIVASRV_P_30M_V2_PCI:
+ case CARDTYPE_DIVASRV_VOICE_P_30M_V2_PCI:
+ return (1);
+ }
+ return (0);
+}
+
+static void diva_pri_set_addresses(diva_os_xdi_adapter_t * a)
+{
+ a->resources.pci.mem_type_id[MEM_TYPE_ADDRESS] = 0;
+ a->resources.pci.mem_type_id[MEM_TYPE_CONTROL] = 2;
+ a->resources.pci.mem_type_id[MEM_TYPE_CONFIG] = 4;
+ a->resources.pci.mem_type_id[MEM_TYPE_RAM] = 0;
+ a->resources.pci.mem_type_id[MEM_TYPE_RESET] = 2;
+ a->resources.pci.mem_type_id[MEM_TYPE_CFG] = 4;
+ a->resources.pci.mem_type_id[MEM_TYPE_PROM] = 3;
+
+ a->xdi_adapter.Address = a->resources.pci.addr[0];
+ a->xdi_adapter.Control = a->resources.pci.addr[2];
+ a->xdi_adapter.Config = a->resources.pci.addr[4];
+
+ a->xdi_adapter.ram = a->resources.pci.addr[0];
+ a->xdi_adapter.ram += MP_SHARED_RAM_OFFSET;
+
+ a->xdi_adapter.reset = a->resources.pci.addr[2];
+ a->xdi_adapter.reset += MP_RESET;
+
+ a->xdi_adapter.cfg = a->resources.pci.addr[4];
+ a->xdi_adapter.cfg += MP_IRQ_RESET;
+
+ a->xdi_adapter.sdram_bar = a->resources.pci.bar[0];
+
+ a->xdi_adapter.prom = a->resources.pci.addr[3];
+}
+
+/*
+** BAR0 - SDRAM, MP_MEMORY_SIZE, MP2_MEMORY_SIZE by Rev.2
+** BAR1 - DEVICES, 0x1000
+** BAR2 - CONTROL (REG), 0x2000
+** BAR3 - FLASH (REG), 0x8000
+** BAR4 - CONFIG (CFG), 0x1000
+*/
+int diva_pri_init_card(diva_os_xdi_adapter_t * a)
+{
+ int bar = 0;
+ int pri_rev_2;
+ unsigned long bar_length[5] = {
+ MP_MEMORY_SIZE,
+ 0x1000,
+ 0x2000,
+ 0x8000,
+ 0x1000
+ };
+
+ pri_rev_2 = pri_is_rev_2_card(a->CardOrdinal);
+
+ if (pri_rev_2) {
+ bar_length[0] = MP2_MEMORY_SIZE;
+ }
+ /*
+ Set properties
+ */
+ a->xdi_adapter.Properties = CardProperties[a->CardOrdinal];
+ DBG_LOG(("Load %s", a->xdi_adapter.Properties.Name))
+
+ /*
+ First initialization step: get and check hardware resoures.
+ Do not map resources and do not acecess card at this step
+ */
+ for (bar = 0; bar < 5; bar++) {
+ a->resources.pci.bar[bar] =
+ divasa_get_pci_bar(a->resources.pci.bus,
+ a->resources.pci.func, bar,
+ a->resources.pci.hdev);
+ if (!a->resources.pci.bar[bar]
+ || (a->resources.pci.bar[bar] == 0xFFFFFFF0)) {
+ DBG_ERR(("A: invalid bar[%d]=%08x", bar,
+ a->resources.pci.bar[bar]))
+ return (-1);
+ }
+ }
+ a->resources.pci.irq =
+ (byte) divasa_get_pci_irq(a->resources.pci.bus,
+ a->resources.pci.func,
+ a->resources.pci.hdev);
+ if (!a->resources.pci.irq) {
+ DBG_ERR(("A: invalid irq"));
+ return (-1);
+ }
+
+ /*
+ Map all BAR's
+ */
+ for (bar = 0; bar < 5; bar++) {
+ a->resources.pci.addr[bar] =
+ divasa_remap_pci_bar(a, bar, a->resources.pci.bar[bar],
+ bar_length[bar]);
+ if (!a->resources.pci.addr[bar]) {
+ DBG_ERR(("A: A(%d), can't map bar[%d]",
+ a->controller, bar))
+ diva_pri_cleanup_adapter(a);
+ return (-1);
+ }
+ }
+
+ /*
+ Set all memory areas
+ */
+ diva_pri_set_addresses(a);
+
+ /*
+ Get Serial Number of this adapter
+ */
+ if (pri_get_serial_number(a)) {
+ dword serNo;
+ serNo = a->resources.pci.bar[1] & 0xffff0000;
+ serNo |= ((dword) a->resources.pci.bus) << 8;
+ serNo += (a->resources.pci.func + a->controller + 1);
+ a->xdi_adapter.serialNo = serNo & ~0xFF000000;
+ DBG_ERR(("A: A(%d) can't get Serial Number, generated serNo=%ld",
+ a->controller, a->xdi_adapter.serialNo))
+ }
+
+
+ /*
+ Initialize os objects
+ */
+ if (diva_os_initialize_spin_lock(&a->xdi_adapter.isr_spin_lock, "isr")) {
+ diva_pri_cleanup_adapter(a);
+ return (-1);
+ }
+ if (diva_os_initialize_spin_lock
+ (&a->xdi_adapter.data_spin_lock, "data")) {
+ diva_pri_cleanup_adapter(a);
+ return (-1);
+ }
+
+ strcpy(a->xdi_adapter.req_soft_isr.dpc_thread_name, "kdivasprid");
+
+ if (diva_os_initialize_soft_isr(&a->xdi_adapter.req_soft_isr,
+ DIDpcRoutine, &a->xdi_adapter)) {
+ diva_pri_cleanup_adapter(a);
+ return (-1);
+ }
+
+ /*
+ Do not initialize second DPC - only one thread will be created
+ */
+ a->xdi_adapter.isr_soft_isr.object =
+ a->xdi_adapter.req_soft_isr.object;
+
+ /*
+ Next step of card initialization:
+ set up all interface pointers
+ */
+ a->xdi_adapter.Channels = CardProperties[a->CardOrdinal].Channels;
+ a->xdi_adapter.e_max = CardProperties[a->CardOrdinal].E_info;
+
+ a->xdi_adapter.e_tbl =
+ diva_os_malloc(0, a->xdi_adapter.e_max * sizeof(E_INFO));
+ if (!a->xdi_adapter.e_tbl) {
+ diva_pri_cleanup_adapter(a);
+ return (-1);
+ }
+ memset(a->xdi_adapter.e_tbl, 0x00, a->xdi_adapter.e_max * sizeof(E_INFO));
+
+ a->xdi_adapter.a.io = &a->xdi_adapter;
+ a->xdi_adapter.DIRequest = request;
+ a->interface.cleanup_adapter_proc = diva_pri_cleanup_adapter;
+ a->interface.cmd_proc = diva_pri_cmd_card_proc;
+
+ if (pri_rev_2) {
+ prepare_pri2_functions(&a->xdi_adapter);
+ } else {
+ prepare_pri_functions(&a->xdi_adapter);
+ }
+
+ a->dsp_mask = diva_pri_detect_dsps(a);
+
+ /*
+ Allocate DMA map
+ */
+ if (pri_rev_2) {
+ diva_init_dma_map(a->resources.pci.hdev,
+ (struct _diva_dma_map_entry **) &a->xdi_adapter.dma_map, 32);
+ }
+
+ /*
+ Set IRQ handler
+ */
+ a->xdi_adapter.irq_info.irq_nr = a->resources.pci.irq;
+ sprintf(a->xdi_adapter.irq_info.irq_name,
+ "DIVA PRI %ld", (long) a->xdi_adapter.serialNo);
+
+ if (diva_os_register_irq(a, a->xdi_adapter.irq_info.irq_nr,
+ a->xdi_adapter.irq_info.irq_name)) {
+ diva_pri_cleanup_adapter(a);
+ return (-1);
+ }
+ a->xdi_adapter.irq_info.registered = 1;
+
+ diva_log_info("%s IRQ:%d SerNo:%d", a->xdi_adapter.Properties.Name,
+ a->resources.pci.irq, a->xdi_adapter.serialNo);
+
+ return (0);
+}
+
+static int diva_pri_cleanup_adapter(diva_os_xdi_adapter_t * a)
+{
+ int bar = 0;
+
+ /*
+ Stop Adapter if adapter is running
+ */
+ if (a->xdi_adapter.Initialized) {
+ diva_pri_stop_adapter(a);
+ }
+
+ /*
+ Remove ISR Handler
+ */
+ if (a->xdi_adapter.irq_info.registered) {
+ diva_os_remove_irq(a, a->xdi_adapter.irq_info.irq_nr);
+ }
+ a->xdi_adapter.irq_info.registered = 0;
+
+ /*
+ Step 1: unmap all BAR's, if any was mapped
+ */
+ for (bar = 0; bar < 5; bar++) {
+ if (a->resources.pci.bar[bar]
+ && a->resources.pci.addr[bar]) {
+ divasa_unmap_pci_bar(a->resources.pci.addr[bar]);
+ a->resources.pci.bar[bar] = 0;
+ a->resources.pci.addr[bar] = NULL;
+ }
+ }
+
+ /*
+ Free OS objects
+ */
+ diva_os_cancel_soft_isr(&a->xdi_adapter.isr_soft_isr);
+ diva_os_cancel_soft_isr(&a->xdi_adapter.req_soft_isr);
+
+ diva_os_remove_soft_isr(&a->xdi_adapter.req_soft_isr);
+ a->xdi_adapter.isr_soft_isr.object = NULL;
+
+ diva_os_destroy_spin_lock(&a->xdi_adapter.isr_spin_lock, "rm");
+ diva_os_destroy_spin_lock(&a->xdi_adapter.data_spin_lock, "rm");
+
+ /*
+ Free memory accupied by XDI adapter
+ */
+ if (a->xdi_adapter.e_tbl) {
+ diva_os_free(0, a->xdi_adapter.e_tbl);
+ a->xdi_adapter.e_tbl = NULL;
+ }
+ a->xdi_adapter.Channels = 0;
+ a->xdi_adapter.e_max = 0;
+
+
+ /*
+ Free adapter DMA map
+ */
+ diva_free_dma_map(a->resources.pci.hdev,
+ (struct _diva_dma_map_entry *) a->xdi_adapter.
+ dma_map);
+ a->xdi_adapter.dma_map = NULL;
+
+
+ /*
+ Detach this adapter from debug driver
+ */
+
+ return (0);
+}
+
+/*
+** Activate On Board Boot Loader
+*/
+static int diva_pri_reset_adapter(PISDN_ADAPTER IoAdapter)
+{
+ dword i;
+ struct mp_load __iomem *boot;
+
+ if (!IoAdapter->Address || !IoAdapter->reset) {
+ return (-1);
+ }
+ if (IoAdapter->Initialized) {
+ DBG_ERR(("A: A(%d) can't reset PRI adapter - please stop first",
+ IoAdapter->ANum))
+ return (-1);
+ }
+
+ boot = (struct mp_load __iomem *) DIVA_OS_MEM_ATTACH_ADDRESS(IoAdapter);
+ WRITE_DWORD(&boot->err, 0);
+ DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, boot);
+
+ IoAdapter->rstFnc(IoAdapter);
+
+ diva_os_wait(10);
+
+ boot = (struct mp_load __iomem *) DIVA_OS_MEM_ATTACH_ADDRESS(IoAdapter);
+ i = READ_DWORD(&boot->live);
+
+ diva_os_wait(10);
+ if (i == READ_DWORD(&boot->live)) {
+ DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, boot);
+ DBG_ERR(("A: A(%d) CPU on PRI %ld is not alive!",
+ IoAdapter->ANum, IoAdapter->serialNo))
+ return (-1);
+ }
+ if (READ_DWORD(&boot->err)) {
+ DBG_ERR(("A: A(%d) PRI %ld Board Selftest failed, error=%08lx",
+ IoAdapter->ANum, IoAdapter->serialNo,
+ READ_DWORD(&boot->err)))
+ DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, boot);
+ return (-1);
+ }
+ DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, boot);
+
+ /*
+ Forget all outstanding entities
+ */
+ IoAdapter->e_count = 0;
+ if (IoAdapter->e_tbl) {
+ memset(IoAdapter->e_tbl, 0x00,
+ IoAdapter->e_max * sizeof(E_INFO));
+ }
+ IoAdapter->head = 0;
+ IoAdapter->tail = 0;
+ IoAdapter->assign = 0;
+ IoAdapter->trapped = 0;
+
+ memset(&IoAdapter->a.IdTable[0], 0x00,
+ sizeof(IoAdapter->a.IdTable));
+ memset(&IoAdapter->a.IdTypeTable[0], 0x00,
+ sizeof(IoAdapter->a.IdTypeTable));
+ memset(&IoAdapter->a.FlowControlIdTable[0], 0x00,
+ sizeof(IoAdapter->a.FlowControlIdTable));
+ memset(&IoAdapter->a.FlowControlSkipTable[0], 0x00,
+ sizeof(IoAdapter->a.FlowControlSkipTable));
+ memset(&IoAdapter->a.misc_flags_table[0], 0x00,
+ sizeof(IoAdapter->a.misc_flags_table));
+ memset(&IoAdapter->a.rx_stream[0], 0x00,
+ sizeof(IoAdapter->a.rx_stream));
+ memset(&IoAdapter->a.tx_stream[0], 0x00,
+ sizeof(IoAdapter->a.tx_stream));
+ memset(&IoAdapter->a.tx_pos[0], 0x00, sizeof(IoAdapter->a.tx_pos));
+ memset(&IoAdapter->a.rx_pos[0], 0x00, sizeof(IoAdapter->a.rx_pos));
+
+ return (0);
+}
+
+static int
+diva_pri_write_sdram_block(PISDN_ADAPTER IoAdapter,
+ dword address,
+ const byte * data, dword length, dword limit)
+{
+ byte __iomem *p = DIVA_OS_MEM_ATTACH_ADDRESS(IoAdapter);
+ byte __iomem *mem = p;
+
+ if (((address + length) >= limit) || !mem) {
+ DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, p);
+ DBG_ERR(("A: A(%d) write PRI address=0x%08lx",
+ IoAdapter->ANum, address + length))
+ return (-1);
+ }
+ mem += address;
+
+ /* memcpy_toio(), maybe? */
+ while (length--) {
+ WRITE_BYTE(mem++, *data++);
+ }
+
+ DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, p);
+ return (0);
+}
+
+static int
+diva_pri_start_adapter(PISDN_ADAPTER IoAdapter,
+ dword start_address, dword features)
+{
+ dword i;
+ int started = 0;
+ byte __iomem *p;
+ struct mp_load __iomem *boot = (struct mp_load __iomem *) DIVA_OS_MEM_ATTACH_ADDRESS(IoAdapter);
+ ADAPTER *a = &IoAdapter->a;
+
+ if (IoAdapter->Initialized) {
+ DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, boot);
+ DBG_ERR(("A: A(%d) pri_start_adapter, adapter already running",
+ IoAdapter->ANum))
+ return (-1);
+ }
+ if (!boot) {
+ DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, boot);
+ DBG_ERR(("A: PRI %ld can't start, adapter not mapped",
+ IoAdapter->serialNo))
+ return (-1);
+ }
+
+ sprintf(IoAdapter->Name, "A(%d)", (int) IoAdapter->ANum);
+ DBG_LOG(("A(%d) start PRI at 0x%08lx", IoAdapter->ANum,
+ start_address))
+
+ WRITE_DWORD(&boot->addr, start_address);
+ WRITE_DWORD(&boot->cmd, 3);
+
+ for (i = 0; i < 300; ++i) {
+ diva_os_wait(10);
+ if ((READ_DWORD(&boot->signature) >> 16) == 0x4447) {
+ DBG_LOG(("A(%d) Protocol startup time %d.%02d seconds",
+ IoAdapter->ANum, (i / 100), (i % 100)))
+ started = 1;
+ break;
+ }
+ }
+
+ if (!started) {
+ byte __iomem *p = (byte __iomem *)boot;
+ dword TrapId;
+ dword debug;
+ TrapId = READ_DWORD(&p[0x80]);
+ debug = READ_DWORD(&p[0x1c]);
+ DBG_ERR(("A(%d) Adapter start failed 0x%08lx, TrapId=%08lx, debug=%08lx",
+ IoAdapter->ANum, READ_DWORD(&boot->signature),
+ TrapId, debug))
+ DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, boot);
+ if (IoAdapter->trapFnc) {
+ (*(IoAdapter->trapFnc)) (IoAdapter);
+ }
+ IoAdapter->stop(IoAdapter);
+ return (-1);
+ }
+ DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, boot);
+
+ IoAdapter->Initialized = true;
+
+ /*
+ Check Interrupt
+ */
+ IoAdapter->IrqCount = 0;
+ p = DIVA_OS_MEM_ATTACH_CFG(IoAdapter);
+ WRITE_DWORD(p, (dword) ~ 0x03E00000);
+ DIVA_OS_MEM_DETACH_CFG(IoAdapter, p);
+ a->ReadyInt = 1;
+ a->ram_out(a, &PR_RAM->ReadyInt, 1);
+
+ for (i = 100; !IoAdapter->IrqCount && (i-- > 0); diva_os_wait(10));
+
+ if (!IoAdapter->IrqCount) {
+ DBG_ERR(("A: A(%d) interrupt test failed",
+ IoAdapter->ANum))
+ IoAdapter->Initialized = false;
+ IoAdapter->stop(IoAdapter);
+ return (-1);
+ }
+
+ IoAdapter->Properties.Features = (word) features;
+
+ diva_xdi_display_adapter_features(IoAdapter->ANum);
+
+ DBG_LOG(("A(%d) PRI adapter successfull started", IoAdapter->ANum))
+ /*
+ Register with DIDD
+ */
+ diva_xdi_didd_register_adapter(IoAdapter->ANum);
+
+ return (0);
+}
+
+static void diva_pri_clear_interrupts(diva_os_xdi_adapter_t * a)
+{
+ PISDN_ADAPTER IoAdapter = &a->xdi_adapter;
+
+ /*
+ clear any pending interrupt
+ */
+ IoAdapter->disIrq(IoAdapter);
+
+ IoAdapter->tst_irq(&IoAdapter->a);
+ IoAdapter->clr_irq(&IoAdapter->a);
+ IoAdapter->tst_irq(&IoAdapter->a);
+
+ /*
+ kill pending dpcs
+ */
+ diva_os_cancel_soft_isr(&IoAdapter->req_soft_isr);
+ diva_os_cancel_soft_isr(&IoAdapter->isr_soft_isr);
+}
+
+/*
+** Stop Adapter, but do not unmap/unregister - adapter
+** will be restarted later
+*/
+static int diva_pri_stop_adapter(diva_os_xdi_adapter_t * a)
+{
+ PISDN_ADAPTER IoAdapter = &a->xdi_adapter;
+ int i = 100;
+
+ if (!IoAdapter->ram) {
+ return (-1);
+ }
+ if (!IoAdapter->Initialized) {
+ DBG_ERR(("A: A(%d) can't stop PRI adapter - not running",
+ IoAdapter->ANum))
+ return (-1); /* nothing to stop */
+ }
+ IoAdapter->Initialized = 0;
+
+ /*
+ Disconnect Adapter from DIDD
+ */
+ diva_xdi_didd_remove_adapter(IoAdapter->ANum);
+
+ /*
+ Stop interrupts
+ */
+ a->clear_interrupts_proc = diva_pri_clear_interrupts;
+ IoAdapter->a.ReadyInt = 1;
+ IoAdapter->a.ram_inc(&IoAdapter->a, &PR_RAM->ReadyInt);
+ do {
+ diva_os_sleep(10);
+ } while (i-- && a->clear_interrupts_proc);
+
+ if (a->clear_interrupts_proc) {
+ diva_pri_clear_interrupts(a);
+ a->clear_interrupts_proc = NULL;
+ DBG_ERR(("A: A(%d) no final interrupt from PRI adapter",
+ IoAdapter->ANum))
+ }
+ IoAdapter->a.ReadyInt = 0;
+
+ /*
+ Stop and reset adapter
+ */
+ IoAdapter->stop(IoAdapter);
+
+ return (0);
+}
+
+/*
+** Process commands form configuration/download framework and from
+** user mode
+**
+** return 0 on success
+*/
+static int
+diva_pri_cmd_card_proc(struct _diva_os_xdi_adapter *a,
+ diva_xdi_um_cfg_cmd_t * cmd, int length)
+{
+ int ret = -1;
+
+ if (cmd->adapter != a->controller) {
+ DBG_ERR(("A: pri_cmd, invalid controller=%d != %d",
+ cmd->adapter, a->controller))
+ return (-1);
+ }
+
+ switch (cmd->command) {
+ case DIVA_XDI_UM_CMD_GET_CARD_ORDINAL:
+ a->xdi_mbox.data_length = sizeof(dword);
+ a->xdi_mbox.data =
+ diva_os_malloc(0, a->xdi_mbox.data_length);
+ if (a->xdi_mbox.data) {
+ *(dword *) a->xdi_mbox.data =
+ (dword) a->CardOrdinal;
+ a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
+ ret = 0;
+ }
+ break;
+
+ case DIVA_XDI_UM_CMD_GET_SERIAL_NR:
+ a->xdi_mbox.data_length = sizeof(dword);
+ a->xdi_mbox.data =
+ diva_os_malloc(0, a->xdi_mbox.data_length);
+ if (a->xdi_mbox.data) {
+ *(dword *) a->xdi_mbox.data =
+ (dword) a->xdi_adapter.serialNo;
+ a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
+ ret = 0;
+ }
+ break;
+
+ case DIVA_XDI_UM_CMD_GET_PCI_HW_CONFIG:
+ a->xdi_mbox.data_length = sizeof(dword) * 9;
+ a->xdi_mbox.data =
+ diva_os_malloc(0, a->xdi_mbox.data_length);
+ if (a->xdi_mbox.data) {
+ int i;
+ dword *data = (dword *) a->xdi_mbox.data;
+
+ for (i = 0; i < 8; i++) {
+ *data++ = a->resources.pci.bar[i];
+ }
+ *data++ = (dword) a->resources.pci.irq;
+ a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
+ ret = 0;
+ }
+ break;
+
+ case DIVA_XDI_UM_CMD_RESET_ADAPTER:
+ ret = diva_pri_reset_adapter(&a->xdi_adapter);
+ break;
+
+ case DIVA_XDI_UM_CMD_WRITE_SDRAM_BLOCK:
+ ret = diva_pri_write_sdram_block(&a->xdi_adapter,
+ cmd->command_data.
+ write_sdram.offset,
+ (byte *) & cmd[1],
+ cmd->command_data.
+ write_sdram.length,
+ pri_is_rev_2_card(a->
+ CardOrdinal)
+ ? MP2_MEMORY_SIZE :
+ MP_MEMORY_SIZE);
+ break;
+
+ case DIVA_XDI_UM_CMD_STOP_ADAPTER:
+ ret = diva_pri_stop_adapter(a);
+ break;
+
+ case DIVA_XDI_UM_CMD_START_ADAPTER:
+ ret = diva_pri_start_adapter(&a->xdi_adapter,
+ cmd->command_data.start.
+ offset,
+ cmd->command_data.start.
+ features);
+ break;
+
+ case DIVA_XDI_UM_CMD_SET_PROTOCOL_FEATURES:
+ a->xdi_adapter.features =
+ cmd->command_data.features.features;
+ a->xdi_adapter.a.protocol_capabilities =
+ a->xdi_adapter.features;
+ DBG_TRC(("Set raw protocol features (%08x)",
+ a->xdi_adapter.features))
+ ret = 0;
+ break;
+
+ case DIVA_XDI_UM_CMD_GET_CARD_STATE:
+ a->xdi_mbox.data_length = sizeof(dword);
+ a->xdi_mbox.data =
+ diva_os_malloc(0, a->xdi_mbox.data_length);
+ if (a->xdi_mbox.data) {
+ dword *data = (dword *) a->xdi_mbox.data;
+ if (!a->xdi_adapter.ram ||
+ !a->xdi_adapter.reset ||
+ !a->xdi_adapter.cfg) {
+ *data = 3;
+ } else if (a->xdi_adapter.trapped) {
+ *data = 2;
+ } else if (a->xdi_adapter.Initialized) {
+ *data = 1;
+ } else {
+ *data = 0;
+ }
+ a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
+ ret = 0;
+ }
+ break;
+
+ case DIVA_XDI_UM_CMD_READ_XLOG_ENTRY:
+ ret = diva_card_read_xlog(a);
+ break;
+
+ case DIVA_XDI_UM_CMD_READ_SDRAM:
+ if (a->xdi_adapter.Address) {
+ if (
+ (a->xdi_mbox.data_length =
+ cmd->command_data.read_sdram.length)) {
+ if (
+ (a->xdi_mbox.data_length +
+ cmd->command_data.read_sdram.offset) <
+ a->xdi_adapter.MemorySize) {
+ a->xdi_mbox.data =
+ diva_os_malloc(0,
+ a->xdi_mbox.
+ data_length);
+ if (a->xdi_mbox.data) {
+ byte __iomem *p = DIVA_OS_MEM_ATTACH_ADDRESS(&a->xdi_adapter);
+ byte __iomem *src = p;
+ byte *dst = a->xdi_mbox.data;
+ dword len = a->xdi_mbox.data_length;
+
+ src += cmd->command_data.read_sdram.offset;
+
+ while (len--) {
+ *dst++ = READ_BYTE(src++);
+ }
+ a->xdi_mbox.status = DIVA_XDI_MBOX_BUSY;
+ DIVA_OS_MEM_DETACH_ADDRESS(&a->xdi_adapter, p);
+ ret = 0;
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ DBG_ERR(("A: A(%d) invalid cmd=%d", a->controller,
+ cmd->command))
+ }
+
+ return (ret);
+}
+
+/*
+** Get Serial Number
+*/
+static int pri_get_serial_number(diva_os_xdi_adapter_t * a)
+{
+ byte data[64];
+ int i;
+ dword len = sizeof(data);
+ volatile byte __iomem *config;
+ volatile byte __iomem *flash;
+ byte c;
+
+/*
+ * First set some GT6401x config registers before accessing the BOOT-ROM
+ */
+ config = DIVA_OS_MEM_ATTACH_CONFIG(&a->xdi_adapter);
+ c = READ_BYTE(&config[0xc3c]);
+ if (!(c & 0x08)) {
+ WRITE_BYTE(&config[0xc3c], c); /* Base Address enable register */
+ }
+ WRITE_BYTE(&config[LOW_BOOTCS_DREG], 0x00);
+ WRITE_BYTE(&config[HI_BOOTCS_DREG], 0xFF);
+ DIVA_OS_MEM_DETACH_CONFIG(&a->xdi_adapter, config);
+/*
+ * Read only the last 64 bytes of manufacturing data
+ */
+ memset(data, '\0', len);
+ flash = DIVA_OS_MEM_ATTACH_PROM(&a->xdi_adapter);
+ for (i = 0; i < len; i++) {
+ data[i] = READ_BYTE(&flash[0x8000 - len + i]);
+ }
+ DIVA_OS_MEM_DETACH_PROM(&a->xdi_adapter, flash);
+
+ config = DIVA_OS_MEM_ATTACH_CONFIG(&a->xdi_adapter);
+ WRITE_BYTE(&config[LOW_BOOTCS_DREG], 0xFC); /* Disable FLASH EPROM access */
+ WRITE_BYTE(&config[HI_BOOTCS_DREG], 0xFF);
+ DIVA_OS_MEM_DETACH_CONFIG(&a->xdi_adapter, config);
+
+ if (memcmp(&data[48], "DIVAserverPR", 12)) {
+#if !defined(DIVA_PRI_NO_PCI_BIOS_WORKAROUND) /* { */
+ word cmd = 0, cmd_org;
+ void *addr;
+ dword addr1, addr3, addr4;
+ byte Bus, Slot;
+ void *hdev;
+ addr4 = a->resources.pci.bar[4];
+ addr3 = a->resources.pci.bar[3]; /* flash */
+ addr1 = a->resources.pci.bar[1]; /* unused */
+
+ DBG_ERR(("A: apply Compaq BIOS workaround"))
+ DBG_LOG(("%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+ data[0], data[1], data[2], data[3],
+ data[4], data[5], data[6], data[7]))
+
+ Bus = a->resources.pci.bus;
+ Slot = a->resources.pci.func;
+ hdev = a->resources.pci.hdev;
+ PCIread(Bus, Slot, 0x04, &cmd_org, sizeof(cmd_org), hdev);
+ PCIwrite(Bus, Slot, 0x04, &cmd, sizeof(cmd), hdev);
+
+ PCIwrite(Bus, Slot, 0x14, &addr4, sizeof(addr4), hdev);
+ PCIwrite(Bus, Slot, 0x20, &addr1, sizeof(addr1), hdev);
+
+ PCIwrite(Bus, Slot, 0x04, &cmd_org, sizeof(cmd_org), hdev);
+
+ addr = a->resources.pci.addr[1];
+ a->resources.pci.addr[1] = a->resources.pci.addr[4];
+ a->resources.pci.addr[4] = addr;
+
+ addr1 = a->resources.pci.bar[1];
+ a->resources.pci.bar[1] = a->resources.pci.bar[4];
+ a->resources.pci.bar[4] = addr1;
+
+ /*
+ Try to read Flash again
+ */
+ len = sizeof(data);
+
+ config = DIVA_OS_MEM_ATTACH_CONFIG(&a->xdi_adapter);
+ if (!(config[0xc3c] & 0x08)) {
+ config[0xc3c] |= 0x08; /* Base Address enable register */
+ }
+ config[LOW_BOOTCS_DREG] = 0x00;
+ config[HI_BOOTCS_DREG] = 0xFF;
+ DIVA_OS_MEM_DETACH_CONFIG(&a->xdi_adapter, config);
+
+ memset(data, '\0', len);
+ flash = DIVA_OS_MEM_ATTACH_PROM(&a->xdi_adapter);
+ for (i = 0; i < len; i++) {
+ data[i] = flash[0x8000 - len + i];
+ }
+ DIVA_OS_MEM_ATTACH_PROM(&a->xdi_adapter, flash);
+ config = DIVA_OS_MEM_ATTACH_CONFIG(&a->xdi_adapter);
+ config[LOW_BOOTCS_DREG] = 0xFC;
+ config[HI_BOOTCS_DREG] = 0xFF;
+ DIVA_OS_MEM_DETACH_CONFIG(&a->xdi_adapter, config);
+
+ if (memcmp(&data[48], "DIVAserverPR", 12)) {
+ DBG_ERR(("A: failed to read serial number"))
+ DBG_LOG(("%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+ data[0], data[1], data[2], data[3],
+ data[4], data[5], data[6], data[7]))
+ return (-1);
+ }
+#else /* } { */
+ DBG_ERR(("A: failed to read DIVA signature word"))
+ DBG_LOG(("%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+ data[0], data[1], data[2], data[3],
+ data[4], data[5], data[6], data[7]))
+ DBG_LOG(("%02x:%02x:%02x:%02x", data[47], data[46],
+ data[45], data[44]))
+#endif /* } */
+ }
+
+ a->xdi_adapter.serialNo =
+ (data[47] << 24) | (data[46] << 16) | (data[45] << 8) |
+ data[44];
+ if (!a->xdi_adapter.serialNo
+ || (a->xdi_adapter.serialNo == 0xffffffff)) {
+ a->xdi_adapter.serialNo = 0;
+ DBG_ERR(("A: failed to read serial number"))
+ return (-1);
+ }
+
+ DBG_LOG(("Serial No. : %ld", a->xdi_adapter.serialNo))
+ DBG_TRC(("Board Revision : %d.%02d", (int) data[41],
+ (int) data[40]))
+ DBG_TRC(("PLD revision : %d.%02d", (int) data[33],
+ (int) data[32]))
+ DBG_TRC(("Boot loader version : %d.%02d", (int) data[37],
+ (int) data[36]))
+
+ DBG_TRC(("Manufacturing Date : %d/%02d/%02d (yyyy/mm/dd)",
+ (int) ((data[28] > 90) ? 1900 : 2000) +
+ (int) data[28], (int) data[29], (int) data[30]))
+
+ return (0);
+}
+
+void diva_os_prepare_pri2_functions(PISDN_ADAPTER IoAdapter)
+{
+}
+
+void diva_os_prepare_pri_functions(PISDN_ADAPTER IoAdapter)
+{
+}
+
+/*
+** Checks presence of DSP on board
+*/
+static int
+dsp_check_presence(volatile byte __iomem * addr, volatile byte __iomem * data, int dsp)
+{
+ word pattern;
+
+ WRITE_WORD(addr, 0x4000);
+ WRITE_WORD(data, DSP_SIGNATURE_PROBE_WORD);
+
+ WRITE_WORD(addr, 0x4000);
+ pattern = READ_WORD(data);
+
+ if (pattern != DSP_SIGNATURE_PROBE_WORD) {
+ DBG_TRC(("W: DSP[%d] %04x(is) != %04x(should)",
+ dsp, pattern, DSP_SIGNATURE_PROBE_WORD))
+ return (-1);
+ }
+
+ WRITE_WORD(addr, 0x4000);
+ WRITE_WORD(data, ~DSP_SIGNATURE_PROBE_WORD);
+
+ WRITE_WORD(addr, 0x4000);
+ pattern = READ_WORD(data);
+
+ if (pattern != (word) ~ DSP_SIGNATURE_PROBE_WORD) {
+ DBG_ERR(("A: DSP[%d] %04x(is) != %04x(should)",
+ dsp, pattern, (word) ~ DSP_SIGNATURE_PROBE_WORD))
+ return (-2);
+ }
+
+ DBG_TRC(("DSP[%d] present", dsp))
+
+ return (0);
+}
+
+
+/*
+** Check if DSP's are present and operating
+** Information about detected DSP's is returned as bit mask
+** Bit 0 - DSP1
+** ...
+** ...
+** ...
+** Bit 29 - DSP30
+*/
+static dword diva_pri_detect_dsps(diva_os_xdi_adapter_t * a)
+{
+ byte __iomem *base;
+ byte __iomem *p;
+ dword ret = 0;
+ dword row_offset[7] = {
+ 0x00000000,
+ 0x00000800, /* 1 - ROW 1 */
+ 0x00000840, /* 2 - ROW 2 */
+ 0x00001000, /* 3 - ROW 3 */
+ 0x00001040, /* 4 - ROW 4 */
+ 0x00000000 /* 5 - ROW 0 */
+ };
+
+ byte __iomem *dsp_addr_port;
+ byte __iomem *dsp_data_port;
+ byte row_state;
+ int dsp_row = 0, dsp_index, dsp_num;
+
+ if (!a->xdi_adapter.Control || !a->xdi_adapter.reset) {
+ return (0);
+ }
+
+ p = DIVA_OS_MEM_ATTACH_RESET(&a->xdi_adapter);
+ WRITE_BYTE(p, _MP_RISC_RESET | _MP_DSP_RESET);
+ DIVA_OS_MEM_DETACH_RESET(&a->xdi_adapter, p);
+ diva_os_wait(5);
+
+ base = DIVA_OS_MEM_ATTACH_CONTROL(&a->xdi_adapter);
+
+ for (dsp_num = 0; dsp_num < 30; dsp_num++) {
+ dsp_row = dsp_num / 7 + 1;
+ dsp_index = dsp_num % 7;
+
+ dsp_data_port = base;
+ dsp_addr_port = base;
+
+ dsp_data_port += row_offset[dsp_row];
+ dsp_addr_port += row_offset[dsp_row];
+
+ dsp_data_port += (dsp_index * 8);
+ dsp_addr_port += (dsp_index * 8) + 0x80;
+
+ if (!dsp_check_presence
+ (dsp_addr_port, dsp_data_port, dsp_num + 1)) {
+ ret |= (1 << dsp_num);
+ }
+ }
+ DIVA_OS_MEM_DETACH_CONTROL(&a->xdi_adapter, base);
+
+ p = DIVA_OS_MEM_ATTACH_RESET(&a->xdi_adapter);
+ WRITE_BYTE(p, _MP_RISC_RESET | _MP_LED1 | _MP_LED2);
+ DIVA_OS_MEM_DETACH_RESET(&a->xdi_adapter, p);
+ diva_os_wait(5);
+
+ /*
+ Verify modules
+ */
+ for (dsp_row = 0; dsp_row < 4; dsp_row++) {
+ row_state = ((ret >> (dsp_row * 7)) & 0x7F);
+ if (row_state && (row_state != 0x7F)) {
+ for (dsp_index = 0; dsp_index < 7; dsp_index++) {
+ if (!(row_state & (1 << dsp_index))) {
+ DBG_ERR(("A: MODULE[%d]-DSP[%d] failed",
+ dsp_row + 1,
+ dsp_index + 1))
+ }
+ }
+ }
+ }
+
+ if (!(ret & 0x10000000)) {
+ DBG_ERR(("A: ON BOARD-DSP[1] failed"))
+ }
+ if (!(ret & 0x20000000)) {
+ DBG_ERR(("A: ON BOARD-DSP[2] failed"))
+ }
+
+ /*
+ Print module population now
+ */
+ DBG_LOG(("+-----------------------+"))
+ DBG_LOG(("| DSP MODULE POPULATION |"))
+ DBG_LOG(("+-----------------------+"))
+ DBG_LOG(("| 1 | 2 | 3 | 4 |"))
+ DBG_LOG(("+-----------------------+"))
+ DBG_LOG(("| %s | %s | %s | %s |",
+ ((ret >> (0 * 7)) & 0x7F) ? "Y" : "N",
+ ((ret >> (1 * 7)) & 0x7F) ? "Y" : "N",
+ ((ret >> (2 * 7)) & 0x7F) ? "Y" : "N",
+ ((ret >> (3 * 7)) & 0x7F) ? "Y" : "N"))
+ DBG_LOG(("+-----------------------+"))
+
+ DBG_LOG(("DSP's(present-absent):%08x-%08x", ret,
+ ~ret & 0x3fffffff))
+
+ return (ret);
+}
diff --git a/drivers/isdn/hardware/eicon/os_pri.h b/drivers/isdn/hardware/eicon/os_pri.h
new file mode 100644
index 0000000..a7c42f9
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/os_pri.h
@@ -0,0 +1,8 @@
+/* $Id: os_pri.h,v 1.1.2.2 2001/02/08 12:25:44 armin Exp $ */
+
+#ifndef __DIVA_OS_PRI_REV_1_H__
+#define __DIVA_OS_PRI_REV_1_H__
+
+int diva_pri_init_card(diva_os_xdi_adapter_t * a);
+
+#endif
diff --git a/drivers/isdn/hardware/eicon/pc.h b/drivers/isdn/hardware/eicon/pc.h
new file mode 100644
index 0000000..1c69457
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/pc.h
@@ -0,0 +1,738 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+#ifndef PC_H_INCLUDED /* { */
+#define PC_H_INCLUDED
+/*------------------------------------------------------------------*/
+/* buffer definition */
+/*------------------------------------------------------------------*/
+typedef struct {
+ word length; /* length of data/parameter field */
+ byte P[270]; /* data/parameter field */
+} PBUFFER;
+/*------------------------------------------------------------------*/
+/* dual port ram structure */
+/*------------------------------------------------------------------*/
+struct dual
+{
+ byte Req; /* request register */
+ byte ReqId; /* request task/entity identification */
+ byte Rc; /* return code register */
+ byte RcId; /* return code task/entity identification */
+ byte Ind; /* Indication register */
+ byte IndId; /* Indication task/entity identification */
+ byte IMask; /* Interrupt Mask Flag */
+ byte RNR; /* Receiver Not Ready (set by PC) */
+ byte XLock; /* XBuffer locked Flag */
+ byte Int; /* ISDN-S interrupt */
+ byte ReqCh; /* Channel field for layer-3 Requests */
+ byte RcCh; /* Channel field for layer-3 Returncodes */
+ byte IndCh; /* Channel field for layer-3 Indications */
+ byte MInd; /* more data indication field */
+ word MLength; /* more data total packet length */
+ byte ReadyInt; /* request field for ready interrupt */
+ byte SWReg; /* Software register for special purposes */
+ byte Reserved[11]; /* reserved space */
+ byte InterfaceType; /* interface type 1=16K interface */
+ word Signature; /* ISDN-S adapter Signature (GD) */
+ PBUFFER XBuffer; /* Transmit Buffer */
+ PBUFFER RBuffer; /* Receive Buffer */
+};
+/*------------------------------------------------------------------*/
+/* SWReg Values (0 means no command) */
+/*------------------------------------------------------------------*/
+#define SWREG_DIE_WITH_LEDON 0x01
+#define SWREG_HALT_CPU 0x02 /* Push CPU into a while(1) loop */
+/*------------------------------------------------------------------*/
+/* Id Fields Coding */
+/*------------------------------------------------------------------*/
+#define ID_MASK 0xe0 /* Mask for the ID field */
+#define GL_ERR_ID 0x1f /* ID for error reporting on global requests*/
+#define DSIG_ID 0x00 /* ID for D-channel signaling */
+#define NL_ID 0x20 /* ID for network-layer access (B or D) */
+#define BLLC_ID 0x60 /* ID for B-channel link level access */
+#define TASK_ID 0x80 /* ID for dynamic user tasks */
+#define TIMER_ID 0xa0 /* ID for timer task */
+#define TEL_ID 0xc0 /* ID for telephone support */
+#define MAN_ID 0xe0 /* ID for management */
+/*------------------------------------------------------------------*/
+/* ASSIGN and REMOVE requests are the same for all entities */
+/*------------------------------------------------------------------*/
+#define ASSIGN 0x01
+#define UREMOVE 0xfe /* without return code */
+#define REMOVE 0xff
+/*------------------------------------------------------------------*/
+/* Timer Interrupt Task Interface */
+/*------------------------------------------------------------------*/
+#define ASSIGN_TIM 0x01
+#define REMOVE_TIM 0xff
+/*------------------------------------------------------------------*/
+/* dynamic user task interface */
+/*------------------------------------------------------------------*/
+#define ASSIGN_TSK 0x01
+#define REMOVE_TSK 0xff
+#define LOAD 0xf0
+#define RELOCATE 0xf1
+#define START 0xf2
+#define LOAD2 0xf3
+#define RELOCATE2 0xf4
+/*------------------------------------------------------------------*/
+/* dynamic user task messages */
+/*------------------------------------------------------------------*/
+#define TSK_B2 0x0000
+#define TSK_WAKEUP 0x2000
+#define TSK_TIMER 0x4000
+#define TSK_TSK 0x6000
+#define TSK_PC 0xe000
+/*------------------------------------------------------------------*/
+/* LL management primitives */
+/*------------------------------------------------------------------*/
+#define ASSIGN_LL 1 /* assign logical link */
+#define REMOVE_LL 0xff /* remove logical link */
+/*------------------------------------------------------------------*/
+/* LL service primitives */
+/*------------------------------------------------------------------*/
+#define LL_UDATA 1 /* link unit data request/indication */
+#define LL_ESTABLISH 2 /* link establish request/indication */
+#define LL_RELEASE 3 /* link release request/indication */
+#define LL_DATA 4 /* data request/indication */
+#define LL_LOCAL 5 /* switch to local operation (COM only) */
+#define LL_DATA_PEND 5 /* data pending indication (SDLC SHM only) */
+#define LL_REMOTE 6 /* switch to remote operation (COM only) */
+#define LL_TEST 8 /* link test request */
+#define LL_MDATA 9 /* more data request/indication */
+#define LL_BUDATA 10 /* broadcast unit data request/indication */
+#define LL_XID 12 /* XID command request/indication */
+#define LL_XID_R 13 /* XID response request/indication */
+/*------------------------------------------------------------------*/
+/* NL service primitives */
+/*------------------------------------------------------------------*/
+#define N_MDATA 1 /* more data to come REQ/IND */
+#define N_CONNECT 2 /* OSI N-CONNECT REQ/IND */
+#define N_CONNECT_ACK 3 /* OSI N-CONNECT CON/RES */
+#define N_DISC 4 /* OSI N-DISC REQ/IND */
+#define N_DISC_ACK 5 /* OSI N-DISC CON/RES */
+#define N_RESET 6 /* OSI N-RESET REQ/IND */
+#define N_RESET_ACK 7 /* OSI N-RESET CON/RES */
+#define N_DATA 8 /* OSI N-DATA REQ/IND */
+#define N_EDATA 9 /* OSI N-EXPEDITED DATA REQ/IND */
+#define N_UDATA 10 /* OSI D-UNIT-DATA REQ/IND */
+#define N_BDATA 11 /* BROADCAST-DATA REQ/IND */
+#define N_DATA_ACK 12 /* data ack ind for D-bit procedure */
+#define N_EDATA_ACK 13 /* data ack ind for INTERRUPT */
+#define N_XON 15 /* clear RNR state */
+#define N_COMBI_IND N_XON /* combined indication */
+#define N_Q_BIT 0x10 /* Q-bit for req/ind */
+#define N_M_BIT 0x20 /* M-bit for req/ind */
+#define N_D_BIT 0x40 /* D-bit for req/ind */
+/*------------------------------------------------------------------*/
+/* Signaling management primitives */
+/*------------------------------------------------------------------*/
+#define ASSIGN_SIG 1 /* assign signaling task */
+#define UREMOVE_SIG 0xfe /* remove signaling task without return code*/
+#define REMOVE_SIG 0xff /* remove signaling task */
+/*------------------------------------------------------------------*/
+/* Signaling service primitives */
+/*------------------------------------------------------------------*/
+#define CALL_REQ 1 /* call request */
+#define CALL_CON 1 /* call confirmation */
+#define CALL_IND 2 /* incoming call connected */
+#define LISTEN_REQ 2 /* listen request */
+#define HANGUP 3 /* hangup request/indication */
+#define SUSPEND 4 /* call suspend request/confirm */
+#define RESUME 5 /* call resume request/confirm */
+#define SUSPEND_REJ 6 /* suspend rejected indication */
+#define USER_DATA 8 /* user data for user to user signaling */
+#define CONGESTION 9 /* network congestion indication */
+#define INDICATE_REQ 10 /* request to indicate an incoming call */
+#define INDICATE_IND 10 /* indicates that there is an incoming call */
+#define CALL_RES 11 /* accept an incoming call */
+#define CALL_ALERT 12 /* send ALERT for incoming call */
+#define INFO_REQ 13 /* INFO request */
+#define INFO_IND 13 /* INFO indication */
+#define REJECT 14 /* reject an incoming call */
+#define RESOURCES 15 /* reserve B-Channel hardware resources */
+#define HW_CTRL 16 /* B-Channel hardware IOCTL req/ind */
+#define TEL_CTRL 16 /* Telephone control request/indication */
+#define STATUS_REQ 17 /* Request D-State (returned in INFO_IND) */
+#define FAC_REG_REQ 18 /* 1TR6 connection independent fac reg */
+#define FAC_REG_ACK 19 /* 1TR6 fac registration acknowledge */
+#define FAC_REG_REJ 20 /* 1TR6 fac registration reject */
+#define CALL_COMPLETE 21/* send a CALL_PROC for incoming call */
+#define SW_CTRL 22 /* extended software features */
+#define REGISTER_REQ 23 /* Q.931 connection independent reg req */
+#define REGISTER_IND 24 /* Q.931 connection independent reg ind */
+#define FACILITY_REQ 25 /* Q.931 connection independent fac req */
+#define FACILITY_IND 26 /* Q.931 connection independent fac ind */
+#define NCR_INFO_REQ 27 /* INFO_REQ with NULL CR */
+#define GCR_MIM_REQ 28 /* MANAGEMENT_INFO_REQ with global CR */
+#define SIG_CTRL 29 /* Control for Signalling Hardware */
+#define DSP_CTRL 30 /* Control for DSPs */
+#define LAW_REQ 31 /* Law config request for (returns info_i) */
+#define SPID_CTRL 32 /* Request/indication SPID related */
+#define NCR_FACILITY 33 /* Request/indication with NULL/DUMMY CR */
+#define CALL_HOLD 34 /* Request/indication to hold a CALL */
+#define CALL_RETRIEVE 35 /* Request/indication to retrieve a CALL */
+#define CALL_HOLD_ACK 36 /* OK of hold a CALL */
+#define CALL_RETRIEVE_ACK 37 /* OK of retrieve a CALL */
+#define CALL_HOLD_REJ 38 /* Reject of hold a CALL */
+#define CALL_RETRIEVE_REJ 39 /* Reject of retrieve a call */
+#define GCR_RESTART 40 /* Send/Receive Restart message */
+#define S_SERVICE 41 /* Send/Receive Supplementary Service */
+#define S_SERVICE_REJ 42 /* Reject Supplementary Service indication */
+#define S_SUPPORTED 43 /* Req/Ind to get Supported Services */
+#define STATUS_ENQ 44 /* Req to send the D-ch request if !state0 */
+#define CALL_GUARD 45 /* Req/Ind to use the FLAGS_CALL_OUTCHECK */
+#define CALL_GUARD_HP 46 /* Call Guard function to reject a call */
+#define CALL_GUARD_IF 47 /* Call Guard function, inform the appl */
+#define SSEXT_REQ 48 /* Supplem.Serv./QSIG specific request */
+#define SSEXT_IND 49 /* Supplem.Serv./QSIG specific indication */
+/* reserved commands for the US protocols */
+#define INT_3PTY_NIND 50 /* US specific indication */
+#define INT_CF_NIND 51 /* US specific indication */
+#define INT_3PTY_DROP 52 /* US specific indication */
+#define INT_MOVE_CONF 53 /* US specific indication */
+#define INT_MOVE_RC 54 /* US specific indication */
+#define INT_MOVE_FLIPPED_CONF 55 /* US specific indication */
+#define INT_X5NI_OK 56 /* internal transfer OK indication */
+#define INT_XDMS_START 57 /* internal transfer OK indication */
+#define INT_XDMS_STOP 58 /* internal transfer finish indication */
+#define INT_XDMS_STOP2 59 /* internal transfer send FA */
+#define INT_CUSTCONF_REJ 60 /* internal conference reject */
+#define INT_CUSTXFER 61 /* internal transfer request */
+#define INT_CUSTX_NIND 62 /* internal transfer ack */
+#define INT_CUSTXREJ_NIND 63 /* internal transfer rej */
+#define INT_X5NI_CF_XFER 64 /* internal transfer OK indication */
+#define VSWITCH_REQ 65 /* communication between protocol and */
+#define VSWITCH_IND 66 /* capifunctions for D-CH-switching */
+#define MWI_POLL 67 /* Message Waiting Status Request fkt */
+#define CALL_PEND_NOTIFY 68 /* notify capi to set new listen */
+#define DO_NOTHING 69 /* dont do somethin if you get this */
+#define INT_CT_REJ 70 /* ECT rejected internal command */
+#define CALL_HOLD_COMPLETE 71 /* In NT Mode indicate hold complete */
+#define CALL_RETRIEVE_COMPLETE 72 /* In NT Mode indicate retrieve complete */
+/*------------------------------------------------------------------*/
+/* management service primitives */
+/*------------------------------------------------------------------*/
+#define MAN_READ 2
+#define MAN_WRITE 3
+#define MAN_EXECUTE 4
+#define MAN_EVENT_ON 5
+#define MAN_EVENT_OFF 6
+#define MAN_LOCK 7
+#define MAN_UNLOCK 8
+#define MAN_INFO_IND 2
+#define MAN_EVENT_IND 3
+#define MAN_TRACE_IND 4
+#define MAN_COMBI_IND 9
+#define MAN_ESC 0x80
+/*------------------------------------------------------------------*/
+/* return code coding */
+/*------------------------------------------------------------------*/
+#define UNKNOWN_COMMAND 0x01 /* unknown command */
+#define WRONG_COMMAND 0x02 /* wrong command */
+#define WRONG_ID 0x03 /* unknown task/entity id */
+#define WRONG_CH 0x04 /* wrong task/entity id */
+#define UNKNOWN_IE 0x05 /* unknown information el. */
+#define WRONG_IE 0x06 /* wrong information el. */
+#define OUT_OF_RESOURCES 0x07 /* ISDN-S card out of res. */
+#define ISDN_GUARD_REJ 0x09 /* ISDN-Guard SuppServ rej */
+#define N_FLOW_CONTROL 0x10 /* Flow-Control, retry */
+#define ASSIGN_RC 0xe0 /* ASSIGN acknowledgement */
+#define ASSIGN_OK 0xef /* ASSIGN OK */
+#define OK_FC 0xfc /* Flow-Control RC */
+#define READY_INT 0xfd /* Ready interrupt */
+#define TIMER_INT 0xfe /* timer interrupt */
+#define OK 0xff /* command accepted */
+/*------------------------------------------------------------------*/
+/* information elements */
+/*------------------------------------------------------------------*/
+#define SHIFT 0x90 /* codeset shift */
+#define MORE 0xa0 /* more data */
+#define SDNCMPL 0xa1 /* sending complete */
+#define CL 0xb0 /* congestion level */
+ /* codeset 0 */
+#define SMSG 0x00 /* segmented message */
+#define BC 0x04 /* Bearer Capability */
+#define CAU 0x08 /* cause */
+#define CAD 0x0c /* Connected address */
+#define CAI 0x10 /* call identity */
+#define CHI 0x18 /* channel identification */
+#define LLI 0x19 /* logical link id */
+#define CHA 0x1a /* charge advice */
+#define FTY 0x1c /* Facility */
+#define DT 0x29 /* ETSI date/time */
+#define KEY 0x2c /* keypad information element */
+#define UID 0x2d /* User id information element */
+#define DSP 0x28 /* display */
+#define SIG 0x34 /* signalling hardware control */
+#define OAD 0x6c /* origination address */
+#define OSA 0x6d /* origination sub-address */
+#define CPN 0x70 /* called party number */
+#define DSA 0x71 /* destination sub-address */
+#define RDX 0x73 /* redirecting number extended */
+#define RDN 0x74 /* redirecting number */
+#define RIN 0x76 /* redirection number */
+#define IUP 0x76 /* VN6 rerouter->PCS (codeset 6) */
+#define IPU 0x77 /* VN6 PCS->rerouter (codeset 6) */
+#define RI 0x79 /* restart indicator */
+#define MIE 0x7a /* management info element */
+#define LLC 0x7c /* low layer compatibility */
+#define HLC 0x7d /* high layer compatibility */
+#define UUI 0x7e /* user user information */
+#define ESC 0x7f /* escape extension */
+#define DLC 0x20 /* data link layer configuration */
+#define NLC 0x21 /* network layer configuration */
+#define REDIRECT_IE 0x22 /* redirection request/indication data */
+#define REDIRECT_NET_IE 0x23 /* redirection network override data */
+ /* codeset 6 */
+#define SIN 0x01 /* service indicator */
+#define CIF 0x02 /* charging information */
+#define DATE 0x03 /* date */
+#define CPS 0x07 /* called party status */
+/*------------------------------------------------------------------*/
+/* ESC information elements */
+/*------------------------------------------------------------------*/
+#define MSGTYPEIE 0x7a /* Messagetype info element */
+#define CRIE 0x7b /* INFO info element */
+#define CODESET6IE 0xec /* Tunnel for Codeset 6 IEs */
+#define VSWITCHIE 0xed /* VSwitch info element */
+#define SSEXTIE 0xee /* Supplem. Service info element */
+#define PROFILEIE 0xef /* Profile info element */
+/*------------------------------------------------------------------*/
+/* TEL_CTRL contents */
+/*------------------------------------------------------------------*/
+#define RING_ON 0x01
+#define RING_OFF 0x02
+#define HANDS_FREE_ON 0x03
+#define HANDS_FREE_OFF 0x04
+#define ON_HOOK 0x80
+#define OFF_HOOK 0x90
+/* operation values used by ETSI supplementary services */
+#define THREE_PTY_BEGIN 0x04
+#define THREE_PTY_END 0x05
+#define ECT_EXECUTE 0x06
+#define ACTIVATION_DIVERSION 0x07
+#define DEACTIVATION_DIVERSION 0x08
+#define CALL_DEFLECTION 0x0D
+#define INTERROGATION_DIVERSION 0x0B
+#define INTERROGATION_SERV_USR_NR 0x11
+#define ACTIVATION_MWI 0x20
+#define DEACTIVATION_MWI 0x21
+#define MWI_INDICATION 0x22
+#define MWI_RESPONSE 0x23
+#define CONF_BEGIN 0x28
+#define CONF_ADD 0x29
+#define CONF_SPLIT 0x2a
+#define CONF_DROP 0x2b
+#define CONF_ISOLATE 0x2c
+#define CONF_REATTACH 0x2d
+#define CONF_PARTYDISC 0x2e
+#define CCBS_INFO_RETAIN 0x2f
+#define CCBS_ERASECALLLINKAGEID 0x30
+#define CCBS_STOP_ALERTING 0x31
+#define CCBS_REQUEST 0x32
+#define CCBS_DEACTIVATE 0x33
+#define CCBS_INTERROGATE 0x34
+#define CCBS_STATUS 0x35
+#define CCBS_ERASE 0x36
+#define CCBS_B_FREE 0x37
+#define CCNR_INFO_RETAIN 0x38
+#define CCBS_REMOTE_USER_FREE 0x39
+#define CCNR_REQUEST 0x3a
+#define CCNR_INTERROGATE 0x3b
+#define GET_SUPPORTED_SERVICES 0xff
+#define DIVERSION_PROCEDURE_CFU 0x70
+#define DIVERSION_PROCEDURE_CFB 0x71
+#define DIVERSION_PROCEDURE_CFNR 0x72
+#define DIVERSION_DEACTIVATION_CFU 0x80
+#define DIVERSION_DEACTIVATION_CFB 0x81
+#define DIVERSION_DEACTIVATION_CFNR 0x82
+#define DIVERSION_INTERROGATE_NUM 0x11
+#define DIVERSION_INTERROGATE_CFU 0x60
+#define DIVERSION_INTERROGATE_CFB 0x61
+#define DIVERSION_INTERROGATE_CFNR 0x62
+/* Service Masks */
+#define SMASK_HOLD_RETRIEVE 0x00000001
+#define SMASK_TERMINAL_PORTABILITY 0x00000002
+#define SMASK_ECT 0x00000004
+#define SMASK_3PTY 0x00000008
+#define SMASK_CALL_FORWARDING 0x00000010
+#define SMASK_CALL_DEFLECTION 0x00000020
+#define SMASK_MCID 0x00000040
+#define SMASK_CCBS 0x00000080
+#define SMASK_MWI 0x00000100
+#define SMASK_CCNR 0x00000200
+#define SMASK_CONF 0x00000400
+/* ----------------------------------------------
+ Types of transfers used to transfer the
+ information in the 'struct RC->Reserved2[8]'
+ The information is transferred as 2 dwords
+ (2 4Byte unsigned values)
+ First of them is the transfer type.
+ 2^32-1 possible messages are possible in this way.
+ The context of the second one had no meaning
+ ---------------------------------------------- */
+#define DIVA_RC_TYPE_NONE 0x00000000
+#define DIVA_RC_TYPE_REMOVE_COMPLETE 0x00000008
+#define DIVA_RC_TYPE_STREAM_PTR 0x00000009
+#define DIVA_RC_TYPE_CMA_PTR 0x0000000a
+#define DIVA_RC_TYPE_OK_FC 0x0000000b
+#define DIVA_RC_TYPE_RX_DMA 0x0000000c
+/* ------------------------------------------------------
+ IO Control codes for IN BAND SIGNALING
+ ------------------------------------------------------ */
+#define CTRL_L1_SET_SIG_ID 5
+#define CTRL_L1_SET_DAD 6
+#define CTRL_L1_RESOURCES 7
+/* ------------------------------------------------------ */
+/* ------------------------------------------------------
+ Layer 2 types
+ ------------------------------------------------------ */
+#define X75T 1 /* x.75 for ttx */
+#define TRF 2 /* transparent with hdlc framing */
+#define TRF_IN 3 /* transparent with hdlc fr. inc. */
+#define SDLC 4 /* sdlc, sna layer-2 */
+#define X75 5 /* x.75 for btx */
+#define LAPD 6 /* lapd (Q.921) */
+#define X25_L2 7 /* x.25 layer-2 */
+#define V120_L2 8 /* V.120 layer-2 protocol */
+#define V42_IN 9 /* V.42 layer-2 protocol, incomming */
+#define V42 10 /* V.42 layer-2 protocol */
+#define MDM_ATP 11 /* AT Parser built in the L2 */
+#define X75_V42BIS 12 /* x.75 with V.42bis */
+#define RTPL2_IN 13 /* RTP layer-2 protocol, incomming */
+#define RTPL2 14 /* RTP layer-2 protocol */
+#define V120_V42BIS 15 /* V.120 asynchronous mode supporting V.42bis compression */
+#define LISTENER 27 /* Layer 2 to listen line */
+#define MTP2 28 /* MTP2 Layer 2 */
+#define PIAFS_CRC 29 /* PIAFS Layer 2 with CRC calculation at L2 */
+/* ------------------------------------------------------
+ PIAFS DLC DEFINITIONS
+ ------------------------------------------------------ */
+#define PIAFS_64K 0x01
+#define PIAFS_VARIABLE_SPEED 0x02
+#define PIAFS_CHINESE_SPEED 0x04
+#define PIAFS_UDATA_ABILITY_ID 0x80
+#define PIAFS_UDATA_ABILITY_DCDON 0x01
+#define PIAFS_UDATA_ABILITY_DDI 0x80
+/*
+DLC of PIAFS :
+Byte | 8 7 6 5 4 3 2 1
+-----+--------------------------------------------------------
+ 0 | 0 0 1 0 0 0 0 0 Data Link Configuration
+ 1 | X X X X X X X X Length of IE (at least 15 Bytes)
+ 2 | 0 0 0 0 0 0 0 0 max. information field, LOW byte (not used, fix 73 Bytes)
+ 3 | 0 0 0 0 0 0 0 0 max. information field, HIGH byte (not used, fix 73 Bytes)
+ 4 | 0 0 0 0 0 0 0 0 address A (not used)
+ 5 | 0 0 0 0 0 0 0 0 address B (not used)
+ 6 | 0 0 0 0 0 0 0 0 Mode (not used, fix 128)
+ 7 | 0 0 0 0 0 0 0 0 Window Size (not used, fix 127)
+ 8 | X X X X X X X X XID Length, Low Byte (at least 7 Bytes)
+ 9 | X X X X X X X X XID Length, High Byte
+ 10 | 0 0 0 0 0 C V S PIAFS Protocol Speed configuration -> Note(1)
+ | S = 0 -> Protocol Speed is 32K
+ | S = 1 -> Protocol Speed is 64K
+ | V = 0 -> Protocol Speed is fixed
+ | V = 1 -> Protocol Speed is variable
+ | C = 0 -> speed setting according to standard
+ | C = 1 -> speed setting for chinese implementation
+ 11 | 0 0 0 0 0 0 R T P0 - V42bis Compression enable/disable, Low Byte
+ | T = 0 -> Transmit Direction enable
+ | T = 1 -> Transmit Direction disable
+ | R = 0 -> Receive Direction enable
+ | R = 1 -> Receive Direction disable
+ 13 | 0 0 0 0 0 0 0 0 P0 - V42bis Compression enable/disable, High Byte
+ 14 | X X X X X X X X P1 - V42bis Dictionary Size, Low Byte
+ 15 | X X X X X X X X P1 - V42bis Dictionary Size, High Byte
+ 16 | X X X X X X X X P2 - V42bis String Length, Low Byte
+ 17 | X X X X X X X X P2 - V42bis String Length, High Byte
+ 18 | X X X X X X X X PIAFS extension length
+ 19 | 1 0 0 0 0 0 0 0 PIAFS extension Id (0x80) - UDATA abilities
+ 20 | U 0 0 0 0 0 0 D UDATA abilities -> Note (2)
+ | up to now the following Bits are defined:
+ | D - signal DCD ON
+ | U - use extensive UDATA control communication
+ | for DDI test application
++ Note (1): ----------+------+-----------------------------------------+
+| PIAFS Protocol | Bit | |
+| Speed configuration | S | Bit 1 - Protocol Speed |
+| | | 0 - 32K |
+| | | 1 - 64K (default) |
+| | V | Bit 2 - Variable Protocol Speed |
+| | | 0 - Speed is fix |
+| | | 1 - Speed is variable (default) |
+| | | OVERWRITES 32k Bit 1 |
+| | C | Bit 3 0 - Speed Settings according to |
+| | | PIAFS specification |
+| | | 1 - Speed setting for chinese |
+| | | PIAFS implementation |
+| | | Explanation for chinese speed settings: |
+| | | if Bit 3 is set the following |
+| | | rules apply: |
+| | | Bit1=0 Bit2=0: 32k fix |
+| | | Bit1=1 Bit2=0: 64k fix |
+| | | Bit1=0 Bit2=1: PIAFS is trying |
+| | | to negotiate 32k is that is |
+| | | not possible it tries to |
+| | | negotiate 64k |
+| | | Bit1=1 Bit2=1: PIAFS is trying |
+| | | to negotiate 64k is that is |
+| | | not possible it tries to |
+| | | negotiate 32k |
++ Note (2): ----------+------+-----------------------------------------+
+| PIAFS | Bit | this byte defines the usage of UDATA |
+| Implementation | | control communication |
+| UDATA usage | D | Bit 1 - DCD-ON signalling |
+| | | 0 - no DCD-ON is signalled |
+| | | (default) |
+| | | 1 - DCD-ON will be signalled |
+| | U | Bit 8 - DDI test application UDATA |
+| | | control communication |
+| | | 0 - no UDATA control |
+| | | communication (default) |
+| | | sets as well the DCD-ON |
+| | | signalling |
+| | | 1 - UDATA control communication |
+| | | ATTENTION: Do not use these |
+| | | setting if you |
+| | | are not really |
+| | | that you need it |
+| | | and you know |
+| | | exactly what you |
+| | | are doing. |
+| | | You can easily |
+| | | disable any |
+| | | data transfer. |
++---------------------+------+-----------------------------------------+
+*/
+/* ------------------------------------------------------
+ LISTENER DLC DEFINITIONS
+ ------------------------------------------------------ */
+#define LISTENER_FEATURE_MASK_CUMMULATIVE 0x0001
+/* ------------------------------------------------------
+ LISTENER META-FRAME CODE/PRIMITIVE DEFINITIONS
+ ------------------------------------------------------ */
+#define META_CODE_LL_UDATA_RX 0x01
+#define META_CODE_LL_UDATA_TX 0x02
+#define META_CODE_LL_DATA_RX 0x03
+#define META_CODE_LL_DATA_TX 0x04
+#define META_CODE_LL_MDATA_RX 0x05
+#define META_CODE_LL_MDATA_TX 0x06
+#define META_CODE_EMPTY 0x10
+#define META_CODE_LOST_FRAMES 0x11
+#define META_FLAG_TRUNCATED 0x0001
+/*------------------------------------------------------------------*/
+/* CAPI-like profile to indicate features on LAW_REQ */
+/*------------------------------------------------------------------*/
+#define GL_INTERNAL_CONTROLLER_SUPPORTED 0x00000001L
+#define GL_EXTERNAL_EQUIPMENT_SUPPORTED 0x00000002L
+#define GL_HANDSET_SUPPORTED 0x00000004L
+#define GL_DTMF_SUPPORTED 0x00000008L
+#define GL_SUPPLEMENTARY_SERVICES_SUPPORTED 0x00000010L
+#define GL_CHANNEL_ALLOCATION_SUPPORTED 0x00000020L
+#define GL_BCHANNEL_OPERATION_SUPPORTED 0x00000040L
+#define GL_LINE_INTERCONNECT_SUPPORTED 0x00000080L
+#define B1_HDLC_SUPPORTED 0x00000001L
+#define B1_TRANSPARENT_SUPPORTED 0x00000002L
+#define B1_V110_ASYNC_SUPPORTED 0x00000004L
+#define B1_V110_SYNC_SUPPORTED 0x00000008L
+#define B1_T30_SUPPORTED 0x00000010L
+#define B1_HDLC_INVERTED_SUPPORTED 0x00000020L
+#define B1_TRANSPARENT_R_SUPPORTED 0x00000040L
+#define B1_MODEM_ALL_NEGOTIATE_SUPPORTED 0x00000080L
+#define B1_MODEM_ASYNC_SUPPORTED 0x00000100L
+#define B1_MODEM_SYNC_HDLC_SUPPORTED 0x00000200L
+#define B2_X75_SUPPORTED 0x00000001L
+#define B2_TRANSPARENT_SUPPORTED 0x00000002L
+#define B2_SDLC_SUPPORTED 0x00000004L
+#define B2_LAPD_SUPPORTED 0x00000008L
+#define B2_T30_SUPPORTED 0x00000010L
+#define B2_PPP_SUPPORTED 0x00000020L
+#define B2_TRANSPARENT_NO_CRC_SUPPORTED 0x00000040L
+#define B2_MODEM_EC_COMPRESSION_SUPPORTED 0x00000080L
+#define B2_X75_V42BIS_SUPPORTED 0x00000100L
+#define B2_V120_ASYNC_SUPPORTED 0x00000200L
+#define B2_V120_ASYNC_V42BIS_SUPPORTED 0x00000400L
+#define B2_V120_BIT_TRANSPARENT_SUPPORTED 0x00000800L
+#define B2_LAPD_FREE_SAPI_SEL_SUPPORTED 0x00001000L
+#define B3_TRANSPARENT_SUPPORTED 0x00000001L
+#define B3_T90NL_SUPPORTED 0x00000002L
+#define B3_ISO8208_SUPPORTED 0x00000004L
+#define B3_X25_DCE_SUPPORTED 0x00000008L
+#define B3_T30_SUPPORTED 0x00000010L
+#define B3_T30_WITH_EXTENSIONS_SUPPORTED 0x00000020L
+#define B3_RESERVED_SUPPORTED 0x00000040L
+#define B3_MODEM_SUPPORTED 0x00000080L
+#define MANUFACTURER_FEATURE_SLAVE_CODEC 0x00000001L
+#define MANUFACTURER_FEATURE_FAX_MORE_DOCUMENTS 0x00000002L
+#define MANUFACTURER_FEATURE_HARDDTMF 0x00000004L
+#define MANUFACTURER_FEATURE_SOFTDTMF_SEND 0x00000008L
+#define MANUFACTURER_FEATURE_DTMF_PARAMETERS 0x00000010L
+#define MANUFACTURER_FEATURE_SOFTDTMF_RECEIVE 0x00000020L
+#define MANUFACTURER_FEATURE_FAX_SUB_SEP_PWD 0x00000040L
+#define MANUFACTURER_FEATURE_V18 0x00000080L
+#define MANUFACTURER_FEATURE_MIXER_CH_CH 0x00000100L
+#define MANUFACTURER_FEATURE_MIXER_CH_PC 0x00000200L
+#define MANUFACTURER_FEATURE_MIXER_PC_CH 0x00000400L
+#define MANUFACTURER_FEATURE_MIXER_PC_PC 0x00000800L
+#define MANUFACTURER_FEATURE_ECHO_CANCELLER 0x00001000L
+#define MANUFACTURER_FEATURE_RTP 0x00002000L
+#define MANUFACTURER_FEATURE_T38 0x00004000L
+#define MANUFACTURER_FEATURE_TRANSP_DELIVERY_CONF 0x00008000L
+#define MANUFACTURER_FEATURE_XONOFF_FLOW_CONTROL 0x00010000L
+#define MANUFACTURER_FEATURE_OOB_CHANNEL 0x00020000L
+#define MANUFACTURER_FEATURE_IN_BAND_CHANNEL 0x00040000L
+#define MANUFACTURER_FEATURE_IN_BAND_FEATURE 0x00080000L
+#define MANUFACTURER_FEATURE_PIAFS 0x00100000L
+#define MANUFACTURER_FEATURE_DTMF_TONE 0x00200000L
+#define MANUFACTURER_FEATURE_FAX_PAPER_FORMATS 0x00400000L
+#define MANUFACTURER_FEATURE_OK_FC_LABEL 0x00800000L
+#define MANUFACTURER_FEATURE_VOWN 0x01000000L
+#define MANUFACTURER_FEATURE_XCONNECT 0x02000000L
+#define MANUFACTURER_FEATURE_DMACONNECT 0x04000000L
+#define MANUFACTURER_FEATURE_AUDIO_TAP 0x08000000L
+#define MANUFACTURER_FEATURE_FAX_NONSTANDARD 0x10000000L
+#define MANUFACTURER_FEATURE_SS7 0x20000000L
+#define MANUFACTURER_FEATURE_MADAPTER 0x40000000L
+#define MANUFACTURER_FEATURE_MEASURE 0x80000000L
+#define MANUFACTURER_FEATURE2_LISTENING 0x00000001L
+#define MANUFACTURER_FEATURE2_SS_DIFFCONTPOSSIBLE 0x00000002L
+#define MANUFACTURER_FEATURE2_GENERIC_TONE 0x00000004L
+#define MANUFACTURER_FEATURE2_COLOR_FAX 0x00000008L
+#define MANUFACTURER_FEATURE2_SS_ECT_DIFFCONTPOSSIBLE 0x00000010L
+#define RTP_PRIM_PAYLOAD_PCMU_8000 0
+#define RTP_PRIM_PAYLOAD_1016_8000 1
+#define RTP_PRIM_PAYLOAD_G726_32_8000 2
+#define RTP_PRIM_PAYLOAD_GSM_8000 3
+#define RTP_PRIM_PAYLOAD_G723_8000 4
+#define RTP_PRIM_PAYLOAD_DVI4_8000 5
+#define RTP_PRIM_PAYLOAD_DVI4_16000 6
+#define RTP_PRIM_PAYLOAD_LPC_8000 7
+#define RTP_PRIM_PAYLOAD_PCMA_8000 8
+#define RTP_PRIM_PAYLOAD_G722_16000 9
+#define RTP_PRIM_PAYLOAD_QCELP_8000 12
+#define RTP_PRIM_PAYLOAD_G728_8000 14
+#define RTP_PRIM_PAYLOAD_G729_8000 18
+#define RTP_PRIM_PAYLOAD_GSM_HR_8000 30
+#define RTP_PRIM_PAYLOAD_GSM_EFR_8000 31
+#define RTP_ADD_PAYLOAD_BASE 32
+#define RTP_ADD_PAYLOAD_RED 32
+#define RTP_ADD_PAYLOAD_CN_8000 33
+#define RTP_ADD_PAYLOAD_DTMF 34
+#define RTP_PRIM_PAYLOAD_PCMU_8000_SUPPORTED (1L << RTP_PRIM_PAYLOAD_PCMU_8000)
+#define RTP_PRIM_PAYLOAD_1016_8000_SUPPORTED (1L << RTP_PRIM_PAYLOAD_1016_8000)
+#define RTP_PRIM_PAYLOAD_G726_32_8000_SUPPORTED (1L << RTP_PRIM_PAYLOAD_G726_32_8000)
+#define RTP_PRIM_PAYLOAD_GSM_8000_SUPPORTED (1L << RTP_PRIM_PAYLOAD_GSM_8000)
+#define RTP_PRIM_PAYLOAD_G723_8000_SUPPORTED (1L << RTP_PRIM_PAYLOAD_G723_8000)
+#define RTP_PRIM_PAYLOAD_DVI4_8000_SUPPORTED (1L << RTP_PRIM_PAYLOAD_DVI4_8000)
+#define RTP_PRIM_PAYLOAD_DVI4_16000_SUPPORTED (1L << RTP_PRIM_PAYLOAD_DVI4_16000)
+#define RTP_PRIM_PAYLOAD_LPC_8000_SUPPORTED (1L << RTP_PRIM_PAYLOAD_LPC_8000)
+#define RTP_PRIM_PAYLOAD_PCMA_8000_SUPPORTED (1L << RTP_PRIM_PAYLOAD_PCMA_8000)
+#define RTP_PRIM_PAYLOAD_G722_16000_SUPPORTED (1L << RTP_PRIM_PAYLOAD_G722_16000)
+#define RTP_PRIM_PAYLOAD_QCELP_8000_SUPPORTED (1L << RTP_PRIM_PAYLOAD_QCELP_8000)
+#define RTP_PRIM_PAYLOAD_G728_8000_SUPPORTED (1L << RTP_PRIM_PAYLOAD_G728_8000)
+#define RTP_PRIM_PAYLOAD_G729_8000_SUPPORTED (1L << RTP_PRIM_PAYLOAD_G729_8000)
+#define RTP_PRIM_PAYLOAD_GSM_HR_8000_SUPPORTED (1L << RTP_PRIM_PAYLOAD_GSM_HR_8000)
+#define RTP_PRIM_PAYLOAD_GSM_EFR_8000_SUPPORTED (1L << RTP_PRIM_PAYLOAD_GSM_EFR_8000)
+#define RTP_ADD_PAYLOAD_RED_SUPPORTED (1L << (RTP_ADD_PAYLOAD_RED - RTP_ADD_PAYLOAD_BASE))
+#define RTP_ADD_PAYLOAD_CN_8000_SUPPORTED (1L << (RTP_ADD_PAYLOAD_CN_8000 - RTP_ADD_PAYLOAD_BASE))
+#define RTP_ADD_PAYLOAD_DTMF_SUPPORTED (1L << (RTP_ADD_PAYLOAD_DTMF - RTP_ADD_PAYLOAD_BASE))
+/* virtual switching definitions */
+#define VSJOIN 1
+#define VSTRANSPORT 2
+#define VSGETPARAMS 3
+#define VSCAD 1
+#define VSRXCPNAME 2
+#define VSCALLSTAT 3
+#define VSINVOKEID 4
+#define VSCLMRKS 5
+#define VSTBCTIDENT 6
+#define VSETSILINKID 7
+#define VSSAMECONTROLLER 8
+/* Errorcodes for VSETSILINKID begin */
+#define VSETSILINKIDRRWC 1
+#define VSETSILINKIDREJECT 2
+#define VSETSILINKIDTIMEOUT 3
+#define VSETSILINKIDFAILCOUNT 4
+#define VSETSILINKIDERROR 5
+/* Errorcodes for VSETSILINKID end */
+/* -----------------------------------------------------------**
+** The PROTOCOL_FEATURE_STRING in feature.h (included **
+** in prstart.sx and astart.sx) defines capabilities and **
+** features of the actual protocol code. It's used as a bit **
+** mask. **
+** The following Bits are defined: **
+** -----------------------------------------------------------*/
+#define PROTCAP_TELINDUS 0x0001 /* Telindus Variant of protocol code */
+#define PROTCAP_MAN_IF 0x0002 /* Management interface implemented */
+#define PROTCAP_V_42 0x0004 /* V42 implemented */
+#define PROTCAP_V90D 0x0008 /* V.90D (implies up to 384k DSP code) */
+#define PROTCAP_EXTD_FAX 0x0010 /* Extended FAX (ECM, 2D, T6, Polling) */
+#define PROTCAP_EXTD_RXFC 0x0020 /* RxFC (Extd Flow Control), OOB Chnl */
+#define PROTCAP_VOIP 0x0040 /* VoIP (implies up to 512k DSP code) */
+#define PROTCAP_CMA_ALLPR 0x0080 /* CMA support for all NL primitives */
+#define PROTCAP_FREE8 0x0100 /* not used */
+#define PROTCAP_FREE9 0x0200 /* not used */
+#define PROTCAP_FREE10 0x0400 /* not used */
+#define PROTCAP_FREE11 0x0800 /* not used */
+#define PROTCAP_FREE12 0x1000 /* not used */
+#define PROTCAP_FREE13 0x2000 /* not used */
+#define PROTCAP_FREE14 0x4000 /* not used */
+#define PROTCAP_EXTENSION 0x8000 /* used for future extentions */
+/* -----------------------------------------------------------* */
+/* Onhook data transmission ETS30065901 */
+/* Message Type */
+/*#define RESERVED4 0x4*/
+#define CALL_SETUP 0x80
+#define MESSAGE_WAITING_INDICATOR 0x82
+/*#define RESERVED84 0x84*/
+/*#define RESERVED85 0x85*/
+#define ADVICE_OF_CHARGE 0x86
+/*1111 0001
+to
+1111 1111
+F1H - Reserved for network operator use
+to
+FFH*/
+/* Parameter Types */
+#define DATE_AND_TIME 1
+#define CLI_PARAMETER_TYPE 2
+#define CALLED_DIRECTORY_NUMBER_PARAMETER_TYPE 3
+#define REASON_FOR_ABSENCE_OF_CLI_PARAMETER_TYPE 4
+#define NAME_PARAMETER_TYPE 7
+#define REASON_FOR_ABSENCE_OF_CALLING_PARTY_NAME_PARAMETER_TYPE 8
+#define VISUAL_INDICATOR_PARAMETER_TYPE 0xb
+#define COMPLEMENTARY_CLI_PARAMETER_TYPE 0x10
+#define CALL_TYPE_PARAMETER_TYPE 0x11
+#define FIRST_CALLED_LINE_DIRECTORY_NUMBER_PARAMETER_TYPE 0x12
+#define NETWORK_MESSAGE_SYSTEM_STATUS_PARAMETER_TYPE 0x13
+#define FORWARDED_CALL_TYPE_PARAMETER_TYPE 0x15
+#define TYPE_OF_CALLING_USER_PARAMETER_TYPE 0x16
+#define REDIRECTING_NUMBER_PARAMETER_TYPE 0x1a
+#define EXTENSION_FOR_NETWORK_OPERATOR_USE_PARAMETER_TYPE 0xe0
+/* -----------------------------------------------------------* */
+#else
+#endif /* PC_H_INCLUDED } */
diff --git a/drivers/isdn/hardware/eicon/pc_init.h b/drivers/isdn/hardware/eicon/pc_init.h
new file mode 100644
index 0000000..a616fc9
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/pc_init.h
@@ -0,0 +1,267 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+#ifndef PC_INIT_H_
+#define PC_INIT_H_
+/*------------------------------------------------------------------*/
+/*
+ Initialisation parameters for the card
+ 0x0008 <byte> TEI
+ 0x0009 <byte> NT2 flag
+ 0x000a <byte> Default DID length
+ 0x000b <byte> Disable watchdog flag
+ 0x000c <byte> Permanent connection flag
+ 0x000d <byte> Bit 3-8: L1 Hunt Group/Tristate
+ 0x000d <byte> Bit 1: QSig small CR length if set to 1
+ 0x000d <byte> Bit 2: QSig small CHI length if set to 1
+ 0x000e <byte> Bit 1-3: Stable L2, 0=OnDemand,1=NoDisc,2=permanent
+ 0x000e <byte> Bit 4: NT mode
+ 0x000e <byte> Bit 5: QSig Channel ID format
+ 0x000e <byte> Bit 6: QSig Call Forwarding Allowed Flag
+ 0x000e <byte> Bit 7: Disable AutoSPID Flag
+ 0x000f <byte> No order check flag
+ 0x0010 <byte> Force companding type:0=default,1=a-law,2=u-law
+ 0x0012 <byte> Low channel flag
+ 0x0013 <byte> Protocol version
+ 0x0014 <byte> CRC4 option:0=default,1=double_frm,2=multi_frm,3=auto
+ 0x0015 <byte> Bit 0: NoHscx30, Bit 1: Loopback flag, Bit 2: ForceHscx30
+ 0x0016 <byte> DSP info
+ 0x0017-0x0019 Serial number
+ 0x001a <byte> Card type
+ 0x0020 <string> OAD 0
+ 0x0040 <string> OSA 0
+ 0x0060 <string> SPID 0 (if not T.1)
+ 0x0060 <struct> if T.1: Robbed Bit Configuration
+ 0x0060 length (8)
+ 0x0061 RBS Answer Delay
+ 0x0062 RBS Config Bit 3, 4:
+ 0 0 -> Wink Start
+ 1 0 -> Loop Start
+ 0 1 -> Ground Start
+ 1 1 -> reserved
+ Bit 5, 6:
+ 0 0 -> Pulse Dial -> Rotary
+ 1 0 -> DTMF
+ 0 1 -> MF
+ 1 1 -> reserved
+ 0x0063 RBS RX Digit Timeout
+ 0x0064 RBS Bearer Capability
+ 0x0065-0x0069 RBS Debug Mask
+ 0x0080 <string> OAD 1
+ 0x00a0 <string> OSA 1
+ 0x00c0 <string> SPID 1
+ 0x00e0 <w-element list> Additional configuration
+*/
+#define PCINIT_END_OF_LIST 0x00
+#define PCINIT_MODEM_GUARD_TONE 0x01
+#define PCINIT_MODEM_MIN_SPEED 0x02
+#define PCINIT_MODEM_MAX_SPEED 0x03
+#define PCINIT_MODEM_PROTOCOL_OPTIONS 0x04
+#define PCINIT_FAX_OPTIONS 0x05
+#define PCINIT_FAX_MAX_SPEED 0x06
+#define PCINIT_MODEM_OPTIONS 0x07
+#define PCINIT_MODEM_NEGOTIATION_MODE 0x08
+#define PCINIT_MODEM_MODULATIONS_MASK 0x09
+#define PCINIT_MODEM_TRANSMIT_LEVEL 0x0a
+#define PCINIT_FAX_DISABLED_RESOLUTIONS 0x0b
+#define PCINIT_FAX_MAX_RECORDING_WIDTH 0x0c
+#define PCINIT_FAX_MAX_RECORDING_LENGTH 0x0d
+#define PCINIT_FAX_MIN_SCANLINE_TIME 0x0e
+#define PCINIT_US_EKTS_CACH_HANDLES 0x0f
+#define PCINIT_US_EKTS_BEGIN_CONF 0x10
+#define PCINIT_US_EKTS_DROP_CONF 0x11
+#define PCINIT_US_EKTS_CALL_TRANSFER 0x12
+#define PCINIT_RINGERTONE_OPTION 0x13
+#define PCINIT_CARD_ADDRESS 0x14
+#define PCINIT_FPGA_FEATURES 0x15
+#define PCINIT_US_EKTS_MWI 0x16
+#define PCINIT_MODEM_SPEAKER_CONTROL 0x17
+#define PCINIT_MODEM_SPEAKER_VOLUME 0x18
+#define PCINIT_MODEM_CARRIER_WAIT_TIME 0x19
+#define PCINIT_MODEM_CARRIER_LOSS_TIME 0x1a
+#define PCINIT_UNCHAN_B_MASK 0x1b
+#define PCINIT_PART68_LIMITER 0x1c
+#define PCINIT_XDI_FEATURES 0x1d
+#define PCINIT_QSIG_DIALECT 0x1e
+#define PCINIT_DISABLE_AUTOSPID_FLAG 0x1f
+#define PCINIT_FORCE_VOICE_MAIL_ALERT 0x20
+#define PCINIT_PIAFS_TURNAROUND_FRAMES 0x21
+#define PCINIT_L2_COUNT 0x22
+#define PCINIT_QSIG_FEATURES 0x23
+#define PCINIT_NO_SIGNALLING 0x24
+#define PCINIT_CARD_SN 0x25
+#define PCINIT_CARD_PORT 0x26
+#define PCINIT_ALERTTO 0x27
+#define PCINIT_MODEM_EYE_SETUP 0x28
+#define PCINIT_FAX_V34_OPTIONS 0x29
+/*------------------------------------------------------------------*/
+#define PCINIT_MODEM_GUARD_TONE_NONE 0x00
+#define PCINIT_MODEM_GUARD_TONE_550HZ 0x01
+#define PCINIT_MODEM_GUARD_TONE_1800HZ 0x02
+#define PCINIT_MODEM_GUARD_TONE_CHOICES 0x03
+#define PCINIT_MODEMPROT_DISABLE_V42_V42BIS 0x0001
+#define PCINIT_MODEMPROT_DISABLE_MNP_MNP5 0x0002
+#define PCINIT_MODEMPROT_REQUIRE_PROTOCOL 0x0004
+#define PCINIT_MODEMPROT_DISABLE_V42_DETECT 0x0008
+#define PCINIT_MODEMPROT_DISABLE_COMPRESSION 0x0010
+#define PCINIT_MODEMPROT_REQUIRE_PROTOCOL_V34UP 0x0020
+#define PCINIT_MODEMPROT_NO_PROTOCOL_IF_1200 0x0100
+#define PCINIT_MODEMPROT_BUFFER_IN_V42_DETECT 0x0200
+#define PCINIT_MODEMPROT_DISABLE_V42_SREJ 0x0400
+#define PCINIT_MODEMPROT_DISABLE_MNP3 0x0800
+#define PCINIT_MODEMPROT_DISABLE_MNP4 0x1000
+#define PCINIT_MODEMPROT_DISABLE_MNP10 0x2000
+#define PCINIT_MODEMPROT_NO_PROTOCOL_IF_V22BIS 0x4000
+#define PCINIT_MODEMPROT_NO_PROTOCOL_IF_V32BIS 0x8000
+#define PCINIT_MODEMCONFIG_LEASED_LINE_MODE 0x00000001L
+#define PCINIT_MODEMCONFIG_4_WIRE_OPERATION 0x00000002L
+#define PCINIT_MODEMCONFIG_DISABLE_BUSY_DETECT 0x00000004L
+#define PCINIT_MODEMCONFIG_DISABLE_CALLING_TONE 0x00000008L
+#define PCINIT_MODEMCONFIG_DISABLE_ANSWER_TONE 0x00000010L
+#define PCINIT_MODEMCONFIG_ENABLE_DIAL_TONE_DET 0x00000020L
+#define PCINIT_MODEMCONFIG_USE_POTS_INTERFACE 0x00000040L
+#define PCINIT_MODEMCONFIG_FORCE_RAY_TAYLOR_FAX 0x00000080L
+#define PCINIT_MODEMCONFIG_DISABLE_RETRAIN 0x00000100L
+#define PCINIT_MODEMCONFIG_DISABLE_STEPDOWN 0x00000200L
+#define PCINIT_MODEMCONFIG_DISABLE_SPLIT_SPEED 0x00000400L
+#define PCINIT_MODEMCONFIG_DISABLE_TRELLIS 0x00000800L
+#define PCINIT_MODEMCONFIG_ALLOW_RDL_TEST_LOOP 0x00001000L
+#define PCINIT_MODEMCONFIG_DISABLE_STEPUP 0x00002000L
+#define PCINIT_MODEMCONFIG_DISABLE_FLUSH_TIMER 0x00004000L
+#define PCINIT_MODEMCONFIG_REVERSE_DIRECTION 0x00008000L
+#define PCINIT_MODEMCONFIG_DISABLE_TX_REDUCTION 0x00010000L
+#define PCINIT_MODEMCONFIG_DISABLE_PRECODING 0x00020000L
+#define PCINIT_MODEMCONFIG_DISABLE_PREEMPHASIS 0x00040000L
+#define PCINIT_MODEMCONFIG_DISABLE_SHAPING 0x00080000L
+#define PCINIT_MODEMCONFIG_DISABLE_NONLINEAR_EN 0x00100000L
+#define PCINIT_MODEMCONFIG_DISABLE_MANUALREDUCT 0x00200000L
+#define PCINIT_MODEMCONFIG_DISABLE_16_POINT_TRN 0x00400000L
+#define PCINIT_MODEMCONFIG_DISABLE_2400_SYMBOLS 0x01000000L
+#define PCINIT_MODEMCONFIG_DISABLE_2743_SYMBOLS 0x02000000L
+#define PCINIT_MODEMCONFIG_DISABLE_2800_SYMBOLS 0x04000000L
+#define PCINIT_MODEMCONFIG_DISABLE_3000_SYMBOLS 0x08000000L
+#define PCINIT_MODEMCONFIG_DISABLE_3200_SYMBOLS 0x10000000L
+#define PCINIT_MODEMCONFIG_DISABLE_3429_SYMBOLS 0x20000000L
+#define PCINIT_MODEM_NEGOTIATE_HIGHEST 0x00
+#define PCINIT_MODEM_NEGOTIATE_DISABLED 0x01
+#define PCINIT_MODEM_NEGOTIATE_IN_CLASS 0x02
+#define PCINIT_MODEM_NEGOTIATE_V100 0x03
+#define PCINIT_MODEM_NEGOTIATE_V8 0x04
+#define PCINIT_MODEM_NEGOTIATE_V8BIS 0x05
+#define PCINIT_MODEM_NEGOTIATE_CHOICES 0x06
+#define PCINIT_MODEMMODULATION_DISABLE_V21 0x00000001L
+#define PCINIT_MODEMMODULATION_DISABLE_V23 0x00000002L
+#define PCINIT_MODEMMODULATION_DISABLE_V22 0x00000004L
+#define PCINIT_MODEMMODULATION_DISABLE_V22BIS 0x00000008L
+#define PCINIT_MODEMMODULATION_DISABLE_V32 0x00000010L
+#define PCINIT_MODEMMODULATION_DISABLE_V32BIS 0x00000020L
+#define PCINIT_MODEMMODULATION_DISABLE_V34 0x00000040L
+#define PCINIT_MODEMMODULATION_DISABLE_V90 0x00000080L
+#define PCINIT_MODEMMODULATION_DISABLE_BELL103 0x00000100L
+#define PCINIT_MODEMMODULATION_DISABLE_BELL212A 0x00000200L
+#define PCINIT_MODEMMODULATION_DISABLE_VFC 0x00000400L
+#define PCINIT_MODEMMODULATION_DISABLE_K56FLEX 0x00000800L
+#define PCINIT_MODEMMODULATION_DISABLE_X2 0x00001000L
+#define PCINIT_MODEMMODULATION_ENABLE_V29FDX 0x00010000L
+#define PCINIT_MODEMMODULATION_ENABLE_V33 0x00020000L
+#define PCINIT_MODEMMODULATION_ENABLE_V90A 0x00040000L
+#define PCINIT_MODEM_TRANSMIT_LEVEL_CHOICES 0x10
+#define PCINIT_MODEM_SPEAKER_OFF 0x00
+#define PCINIT_MODEM_SPEAKER_DURING_TRAIN 0x01
+#define PCINIT_MODEM_SPEAKER_TIL_CONNECT 0x02
+#define PCINIT_MODEM_SPEAKER_ALWAYS_ON 0x03
+#define PCINIT_MODEM_SPEAKER_CHOICES 0x04
+#define PCINIT_MODEM_SPEAKER_VOLUME_MIN 0x00
+#define PCINIT_MODEM_SPEAKER_VOLUME_LOW 0x01
+#define PCINIT_MODEM_SPEAKER_VOLUME_HIGH 0x02
+#define PCINIT_MODEM_SPEAKER_VOLUME_MAX 0x03
+#define PCINIT_MODEM_SPEAKER_VOLUME_CHOICES 0x04
+/*------------------------------------------------------------------*/
+#define PCINIT_FAXCONFIG_DISABLE_FINE 0x0001
+#define PCINIT_FAXCONFIG_DISABLE_ECM 0x0002
+#define PCINIT_FAXCONFIG_ECM_64_BYTES 0x0004
+#define PCINIT_FAXCONFIG_DISABLE_2D_CODING 0x0008
+#define PCINIT_FAXCONFIG_DISABLE_T6_CODING 0x0010
+#define PCINIT_FAXCONFIG_DISABLE_UNCOMPR 0x0020
+#define PCINIT_FAXCONFIG_REFUSE_POLLING 0x0040
+#define PCINIT_FAXCONFIG_HIDE_TOTAL_PAGES 0x0080
+#define PCINIT_FAXCONFIG_HIDE_ALL_HEADLINE 0x0100
+#define PCINIT_FAXCONFIG_HIDE_PAGE_INFO 0x0180
+#define PCINIT_FAXCONFIG_HEADLINE_OPTIONS_MASK 0x0180
+#define PCINIT_FAXCONFIG_DISABLE_FEATURE_FALLBACK 0x0200
+#define PCINIT_FAXCONFIG_V34FAX_CONTROL_RATE_1200 0x0800
+#define PCINIT_FAXCONFIG_DISABLE_V34FAX 0x1000
+#define PCINIT_FAXCONFIG_DISABLE_R8_0770_OR_200 0x01
+#define PCINIT_FAXCONFIG_DISABLE_R8_1540 0x02
+#define PCINIT_FAXCONFIG_DISABLE_R16_1540_OR_400 0x04
+#define PCINIT_FAXCONFIG_DISABLE_R4_0385_OR_100 0x08
+#define PCINIT_FAXCONFIG_DISABLE_300_300 0x10
+#define PCINIT_FAXCONFIG_DISABLE_INCH_BASED 0x40
+#define PCINIT_FAXCONFIG_DISABLE_METRIC_BASED 0x80
+#define PCINIT_FAXCONFIG_REC_WIDTH_ISO_A3 0
+#define PCINIT_FAXCONFIG_REC_WIDTH_ISO_B4 1
+#define PCINIT_FAXCONFIG_REC_WIDTH_ISO_A4 2
+#define PCINIT_FAXCONFIG_REC_WIDTH_COUNT 3
+#define PCINIT_FAXCONFIG_REC_LENGTH_UNLIMITED 0
+#define PCINIT_FAXCONFIG_REC_LENGTH_ISO_B4 1
+#define PCINIT_FAXCONFIG_REC_LENGTH_ISO_A4 2
+#define PCINIT_FAXCONFIG_REC_LENGTH_COUNT 3
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_00_00_00 0
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_05_05_05 1
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_10_05_05 2
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_10_10_10 3
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_20_10_10 4
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_20_20_20 5
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_40_20_20 6
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_40_40_40 7
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_RES_8 8
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_RES_9 9
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_RES_10 10
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_10_10_05 11
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_20_10_05 12
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_20_20_10 13
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_40_20_10 14
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_40_40_20 15
+#define PCINIT_FAXCONFIG_SCANLINE_TIME_COUNT 16
+#define PCINIT_FAXCONFIG_DISABLE_TX_REDUCTION 0x00010000L
+#define PCINIT_FAXCONFIG_DISABLE_PRECODING 0x00020000L
+#define PCINIT_FAXCONFIG_DISABLE_PREEMPHASIS 0x00040000L
+#define PCINIT_FAXCONFIG_DISABLE_SHAPING 0x00080000L
+#define PCINIT_FAXCONFIG_DISABLE_NONLINEAR_EN 0x00100000L
+#define PCINIT_FAXCONFIG_DISABLE_MANUALREDUCT 0x00200000L
+#define PCINIT_FAXCONFIG_DISABLE_16_POINT_TRN 0x00400000L
+#define PCINIT_FAXCONFIG_DISABLE_2400_SYMBOLS 0x01000000L
+#define PCINIT_FAXCONFIG_DISABLE_2743_SYMBOLS 0x02000000L
+#define PCINIT_FAXCONFIG_DISABLE_2800_SYMBOLS 0x04000000L
+#define PCINIT_FAXCONFIG_DISABLE_3000_SYMBOLS 0x08000000L
+#define PCINIT_FAXCONFIG_DISABLE_3200_SYMBOLS 0x10000000L
+#define PCINIT_FAXCONFIG_DISABLE_3429_SYMBOLS 0x20000000L
+/*--------------------------------------------------------------------------*/
+#define PCINIT_XDI_CMA_FOR_ALL_NL_PRIMITIVES 0x01
+/*--------------------------------------------------------------------------*/
+#define PCINIT_FPGA_PLX_ACCESS_SUPPORTED 0x01
+/*--------------------------------------------------------------------------*/
+#endif
+/*--------------------------------------------------------------------------*/
diff --git a/drivers/isdn/hardware/eicon/pc_maint.h b/drivers/isdn/hardware/eicon/pc_maint.h
new file mode 100644
index 0000000..352ab8d
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/pc_maint.h
@@ -0,0 +1,160 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+#ifdef PLATFORM_GT_32BIT
+/* #define POINTER_32BIT byte * __ptr32 */
+#define POINTER_32BIT dword
+#else
+#define POINTER_32BIT byte *
+#endif
+#if !defined(MIPS_SCOM)
+#define BUFFER_SZ 48
+#define MAINT_OFFS 0x380
+#else
+#define BUFFER_SZ 128
+#if defined(PRI)
+#define MAINT_OFFS 0xef00
+#else
+#define MAINT_OFFS 0xff00
+#endif
+#endif
+#define MIPS_BUFFER_SZ 128
+#if defined(PRI)
+#define MIPS_MAINT_OFFS 0xef00
+#else
+#define MIPS_MAINT_OFFS 0xff00
+#endif
+#define LOG 1
+#define MEMR 2
+#define MEMW 3
+#define IOR 4
+#define IOW 5
+#define B1TEST 6
+#define B2TEST 7
+#define BTESTOFF 8
+#define DSIG_STATS 9
+#define B_CH_STATS 10
+#define D_CH_STATS 11
+#define BL1_STATS 12
+#define BL1_STATS_C 13
+#define GET_VERSION 14
+#define OS_STATS 15
+#define XLOG_SET_MASK 16
+#define XLOG_GET_MASK 17
+#define DSP_READ 20
+#define DSP_WRITE 21
+#define OK 0xff
+#define MORE_EVENTS 0xfe
+#define NO_EVENT 1
+struct DSigStruc
+{
+ byte Id;
+ byte u;
+ byte listen;
+ byte active;
+ byte sin[3];
+ byte bc[6];
+ byte llc[6];
+ byte hlc[6];
+ byte oad[20];
+};
+struct BL1Struc {
+ dword cx_b1;
+ dword cx_b2;
+ dword cr_b1;
+ dword cr_b2;
+ dword px_b1;
+ dword px_b2;
+ dword pr_b1;
+ dword pr_b2;
+ word er_b1;
+ word er_b2;
+};
+struct L2Struc {
+ dword XTotal;
+ dword RTotal;
+ word XError;
+ word RError;
+};
+struct OSStruc {
+ dword free_n;
+};
+typedef union
+{
+ struct DSigStruc DSigStats;
+ struct BL1Struc BL1Stats;
+ struct L2Struc L2Stats;
+ struct OSStruc OSStats;
+ byte b[BUFFER_SZ];
+ word w[BUFFER_SZ>>1];
+ word l[BUFFER_SZ>>2]; /* word is wrong, do not use! Use 'd' instead. */
+ dword d[BUFFER_SZ>>2];
+} BUFFER;
+typedef union
+{
+ struct DSigStruc DSigStats;
+ struct BL1Struc BL1Stats;
+ struct L2Struc L2Stats;
+ struct OSStruc OSStats;
+ byte b[MIPS_BUFFER_SZ];
+ word w[MIPS_BUFFER_SZ>>1];
+ word l[BUFFER_SZ>>2]; /* word is wrong, do not use! Use 'd' instead. */
+ dword d[MIPS_BUFFER_SZ>>2];
+} MIPS_BUFFER;
+#if !defined(MIPS_SCOM)
+struct pc_maint
+{
+ byte req;
+ byte rc;
+ POINTER_32BIT mem;
+ short length;
+ word port;
+ byte fill[6];
+ BUFFER data;
+};
+#else
+struct pc_maint
+{
+ byte req;
+ byte rc;
+ byte reserved[2]; /* R3000 alignment ... */
+ POINTER_32BIT mem;
+ short length;
+ word port;
+ byte fill[4]; /* data at offset 16 */
+ BUFFER data;
+};
+#endif
+struct mi_pc_maint
+{
+ byte req;
+ byte rc;
+ byte reserved[2]; /* R3000 alignment ... */
+ POINTER_32BIT mem;
+ short length;
+ word port;
+ byte fill[4]; /* data at offset 16 */
+ MIPS_BUFFER data;
+};
diff --git a/drivers/isdn/hardware/eicon/pkmaint.h b/drivers/isdn/hardware/eicon/pkmaint.h
new file mode 100644
index 0000000..722f85f
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/pkmaint.h
@@ -0,0 +1,44 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+#ifndef __DIVA_XDI_OS_DEPENDENT_PACK_MAIN_ON_BYTE_INC__
+#define __DIVA_XDI_OS_DEPENDENT_PACK_MAIN_ON_BYTE_INC__
+
+
+/*
+ Only one purpose of this compiler dependent file to pack
+ structures, described in pc_maint.h so that no padding
+ will be included.
+
+ With microsoft compile it is done by "pshpack1.h" and
+ after is restored by "poppack.h"
+ */
+
+
+#include "pc_maint.h"
+
+
+#endif
+
diff --git a/drivers/isdn/hardware/eicon/platform.h b/drivers/isdn/hardware/eicon/platform.h
new file mode 100644
index 0000000..15d4942
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/platform.h
@@ -0,0 +1,372 @@
+/* $Id: platform.h,v 1.37.4.6 2005/01/31 12:22:20 armin Exp $
+ *
+ * platform.h
+ *
+ *
+ * Copyright 2000-2003 by Armin Schindler (mac@melware.de)
+ * Copyright 2000 Eicon Networks
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+
+#ifndef __PLATFORM_H__
+#define __PLATFORM_H__
+
+#if !defined(DIVA_BUILD)
+#define DIVA_BUILD "local"
+#endif
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/skbuff.h>
+#include <linux/vmalloc.h>
+#include <linux/proc_fs.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <asm/types.h>
+#include <asm/io.h>
+
+#include "cardtype.h"
+
+/* activate debuglib for modules only */
+#ifndef MODULE
+#define DIVA_NO_DEBUGLIB
+#endif
+
+#define DIVA_INIT_FUNCTION __init
+#define DIVA_EXIT_FUNCTION __exit
+
+#define DIVA_USER_MODE_CARD_CONFIG 1
+#define USE_EXTENDED_DEBUGS 1
+
+#define MAX_ADAPTER 32
+
+#define DIVA_ISTREAM 1
+
+#define MEMORY_SPACE_TYPE 0
+#define PORT_SPACE_TYPE 1
+
+
+#include <linux/string.h>
+
+#ifndef byte
+#define byte u8
+#endif
+
+#ifndef word
+#define word u16
+#endif
+
+#ifndef dword
+#define dword u32
+#endif
+
+#ifndef qword
+#define qword u64
+#endif
+
+#ifndef NULL
+#define NULL ((void *) 0)
+#endif
+
+#ifndef far
+#define far
+#endif
+
+#ifndef _pascal
+#define _pascal
+#endif
+
+#ifndef _loadds
+#define _loadds
+#endif
+
+#ifndef _cdecl
+#define _cdecl
+#endif
+
+#define MEM_TYPE_RAM 0
+#define MEM_TYPE_PORT 1
+#define MEM_TYPE_PROM 2
+#define MEM_TYPE_CTLREG 3
+#define MEM_TYPE_RESET 4
+#define MEM_TYPE_CFG 5
+#define MEM_TYPE_ADDRESS 6
+#define MEM_TYPE_CONFIG 7
+#define MEM_TYPE_CONTROL 8
+
+#define MAX_MEM_TYPE 10
+
+#define DIVA_OS_MEM_ATTACH_RAM(a) ((a)->ram)
+#define DIVA_OS_MEM_ATTACH_PORT(a) ((a)->port)
+#define DIVA_OS_MEM_ATTACH_PROM(a) ((a)->prom)
+#define DIVA_OS_MEM_ATTACH_CTLREG(a) ((a)->ctlReg)
+#define DIVA_OS_MEM_ATTACH_RESET(a) ((a)->reset)
+#define DIVA_OS_MEM_ATTACH_CFG(a) ((a)->cfg)
+#define DIVA_OS_MEM_ATTACH_ADDRESS(a) ((a)->Address)
+#define DIVA_OS_MEM_ATTACH_CONFIG(a) ((a)->Config)
+#define DIVA_OS_MEM_ATTACH_CONTROL(a) ((a)->Control)
+
+#define DIVA_OS_MEM_DETACH_RAM(a, x) do { } while(0)
+#define DIVA_OS_MEM_DETACH_PORT(a, x) do { } while(0)
+#define DIVA_OS_MEM_DETACH_PROM(a, x) do { } while(0)
+#define DIVA_OS_MEM_DETACH_CTLREG(a, x) do { } while(0)
+#define DIVA_OS_MEM_DETACH_RESET(a, x) do { } while(0)
+#define DIVA_OS_MEM_DETACH_CFG(a, x) do { } while(0)
+#define DIVA_OS_MEM_DETACH_ADDRESS(a, x) do { } while(0)
+#define DIVA_OS_MEM_DETACH_CONFIG(a, x) do { } while(0)
+#define DIVA_OS_MEM_DETACH_CONTROL(a, x) do { } while(0)
+
+#define DIVA_INVALID_FILE_HANDLE ((dword)(-1))
+
+#define DIVAS_CONTAINING_RECORD(address, type, field) \
+ ((type *)((char*)(address) - (char*)(&((type *)0)->field)))
+
+extern int sprintf(char *, const char*, ...);
+
+typedef void* LIST_ENTRY;
+
+typedef char DEVICE_NAME[64];
+typedef struct _ISDN_ADAPTER ISDN_ADAPTER;
+typedef struct _ISDN_ADAPTER* PISDN_ADAPTER;
+
+typedef void (* DIVA_DI_PRINTF) (unsigned char *, ...);
+#include "debuglib.h"
+
+#define dtrc(p) DBG_PRV0(p)
+#define dbug(a,p) DBG_PRV1(p)
+
+
+typedef struct e_info_s E_INFO ;
+
+typedef char diva_os_dependent_devica_name_t[64];
+typedef void* PDEVICE_OBJECT;
+
+struct _diva_os_soft_isr;
+struct _diva_os_timer;
+struct _ISDN_ADAPTER;
+
+void diva_log_info(unsigned char *, ...);
+
+/*
+** XDI DIDD Interface
+*/
+void diva_xdi_didd_register_adapter (int card);
+void diva_xdi_didd_remove_adapter (int card);
+
+/*
+** memory allocation
+*/
+static __inline__ void* diva_os_malloc (unsigned long flags, unsigned long size)
+{
+ void *ret = NULL;
+
+ if (size) {
+ ret = (void *) vmalloc((unsigned int) size);
+ }
+ return (ret);
+}
+static __inline__ void diva_os_free (unsigned long flags, void* ptr)
+{
+ vfree(ptr);
+}
+
+/*
+** use skbuffs for message buffer
+*/
+typedef struct sk_buff diva_os_message_buffer_s;
+diva_os_message_buffer_s *diva_os_alloc_message_buffer(unsigned long size, void **data_buf);
+void diva_os_free_message_buffer(diva_os_message_buffer_s *dmb);
+#define DIVA_MESSAGE_BUFFER_LEN(x) x->len
+#define DIVA_MESSAGE_BUFFER_DATA(x) x->data
+
+/*
+** mSeconds waiting
+*/
+static __inline__ void diva_os_sleep(dword mSec)
+{
+ msleep(mSec);
+}
+static __inline__ void diva_os_wait(dword mSec)
+{
+ mdelay(mSec);
+}
+
+/*
+** PCI Configuration space access
+*/
+void PCIwrite (byte bus, byte func, int offset, void* data, int length, void* pci_dev_handle);
+void PCIread (byte bus, byte func, int offset, void* data, int length, void* pci_dev_handle);
+
+/*
+** I/O Port utilities
+*/
+int diva_os_register_io_port (void *adapter, int register, unsigned long port,
+ unsigned long length, const char* name, int id);
+/*
+** I/O port access abstraction
+*/
+byte inpp (void __iomem *);
+word inppw (void __iomem *);
+void inppw_buffer (void __iomem *, void*, int);
+void outppw (void __iomem *, word);
+void outppw_buffer (void __iomem * , void*, int);
+void outpp (void __iomem *, word);
+
+/*
+** IRQ
+*/
+typedef struct _diva_os_adapter_irq_info {
+ byte irq_nr;
+ int registered;
+ char irq_name[24];
+} diva_os_adapter_irq_info_t;
+int diva_os_register_irq (void* context, byte irq, const char* name);
+void diva_os_remove_irq (void* context, byte irq);
+
+#define diva_os_in_irq() in_irq()
+
+/*
+** Spin Lock framework
+*/
+typedef long diva_os_spin_lock_magic_t;
+typedef spinlock_t diva_os_spin_lock_t;
+static __inline__ int diva_os_initialize_spin_lock (spinlock_t *lock, void * unused) { \
+ spin_lock_init (lock); return(0); }
+static __inline__ void diva_os_enter_spin_lock (diva_os_spin_lock_t* a, \
+ diva_os_spin_lock_magic_t* old_irql, \
+ void* dbg) { spin_lock_bh(a); }
+static __inline__ void diva_os_leave_spin_lock (diva_os_spin_lock_t* a, \
+ diva_os_spin_lock_magic_t* old_irql, \
+ void* dbg) { spin_unlock_bh(a); }
+
+#define diva_os_destroy_spin_lock(a,b) do { } while(0)
+
+/*
+** Deffered processing framework
+*/
+typedef int (*diva_os_isr_callback_t)(struct _ISDN_ADAPTER*);
+typedef void (*diva_os_soft_isr_callback_t)(struct _diva_os_soft_isr* psoft_isr, void* context);
+
+typedef struct _diva_os_soft_isr {
+ void* object;
+ diva_os_soft_isr_callback_t callback;
+ void* callback_context;
+ char dpc_thread_name[24];
+} diva_os_soft_isr_t;
+
+int diva_os_initialize_soft_isr (diva_os_soft_isr_t* psoft_isr, diva_os_soft_isr_callback_t callback, void* callback_context);
+int diva_os_schedule_soft_isr (diva_os_soft_isr_t* psoft_isr);
+int diva_os_cancel_soft_isr (diva_os_soft_isr_t* psoft_isr);
+void diva_os_remove_soft_isr (diva_os_soft_isr_t* psoft_isr);
+
+/*
+ Get time service
+ */
+void diva_os_get_time (dword* sec, dword* usec);
+
+/*
+** atomic operation, fake because we use threads
+*/
+typedef int diva_os_atomic_t;
+static diva_os_atomic_t __inline__
+diva_os_atomic_increment(diva_os_atomic_t* pv)
+{
+ *pv += 1;
+ return (*pv);
+}
+static diva_os_atomic_t __inline__
+diva_os_atomic_decrement(diva_os_atomic_t* pv)
+{
+ *pv -= 1;
+ return (*pv);
+}
+
+/*
+** CAPI SECTION
+*/
+#define NO_CORNETN
+#define IMPLEMENT_DTMF 1
+#define IMPLEMENT_ECHO_CANCELLER 1
+#define IMPLEMENT_RTP 1
+#define IMPLEMENT_T38 1
+#define IMPLEMENT_FAX_SUB_SEP_PWD 1
+#define IMPLEMENT_V18 1
+#define IMPLEMENT_DTMF_TONE 1
+#define IMPLEMENT_PIAFS 1
+#define IMPLEMENT_FAX_PAPER_FORMATS 1
+#define IMPLEMENT_VOWN 1
+#define IMPLEMENT_CAPIDTMF 1
+#define IMPLEMENT_FAX_NONSTANDARD 1
+#define VSWITCH_SUPPORT 1
+
+#define IMPLEMENT_MARKED_OK_AFTER_FC 1
+
+#define DIVA_IDI_RX_DMA 1
+
+/*
+** endian macros
+**
+** If only... In some cases we did use them for endianness conversion;
+** unfortunately, other uses were real iomem accesses.
+*/
+#define READ_BYTE(addr) readb(addr)
+#define READ_WORD(addr) readw(addr)
+#define READ_DWORD(addr) readl(addr)
+
+#define WRITE_BYTE(addr,v) writeb(v,addr)
+#define WRITE_WORD(addr,v) writew(v,addr)
+#define WRITE_DWORD(addr,v) writel(v,addr)
+
+static inline __u16 GET_WORD(void *addr)
+{
+ return le16_to_cpu(*(__le16 *)addr);
+}
+static inline __u32 GET_DWORD(void *addr)
+{
+ return le32_to_cpu(*(__le32 *)addr);
+}
+static inline void PUT_WORD(void *addr, __u16 v)
+{
+ *(__le16 *)addr = cpu_to_le16(v);
+}
+static inline void PUT_DWORD(void *addr, __u32 v)
+{
+ *(__le32 *)addr = cpu_to_le32(v);
+}
+
+/*
+** 32/64 bit macors
+*/
+#ifdef BITS_PER_LONG
+ #if BITS_PER_LONG > 32
+ #define PLATFORM_GT_32BIT
+ #define ULongToPtr(x) (void *)(unsigned long)(x)
+ #endif
+#endif
+
+/*
+** undef os definitions of macros we use
+*/
+#undef ID_MASK
+#undef N_DATA
+#undef ADDR
+
+/*
+** dump file
+*/
+#define diva_os_dump_file_t char
+#define diva_os_board_trace_t char
+#define diva_os_dump_file(__x__) do { } while(0)
+
+/*
+** size of internal arrays
+*/
+#define MAX_DESCRIPTORS 64
+
+#endif /* __PLATFORM_H__ */
diff --git a/drivers/isdn/hardware/eicon/pr_pc.h b/drivers/isdn/hardware/eicon/pr_pc.h
new file mode 100644
index 0000000..bf49a5a
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/pr_pc.h
@@ -0,0 +1,76 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+struct pr_ram {
+ word NextReq; /* pointer to next Req Buffer */
+ word NextRc; /* pointer to next Rc Buffer */
+ word NextInd; /* pointer to next Ind Buffer */
+ byte ReqInput; /* number of Req Buffers sent */
+ byte ReqOutput; /* number of Req Buffers returned */
+ byte ReqReserved; /* number of Req Buffers reserved */
+ byte Int; /* ISDN-P interrupt */
+ byte XLock; /* Lock field for arbitration */
+ byte RcOutput; /* number of Rc buffers received */
+ byte IndOutput; /* number of Ind buffers received */
+ byte IMask; /* Interrupt Mask Flag */
+ byte Reserved1[2]; /* reserved field, do not use */
+ byte ReadyInt; /* request field for ready interrupt */
+ byte Reserved2[12]; /* reserved field, do not use */
+ byte InterfaceType; /* interface type 1=16K interface */
+ word Signature; /* ISDN-P initialized indication */
+ byte B[1]; /* buffer space for Req,Ind and Rc */
+};
+typedef struct {
+ word next;
+ byte Req;
+ byte ReqId;
+ byte ReqCh;
+ byte Reserved1;
+ word Reference;
+ byte Reserved[8];
+ PBUFFER XBuffer;
+} REQ;
+typedef struct {
+ word next;
+ byte Rc;
+ byte RcId;
+ byte RcCh;
+ byte Reserved1;
+ word Reference;
+ byte Reserved2[8];
+} RC;
+typedef struct {
+ word next;
+ byte Ind;
+ byte IndId;
+ byte IndCh;
+ byte MInd;
+ word MLength;
+ word Reference;
+ byte RNR;
+ byte Reserved;
+ dword Ack;
+ PBUFFER RBuffer;
+} IND;
diff --git a/drivers/isdn/hardware/eicon/s_4bri.c b/drivers/isdn/hardware/eicon/s_4bri.c
new file mode 100644
index 0000000..25c5d7f
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/s_4bri.c
@@ -0,0 +1,510 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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 "platform.h"
+#include "di_defs.h"
+#include "pc.h"
+#include "pr_pc.h"
+#include "di.h"
+#include "mi_pc.h"
+#include "pc_maint.h"
+#include "divasync.h"
+#include "pc_init.h"
+#include "io.h"
+#include "helpers.h"
+#include "dsrv4bri.h"
+#include "dsp_defs.h"
+#include "sdp_hdr.h"
+
+/*****************************************************************************/
+#define MAX_XLOG_SIZE (64 * 1024)
+
+/* --------------------------------------------------------------------------
+ Recovery XLOG from QBRI Card
+ -------------------------------------------------------------------------- */
+static void qBri_cpu_trapped (PISDN_ADAPTER IoAdapter) {
+ byte __iomem *base ;
+ word *Xlog ;
+ dword regs[4], TrapID, offset, size ;
+ Xdesc xlogDesc ;
+ int factor = (IoAdapter->tasks == 1) ? 1 : 2;
+
+/*
+ * check for trapped MIPS 46xx CPU, dump exception frame
+ */
+
+ base = DIVA_OS_MEM_ATTACH_CONTROL(IoAdapter);
+ offset = IoAdapter->ControllerNumber * (IoAdapter->MemorySize >> factor) ;
+
+ TrapID = READ_DWORD(&base[0x80]) ;
+
+ if ( (TrapID == 0x99999999) || (TrapID == 0x99999901) )
+ {
+ dump_trap_frame (IoAdapter, &base[0x90]) ;
+ IoAdapter->trapped = 1 ;
+ }
+
+ regs[0] = READ_DWORD((base + offset) + 0x70);
+ regs[1] = READ_DWORD((base + offset) + 0x74);
+ regs[2] = READ_DWORD((base + offset) + 0x78);
+ regs[3] = READ_DWORD((base + offset) + 0x7c);
+ regs[0] &= IoAdapter->MemorySize - 1 ;
+
+ if ( (regs[0] >= offset)
+ && (regs[0] < offset + (IoAdapter->MemorySize >> factor) - 1) )
+ {
+ if ( !(Xlog = (word *)diva_os_malloc (0, MAX_XLOG_SIZE)) ) {
+ DIVA_OS_MEM_DETACH_CONTROL(IoAdapter, base);
+ return ;
+ }
+
+ size = offset + (IoAdapter->MemorySize >> factor) - regs[0] ;
+ if ( size > MAX_XLOG_SIZE )
+ size = MAX_XLOG_SIZE ;
+ memcpy_fromio (Xlog, &base[regs[0]], size) ;
+ xlogDesc.buf = Xlog ;
+ xlogDesc.cnt = READ_WORD(&base[regs[1] & (IoAdapter->MemorySize - 1)]) ;
+ xlogDesc.out = READ_WORD(&base[regs[2] & (IoAdapter->MemorySize - 1)]) ;
+ dump_xlog_buffer (IoAdapter, &xlogDesc) ;
+ diva_os_free (0, Xlog) ;
+ IoAdapter->trapped = 2 ;
+ }
+ DIVA_OS_MEM_DETACH_CONTROL(IoAdapter, base);
+}
+
+/* --------------------------------------------------------------------------
+ Reset QBRI Hardware
+ -------------------------------------------------------------------------- */
+static void reset_qBri_hardware (PISDN_ADAPTER IoAdapter) {
+ word volatile __iomem *qBriReset ;
+ byte volatile __iomem *qBriCntrl ;
+ byte volatile __iomem *p ;
+
+ qBriReset = (word volatile __iomem *)DIVA_OS_MEM_ATTACH_PROM(IoAdapter);
+ WRITE_WORD(qBriReset, READ_WORD(qBriReset) | PLX9054_SOFT_RESET) ;
+ diva_os_wait (1) ;
+ WRITE_WORD(qBriReset, READ_WORD(qBriReset) & ~PLX9054_SOFT_RESET) ;
+ diva_os_wait (1);
+ WRITE_WORD(qBriReset, READ_WORD(qBriReset) | PLX9054_RELOAD_EEPROM) ;
+ diva_os_wait (1) ;
+ WRITE_WORD(qBriReset, READ_WORD(qBriReset) & ~PLX9054_RELOAD_EEPROM) ;
+ diva_os_wait (1);
+ DIVA_OS_MEM_DETACH_PROM(IoAdapter, qBriReset);
+
+ qBriCntrl = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
+ p = &qBriCntrl[DIVA_4BRI_REVISION(IoAdapter) ? (MQ2_BREG_RISC) : (MQ_BREG_RISC)];
+ WRITE_DWORD(p, 0) ;
+ DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, qBriCntrl);
+
+ DBG_TRC(("resetted board @ reset addr 0x%08lx", qBriReset))
+ DBG_TRC(("resetted board @ cntrl addr 0x%08lx", p))
+}
+
+/* --------------------------------------------------------------------------
+ Start Card CPU
+ -------------------------------------------------------------------------- */
+void start_qBri_hardware (PISDN_ADAPTER IoAdapter) {
+ byte volatile __iomem *qBriReset ;
+ byte volatile __iomem *p ;
+
+ p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
+ qBriReset = &p[(DIVA_4BRI_REVISION(IoAdapter)) ? (MQ2_BREG_RISC) : (MQ_BREG_RISC)];
+ WRITE_DWORD(qBriReset, MQ_RISC_COLD_RESET_MASK) ;
+ diva_os_wait (2) ;
+ WRITE_DWORD(qBriReset, MQ_RISC_WARM_RESET_MASK | MQ_RISC_COLD_RESET_MASK) ;
+ diva_os_wait (10) ;
+ DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p);
+
+ DBG_TRC(("started processor @ addr 0x%08lx", qBriReset))
+}
+
+/* --------------------------------------------------------------------------
+ Stop Card CPU
+ -------------------------------------------------------------------------- */
+static void stop_qBri_hardware (PISDN_ADAPTER IoAdapter) {
+ byte volatile __iomem *p ;
+ dword volatile __iomem *qBriReset ;
+ dword volatile __iomem *qBriIrq ;
+ dword volatile __iomem *qBriIsacDspReset ;
+ int rev2 = DIVA_4BRI_REVISION(IoAdapter);
+ int reset_offset = rev2 ? (MQ2_BREG_RISC) : (MQ_BREG_RISC);
+ int irq_offset = rev2 ? (MQ2_BREG_IRQ_TEST) : (MQ_BREG_IRQ_TEST);
+ int hw_offset = rev2 ? (MQ2_ISAC_DSP_RESET) : (MQ_ISAC_DSP_RESET);
+
+ if ( IoAdapter->ControllerNumber > 0 )
+ return ;
+ p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
+ qBriReset = (dword volatile __iomem *)&p[reset_offset];
+ qBriIsacDspReset = (dword volatile __iomem *)&p[hw_offset];
+/*
+ * clear interrupt line (reset Local Interrupt Test Register)
+ */
+ WRITE_DWORD(qBriReset, 0) ;
+ WRITE_DWORD(qBriIsacDspReset, 0) ;
+ DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p);
+
+ p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter);
+ WRITE_BYTE(&p[PLX9054_INTCSR], 0x00); /* disable PCI interrupts */
+ DIVA_OS_MEM_DETACH_RESET(IoAdapter, p);
+
+ p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
+ qBriIrq = (dword volatile __iomem *)&p[irq_offset];
+ WRITE_DWORD(qBriIrq, MQ_IRQ_REQ_OFF) ;
+ DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p);
+
+ DBG_TRC(("stopped processor @ addr 0x%08lx", qBriReset))
+
+}
+
+/* --------------------------------------------------------------------------
+ FPGA download
+ -------------------------------------------------------------------------- */
+#define FPGA_NAME_OFFSET 0x10
+
+static byte * qBri_check_FPGAsrc (PISDN_ADAPTER IoAdapter, char *FileName,
+ dword *Length, dword *code) {
+ byte *File ;
+ char *fpgaFile, *fpgaType, *fpgaDate, *fpgaTime ;
+ dword fpgaFlen, fpgaTlen, fpgaDlen, cnt, year, i ;
+
+ if (!(File = (byte *)xdiLoadFile (FileName, Length, 0))) {
+ return (NULL) ;
+ }
+/*
+ * scan file until FF and put id string into buffer
+ */
+ for ( i = 0 ; File[i] != 0xff ; )
+ {
+ if ( ++i >= *Length )
+ {
+ DBG_FTL(("FPGA download: start of data header not found"))
+ xdiFreeFile (File) ;
+ return (NULL) ;
+ }
+ }
+ *code = i++ ;
+
+ if ( (File[i] & 0xF0) != 0x20 )
+ {
+ DBG_FTL(("FPGA download: data header corrupted"))
+ xdiFreeFile (File) ;
+ return (NULL) ;
+ }
+ fpgaFlen = (dword) File[FPGA_NAME_OFFSET - 1] ;
+ if ( fpgaFlen == 0 )
+ fpgaFlen = 12 ;
+ fpgaFile = (char *)&File[FPGA_NAME_OFFSET] ;
+ fpgaTlen = (dword) fpgaFile[fpgaFlen + 2] ;
+ if ( fpgaTlen == 0 )
+ fpgaTlen = 10 ;
+ fpgaType = (char *)&fpgaFile[fpgaFlen + 3] ;
+ fpgaDlen = (dword) fpgaType[fpgaTlen + 2] ;
+ if ( fpgaDlen == 0 )
+ fpgaDlen = 11 ;
+ fpgaDate = (char *)&fpgaType[fpgaTlen + 3] ;
+ fpgaTime = (char *)&fpgaDate[fpgaDlen + 3] ;
+ cnt = (dword)(((File[ i ] & 0x0F) << 20) + (File[i + 1] << 12)
+ + (File[i + 2] << 4) + (File[i + 3] >> 4)) ;
+
+ if ( (dword)(i + (cnt / 8)) > *Length )
+ {
+ DBG_FTL(("FPGA download: '%s' file too small (%ld < %ld)",
+ FileName, *Length, code + ((cnt + 7) / 8) ))
+ xdiFreeFile (File) ;
+ return (NULL) ;
+ }
+ i = 0 ;
+ do
+ {
+ while ( (fpgaDate[i] != '\0')
+ && ((fpgaDate[i] < '0') || (fpgaDate[i] > '9')) )
+ {
+ i++;
+ }
+ year = 0 ;
+ while ( (fpgaDate[i] >= '0') && (fpgaDate[i] <= '9') )
+ year = year * 10 + (fpgaDate[i++] - '0') ;
+ } while ( (year < 2000) && (fpgaDate[i] != '\0') );
+
+ switch (IoAdapter->cardType) {
+ case CARDTYPE_DIVASRV_B_2F_PCI:
+ break;
+
+ default:
+ if ( year >= 2001 ) {
+ IoAdapter->fpga_features |= PCINIT_FPGA_PLX_ACCESS_SUPPORTED ;
+ }
+ }
+
+ DBG_LOG(("FPGA[%s] file %s (%s %s) len %d",
+ fpgaType, fpgaFile, fpgaDate, fpgaTime, cnt))
+ return (File) ;
+}
+
+/******************************************************************************/
+
+#define FPGA_PROG 0x0001 /* PROG enable low */
+#define FPGA_BUSY 0x0002 /* BUSY high, DONE low */
+#define FPGA_CS 0x000C /* Enable I/O pins */
+#define FPGA_CCLK 0x0100
+#define FPGA_DOUT 0x0400
+#define FPGA_DIN FPGA_DOUT /* bidirectional I/O */
+
+int qBri_FPGA_download (PISDN_ADAPTER IoAdapter) {
+ int bit ;
+ byte *File ;
+ dword code, FileLength ;
+ word volatile __iomem *addr = (word volatile __iomem *)DIVA_OS_MEM_ATTACH_PROM(IoAdapter);
+ word val, baseval = FPGA_CS | FPGA_PROG ;
+
+
+
+ if (DIVA_4BRI_REVISION(IoAdapter))
+ {
+ char* name;
+
+ switch (IoAdapter->cardType) {
+ case CARDTYPE_DIVASRV_B_2F_PCI:
+ name = "dsbri2f.bit";
+ break;
+
+ case CARDTYPE_DIVASRV_B_2M_V2_PCI:
+ case CARDTYPE_DIVASRV_VOICE_B_2M_V2_PCI:
+ name = "dsbri2m.bit";
+ break;
+
+ default:
+ name = "ds4bri2.bit";
+ }
+
+ File = qBri_check_FPGAsrc (IoAdapter, name,
+ &FileLength, &code);
+ }
+ else
+ {
+ File = qBri_check_FPGAsrc (IoAdapter, "ds4bri.bit",
+ &FileLength, &code) ;
+ }
+ if ( !File ) {
+ DIVA_OS_MEM_DETACH_PROM(IoAdapter, addr);
+ return (0) ;
+ }
+/*
+ * prepare download, pulse PROGRAM pin down.
+ */
+ WRITE_WORD(addr, baseval & ~FPGA_PROG) ; /* PROGRAM low pulse */
+ WRITE_WORD(addr, baseval) ; /* release */
+ diva_os_wait (50) ; /* wait until FPGA finished internal memory clear */
+/*
+ * check done pin, must be low
+ */
+ if ( READ_WORD(addr) & FPGA_BUSY )
+ {
+ DBG_FTL(("FPGA download: acknowledge for FPGA memory clear missing"))
+ xdiFreeFile (File) ;
+ DIVA_OS_MEM_DETACH_PROM(IoAdapter, addr);
+ return (0) ;
+ }
+/*
+ * put data onto the FPGA
+ */
+ while ( code < FileLength )
+ {
+ val = ((word)File[code++]) << 3 ;
+
+ for ( bit = 8 ; bit-- > 0 ; val <<= 1 ) /* put byte onto FPGA */
+ {
+ baseval &= ~FPGA_DOUT ; /* clr data bit */
+ baseval |= (val & FPGA_DOUT) ; /* copy data bit */
+ WRITE_WORD(addr, baseval) ;
+ WRITE_WORD(addr, baseval | FPGA_CCLK) ; /* set CCLK hi */
+ WRITE_WORD(addr, baseval | FPGA_CCLK) ; /* set CCLK hi */
+ WRITE_WORD(addr, baseval) ; /* set CCLK lo */
+ }
+ }
+ xdiFreeFile (File) ;
+ diva_os_wait (100) ;
+ val = READ_WORD(addr) ;
+
+ DIVA_OS_MEM_DETACH_PROM(IoAdapter, addr);
+
+ if ( !(val & FPGA_BUSY) )
+ {
+ DBG_FTL(("FPGA download: chip remains in busy state (0x%04x)", val))
+ return (0) ;
+ }
+
+ return (1) ;
+}
+
+static int load_qBri_hardware (PISDN_ADAPTER IoAdapter) {
+ return (0);
+}
+
+/* --------------------------------------------------------------------------
+ Card ISR
+ -------------------------------------------------------------------------- */
+static int qBri_ISR (struct _ISDN_ADAPTER* IoAdapter) {
+ dword volatile __iomem *qBriIrq ;
+
+ PADAPTER_LIST_ENTRY QuadroList = IoAdapter->QuadroList ;
+
+ word i ;
+ int serviced = 0 ;
+ byte __iomem *p;
+
+ p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter);
+
+ if ( !(READ_BYTE(&p[PLX9054_INTCSR]) & 0x80) ) {
+ DIVA_OS_MEM_DETACH_RESET(IoAdapter, p);
+ return (0) ;
+ }
+ DIVA_OS_MEM_DETACH_RESET(IoAdapter, p);
+
+/*
+ * clear interrupt line (reset Local Interrupt Test Register)
+ */
+ p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
+ qBriIrq = (dword volatile __iomem *)(&p[DIVA_4BRI_REVISION(IoAdapter) ? (MQ2_BREG_IRQ_TEST) : (MQ_BREG_IRQ_TEST)]);
+ WRITE_DWORD(qBriIrq, MQ_IRQ_REQ_OFF) ;
+ DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p);
+
+ for ( i = 0 ; i < IoAdapter->tasks; ++i )
+ {
+ IoAdapter = QuadroList->QuadroAdapter[i] ;
+
+ if ( IoAdapter && IoAdapter->Initialized
+ && IoAdapter->tst_irq (&IoAdapter->a) )
+ {
+ IoAdapter->IrqCount++ ;
+ serviced = 1 ;
+ diva_os_schedule_soft_isr (&IoAdapter->isr_soft_isr);
+ }
+ }
+
+ return (serviced) ;
+}
+
+/* --------------------------------------------------------------------------
+ Does disable the interrupt on the card
+ -------------------------------------------------------------------------- */
+static void disable_qBri_interrupt (PISDN_ADAPTER IoAdapter) {
+ dword volatile __iomem *qBriIrq ;
+ byte __iomem *p;
+
+ if ( IoAdapter->ControllerNumber > 0 )
+ return ;
+/*
+ * clear interrupt line (reset Local Interrupt Test Register)
+ */
+ p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter);
+ WRITE_BYTE(&p[PLX9054_INTCSR], 0x00); /* disable PCI interrupts */
+ DIVA_OS_MEM_DETACH_RESET(IoAdapter, p);
+
+ p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
+ qBriIrq = (dword volatile __iomem *)(&p[DIVA_4BRI_REVISION(IoAdapter) ? (MQ2_BREG_IRQ_TEST) : (MQ_BREG_IRQ_TEST)]);
+ WRITE_DWORD(qBriIrq, MQ_IRQ_REQ_OFF) ;
+ DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p);
+}
+
+/* --------------------------------------------------------------------------
+ Install Adapter Entry Points
+ -------------------------------------------------------------------------- */
+static void set_common_qBri_functions (PISDN_ADAPTER IoAdapter) {
+ ADAPTER *a;
+
+ a = &IoAdapter->a ;
+
+ a->ram_in = mem_in ;
+ a->ram_inw = mem_inw ;
+ a->ram_in_buffer = mem_in_buffer ;
+ a->ram_look_ahead = mem_look_ahead ;
+ a->ram_out = mem_out ;
+ a->ram_outw = mem_outw ;
+ a->ram_out_buffer = mem_out_buffer ;
+ a->ram_inc = mem_inc ;
+
+ IoAdapter->out = pr_out ;
+ IoAdapter->dpc = pr_dpc ;
+ IoAdapter->tst_irq = scom_test_int ;
+ IoAdapter->clr_irq = scom_clear_int ;
+ IoAdapter->pcm = (struct pc_maint *)MIPS_MAINT_OFFS ;
+
+ IoAdapter->load = load_qBri_hardware ;
+
+ IoAdapter->disIrq = disable_qBri_interrupt ;
+ IoAdapter->rstFnc = reset_qBri_hardware ;
+ IoAdapter->stop = stop_qBri_hardware ;
+ IoAdapter->trapFnc = qBri_cpu_trapped ;
+
+ IoAdapter->diva_isr_handler = qBri_ISR;
+
+ IoAdapter->a.io = (void*)IoAdapter ;
+}
+
+static void set_qBri_functions (PISDN_ADAPTER IoAdapter) {
+ if (!IoAdapter->tasks) {
+ IoAdapter->tasks = MQ_INSTANCE_COUNT;
+ }
+ IoAdapter->MemorySize = MQ_MEMORY_SIZE ;
+ set_common_qBri_functions (IoAdapter) ;
+ diva_os_set_qBri_functions (IoAdapter) ;
+}
+
+static void set_qBri2_functions (PISDN_ADAPTER IoAdapter) {
+ if (!IoAdapter->tasks) {
+ IoAdapter->tasks = MQ_INSTANCE_COUNT;
+ }
+ IoAdapter->MemorySize = (IoAdapter->tasks == 1) ? BRI2_MEMORY_SIZE : MQ2_MEMORY_SIZE;
+ set_common_qBri_functions (IoAdapter) ;
+ diva_os_set_qBri2_functions (IoAdapter) ;
+}
+
+/******************************************************************************/
+
+void prepare_qBri_functions (PISDN_ADAPTER IoAdapter) {
+
+ set_qBri_functions (IoAdapter->QuadroList->QuadroAdapter[0]) ;
+ set_qBri_functions (IoAdapter->QuadroList->QuadroAdapter[1]) ;
+ set_qBri_functions (IoAdapter->QuadroList->QuadroAdapter[2]) ;
+ set_qBri_functions (IoAdapter->QuadroList->QuadroAdapter[3]) ;
+
+}
+
+void prepare_qBri2_functions (PISDN_ADAPTER IoAdapter) {
+ if (!IoAdapter->tasks) {
+ IoAdapter->tasks = MQ_INSTANCE_COUNT;
+ }
+
+ set_qBri2_functions (IoAdapter->QuadroList->QuadroAdapter[0]) ;
+ if (IoAdapter->tasks > 1) {
+ set_qBri2_functions (IoAdapter->QuadroList->QuadroAdapter[1]) ;
+ set_qBri2_functions (IoAdapter->QuadroList->QuadroAdapter[2]) ;
+ set_qBri2_functions (IoAdapter->QuadroList->QuadroAdapter[3]) ;
+ }
+
+}
+
+/* -------------------------------------------------------------------------- */
diff --git a/drivers/isdn/hardware/eicon/s_bri.c b/drivers/isdn/hardware/eicon/s_bri.c
new file mode 100644
index 0000000..5c87552
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/s_bri.c
@@ -0,0 +1,191 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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 "platform.h"
+#include "di_defs.h"
+#include "pc.h"
+#include "pr_pc.h"
+#include "di.h"
+#include "mi_pc.h"
+#include "pc_maint.h"
+#include "divasync.h"
+#include "io.h"
+#include "helpers.h"
+#include "dsrv_bri.h"
+#include "dsp_defs.h"
+/*****************************************************************************/
+#define MAX_XLOG_SIZE (64 * 1024)
+/* --------------------------------------------------------------------------
+ Investigate card state, recovery trace buffer
+ -------------------------------------------------------------------------- */
+static void bri_cpu_trapped (PISDN_ADAPTER IoAdapter) {
+ byte __iomem *addrHi, *addrLo, *ioaddr ;
+ word *Xlog ;
+ dword regs[4], i, size ;
+ Xdesc xlogDesc ;
+ byte __iomem *Port;
+/*
+ * first read pointers and trap frame
+ */
+ if ( !(Xlog = (word *)diva_os_malloc (0, MAX_XLOG_SIZE)) )
+ return ;
+ Port = DIVA_OS_MEM_ATTACH_PORT(IoAdapter);
+ addrHi = Port + ((IoAdapter->Properties.Bus == BUS_PCI) ? M_PCI_ADDRH : ADDRH) ;
+ addrLo = Port + ADDR ;
+ ioaddr = Port + DATA ;
+ outpp (addrHi, 0) ;
+ outppw (addrLo, 0) ;
+ for ( i = 0 ; i < 0x100 ; Xlog[i++] = inppw(ioaddr) ) ;
+/*
+ * check for trapped MIPS 3xxx CPU, dump only exception frame
+ */
+ if ( GET_DWORD(&Xlog[0x80 / sizeof(Xlog[0])]) == 0x99999999 )
+ {
+ dump_trap_frame (IoAdapter, &((byte *)Xlog)[0x90]) ;
+ IoAdapter->trapped = 1 ;
+ }
+ regs[0] = GET_DWORD(&((byte *)Xlog)[0x70]);
+ regs[1] = GET_DWORD(&((byte *)Xlog)[0x74]);
+ regs[2] = GET_DWORD(&((byte *)Xlog)[0x78]);
+ regs[3] = GET_DWORD(&((byte *)Xlog)[0x7c]);
+ outpp (addrHi, (regs[1] >> 16) & 0x7F) ;
+ outppw (addrLo, regs[1] & 0xFFFF) ;
+ xlogDesc.cnt = inppw(ioaddr) ;
+ outpp (addrHi, (regs[2] >> 16) & 0x7F) ;
+ outppw (addrLo, regs[2] & 0xFFFF) ;
+ xlogDesc.out = inppw(ioaddr) ;
+ xlogDesc.buf = Xlog ;
+ regs[0] &= IoAdapter->MemorySize - 1 ;
+ if ( (regs[0] < IoAdapter->MemorySize - 1) )
+ {
+ size = IoAdapter->MemorySize - regs[0] ;
+ if ( size > MAX_XLOG_SIZE )
+ size = MAX_XLOG_SIZE ;
+ for ( i = 0 ; i < (size / sizeof(*Xlog)) ; regs[0] += 2 )
+ {
+ outpp (addrHi, (regs[0] >> 16) & 0x7F) ;
+ outppw (addrLo, regs[0] & 0xFFFF) ;
+ Xlog[i++] = inppw(ioaddr) ;
+ }
+ dump_xlog_buffer (IoAdapter, &xlogDesc) ;
+ diva_os_free (0, Xlog) ;
+ IoAdapter->trapped = 2 ;
+ }
+ outpp (addrHi, (byte)((BRI_UNCACHED_ADDR (IoAdapter->MemoryBase + IoAdapter->MemorySize -
+ BRI_SHARED_RAM_SIZE)) >> 16)) ;
+ outppw (addrLo, 0x00) ;
+ DIVA_OS_MEM_DETACH_PORT(IoAdapter, Port);
+}
+/* ---------------------------------------------------------------------
+ Reset hardware
+ --------------------------------------------------------------------- */
+static void reset_bri_hardware (PISDN_ADAPTER IoAdapter) {
+ byte __iomem *p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
+ outpp (p, 0x00) ;
+ DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p);
+}
+/* ---------------------------------------------------------------------
+ Halt system
+ --------------------------------------------------------------------- */
+static void stop_bri_hardware (PISDN_ADAPTER IoAdapter) {
+ byte __iomem *p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter);
+ if (p) {
+ outpp (p, 0x00) ; /* disable interrupts ! */
+ }
+ DIVA_OS_MEM_DETACH_RESET(IoAdapter, p);
+ p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
+ outpp (p, 0x00) ; /* clear int, halt cpu */
+ DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p);
+}
+static int load_bri_hardware (PISDN_ADAPTER IoAdapter) {
+ return (0);
+}
+/******************************************************************************/
+static int bri_ISR (struct _ISDN_ADAPTER* IoAdapter) {
+ byte __iomem *p;
+
+ p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
+ if ( !(inpp (p) & 0x01) ) {
+ DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p);
+ return (0) ;
+ }
+ /*
+ clear interrupt line
+ */
+ outpp (p, 0x08) ;
+ DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p);
+ IoAdapter->IrqCount++ ;
+ if ( IoAdapter->Initialized ) {
+ diva_os_schedule_soft_isr (&IoAdapter->isr_soft_isr);
+ }
+ return (1) ;
+}
+/* --------------------------------------------------------------------------
+ Disable IRQ in the card hardware
+ -------------------------------------------------------------------------- */
+static void disable_bri_interrupt (PISDN_ADAPTER IoAdapter) {
+ byte __iomem *p;
+ p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter);
+ if ( p )
+ {
+ outpp (p, 0x00) ; /* disable interrupts ! */
+ }
+ DIVA_OS_MEM_DETACH_RESET(IoAdapter, p);
+ p = DIVA_OS_MEM_ATTACH_CTLREG(IoAdapter);
+ outpp (p, 0x00) ; /* clear int, halt cpu */
+ DIVA_OS_MEM_DETACH_CTLREG(IoAdapter, p);
+}
+/* -------------------------------------------------------------------------
+ Fill card entry points
+ ------------------------------------------------------------------------- */
+void prepare_maestra_functions (PISDN_ADAPTER IoAdapter) {
+ ADAPTER *a = &IoAdapter->a ;
+ a->ram_in = io_in ;
+ a->ram_inw = io_inw ;
+ a->ram_in_buffer = io_in_buffer ;
+ a->ram_look_ahead = io_look_ahead ;
+ a->ram_out = io_out ;
+ a->ram_outw = io_outw ;
+ a->ram_out_buffer = io_out_buffer ;
+ a->ram_inc = io_inc ;
+ IoAdapter->MemoryBase = BRI_MEMORY_BASE ;
+ IoAdapter->MemorySize = BRI_MEMORY_SIZE ;
+ IoAdapter->out = pr_out ;
+ IoAdapter->dpc = pr_dpc ;
+ IoAdapter->tst_irq = scom_test_int ;
+ IoAdapter->clr_irq = scom_clear_int ;
+ IoAdapter->pcm = (struct pc_maint *)MIPS_MAINT_OFFS ;
+ IoAdapter->load = load_bri_hardware ;
+ IoAdapter->disIrq = disable_bri_interrupt ;
+ IoAdapter->rstFnc = reset_bri_hardware ;
+ IoAdapter->stop = stop_bri_hardware ;
+ IoAdapter->trapFnc = bri_cpu_trapped ;
+ IoAdapter->diva_isr_handler = bri_ISR;
+ /*
+ Prepare OS dependent functions
+ */
+ diva_os_prepare_maestra_functions (IoAdapter);
+}
+/* -------------------------------------------------------------------------- */
diff --git a/drivers/isdn/hardware/eicon/s_pri.c b/drivers/isdn/hardware/eicon/s_pri.c
new file mode 100644
index 0000000..18f2878
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/s_pri.c
@@ -0,0 +1,205 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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 "platform.h"
+#include "di_defs.h"
+#include "pc.h"
+#include "pr_pc.h"
+#include "di.h"
+#include "mi_pc.h"
+#include "pc_maint.h"
+#include "divasync.h"
+#include "io.h"
+#include "helpers.h"
+#include "dsrv_pri.h"
+#include "dsp_defs.h"
+/*****************************************************************************/
+#define MAX_XLOG_SIZE (64 * 1024)
+/* -------------------------------------------------------------------------
+ Does return offset between ADAPTER->ram and real begin of memory
+ ------------------------------------------------------------------------- */
+static dword pri_ram_offset (ADAPTER* a) {
+ return ((dword)MP_SHARED_RAM_OFFSET);
+}
+/* -------------------------------------------------------------------------
+ Recovery XLOG buffer from the card
+ ------------------------------------------------------------------------- */
+static void pri_cpu_trapped (PISDN_ADAPTER IoAdapter) {
+ byte __iomem *base ;
+ word *Xlog ;
+ dword regs[4], TrapID, size ;
+ Xdesc xlogDesc ;
+/*
+ * check for trapped MIPS 46xx CPU, dump exception frame
+ */
+ base = DIVA_OS_MEM_ATTACH_ADDRESS(IoAdapter);
+ TrapID = READ_DWORD(&base[0x80]) ;
+ if ( (TrapID == 0x99999999) || (TrapID == 0x99999901) )
+ {
+ dump_trap_frame (IoAdapter, &base[0x90]) ;
+ IoAdapter->trapped = 1 ;
+ }
+ regs[0] = READ_DWORD(&base[MP_PROTOCOL_OFFSET + 0x70]);
+ regs[1] = READ_DWORD(&base[MP_PROTOCOL_OFFSET + 0x74]);
+ regs[2] = READ_DWORD(&base[MP_PROTOCOL_OFFSET + 0x78]);
+ regs[3] = READ_DWORD(&base[MP_PROTOCOL_OFFSET + 0x7c]);
+ regs[0] &= IoAdapter->MemorySize - 1 ;
+ if ( (regs[0] < IoAdapter->MemorySize - 1) )
+ {
+ if ( !(Xlog = (word *)diva_os_malloc (0, MAX_XLOG_SIZE)) ) {
+ DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, base);
+ return ;
+ }
+ size = IoAdapter->MemorySize - regs[0] ;
+ if ( size > MAX_XLOG_SIZE )
+ size = MAX_XLOG_SIZE ;
+ memcpy_fromio(Xlog, &base[regs[0]], size) ;
+ xlogDesc.buf = Xlog ;
+ xlogDesc.cnt = READ_WORD(&base[regs[1] & (IoAdapter->MemorySize - 1)]) ;
+ xlogDesc.out = READ_WORD(&base[regs[2] & (IoAdapter->MemorySize - 1)]) ;
+ dump_xlog_buffer (IoAdapter, &xlogDesc) ;
+ diva_os_free (0, Xlog) ;
+ IoAdapter->trapped = 2 ;
+ }
+ DIVA_OS_MEM_DETACH_ADDRESS(IoAdapter, base);
+}
+/* -------------------------------------------------------------------------
+ Hardware reset of PRI card
+ ------------------------------------------------------------------------- */
+static void reset_pri_hardware (PISDN_ADAPTER IoAdapter) {
+ byte __iomem *p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter);
+ WRITE_BYTE(p, _MP_RISC_RESET | _MP_LED1 | _MP_LED2);
+ diva_os_wait (50) ;
+ WRITE_BYTE(p, 0x00);
+ diva_os_wait (50) ;
+ DIVA_OS_MEM_DETACH_RESET(IoAdapter, p);
+}
+/* -------------------------------------------------------------------------
+ Stop Card Hardware
+ ------------------------------------------------------------------------- */
+static void stop_pri_hardware (PISDN_ADAPTER IoAdapter) {
+ dword i;
+ byte __iomem *p;
+ dword volatile __iomem *cfgReg = (void __iomem *)DIVA_OS_MEM_ATTACH_CFG(IoAdapter);
+ WRITE_DWORD(&cfgReg[3], 0);
+ WRITE_DWORD(&cfgReg[1], 0);
+ DIVA_OS_MEM_DETACH_CFG(IoAdapter, cfgReg);
+ IoAdapter->a.ram_out (&IoAdapter->a, &RAM->SWReg, SWREG_HALT_CPU) ;
+ i = 0 ;
+ while ( (i < 100) && (IoAdapter->a.ram_in (&IoAdapter->a, &RAM->SWReg) != 0) )
+ {
+ diva_os_wait (1) ;
+ i++ ;
+ }
+ DBG_TRC(("%s: PRI stopped (%d)", IoAdapter->Name, i))
+ cfgReg = (void __iomem *)DIVA_OS_MEM_ATTACH_CFG(IoAdapter);
+ WRITE_DWORD(&cfgReg[0],((dword)(~0x03E00000)));
+ DIVA_OS_MEM_DETACH_CFG(IoAdapter, cfgReg);
+ diva_os_wait (1) ;
+ p = DIVA_OS_MEM_ATTACH_RESET(IoAdapter);
+ WRITE_BYTE(p, _MP_RISC_RESET | _MP_LED1 | _MP_LED2);
+ DIVA_OS_MEM_DETACH_RESET(IoAdapter, p);
+}
+static int load_pri_hardware (PISDN_ADAPTER IoAdapter) {
+ return (0);
+}
+/* --------------------------------------------------------------------------
+ PRI Adapter interrupt Service Routine
+ -------------------------------------------------------------------------- */
+static int pri_ISR (struct _ISDN_ADAPTER* IoAdapter) {
+ byte __iomem *cfg = DIVA_OS_MEM_ATTACH_CFG(IoAdapter);
+ if ( !(READ_DWORD(cfg) & 0x80000000) ) {
+ DIVA_OS_MEM_DETACH_CFG(IoAdapter, cfg);
+ return (0) ;
+ }
+ /*
+ clear interrupt line
+ */
+ WRITE_DWORD(cfg, (dword)~0x03E00000) ;
+ DIVA_OS_MEM_DETACH_CFG(IoAdapter, cfg);
+ IoAdapter->IrqCount++ ;
+ if ( IoAdapter->Initialized )
+ {
+ diva_os_schedule_soft_isr (&IoAdapter->isr_soft_isr);
+ }
+ return (1) ;
+}
+/* -------------------------------------------------------------------------
+ Disable interrupt in the card hardware
+ ------------------------------------------------------------------------- */
+static void disable_pri_interrupt (PISDN_ADAPTER IoAdapter) {
+ dword volatile __iomem *cfgReg = (dword volatile __iomem *)DIVA_OS_MEM_ATTACH_CFG(IoAdapter) ;
+ WRITE_DWORD(&cfgReg[3], 0);
+ WRITE_DWORD(&cfgReg[1], 0);
+ WRITE_DWORD(&cfgReg[0], (dword)(~0x03E00000)) ;
+ DIVA_OS_MEM_DETACH_CFG(IoAdapter, cfgReg);
+}
+/* -------------------------------------------------------------------------
+ Install entry points for PRI Adapter
+ ------------------------------------------------------------------------- */
+static void prepare_common_pri_functions (PISDN_ADAPTER IoAdapter) {
+ ADAPTER *a = &IoAdapter->a ;
+ a->ram_in = mem_in ;
+ a->ram_inw = mem_inw ;
+ a->ram_in_buffer = mem_in_buffer ;
+ a->ram_look_ahead = mem_look_ahead ;
+ a->ram_out = mem_out ;
+ a->ram_outw = mem_outw ;
+ a->ram_out_buffer = mem_out_buffer ;
+ a->ram_inc = mem_inc ;
+ a->ram_offset = pri_ram_offset ;
+ a->ram_out_dw = mem_out_dw;
+ a->ram_in_dw = mem_in_dw;
+ a->istream_wakeup = pr_stream;
+ IoAdapter->out = pr_out ;
+ IoAdapter->dpc = pr_dpc ;
+ IoAdapter->tst_irq = scom_test_int ;
+ IoAdapter->clr_irq = scom_clear_int ;
+ IoAdapter->pcm = (struct pc_maint *)(MIPS_MAINT_OFFS
+ - MP_SHARED_RAM_OFFSET) ;
+ IoAdapter->load = load_pri_hardware ;
+ IoAdapter->disIrq = disable_pri_interrupt ;
+ IoAdapter->rstFnc = reset_pri_hardware ;
+ IoAdapter->stop = stop_pri_hardware ;
+ IoAdapter->trapFnc = pri_cpu_trapped ;
+ IoAdapter->diva_isr_handler = pri_ISR;
+}
+/* -------------------------------------------------------------------------
+ Install entry points for PRI Adapter
+ ------------------------------------------------------------------------- */
+void prepare_pri_functions (PISDN_ADAPTER IoAdapter) {
+ IoAdapter->MemorySize = MP_MEMORY_SIZE ;
+ prepare_common_pri_functions (IoAdapter) ;
+ diva_os_prepare_pri_functions (IoAdapter);
+}
+/* -------------------------------------------------------------------------
+ Install entry points for PRI Rev.2 Adapter
+ ------------------------------------------------------------------------- */
+void prepare_pri2_functions (PISDN_ADAPTER IoAdapter) {
+ IoAdapter->MemorySize = MP2_MEMORY_SIZE ;
+ prepare_common_pri_functions (IoAdapter) ;
+ diva_os_prepare_pri2_functions (IoAdapter);
+}
+/* ------------------------------------------------------------------------- */
diff --git a/drivers/isdn/hardware/eicon/sdp_hdr.h b/drivers/isdn/hardware/eicon/sdp_hdr.h
new file mode 100644
index 0000000..8f61c69
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/sdp_hdr.h
@@ -0,0 +1,117 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+#ifndef __DIVA_SOFT_DSP_TASK_ENTRY_H__
+#define __DIVA_SOFT_DSP_TASK_ENTRY_H__
+/*
+ The soft DSP image is described by binary header contained on begin of this
+ image:
+OFFSET FROM IMAGE START | VARIABLE
+------------------------------------------------------------------------
+ DIVA_MIPS_TASK_IMAGE_LINK_OFFS | link to the next image
+ ----------------------------------------------------------------------
+ DIVA_MIPS_TASK_IMAGE_GP_OFFS | image gp register value, void*
+ ----------------------------------------------------------------------
+ DIVA_MIPS_TASK_IMAGE_ENTRY_OFFS | diva_mips_sdp_task_entry_t*
+ ----------------------------------------------------------------------
+ DIVA_MIPS_TASK_IMAGE_LOAD_ADDR_OFFS | image image start address (void*)
+ ----------------------------------------------------------------------
+ DIVA_MIPS_TASK_IMAGE_END_ADDR_OFFS | image image end address (void*)
+ ----------------------------------------------------------------------
+ DIVA_MIPS_TASK_IMAGE_ID_STRING_OFFS | image id string char[...];
+ ----------------------------------------------------------------------
+ */
+#define DIVA_MIPS_TASK_IMAGE_LINK_OFFS 0x6C
+#define DIVA_MIPS_TASK_IMAGE_GP_OFFS 0x70
+#define DIVA_MIPS_TASK_IMAGE_ENTRY_OFFS 0x74
+#define DIVA_MIPS_TASK_IMAGE_LOAD_ADDR_OFFS 0x78
+#define DIVA_MIPS_TASK_IMAGE_END_ADDR_OFFS 0x7c
+#define DIVA_MIPS_TASK_IMAGE_ID_STRING_OFFS 0x80
+/*
+ This function is called in order to set GP register of this task
+ This function should be always called before any function of the
+ task is called
+ */
+typedef void (*diva_task_set_prog_gp_proc_t)(void* new_gp);
+/*
+ This function is called to clear .bss at task initialization step
+ */
+typedef void (*diva_task_sys_reset_proc_t)(void);
+/*
+ This function is called in order to provide GP of master call to
+ task, that will be used by calls from the task to the master
+ */
+typedef void (*diva_task_set_main_gp_proc_t)(void* main_gp);
+/*
+ This function is called to provide address of 'dprintf' function
+ to the task
+ */
+typedef word (*diva_prt_proc_t)(char *, ...);
+typedef void (*diva_task_set_prt_proc_t)(diva_prt_proc_t fn);
+/*
+ This function is called to set task PID
+ */
+typedef void (*diva_task_set_pid_proc_t)(dword id);
+/*
+ This function is called for run-time task init
+ */
+typedef int (*diva_task_run_time_init_proc_t)(void*, dword);
+/*
+ This function is called from system scheduler or from timer
+ */
+typedef void (*diva_task_callback_proc_t)(void);
+/*
+ This callback is used by task to get current time im mS
+ */
+typedef dword (*diva_task_get_tick_count_proc_t)(void);
+typedef void (*diva_task_set_get_time_proc_t)(\
+ diva_task_get_tick_count_proc_t fn);
+typedef struct _diva_mips_sdp_task_entry {
+ diva_task_set_prog_gp_proc_t set_gp_proc;
+ diva_task_sys_reset_proc_t sys_reset_proc;
+ diva_task_set_main_gp_proc_t set_main_gp_proc;
+ diva_task_set_prt_proc_t set_dprintf_proc;
+ diva_task_set_pid_proc_t set_pid_proc;
+ diva_task_run_time_init_proc_t run_time_init_proc;
+ diva_task_callback_proc_t task_callback_proc;
+ diva_task_callback_proc_t timer_callback_proc;
+ diva_task_set_get_time_proc_t set_get_time_proc;
+ void* last_entry_proc;
+} diva_mips_sdp_task_entry_t;
+/*
+ 'last_entry_proc' should be set to zero and is used for future extensuios
+ */
+typedef struct _diva_mips_sw_task {
+ diva_mips_sdp_task_entry_t sdp_entry;
+ void* sdp_gp_reg;
+ void* own_gp_reg;
+} diva_mips_sw_task_t;
+#if !defined(DIVA_BRI2F_SDP_1_NAME)
+#define DIVA_BRI2F_SDP_1_NAME "sdp0.2q0"
+#endif
+#if !defined(DIVA_BRI2F_SDP_2_NAME)
+#define DIVA_BRI2F_SDP_2_NAME "sdp1.2q0"
+#endif
+#endif
diff --git a/drivers/isdn/hardware/eicon/um_idi.c b/drivers/isdn/hardware/eicon/um_idi.c
new file mode 100644
index 0000000..6563db9
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/um_idi.c
@@ -0,0 +1,885 @@
+/* $Id: um_idi.c,v 1.14 2004/03/21 17:54:37 armin Exp $ */
+
+#include "platform.h"
+#include "di_defs.h"
+#include "pc.h"
+#include "dqueue.h"
+#include "adapter.h"
+#include "entity.h"
+#include "um_xdi.h"
+#include "um_idi.h"
+#include "debuglib.h"
+#include "divasync.h"
+
+#define DIVAS_MAX_XDI_ADAPTERS 64
+
+/* --------------------------------------------------------------------------
+ IMPORTS
+ -------------------------------------------------------------------------- */
+extern void diva_os_wakeup_read(void *os_context);
+extern void diva_os_wakeup_close(void *os_context);
+/* --------------------------------------------------------------------------
+ LOCALS
+ -------------------------------------------------------------------------- */
+static LIST_HEAD(adapter_q);
+static diva_os_spin_lock_t adapter_lock;
+
+static diva_um_idi_adapter_t *diva_um_idi_find_adapter(dword nr);
+static void cleanup_adapter(diva_um_idi_adapter_t * a);
+static void cleanup_entity(divas_um_idi_entity_t * e);
+static int diva_user_mode_idi_adapter_features(diva_um_idi_adapter_t * a,
+ diva_um_idi_adapter_features_t
+ * features);
+static int process_idi_request(divas_um_idi_entity_t * e,
+ const diva_um_idi_req_hdr_t * req);
+static int process_idi_rc(divas_um_idi_entity_t * e, byte rc);
+static int process_idi_ind(divas_um_idi_entity_t * e, byte ind);
+static int write_return_code(divas_um_idi_entity_t * e, byte rc);
+
+/* --------------------------------------------------------------------------
+ MAIN
+ -------------------------------------------------------------------------- */
+int diva_user_mode_idi_init(void)
+{
+ diva_os_initialize_spin_lock(&adapter_lock, "adapter");
+ return (0);
+}
+
+/* --------------------------------------------------------------------------
+ Copy adapter features to user supplied buffer
+ -------------------------------------------------------------------------- */
+static int
+diva_user_mode_idi_adapter_features(diva_um_idi_adapter_t * a,
+ diva_um_idi_adapter_features_t *
+ features)
+{
+ IDI_SYNC_REQ sync_req;
+
+ if ((a) && (a->d.request)) {
+ features->type = a->d.type;
+ features->features = a->d.features;
+ features->channels = a->d.channels;
+ memset(features->name, 0, sizeof(features->name));
+
+ sync_req.GetName.Req = 0;
+ sync_req.GetName.Rc = IDI_SYNC_REQ_GET_NAME;
+ (*(a->d.request)) ((ENTITY *) & sync_req);
+ strlcpy(features->name, sync_req.GetName.name,
+ sizeof(features->name));
+
+ sync_req.GetSerial.Req = 0;
+ sync_req.GetSerial.Rc = IDI_SYNC_REQ_GET_SERIAL;
+ sync_req.GetSerial.serial = 0;
+ (*(a->d.request)) ((ENTITY *) & sync_req);
+ features->serial_number = sync_req.GetSerial.serial;
+ }
+
+ return ((a) ? 0 : -1);
+}
+
+/* --------------------------------------------------------------------------
+ REMOVE ADAPTER
+ -------------------------------------------------------------------------- */
+void diva_user_mode_idi_remove_adapter(int adapter_nr)
+{
+ struct list_head *tmp;
+ diva_um_idi_adapter_t *a;
+
+ list_for_each(tmp, &adapter_q) {
+ a = list_entry(tmp, diva_um_idi_adapter_t, link);
+ if (a->adapter_nr == adapter_nr) {
+ list_del(tmp);
+ cleanup_adapter(a);
+ DBG_LOG(("DIDD: del adapter(%d)", a->adapter_nr));
+ diva_os_free(0, a);
+ break;
+ }
+ }
+}
+
+/* --------------------------------------------------------------------------
+ CALLED ON DRIVER EXIT (UNLOAD)
+ -------------------------------------------------------------------------- */
+void diva_user_mode_idi_finit(void)
+{
+ struct list_head *tmp, *safe;
+ diva_um_idi_adapter_t *a;
+
+ list_for_each_safe(tmp, safe, &adapter_q) {
+ a = list_entry(tmp, diva_um_idi_adapter_t, link);
+ list_del(tmp);
+ cleanup_adapter(a);
+ DBG_LOG(("DIDD: del adapter(%d)", a->adapter_nr));
+ diva_os_free(0, a);
+ }
+ diva_os_destroy_spin_lock(&adapter_lock, "adapter");
+}
+
+/* -------------------------------------------------------------------------
+ CREATE AND INIT IDI ADAPTER
+ ------------------------------------------------------------------------- */
+int diva_user_mode_idi_create_adapter(const DESCRIPTOR * d, int adapter_nr)
+{
+ diva_os_spin_lock_magic_t old_irql;
+ diva_um_idi_adapter_t *a =
+ (diva_um_idi_adapter_t *) diva_os_malloc(0,
+ sizeof
+ (diva_um_idi_adapter_t));
+
+ if (!a) {
+ return (-1);
+ }
+ memset(a, 0x00, sizeof(*a));
+ INIT_LIST_HEAD(&a->entity_q);
+
+ a->d = *d;
+ a->adapter_nr = adapter_nr;
+
+ DBG_LOG(("DIDD_ADD A(%d), type:%02x, features:%04x, channels:%d",
+ adapter_nr, a->d.type, a->d.features, a->d.channels));
+
+ diva_os_enter_spin_lock(&adapter_lock, &old_irql, "create_adapter");
+ list_add_tail(&a->link, &adapter_q);
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "create_adapter");
+ return (0);
+}
+
+/* ------------------------------------------------------------------------
+ Find adapter by Adapter number
+ ------------------------------------------------------------------------ */
+static diva_um_idi_adapter_t *diva_um_idi_find_adapter(dword nr)
+{
+ diva_um_idi_adapter_t *a = NULL;
+ struct list_head *tmp;
+
+ list_for_each(tmp, &adapter_q) {
+ a = list_entry(tmp, diva_um_idi_adapter_t, link);
+ DBG_TRC(("find_adapter: (%d)-(%d)", nr, a->adapter_nr));
+ if (a->adapter_nr == (int)nr)
+ break;
+ a = NULL;
+ }
+ return(a);
+}
+
+/* ------------------------------------------------------------------------
+ Cleanup this adapter and cleanup/delete all entities assigned
+ to this adapter
+ ------------------------------------------------------------------------ */
+static void cleanup_adapter(diva_um_idi_adapter_t * a)
+{
+ struct list_head *tmp, *safe;
+ divas_um_idi_entity_t *e;
+
+ list_for_each_safe(tmp, safe, &a->entity_q) {
+ e = list_entry(tmp, divas_um_idi_entity_t, link);
+ list_del(tmp);
+ cleanup_entity(e);
+ if (e->os_context) {
+ diva_os_wakeup_read(e->os_context);
+ diva_os_wakeup_close(e->os_context);
+ }
+ }
+ memset(&a->d, 0x00, sizeof(DESCRIPTOR));
+}
+
+/* ------------------------------------------------------------------------
+ Cleanup, but NOT delete this entity
+ ------------------------------------------------------------------------ */
+static void cleanup_entity(divas_um_idi_entity_t * e)
+{
+ e->os_ref = NULL;
+ e->status = 0;
+ e->adapter = NULL;
+ e->e.Id = 0;
+ e->rc_count = 0;
+
+ e->status |= DIVA_UM_IDI_REMOVED;
+ e->status |= DIVA_UM_IDI_REMOVE_PENDING;
+
+ diva_data_q_finit(&e->data);
+ diva_data_q_finit(&e->rc);
+}
+
+
+/* ------------------------------------------------------------------------
+ Create ENTITY, link it to the adapter and remove pointer to entity
+ ------------------------------------------------------------------------ */
+void *divas_um_idi_create_entity(dword adapter_nr, void *file)
+{
+ divas_um_idi_entity_t *e;
+ diva_um_idi_adapter_t *a;
+ diva_os_spin_lock_magic_t old_irql;
+
+ if ((e = (divas_um_idi_entity_t *) diva_os_malloc(0, sizeof(*e)))) {
+ memset(e, 0x00, sizeof(*e));
+ if (!
+ (e->os_context =
+ diva_os_malloc(0, diva_os_get_context_size()))) {
+ DBG_LOG(("E(%08x) no memory for os context", e));
+ diva_os_free(0, e);
+ return NULL;
+ }
+ memset(e->os_context, 0x00, diva_os_get_context_size());
+
+ if ((diva_data_q_init(&e->data, 2048 + 512, 16))) {
+ diva_os_free(0, e->os_context);
+ diva_os_free(0, e);
+ return NULL;
+ }
+ if ((diva_data_q_init(&e->rc, sizeof(diva_um_idi_ind_hdr_t), 2))) {
+ diva_data_q_finit(&e->data);
+ diva_os_free(0, e->os_context);
+ diva_os_free(0, e);
+ return NULL;
+ }
+
+ diva_os_enter_spin_lock(&adapter_lock, &old_irql, "create_entity");
+ /*
+ Look for Adapter requested
+ */
+ if (!(a = diva_um_idi_find_adapter(adapter_nr))) {
+ /*
+ No adapter was found, or this adapter was removed
+ */
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "create_entity");
+
+ DBG_LOG(("A: no adapter(%ld)", adapter_nr));
+
+ cleanup_entity(e);
+ diva_os_free(0, e->os_context);
+ diva_os_free(0, e);
+
+ return NULL;
+ }
+
+ e->os_ref = file; /* link to os handle */
+ e->adapter = a; /* link to adapter */
+
+ list_add_tail(&e->link, &a->entity_q); /* link from adapter */
+
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "create_entity");
+
+ DBG_LOG(("A(%ld), create E(%08x)", adapter_nr, e));
+ }
+
+ return (e);
+}
+
+/* ------------------------------------------------------------------------
+ Unlink entity and free memory
+ ------------------------------------------------------------------------ */
+int divas_um_idi_delete_entity(int adapter_nr, void *entity)
+{
+ divas_um_idi_entity_t *e;
+ diva_um_idi_adapter_t *a;
+ diva_os_spin_lock_magic_t old_irql;
+
+ if (!(e = (divas_um_idi_entity_t *) entity))
+ return (-1);
+
+ diva_os_enter_spin_lock(&adapter_lock, &old_irql, "delete_entity");
+ if ((a = e->adapter)) {
+ list_del(&e->link);
+ }
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "delete_entity");
+
+ diva_um_idi_stop_wdog(entity);
+ cleanup_entity(e);
+ diva_os_free(0, e->os_context);
+ memset(e, 0x00, sizeof(*e));
+ diva_os_free(0, e);
+
+ DBG_LOG(("A(%d) remove E:%08x", adapter_nr, e));
+
+ return (0);
+}
+
+/* --------------------------------------------------------------------------
+ Called by application to read data from IDI
+ -------------------------------------------------------------------------- */
+int diva_um_idi_read(void *entity,
+ void *os_handle,
+ void *dst,
+ int max_length, divas_um_idi_copy_to_user_fn_t cp_fn)
+{
+ divas_um_idi_entity_t *e;
+ diva_um_idi_adapter_t *a;
+ const void *data;
+ int length, ret = 0;
+ diva_um_idi_data_queue_t *q;
+ diva_os_spin_lock_magic_t old_irql;
+
+ diva_os_enter_spin_lock(&adapter_lock, &old_irql, "read");
+
+ e = (divas_um_idi_entity_t *) entity;
+ if (!e || (!(a = e->adapter)) ||
+ (e->status & DIVA_UM_IDI_REMOVE_PENDING) ||
+ (e->status & DIVA_UM_IDI_REMOVED) ||
+ (a->status & DIVA_UM_IDI_ADAPTER_REMOVED)) {
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "read");
+ DBG_ERR(("E(%08x) read failed - adapter removed", e))
+ return (-1);
+ }
+
+ DBG_TRC(("A(%d) E(%08x) read(%d)", a->adapter_nr, e, max_length));
+
+ /*
+ Try to read return code first
+ */
+ data = diva_data_q_get_segment4read(&e->rc);
+ q = &e->rc;
+
+ /*
+ No return codes available, read indications now
+ */
+ if (!data) {
+ if (!(e->status & DIVA_UM_IDI_RC_PENDING)) {
+ DBG_TRC(("A(%d) E(%08x) read data", a->adapter_nr, e));
+ data = diva_data_q_get_segment4read(&e->data);
+ q = &e->data;
+ }
+ } else {
+ e->status &= ~DIVA_UM_IDI_RC_PENDING;
+ DBG_TRC(("A(%d) E(%08x) read rc", a->adapter_nr, e));
+ }
+
+ if (data) {
+ if ((length = diva_data_q_get_segment_length(q)) >
+ max_length) {
+ /*
+ Not enough space to read message
+ */
+ DBG_ERR(("A: A(%d) E(%08x) read small buffer",
+ a->adapter_nr, e, ret));
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql,
+ "read");
+ return (-2);
+ }
+ /*
+ Copy it to user, this function does access ONLY locked an verified
+ memory, also we can access it witch spin lock held
+ */
+
+ if ((ret = (*cp_fn) (os_handle, dst, data, length)) >= 0) {
+ /*
+ Acknowledge only if read was successfull
+ */
+ diva_data_q_ack_segment4read(q);
+ }
+ }
+
+
+ DBG_TRC(("A(%d) E(%08x) read=%d", a->adapter_nr, e, ret));
+
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "read");
+
+ return (ret);
+}
+
+
+int diva_um_idi_write(void *entity,
+ void *os_handle,
+ const void *src,
+ int length, divas_um_idi_copy_from_user_fn_t cp_fn)
+{
+ divas_um_idi_entity_t *e;
+ diva_um_idi_adapter_t *a;
+ diva_um_idi_req_hdr_t *req;
+ void *data;
+ int ret = 0;
+ diva_os_spin_lock_magic_t old_irql;
+
+ diva_os_enter_spin_lock(&adapter_lock, &old_irql, "write");
+
+ e = (divas_um_idi_entity_t *) entity;
+ if (!e || (!(a = e->adapter)) ||
+ (e->status & DIVA_UM_IDI_REMOVE_PENDING) ||
+ (e->status & DIVA_UM_IDI_REMOVED) ||
+ (a->status & DIVA_UM_IDI_ADAPTER_REMOVED)) {
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write");
+ DBG_ERR(("E(%08x) write failed - adapter removed", e))
+ return (-1);
+ }
+
+ DBG_TRC(("A(%d) E(%08x) write(%d)", a->adapter_nr, e, length));
+
+ if ((length < sizeof(*req)) || (length > sizeof(e->buffer))) {
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write");
+ return (-2);
+ }
+
+ if (e->status & DIVA_UM_IDI_RC_PENDING) {
+ DBG_ERR(("A: A(%d) E(%08x) rc pending", a->adapter_nr, e));
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write");
+ return (-1); /* should wait for RC code first */
+ }
+
+ /*
+ Copy function does access only locked verified memory,
+ also it can be called with spin lock held
+ */
+ if ((ret = (*cp_fn) (os_handle, e->buffer, src, length)) < 0) {
+ DBG_TRC(("A: A(%d) E(%08x) write error=%d", a->adapter_nr,
+ e, ret));
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write");
+ return (ret);
+ }
+
+ req = (diva_um_idi_req_hdr_t *) & e->buffer[0];
+
+ switch (req->type) {
+ case DIVA_UM_IDI_GET_FEATURES:{
+ DBG_LOG(("A(%d) get_features", a->adapter_nr));
+ if (!(data =
+ diva_data_q_get_segment4write(&e->data))) {
+ DBG_ERR(("A(%d) get_features, no free buffer",
+ a->adapter_nr));
+ diva_os_leave_spin_lock(&adapter_lock,
+ &old_irql,
+ "write");
+ return (0);
+ }
+ diva_user_mode_idi_adapter_features(a, &(((diva_um_idi_ind_hdr_t
+ *) data)->hdr.features));
+ ((diva_um_idi_ind_hdr_t *) data)->type =
+ DIVA_UM_IDI_IND_FEATURES;
+ ((diva_um_idi_ind_hdr_t *) data)->data_length = 0;
+ diva_data_q_ack_segment4write(&e->data,
+ sizeof(diva_um_idi_ind_hdr_t));
+
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write");
+
+ diva_os_wakeup_read(e->os_context);
+ }
+ break;
+
+ case DIVA_UM_IDI_REQ:
+ case DIVA_UM_IDI_REQ_MAN:
+ case DIVA_UM_IDI_REQ_SIG:
+ case DIVA_UM_IDI_REQ_NET:
+ DBG_TRC(("A(%d) REQ(%02d)-(%02d)-(%08x)", a->adapter_nr,
+ req->Req, req->ReqCh,
+ req->type & DIVA_UM_IDI_REQ_TYPE_MASK));
+ switch (process_idi_request(e, req)) {
+ case -1:
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write");
+ return (-1);
+ case -2:
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write");
+ diva_os_wakeup_read(e->os_context);
+ break;
+ default:
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write");
+ break;
+ }
+ break;
+
+ default:
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "write");
+ return (-1);
+ }
+
+ DBG_TRC(("A(%d) E(%08x) write=%d", a->adapter_nr, e, ret));
+
+ return (ret);
+}
+
+/* --------------------------------------------------------------------------
+ CALLBACK FROM XDI
+ -------------------------------------------------------------------------- */
+static void diva_um_idi_xdi_callback(ENTITY * entity)
+{
+ divas_um_idi_entity_t *e = DIVAS_CONTAINING_RECORD(entity,
+ divas_um_idi_entity_t,
+ e);
+ diva_os_spin_lock_magic_t old_irql;
+ int call_wakeup = 0;
+
+ diva_os_enter_spin_lock(&adapter_lock, &old_irql, "xdi_callback");
+
+ if (e->e.complete == 255) {
+ if (!(e->status & DIVA_UM_IDI_REMOVE_PENDING)) {
+ diva_um_idi_stop_wdog(e);
+ }
+ if ((call_wakeup = process_idi_rc(e, e->e.Rc))) {
+ if (e->rc_count) {
+ e->rc_count--;
+ }
+ }
+ e->e.Rc = 0;
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "xdi_callback");
+
+ if (call_wakeup) {
+ diva_os_wakeup_read(e->os_context);
+ diva_os_wakeup_close(e->os_context);
+ }
+ } else {
+ if (e->status & DIVA_UM_IDI_REMOVE_PENDING) {
+ e->e.RNum = 0;
+ e->e.RNR = 2;
+ } else {
+ call_wakeup = process_idi_ind(e, e->e.Ind);
+ }
+ e->e.Ind = 0;
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "xdi_callback");
+ if (call_wakeup) {
+ diva_os_wakeup_read(e->os_context);
+ }
+ }
+}
+
+static int process_idi_request(divas_um_idi_entity_t * e,
+ const diva_um_idi_req_hdr_t * req)
+{
+ int assign = 0;
+ byte Req = (byte) req->Req;
+ dword type = req->type & DIVA_UM_IDI_REQ_TYPE_MASK;
+
+ if (!e->e.Id || !e->e.callback) { /* not assigned */
+ if (Req != ASSIGN) {
+ DBG_ERR(("A: A(%d) E(%08x) not assigned",
+ e->adapter->adapter_nr, e));
+ return (-1); /* NOT ASSIGNED */
+ } else {
+ switch (type) {
+ case DIVA_UM_IDI_REQ_TYPE_MAN:
+ e->e.Id = MAN_ID;
+ DBG_TRC(("A(%d) E(%08x) assign MAN",
+ e->adapter->adapter_nr, e));
+ break;
+
+ case DIVA_UM_IDI_REQ_TYPE_SIG:
+ e->e.Id = DSIG_ID;
+ DBG_TRC(("A(%d) E(%08x) assign SIG",
+ e->adapter->adapter_nr, e));
+ break;
+
+ case DIVA_UM_IDI_REQ_TYPE_NET:
+ e->e.Id = NL_ID;
+ DBG_TRC(("A(%d) E(%08x) assign NET",
+ e->adapter->adapter_nr, e));
+ break;
+
+ default:
+ DBG_ERR(("A: A(%d) E(%08x) unknown type=%08x",
+ e->adapter->adapter_nr, e,
+ type));
+ return (-1);
+ }
+ }
+ e->e.XNum = 1;
+ e->e.RNum = 1;
+ e->e.callback = diva_um_idi_xdi_callback;
+ e->e.X = &e->XData;
+ e->e.R = &e->RData;
+ assign = 1;
+ }
+ e->status |= DIVA_UM_IDI_RC_PENDING;
+ e->e.Req = Req;
+ e->e.ReqCh = (byte) req->ReqCh;
+ e->e.X->PLength = (word) req->data_length;
+ e->e.X->P = (byte *) & req[1]; /* Our buffer is safe */
+
+ DBG_TRC(("A(%d) E(%08x) request(%02x-%02x-%02x (%d))",
+ e->adapter->adapter_nr, e, e->e.Id, e->e.Req,
+ e->e.ReqCh, e->e.X->PLength));
+
+ e->rc_count++;
+
+ if (e->adapter && e->adapter->d.request) {
+ diva_um_idi_start_wdog(e);
+ (*(e->adapter->d.request)) (&e->e);
+ }
+
+ if (assign) {
+ if (e->e.Rc == OUT_OF_RESOURCES) {
+ /*
+ XDI has no entities more, call was not forwarded to the card,
+ no callback will be scheduled
+ */
+ DBG_ERR(("A: A(%d) E(%08x) XDI out of entities",
+ e->adapter->adapter_nr, e));
+
+ e->e.Id = 0;
+ e->e.ReqCh = 0;
+ e->e.RcCh = 0;
+ e->e.Ind = 0;
+ e->e.IndCh = 0;
+ e->e.XNum = 0;
+ e->e.RNum = 0;
+ e->e.callback = NULL;
+ e->e.X = NULL;
+ e->e.R = NULL;
+ write_return_code(e, ASSIGN_RC | OUT_OF_RESOURCES);
+ return (-2);
+ } else {
+ e->status |= DIVA_UM_IDI_ASSIGN_PENDING;
+ }
+ }
+
+ return (0);
+}
+
+static int process_idi_rc(divas_um_idi_entity_t * e, byte rc)
+{
+ DBG_TRC(("A(%d) E(%08x) rc(%02x-%02x-%02x)",
+ e->adapter->adapter_nr, e, e->e.Id, rc, e->e.RcCh));
+
+ if (e->status & DIVA_UM_IDI_ASSIGN_PENDING) {
+ e->status &= ~DIVA_UM_IDI_ASSIGN_PENDING;
+ if (rc != ASSIGN_OK) {
+ DBG_ERR(("A: A(%d) E(%08x) ASSIGN failed",
+ e->adapter->adapter_nr, e));
+ e->e.callback = NULL;
+ e->e.Id = 0;
+ e->e.Req = 0;
+ e->e.ReqCh = 0;
+ e->e.Rc = 0;
+ e->e.RcCh = 0;
+ e->e.Ind = 0;
+ e->e.IndCh = 0;
+ e->e.X = NULL;
+ e->e.R = NULL;
+ e->e.XNum = 0;
+ e->e.RNum = 0;
+ }
+ }
+ if ((e->e.Req == REMOVE) && e->e.Id && (rc == 0xff)) {
+ DBG_ERR(("A: A(%d) E(%08x) discard OK in REMOVE",
+ e->adapter->adapter_nr, e));
+ return (0); /* let us do it in the driver */
+ }
+ if ((e->e.Req == REMOVE) && (!e->e.Id)) { /* REMOVE COMPLETE */
+ e->e.callback = NULL;
+ e->e.Id = 0;
+ e->e.Req = 0;
+ e->e.ReqCh = 0;
+ e->e.Rc = 0;
+ e->e.RcCh = 0;
+ e->e.Ind = 0;
+ e->e.IndCh = 0;
+ e->e.X = NULL;
+ e->e.R = NULL;
+ e->e.XNum = 0;
+ e->e.RNum = 0;
+ e->rc_count = 0;
+ }
+ if ((e->e.Req == REMOVE) && (rc != 0xff)) { /* REMOVE FAILED */
+ DBG_ERR(("A: A(%d) E(%08x) REMOVE FAILED",
+ e->adapter->adapter_nr, e));
+ }
+ write_return_code(e, rc);
+
+ return (1);
+}
+
+static int process_idi_ind(divas_um_idi_entity_t * e, byte ind)
+{
+ int do_wakeup = 0;
+
+ if (e->e.complete != 0x02) {
+ diva_um_idi_ind_hdr_t *pind =
+ (diva_um_idi_ind_hdr_t *)
+ diva_data_q_get_segment4write(&e->data);
+ if (pind) {
+ e->e.RNum = 1;
+ e->e.R->P = (byte *) & pind[1];
+ e->e.R->PLength =
+ (word) (diva_data_q_get_max_length(&e->data) -
+ sizeof(*pind));
+ DBG_TRC(("A(%d) E(%08x) ind_1(%02x-%02x-%02x)-[%d-%d]",
+ e->adapter->adapter_nr, e, e->e.Id, ind,
+ e->e.IndCh, e->e.RLength,
+ e->e.R->PLength));
+
+ } else {
+ DBG_TRC(("A(%d) E(%08x) ind(%02x-%02x-%02x)-RNR",
+ e->adapter->adapter_nr, e, e->e.Id, ind,
+ e->e.IndCh));
+ e->e.RNum = 0;
+ e->e.RNR = 1;
+ do_wakeup = 1;
+ }
+ } else {
+ diva_um_idi_ind_hdr_t *pind =
+ (diva_um_idi_ind_hdr_t *) (e->e.R->P);
+
+ DBG_TRC(("A(%d) E(%08x) ind(%02x-%02x-%02x)-[%d]",
+ e->adapter->adapter_nr, e, e->e.Id, ind,
+ e->e.IndCh, e->e.R->PLength));
+
+ pind--;
+ pind->type = DIVA_UM_IDI_IND;
+ pind->hdr.ind.Ind = ind;
+ pind->hdr.ind.IndCh = e->e.IndCh;
+ pind->data_length = e->e.R->PLength;
+ diva_data_q_ack_segment4write(&e->data,
+ (int) (sizeof(*pind) +
+ e->e.R->PLength));
+ do_wakeup = 1;
+ }
+
+ if ((e->status & DIVA_UM_IDI_RC_PENDING) && !e->rc.count) {
+ do_wakeup = 0;
+ }
+
+ return (do_wakeup);
+}
+
+/* --------------------------------------------------------------------------
+ Write return code to the return code queue of entity
+ -------------------------------------------------------------------------- */
+static int write_return_code(divas_um_idi_entity_t * e, byte rc)
+{
+ diva_um_idi_ind_hdr_t *prc;
+
+ if (!(prc =
+ (diva_um_idi_ind_hdr_t *) diva_data_q_get_segment4write(&e->rc)))
+ {
+ DBG_ERR(("A: A(%d) E(%08x) rc(%02x) lost",
+ e->adapter->adapter_nr, e, rc));
+ e->status &= ~DIVA_UM_IDI_RC_PENDING;
+ return (-1);
+ }
+
+ prc->type = DIVA_UM_IDI_IND_RC;
+ prc->hdr.rc.Rc = rc;
+ prc->hdr.rc.RcCh = e->e.RcCh;
+ prc->data_length = 0;
+ diva_data_q_ack_segment4write(&e->rc, sizeof(*prc));
+
+ return (0);
+}
+
+/* --------------------------------------------------------------------------
+ Return amount of entries that can be bead from this entity or
+ -1 if adapter was removed
+ -------------------------------------------------------------------------- */
+int diva_user_mode_idi_ind_ready(void *entity, void *os_handle)
+{
+ divas_um_idi_entity_t *e;
+ diva_um_idi_adapter_t *a;
+ diva_os_spin_lock_magic_t old_irql;
+ int ret;
+
+ if (!entity)
+ return (-1);
+ diva_os_enter_spin_lock(&adapter_lock, &old_irql, "ind_ready");
+ e = (divas_um_idi_entity_t *) entity;
+ a = e->adapter;
+
+ if ((!a) || (a->status & DIVA_UM_IDI_ADAPTER_REMOVED)) {
+ /*
+ Adapter was unloaded
+ */
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "ind_ready");
+ return (-1); /* adapter was removed */
+ }
+ if (e->status & DIVA_UM_IDI_REMOVED) {
+ /*
+ entity was removed as result of adapter removal
+ user should assign this entity again
+ */
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "ind_ready");
+ return (-1);
+ }
+
+ ret = e->rc.count + e->data.count;
+
+ if ((e->status & DIVA_UM_IDI_RC_PENDING) && !e->rc.count) {
+ ret = 0;
+ }
+
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "ind_ready");
+
+ return (ret);
+}
+
+void *diva_um_id_get_os_context(void *entity)
+{
+ return (((divas_um_idi_entity_t *) entity)->os_context);
+}
+
+int divas_um_idi_entity_assigned(void *entity)
+{
+ divas_um_idi_entity_t *e;
+ diva_um_idi_adapter_t *a;
+ int ret;
+ diva_os_spin_lock_magic_t old_irql;
+
+ diva_os_enter_spin_lock(&adapter_lock, &old_irql, "assigned?");
+
+
+ e = (divas_um_idi_entity_t *) entity;
+ if (!e || (!(a = e->adapter)) ||
+ (e->status & DIVA_UM_IDI_REMOVED) ||
+ (a->status & DIVA_UM_IDI_ADAPTER_REMOVED)) {
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "assigned?");
+ return (0);
+ }
+
+ e->status |= DIVA_UM_IDI_REMOVE_PENDING;
+
+ ret = (e->e.Id || e->rc_count
+ || (e->status & DIVA_UM_IDI_ASSIGN_PENDING));
+
+ DBG_TRC(("Id:%02x, rc_count:%d, status:%08x", e->e.Id, e->rc_count,
+ e->status))
+
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "assigned?");
+
+ return (ret);
+}
+
+int divas_um_idi_entity_start_remove(void *entity)
+{
+ divas_um_idi_entity_t *e;
+ diva_um_idi_adapter_t *a;
+ diva_os_spin_lock_magic_t old_irql;
+
+ diva_os_enter_spin_lock(&adapter_lock, &old_irql, "start_remove");
+
+ e = (divas_um_idi_entity_t *) entity;
+ if (!e || (!(a = e->adapter)) ||
+ (e->status & DIVA_UM_IDI_REMOVED) ||
+ (a->status & DIVA_UM_IDI_ADAPTER_REMOVED)) {
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "start_remove");
+ return (0);
+ }
+
+ if (e->rc_count) {
+ /*
+ Entity BUSY
+ */
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "start_remove");
+ return (1);
+ }
+
+ if (!e->e.Id) {
+ /*
+ Remove request was already pending, and arrived now
+ */
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "start_remove");
+ return (0); /* REMOVE was pending */
+ }
+
+ /*
+ Now send remove request
+ */
+ e->e.Req = REMOVE;
+ e->e.ReqCh = 0;
+
+ e->rc_count++;
+
+ DBG_TRC(("A(%d) E(%08x) request(%02x-%02x-%02x (%d))",
+ e->adapter->adapter_nr, e, e->e.Id, e->e.Req,
+ e->e.ReqCh, e->e.X->PLength));
+
+ if (a->d.request)
+ (*(a->d.request)) (&e->e);
+
+ diva_os_leave_spin_lock(&adapter_lock, &old_irql, "start_remove");
+
+ return (0);
+}
diff --git a/drivers/isdn/hardware/eicon/um_idi.h b/drivers/isdn/hardware/eicon/um_idi.h
new file mode 100644
index 0000000..141072f
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/um_idi.h
@@ -0,0 +1,43 @@
+/* $Id: um_idi.h,v 1.6 2004/03/21 17:26:01 armin Exp $ */
+
+#ifndef __DIVA_USER_MODE_IDI_CORE_H__
+#define __DIVA_USER_MODE_IDI_CORE_H__
+
+
+/*
+ interface between UM IDI core and OS dependent part
+ */
+int diva_user_mode_idi_init(void);
+void diva_user_mode_idi_finit(void);
+void *divas_um_idi_create_entity(dword adapter_nr, void *file);
+int divas_um_idi_delete_entity(int adapter_nr, void *entity);
+
+typedef int (*divas_um_idi_copy_to_user_fn_t) (void *os_handle,
+ void *dst,
+ const void *src,
+ int length);
+typedef int (*divas_um_idi_copy_from_user_fn_t) (void *os_handle,
+ void *dst,
+ const void *src,
+ int length);
+
+int diva_um_idi_read(void *entity,
+ void *os_handle,
+ void *dst,
+ int max_length, divas_um_idi_copy_to_user_fn_t cp_fn);
+
+int diva_um_idi_write(void *entity,
+ void *os_handle,
+ const void *src,
+ int length, divas_um_idi_copy_from_user_fn_t cp_fn);
+
+int diva_user_mode_idi_ind_ready(void *entity, void *os_handle);
+void *diva_um_id_get_os_context(void *entity);
+int diva_os_get_context_size(void);
+int divas_um_idi_entity_assigned(void *entity);
+int divas_um_idi_entity_start_remove(void *entity);
+
+void diva_um_idi_start_wdog(void *entity);
+void diva_um_idi_stop_wdog(void *entity);
+
+#endif
diff --git a/drivers/isdn/hardware/eicon/um_xdi.h b/drivers/isdn/hardware/eicon/um_xdi.h
new file mode 100644
index 0000000..b48fc04
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/um_xdi.h
@@ -0,0 +1,68 @@
+/* $Id: um_xdi.h,v 1.1.2.2 2002/10/02 14:38:38 armin Exp $ */
+
+#ifndef __DIVA_USER_MODE_XDI_H__
+#define __DIVA_USER_MODE_XDI_H__
+
+/*
+ Contains declaratiom of structures shared between application
+ and user mode idi driver
+*/
+
+typedef struct _diva_um_idi_adapter_features {
+ dword type;
+ dword features;
+ dword channels;
+ dword serial_number;
+ char name[128];
+} diva_um_idi_adapter_features_t;
+
+#define DIVA_UM_IDI_REQ_MASK 0x0000FFFF
+#define DIVA_UM_IDI_REQ_TYPE_MASK (~(DIVA_UM_IDI_REQ_MASK))
+#define DIVA_UM_IDI_GET_FEATURES 1 /* trigger features indication */
+#define DIVA_UM_IDI_REQ 2
+#define DIVA_UM_IDI_REQ_TYPE_MAN 0x10000000
+#define DIVA_UM_IDI_REQ_TYPE_SIG 0x20000000
+#define DIVA_UM_IDI_REQ_TYPE_NET 0x30000000
+#define DIVA_UM_IDI_REQ_MAN (DIVA_UM_IDI_REQ | DIVA_UM_IDI_REQ_TYPE_MAN)
+#define DIVA_UM_IDI_REQ_SIG (DIVA_UM_IDI_REQ | DIVA_UM_IDI_REQ_TYPE_SIG)
+#define DIVA_UM_IDI_REQ_NET (DIVA_UM_IDI_REQ | DIVA_UM_IDI_REQ_TYPE_NET)
+/*
+ data_length bytes will follow this structure
+*/
+typedef struct _diva_um_idi_req_hdr {
+ dword type;
+ dword Req;
+ dword ReqCh;
+ dword data_length;
+} diva_um_idi_req_hdr_t;
+
+typedef struct _diva_um_idi_ind_parameters {
+ dword Ind;
+ dword IndCh;
+} diva_um_idi_ind_parameters_t;
+
+typedef struct _diva_um_idi_rc_parameters {
+ dword Rc;
+ dword RcCh;
+} diva_um_idi_rc_parameters_t;
+
+typedef union _diva_um_idi_ind {
+ diva_um_idi_adapter_features_t features;
+ diva_um_idi_ind_parameters_t ind;
+ diva_um_idi_rc_parameters_t rc;
+} diva_um_idi_ind_t;
+
+#define DIVA_UM_IDI_IND_FEATURES 1 /* features indication */
+#define DIVA_UM_IDI_IND 2
+#define DIVA_UM_IDI_IND_RC 3
+/*
+ data_length bytes of data follow
+ this structure
+*/
+typedef struct _diva_um_idi_ind_hdr {
+ dword type;
+ diva_um_idi_ind_t hdr;
+ dword data_length;
+} diva_um_idi_ind_hdr_t;
+
+#endif
diff --git a/drivers/isdn/hardware/eicon/xdi_adapter.h b/drivers/isdn/hardware/eicon/xdi_adapter.h
new file mode 100644
index 0000000..a3bd163
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/xdi_adapter.h
@@ -0,0 +1,70 @@
+/* $Id: xdi_adapter.h,v 1.7 2004/03/21 17:26:01 armin Exp $ */
+
+#ifndef __DIVA_OS_XDI_ADAPTER_H__
+#define __DIVA_OS_XDI_ADAPTER_H__
+
+#define DIVAS_XDI_ADAPTER_BUS_PCI 0
+#define DIVAS_XDI_ADAPTER_BUS_ISA 1
+
+typedef struct _divas_pci_card_resources {
+ byte bus;
+ byte func;
+ void *hdev;
+
+ dword bar[8]; /* contains context of appropriate BAR Register */
+ void __iomem *addr[8]; /* same bar, but mapped into memory */
+ dword length[8]; /* bar length */
+ int mem_type_id[MAX_MEM_TYPE];
+ unsigned int qoffset;
+ byte irq;
+} divas_pci_card_resources_t;
+
+typedef union _divas_card_resources {
+ divas_pci_card_resources_t pci;
+} divas_card_resources_t;
+
+struct _diva_os_xdi_adapter;
+typedef int (*diva_init_card_proc_t) (struct _diva_os_xdi_adapter * a);
+typedef int (*diva_cmd_card_proc_t) (struct _diva_os_xdi_adapter * a,
+ diva_xdi_um_cfg_cmd_t * data,
+ int length);
+typedef void (*diva_xdi_clear_interrupts_proc_t) (struct
+ _diva_os_xdi_adapter *);
+
+#define DIVA_XDI_MBOX_BUSY 1
+#define DIVA_XDI_MBOX_WAIT_XLOG 2
+
+typedef struct _xdi_mbox_t {
+ dword status;
+ diva_xdi_um_cfg_cmd_data_t cmd_data;
+ dword data_length;
+ void *data;
+} xdi_mbox_t;
+
+typedef struct _diva_os_idi_adapter_interface {
+ diva_init_card_proc_t cleanup_adapter_proc;
+ diva_cmd_card_proc_t cmd_proc;
+} diva_os_idi_adapter_interface_t;
+
+typedef struct _diva_os_xdi_adapter {
+ struct list_head link;
+ int CardIndex;
+ int CardOrdinal;
+ int controller; /* number of this controller */
+ int Bus; /* PCI, ISA, ... */
+ divas_card_resources_t resources;
+ char port_name[24];
+ ISDN_ADAPTER xdi_adapter;
+ xdi_mbox_t xdi_mbox;
+ diva_os_idi_adapter_interface_t interface;
+ struct _diva_os_xdi_adapter *slave_adapters[3];
+ void *slave_list;
+ void *proc_adapter_dir; /* adapterX proc entry */
+ void *proc_info; /* info proc entry */
+ void *proc_grp_opt; /* group_optimization */
+ void *proc_d_l1_down; /* dynamic_l1_down */
+ volatile diva_xdi_clear_interrupts_proc_t clear_interrupts_proc;
+ dword dsp_mask;
+} diva_os_xdi_adapter_t;
+
+#endif
diff --git a/drivers/isdn/hardware/eicon/xdi_msg.h b/drivers/isdn/hardware/eicon/xdi_msg.h
new file mode 100644
index 0000000..3ade28f
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/xdi_msg.h
@@ -0,0 +1,127 @@
+/* $Id: xdi_msg.h,v 1.1.2.2 2001/02/16 08:40:36 armin Exp $ */
+
+#ifndef __DIVA_XDI_UM_CFG_MESSSGE_H__
+#define __DIVA_XDI_UM_CFG_MESSAGE_H__
+
+/*
+ Definition of messages used to communicate between
+ XDI device driver and user mode configuration utility
+*/
+
+/*
+ As acknowledge one DWORD - card ordinal will be read from the card
+*/
+#define DIVA_XDI_UM_CMD_GET_CARD_ORDINAL 0
+
+/*
+ no acknowledge will be generated, memory block will be written in the
+ memory at given offset
+*/
+#define DIVA_XDI_UM_CMD_WRITE_SDRAM_BLOCK 1
+
+/*
+ no acknowledge will be genatated, FPGA will be programmed
+*/
+#define DIVA_XDI_UM_CMD_WRITE_FPGA 2
+
+/*
+ As acknowledge block of SDRAM will be read in the user buffer
+*/
+#define DIVA_XDI_UM_CMD_READ_SDRAM 3
+
+/*
+ As acknowledge dword with serial number will be read in the user buffer
+*/
+#define DIVA_XDI_UM_CMD_GET_SERIAL_NR 4
+
+/*
+ As acknowledge struct consisting from 9 dwords with PCI info.
+ dword[0...7] = 8 PCI BARS
+ dword[9] = IRQ
+*/
+#define DIVA_XDI_UM_CMD_GET_PCI_HW_CONFIG 5
+
+/*
+ Reset of the board + activation of primary
+ boot loader
+*/
+#define DIVA_XDI_UM_CMD_RESET_ADAPTER 6
+
+/*
+ Called after code download to start adapter
+ at specified address
+ Start does set new set of features due to fact that we not know
+ if protocol features have changed
+*/
+#define DIVA_XDI_UM_CMD_START_ADAPTER 7
+
+/*
+ Stop adapter, called if user
+ wishes to stop adapter without unload
+ of the driver, to reload adapter with
+ different protocol
+*/
+#define DIVA_XDI_UM_CMD_STOP_ADAPTER 8
+
+/*
+ Get state of current adapter
+ Acknowledge is one dword with following values:
+ 0 - adapter ready for download
+ 1 - adapter running
+ 2 - adapter dead
+ 3 - out of service, driver should be restarted or hardware problem
+*/
+#define DIVA_XDI_UM_CMD_GET_CARD_STATE 9
+
+/*
+ Reads XLOG entry from the card
+*/
+#define DIVA_XDI_UM_CMD_READ_XLOG_ENTRY 10
+
+/*
+ Set untranslated protocol code features
+ */
+#define DIVA_XDI_UM_CMD_SET_PROTOCOL_FEATURES 11
+
+typedef struct _diva_xdi_um_cfg_cmd_data_set_features {
+ dword features;
+} diva_xdi_um_cfg_cmd_data_set_features_t;
+
+typedef struct _diva_xdi_um_cfg_cmd_data_start {
+ dword offset;
+ dword features;
+} diva_xdi_um_cfg_cmd_data_start_t;
+
+typedef struct _diva_xdi_um_cfg_cmd_data_write_sdram {
+ dword ram_number;
+ dword offset;
+ dword length;
+} diva_xdi_um_cfg_cmd_data_write_sdram_t;
+
+typedef struct _diva_xdi_um_cfg_cmd_data_write_fpga {
+ dword fpga_number;
+ dword image_length;
+} diva_xdi_um_cfg_cmd_data_write_fpga_t;
+
+typedef struct _diva_xdi_um_cfg_cmd_data_read_sdram {
+ dword ram_number;
+ dword offset;
+ dword length;
+} diva_xdi_um_cfg_cmd_data_read_sdram_t;
+
+typedef union _diva_xdi_um_cfg_cmd_data {
+ diva_xdi_um_cfg_cmd_data_write_sdram_t write_sdram;
+ diva_xdi_um_cfg_cmd_data_write_fpga_t write_fpga;
+ diva_xdi_um_cfg_cmd_data_read_sdram_t read_sdram;
+ diva_xdi_um_cfg_cmd_data_start_t start;
+ diva_xdi_um_cfg_cmd_data_set_features_t features;
+} diva_xdi_um_cfg_cmd_data_t;
+
+typedef struct _diva_xdi_um_cfg_cmd {
+ dword adapter; /* Adapter number 1...N */
+ dword command;
+ diva_xdi_um_cfg_cmd_data_t command_data;
+ dword data_length; /* Plain binary data will follow */
+} diva_xdi_um_cfg_cmd_t;
+
+#endif
diff --git a/drivers/isdn/hardware/eicon/xdi_vers.h b/drivers/isdn/hardware/eicon/xdi_vers.h
new file mode 100644
index 0000000..cf34941
--- /dev/null
+++ b/drivers/isdn/hardware/eicon/xdi_vers.h
@@ -0,0 +1,26 @@
+
+/*
+ *
+ Copyright (c) Eicon Networks, 2002.
+ *
+ This source file is supplied for the use with
+ Eicon Networks range of DIVA Server Adapters.
+ *
+ Eicon File Revision : 2.1
+ *
+ 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 OF ANY KIND WHATSOEVER INCLUDING ANY
+ 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.
+ *
+ */
+static char diva_xdi_common_code_build[] = "102-52";
diff --git a/drivers/isdn/hardware/mISDN/Kconfig b/drivers/isdn/hardware/mISDN/Kconfig
new file mode 100644
index 0000000..1479348
--- /dev/null
+++ b/drivers/isdn/hardware/mISDN/Kconfig
@@ -0,0 +1,25 @@
+#
+# Hardware for mISDN
+#
+comment "mISDN hardware drivers"
+
+config MISDN_HFCPCI
+ tristate "Support for HFC PCI cards"
+ depends on MISDN
+ depends on PCI
+ help
+ Enable support for cards with Cologne Chip AG's
+ HFC PCI chip.
+
+config MISDN_HFCMULTI
+ tristate "Support for HFC multiport cards (HFC-4S/8S/E1)"
+ depends on PCI
+ depends on MISDN
+ help
+ Enable support for cards 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)
+
diff --git a/drivers/isdn/hardware/mISDN/Makefile b/drivers/isdn/hardware/mISDN/Makefile
new file mode 100644
index 0000000..1e7ca53
--- /dev/null
+++ b/drivers/isdn/hardware/mISDN/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the modular ISDN hardware drivers
+#
+#
+
+obj-$(CONFIG_MISDN_HFCPCI) += hfcpci.o
+obj-$(CONFIG_MISDN_HFCMULTI) += hfcmulti.o
diff --git a/drivers/isdn/hardware/mISDN/hfc_multi.h b/drivers/isdn/hardware/mISDN/hfc_multi.h
new file mode 100644
index 0000000..a33d87a
--- /dev/null
+++ b/drivers/isdn/hardware/mISDN/hfc_multi.h
@@ -0,0 +1,1204 @@
+/*
+ * see notice in hfc_multi.c
+ */
+
+extern void ztdummy_extern_interrupt(void);
+extern void ztdummy_register_interrupt(void);
+extern int ztdummy_unregister_interrupt(void);
+
+#define DEBUG_HFCMULTI_FIFO 0x00010000
+#define DEBUG_HFCMULTI_CRC 0x00020000
+#define DEBUG_HFCMULTI_INIT 0x00040000
+#define DEBUG_HFCMULTI_PLXSD 0x00080000
+#define DEBUG_HFCMULTI_MODE 0x00100000
+#define DEBUG_HFCMULTI_MSG 0x00200000
+#define DEBUG_HFCMULTI_STATE 0x00400000
+#define DEBUG_HFCMULTI_SYNC 0x01000000
+#define DEBUG_HFCMULTI_DTMF 0x02000000
+#define DEBUG_HFCMULTI_LOCK 0x80000000
+
+#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 {
+ struct dchannel *dch; /* link if channel is a D-channel */
+ struct bchannel *bch; /* link if channel is a B-channel */
+ int port; /* the interface port this */
+ /* channel is associated with */
+ int nt_timer; /* -1 if off, 0 if elapsed, >0 if running */
+ int los, ais, slip_tx, slip_rx, rdi; /* current alarms */
+ int jitter;
+ u_long cfg; /* port configuration */
+ int sync; /* sync state (used by E1) */
+ u_int 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 rx_off; /* set to turn fifo receive off */
+ int coeff_count; /* curren coeff block */
+ s32 *coeff; /* memory pointer to 8 coeff blocks */
+};
+
+
+struct hfcm_hw {
+ u_char r_ctrl;
+ u_char r_irq_ctrl;
+ u_char r_cirm;
+ u_char r_ram_sz;
+ u_char r_pcm_md0;
+ u_char r_irqmsk_misc;
+ u_char r_dtmf;
+ u_char r_st_sync;
+ u_char r_sci_msk;
+ u_char r_tx0, r_tx1;
+ u_char a_st_ctrl0[8];
+ timer_t timer;
+};
+
+
+/* for each stack these flags are used (cfg) */
+#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 3 /* register E-channel */
+#define HFC_CFG_OPTICAL 4 /* the E1 interface is optical */
+#define HFC_CFG_REPORT_LOS 5 /* the card should report loss of signal */
+#define HFC_CFG_REPORT_AIS 6 /* the card should report alarm ind. sign. */
+#define HFC_CFG_REPORT_SLIP 7 /* the card should report bit slips */
+#define HFC_CFG_REPORT_RDI 8 /* the card should report remote alarm */
+#define HFC_CFG_DTMF 9 /* enable DTMF-detection */
+#define HFC_CFG_CRC4 10 /* disable CRC-4 Multiframe mode, */
+ /* use double frame instead. */
+
+#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_PCM_MASTER 4 /* PCM is master */
+#define HFC_CHIP_RX_SYNC 5 /* disable pll sync for pcm */
+#define HFC_CHIP_DTMF 6 /* DTMF decoding is enabled */
+#define HFC_CHIP_ULAW 7 /* ULAW mode */
+#define HFC_CHIP_CLOCK2 8 /* double clock mode */
+#define HFC_CHIP_E1CLOCK_GET 9 /* always get clock from E1 interface */
+#define HFC_CHIP_E1CLOCK_PUT 10 /* always put clock from E1 interface */
+#define HFC_CHIP_WATCHDOG 11 /* whether we should send signals */
+ /* to the watchdog */
+#define HFC_CHIP_B410P 12 /* whether we have a b410p with echocan in */
+ /* hw */
+#define HFC_CHIP_PLXSD 13 /* whether we have a Speech-Design PLX */
+
+#define HFC_IO_MODE_PCIMEM 0x00 /* normal memory mapped IO */
+#define HFC_IO_MODE_REGIO 0x01 /* PCI io access */
+#define HFC_IO_MODE_PLXSD 0x02 /* access HFC via PLX9030 */
+
+/* table entry in the PCI devices list */
+struct hm_map {
+ char *vendor_name;
+ char *card_name;
+ int type;
+ int ports;
+ int clock2;
+ int leds;
+ int opticalsupport;
+ int dip_type;
+ int io_mode;
+};
+
+struct hfc_multi {
+ struct list_head list;
+ struct hm_map *mtyp;
+ int id;
+ int pcm; /* id of pcm bus */
+ int type;
+ int ports;
+
+ u_int irq; /* irq used by card */
+ u_int irqcnt;
+ struct pci_dev *pci_dev;
+ int io_mode; /* selects mode */
+#ifdef HFC_REGISTER_DEBUG
+ void (*HFC_outb)(struct hfc_multi *hc, u_char reg,
+ u_char val, const char *function, int line);
+ void (*HFC_outb_nodebug)(struct hfc_multi *hc, u_char reg,
+ u_char val, const char *function, int line);
+ u_char (*HFC_inb)(struct hfc_multi *hc, u_char reg,
+ const char *function, int line);
+ u_char (*HFC_inb_nodebug)(struct hfc_multi *hc, u_char reg,
+ const char *function, int line);
+ u_short (*HFC_inw)(struct hfc_multi *hc, u_char reg,
+ const char *function, int line);
+ u_short (*HFC_inw_nodebug)(struct hfc_multi *hc, u_char reg,
+ const char *function, int line);
+ void (*HFC_wait)(struct hfc_multi *hc,
+ const char *function, int line);
+ void (*HFC_wait_nodebug)(struct hfc_multi *hc,
+ const char *function, int line);
+#else
+ void (*HFC_outb)(struct hfc_multi *hc, u_char reg,
+ u_char val);
+ void (*HFC_outb_nodebug)(struct hfc_multi *hc, u_char reg,
+ u_char val);
+ u_char (*HFC_inb)(struct hfc_multi *hc, u_char reg);
+ u_char (*HFC_inb_nodebug)(struct hfc_multi *hc, u_char reg);
+ u_short (*HFC_inw)(struct hfc_multi *hc, u_char reg);
+ u_short (*HFC_inw_nodebug)(struct hfc_multi *hc, u_char reg);
+ void (*HFC_wait)(struct hfc_multi *hc);
+ void (*HFC_wait_nodebug)(struct hfc_multi *hc);
+#endif
+ void (*read_fifo)(struct hfc_multi *hc, u_char *data,
+ int len);
+ void (*write_fifo)(struct hfc_multi *hc, u_char *data,
+ int len);
+ u_long pci_origmembase, plx_origmembase, dsp_origmembase;
+ u_char *pci_membase; /* PCI memory (MUST BE BYTE POINTER) */
+ u_char *plx_membase; /* PLX memory */
+ u_char *dsp_membase; /* DSP on PLX */
+ u_long pci_iobase; /* PCI IO */
+ struct hfcm_hw 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 (must be int for calculation)*/
+ int max_trans; /* maximum transparent fifo fill */
+ 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_long ledstate; /* save last state of leds */
+ int opticalsupport; /* has the e1 board */
+ /* an optical Interface */
+ int dslot; /* channel # of d-channel (E1) default 16 */
+
+ u_long wdcount; /* every 500 ms we need to */
+ /* send the watchdog a signal */
+ u_char wdbyte; /* watchdog toggle byte */
+ u_int activity[8]; /* if there is any action on this */
+ /* port (will be cleared after */
+ /* showing led-states) */
+ int e1_state; /* keep track of last state */
+ int e1_getclock; /* if sync is retrieved from interface */
+ int syncronized; /* keep track of existing sync interface */
+ int e1_resync; /* resync jobs */
+
+ spinlock_t lock; /* the lock */
+
+ /*
+ * 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
+ */
+ struct hfc_chan chan[32];
+ u_char created[8]; /* what port is created */
+ signed char slot_owner[256]; /* owner channel of slot */
+};
+
+/* PLX GPIOs */
+#define PLX_GPIO4_DIR_BIT 13
+#define PLX_GPIO4_BIT 14
+#define PLX_GPIO5_DIR_BIT 16
+#define PLX_GPIO5_BIT 17
+#define PLX_GPIO6_DIR_BIT 19
+#define PLX_GPIO6_BIT 20
+#define PLX_GPIO7_DIR_BIT 22
+#define PLX_GPIO7_BIT 23
+#define PLX_GPIO8_DIR_BIT 25
+#define PLX_GPIO8_BIT 26
+
+#define PLX_GPIO4 (1 << PLX_GPIO4_BIT)
+#define PLX_GPIO5 (1 << PLX_GPIO5_BIT)
+#define PLX_GPIO6 (1 << PLX_GPIO6_BIT)
+#define PLX_GPIO7 (1 << PLX_GPIO7_BIT)
+#define PLX_GPIO8 (1 << PLX_GPIO8_BIT)
+
+#define PLX_GPIO4_DIR (1 << PLX_GPIO4_DIR_BIT)
+#define PLX_GPIO5_DIR (1 << PLX_GPIO5_DIR_BIT)
+#define PLX_GPIO6_DIR (1 << PLX_GPIO6_DIR_BIT)
+#define PLX_GPIO7_DIR (1 << PLX_GPIO7_DIR_BIT)
+#define PLX_GPIO8_DIR (1 << PLX_GPIO8_DIR_BIT)
+
+#define PLX_TERM_ON PLX_GPIO7
+#define PLX_SLAVE_EN_N PLX_GPIO5
+#define PLX_MASTER_EN PLX_GPIO6
+#define PLX_SYNC_O_EN PLX_GPIO4
+#define PLX_DSP_RES_N PLX_GPIO8
+/* GPIO4..8 Enable & Set to OUT, SLAVE_EN_N = 1 */
+#define PLX_GPIOC_INIT (PLX_GPIO4_DIR | PLX_GPIO5_DIR | PLX_GPIO6_DIR \
+ | PLX_GPIO7_DIR | PLX_GPIO8_DIR | PLX_SLAVE_EN_N)
+
+/* PLX Interrupt Control/STATUS */
+#define PLX_INTCSR_LINTI1_ENABLE 0x01
+#define PLX_INTCSR_LINTI1_STATUS 0x04
+#define PLX_INTCSR_LINTI2_ENABLE 0x08
+#define PLX_INTCSR_LINTI2_STATUS 0x20
+#define PLX_INTCSR_PCIINT_ENABLE 0x40
+
+/* PLX Registers */
+#define PLX_INTCSR 0x4c
+#define PLX_CNTRL 0x50
+#define PLX_GPIOC 0x54
+
+
+/*
+ * 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_MD0 0x14
+#define R_PCM_MD1 0x15
+#define R_PCM_MD2 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 0x31
+#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_SYNC_STA 0x24
+#define R_RX_SL0_0 0x25
+#define R_RX_SL0_1 0x26
+#define R_RX_SL0_2 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_OFF 0x80
+/* R_STATE */
+#define V_E1_STA 0x01
+#define V_ALT_FR_RX 0x40
+#define V_ALT_FR_TX 0x80
+/* R_SYNC_STA */
+#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_SL0_0 */
+#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_MD0 */
+#define V_PCM_MD 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_SYNC_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_DEBUG
+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/R_SYNC_OUT", 0x31},
+ {"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_SYNC_STA", 0x24},
+ {"R_RX_SL0_0", 0x25},
+ {"R_RX_SL0_1", 0x26},
+ {"R_RX_SL0_2", 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_DEBUG */
+
diff --git a/drivers/isdn/hardware/mISDN/hfc_pci.h b/drivers/isdn/hardware/mISDN/hfc_pci.h
new file mode 100644
index 0000000..5783d22
--- /dev/null
+++ b/drivers/isdn/hardware/mISDN/hfc_pci.h
@@ -0,0 +1,228 @@
+/*
+ * 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_MAX 256
+#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
+
+struct zt {
+ __le16 z1; /* Z1 pointer 16 Bit */
+ __le16 z2; /* Z2 pointer 16 Bit */
+};
+
+struct dfifo {
+ 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 */
+ /* mask index with D_FREG_MASK for access */
+ struct zt za[MAX_D_FRAMES+1];
+ u_char fill3[0x4000-0x2100]; /* align 16K */
+};
+
+struct bzfifo {
+ struct zt za[MAX_B_FRAMES+1]; /* only range 0x0..0x1F allowed */
+ u_char f1, f2; /* f pointers */
+ u_char fill[0x2100-0x2082]; /* alignment */
+};
+
+
+union fifo_area {
+ struct {
+ struct dfifo d_tx; /* D-send channel */
+ struct dfifo d_rx; /* D-receive channel */
+ } d_chan;
+ struct {
+ u_char fill1[0x200];
+ u_char txdat_b1[B_FIFO_SIZE];
+ struct bzfifo txbz_b1;
+ struct bzfifo txbz_b2;
+ u_char txdat_b2[B_FIFO_SIZE];
+ u_char fill2[D_FIFO_SIZE];
+ u_char rxdat_b1[B_FIFO_SIZE];
+ struct bzfifo rxbz_b1;
+ struct bzfifo rxbz_b2;
+ u_char rxdat_b2[B_FIFO_SIZE];
+ } b_chans;
+ u_char fill[32768];
+};
+
+#define Write_hfc(a, b, c) (writeb(c, (a->hw.pci_io)+b))
+#define Read_hfc(a, b) (readb((a->hw.pci_io)+b))
diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c
new file mode 100644
index 0000000..1eac03f
--- /dev/null
+++ b/drivers/isdn/hardware/mISDN/hfcmulti.c
@@ -0,0 +1,5319 @@
+/*
+ * hfcmulti.c low level driver for hfc-4s/hfc-8s/hfc-e1 based cards
+ *
+ * Author Andreas Eversberg (jolly@eversberg.eu)
+ * ported to mqueue mechanism:
+ * Peter Sprenger (sprengermoving-bytes.de)
+ *
+ * inspired by existing hfc-pci driver:
+ * Copyright 1999 by Werner Cornelius (werner@isdn-development.de)
+ * Copyright 2008 by Karsten Keil (kkeil@suse.de)
+ * Copyright 2008 by Andreas Eversberg (jolly@eversberg.eu)
+ *
+ * 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.
+ *
+ *
+ * Thanks to Cologne Chip AG for this great controller!
+ */
+
+/*
+ * module parameters:
+ * type:
+ * By default (0), the card is automatically detected.
+ * Or use the following combinations:
+ * Bit 0-7 = 0x00001 = HFC-E1 (1 port)
+ * or Bit 0-7 = 0x00004 = HFC-4S (4 ports)
+ * or Bit 0-7 = 0x00008 = HFC-8S (8 ports)
+ * Bit 8 = 0x00100 = uLaw (instead of aLaw)
+ * Bit 9 = 0x00200 = Disable DTMF detect on all B-channels via hardware
+ * Bit 10 = spare
+ * Bit 11 = 0x00800 = Force PCM bus into slave mode. (otherwhise auto)
+ * or Bit 12 = 0x01000 = Force PCM bus into master mode. (otherwhise auto)
+ * Bit 13 = spare
+ * Bit 14 = 0x04000 = Use external ram (128K)
+ * Bit 15 = 0x08000 = Use external ram (512K)
+ * Bit 16 = 0x10000 = Use 64 timeslots instead of 32
+ * or Bit 17 = 0x20000 = Use 128 timeslots instead of anything else
+ * Bit 18 = spare
+ * Bit 19 = 0x80000 = Send the Watchdog a Signal (Dual E1 with Watchdog)
+ * (all other bits are reserved and shall be 0)
+ * example: 0x20204 one HFC-4S with dtmf detection and 128 timeslots on PCM
+ * bus (PCM master)
+ *
+ * port: (optional or required for all ports on all installed cards)
+ * HFC-4S/HFC-8S only bits:
+ * Bit 0 = 0x001 = Use master clock for this S/T interface
+ * (ony once per chip).
+ * Bit 1 = 0x002 = transmitter line setup (non capacitive mode)
+ * Don't use this unless you know what you are doing!
+ * Bit 2 = 0x004 = Disable E-channel. (No E-channel processing)
+ * example: 0x0001,0x0000,0x0000,0x0000 one HFC-4S with master clock
+ * received from port 1
+ *
+ * HFC-E1 only bits:
+ * Bit 0 = 0x0001 = interface: 0=copper, 1=optical
+ * Bit 1 = 0x0002 = reserved (later for 32 B-channels transparent mode)
+ * Bit 2 = 0x0004 = Report LOS
+ * Bit 3 = 0x0008 = Report AIS
+ * Bit 4 = 0x0010 = Report SLIP
+ * Bit 5 = 0x0020 = Report RDI
+ * Bit 8 = 0x0100 = Turn off CRC-4 Multiframe Mode, use double frame
+ * mode instead.
+ * Bit 9 = 0x0200 = Force get clock from interface, even in NT mode.
+ * or Bit 10 = 0x0400 = Force put clock to interface, even in TE mode.
+ * Bit 11 = 0x0800 = Use direct RX clock for PCM sync rather than PLL.
+ * (E1 only)
+ * Bit 12-13 = 0xX000 = elastic jitter buffer (1-3), Set both bits to 0
+ * for default.
+ * (all other bits are reserved and shall be 0)
+ *
+ * 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 every card.
+ * The PCM bus id tells the mISDNdsp module about the connected PCM bus.
+ * By default (0), the PCM bus id is 100 for the card that is PCM master.
+ * If multiple cards are PCM master (because they are not interconnected),
+ * each card with PCM master will have increasing PCM id.
+ * All PCM busses with the same ID are expected to be connected and have
+ * common time slots slots.
+ * Only one chip of the PCM bus must be master, the others slave.
+ * -1 means no support of PCM bus not even.
+ * Omit this value, if all cards are interconnected or none is connected.
+ * If unsure, don't give this parameter.
+ *
+ * dslot:
+ * NOTE: only one poll value must be given for every card.
+ * Also this value must be given for non-E1 cards. If omitted, the E1
+ * card has D-channel on time slot 16, which is default.
+ * If 1..15 or 17..31, an alternate time slot is used for D-channel.
+ * In this case, the application must be able to handle this.
+ * If -1 is given, the D-channel is disabled and all 31 slots can be used
+ * for B-channel. (only for specific applications)
+ * If you don't know how to use it, you don't need it!
+ *
+ * iomode:
+ * NOTE: only one mode value must be given for every card.
+ * -> See hfc_multi.h for HFC_IO_MODE_* values
+ * By default, the IO mode is pci memory IO (MEMIO).
+ * Some cards requre specific IO mode, so it cannot be changed.
+ * It may be usefull to set IO mode to register io (REGIO) to solve
+ * PCI bridge problems.
+ * If unsure, don't give this parameter.
+ *
+ * clockdelay_nt:
+ * NOTE: only one clockdelay_nt value must be given once for all cards.
+ * Give the value of the clock control register (A_ST_CLK_DLY)
+ * of the S/T interfaces in NT mode.
+ * This register is needed for the TBR3 certification, so don't change it.
+ *
+ * clockdelay_te:
+ * NOTE: only one clockdelay_te value must be given once
+ * Give the value of the clock control register (A_ST_CLK_DLY)
+ * of the S/T interfaces in TE mode.
+ * This register is needed for the TBR3 certification, so don't change it.
+ */
+
+/*
+ * debug register access (never use this, it will flood your system log)
+ * #define HFC_REGISTER_DEBUG
+ */
+
+static const char *hfcmulti_revision = "2.02";
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/mISDNhw.h>
+#include <linux/mISDNdsp.h>
+
+/*
+#define IRQCOUNT_DEBUG
+#define IRQ_DEBUG
+*/
+
+#include "hfc_multi.h"
+#ifdef ECHOPREP
+#include "gaintab.h"
+#endif
+
+#define MAX_CARDS 8
+#define MAX_PORTS (8 * MAX_CARDS)
+
+static LIST_HEAD(HFClist);
+static spinlock_t HFClock; /* global hfc list lock */
+
+static void ph_state_change(struct dchannel *);
+static void (*hfc_interrupt)(void);
+static void (*register_interrupt)(void);
+static int (*unregister_interrupt)(void);
+static int interrupt_registered;
+
+static struct hfc_multi *syncmaster;
+int plxsd_master; /* if we have a master card (yet) */
+static spinlock_t plx_lock; /* may not acquire other lock inside */
+EXPORT_SYMBOL(plx_lock);
+
+#define TYP_E1 1
+#define TYP_4S 4
+#define TYP_8S 8
+
+static int poll_timer = 6; /* default = 128 samples = 16ms */
+/* number of POLL_TIMER interrupts for G2 timeout (ca 1s) */
+static int nt_t1_count[] = { 3840, 1920, 960, 480, 240, 120, 60, 30 };
+#define CLKDEL_TE 0x0f /* CLKDEL in TE mode */
+#define CLKDEL_NT 0x6c /* CLKDEL in NT mode
+ (0x60 MUST be included!) */
+static u_char silence = 0xff; /* silence by LAW */
+
+#define DIP_4S 0x1 /* DIP Switches for Beronet 1S/2S/4S cards */
+#define DIP_8S 0x2 /* DIP Switches for Beronet 8S+ cards */
+#define DIP_E1 0x3 /* DIP Switches for Beronet E1 cards */
+
+/*
+ * module stuff
+ */
+
+static uint type[MAX_CARDS];
+static uint pcm[MAX_CARDS];
+static uint dslot[MAX_CARDS];
+static uint iomode[MAX_CARDS];
+static uint port[MAX_PORTS];
+static uint debug;
+static uint poll;
+static uint timer;
+static uint clockdelay_te = CLKDEL_TE;
+static uint clockdelay_nt = CLKDEL_NT;
+
+static int HFC_cnt, Port_cnt, PCM_cnt = 99;
+
+MODULE_AUTHOR("Andreas Eversberg");
+MODULE_LICENSE("GPL");
+module_param(debug, uint, S_IRUGO | S_IWUSR);
+module_param(poll, uint, S_IRUGO | S_IWUSR);
+module_param(timer, uint, S_IRUGO | S_IWUSR);
+module_param(clockdelay_te, uint, S_IRUGO | S_IWUSR);
+module_param(clockdelay_nt, uint, S_IRUGO | S_IWUSR);
+module_param_array(type, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(pcm, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(dslot, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(iomode, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(port, uint, NULL, S_IRUGO | S_IWUSR);
+
+#ifdef HFC_REGISTER_DEBUG
+#define HFC_outb(hc, reg, val) \
+ (hc->HFC_outb(hc, reg, val, __func__, __LINE__))
+#define HFC_outb_nodebug(hc, reg, val) \
+ (hc->HFC_outb_nodebug(hc, reg, val, __func__, __LINE__))
+#define HFC_inb(hc, reg) \
+ (hc->HFC_inb(hc, reg, __func__, __LINE__))
+#define HFC_inb_nodebug(hc, reg) \
+ (hc->HFC_inb_nodebug(hc, reg, __func__, __LINE__))
+#define HFC_inw(hc, reg) \
+ (hc->HFC_inw(hc, reg, __func__, __LINE__))
+#define HFC_inw_nodebug(hc, reg) \
+ (hc->HFC_inw_nodebug(hc, reg, __func__, __LINE__))
+#define HFC_wait(hc) \
+ (hc->HFC_wait(hc, __func__, __LINE__))
+#define HFC_wait_nodebug(hc) \
+ (hc->HFC_wait_nodebug(hc, __func__, __LINE__))
+#else
+#define HFC_outb(hc, reg, val) (hc->HFC_outb(hc, reg, val))
+#define HFC_outb_nodebug(hc, reg, val) (hc->HFC_outb_nodebug(hc, reg, val))
+#define HFC_inb(hc, reg) (hc->HFC_inb(hc, reg))
+#define HFC_inb_nodebug(hc, reg) (hc->HFC_inb_nodebug(hc, reg))
+#define HFC_inw(hc, reg) (hc->HFC_inw(hc, reg))
+#define HFC_inw_nodebug(hc, reg) (hc->HFC_inw_nodebug(hc, reg))
+#define HFC_wait(hc) (hc->HFC_wait(hc))
+#define HFC_wait_nodebug(hc) (hc->HFC_wait_nodebug(hc))
+#endif
+
+/* HFC_IO_MODE_PCIMEM */
+static void
+#ifdef HFC_REGISTER_DEBUG
+HFC_outb_pcimem(struct hfc_multi *hc, u_char reg, u_char val,
+ const char *function, int line)
+#else
+HFC_outb_pcimem(struct hfc_multi *hc, u_char reg, u_char val)
+#endif
+{
+ writeb(val, (hc->pci_membase)+reg);
+}
+static u_char
+#ifdef HFC_REGISTER_DEBUG
+HFC_inb_pcimem(struct hfc_multi *hc, u_char reg, const char *function, int line)
+#else
+HFC_inb_pcimem(struct hfc_multi *hc, u_char reg)
+#endif
+{
+ return readb((hc->pci_membase)+reg);
+}
+static u_short
+#ifdef HFC_REGISTER_DEBUG
+HFC_inw_pcimem(struct hfc_multi *hc, u_char reg, const char *function, int line)
+#else
+HFC_inw_pcimem(struct hfc_multi *hc, u_char reg)
+#endif
+{
+ return readw((hc->pci_membase)+reg);
+}
+static void
+#ifdef HFC_REGISTER_DEBUG
+HFC_wait_pcimem(struct hfc_multi *hc, const char *function, int line)
+#else
+HFC_wait_pcimem(struct hfc_multi *hc)
+#endif
+{
+ while (readb((hc->pci_membase)+R_STATUS) & V_BUSY);
+}
+
+/* HFC_IO_MODE_REGIO */
+static void
+#ifdef HFC_REGISTER_DEBUG
+HFC_outb_regio(struct hfc_multi *hc, u_char reg, u_char val,
+ const char *function, int line)
+#else
+HFC_outb_regio(struct hfc_multi *hc, u_char reg, u_char val)
+#endif
+{
+ outb(reg, (hc->pci_iobase)+4);
+ outb(val, hc->pci_iobase);
+}
+static u_char
+#ifdef HFC_REGISTER_DEBUG
+HFC_inb_regio(struct hfc_multi *hc, u_char reg, const char *function, int line)
+#else
+HFC_inb_regio(struct hfc_multi *hc, u_char reg)
+#endif
+{
+ outb(reg, (hc->pci_iobase)+4);
+ return inb(hc->pci_iobase);
+}
+static u_short
+#ifdef HFC_REGISTER_DEBUG
+HFC_inw_regio(struct hfc_multi *hc, u_char reg, const char *function, int line)
+#else
+HFC_inw_regio(struct hfc_multi *hc, u_char reg)
+#endif
+{
+ outb(reg, (hc->pci_iobase)+4);
+ return inw(hc->pci_iobase);
+}
+static void
+#ifdef HFC_REGISTER_DEBUG
+HFC_wait_regio(struct hfc_multi *hc, const char *function, int line)
+#else
+HFC_wait_regio(struct hfc_multi *hc)
+#endif
+{
+ outb(R_STATUS, (hc->pci_iobase)+4);
+ while (inb(hc->pci_iobase) & V_BUSY);
+}
+
+#ifdef HFC_REGISTER_DEBUG
+static void
+HFC_outb_debug(struct hfc_multi *hc, u_char reg, u_char val,
+ const 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 == reg)
+ strcat(regname, hfc_register_names[i].name);
+ }
+ if (regname[0] == '\0')
+ strcpy(regname, "register");
+
+ bits[7] = '0'+(!!(val&1));
+ bits[6] = '0'+(!!(val&2));
+ bits[5] = '0'+(!!(val&4));
+ bits[4] = '0'+(!!(val&8));
+ bits[3] = '0'+(!!(val&16));
+ bits[2] = '0'+(!!(val&32));
+ bits[1] = '0'+(!!(val&64));
+ bits[0] = '0'+(!!(val&128));
+ printk(KERN_DEBUG
+ "HFC_outb(chip %d, %02x=%s, 0x%02x=%s); in %s() line %d\n",
+ hc->id, reg, regname, val, bits, function, line);
+ HFC_outb_nodebug(hc, reg, val);
+}
+static u_char
+HFC_inb_debug(struct hfc_multi *hc, u_char reg, const char *function, int line)
+{
+ char regname[256] = "", bits[9] = "xxxxxxxx";
+ u_char val = HFC_inb_nodebug(hc, reg);
+ int i;
+
+ i = 0;
+ while (hfc_register_names[i++].name)
+ ;
+ while (hfc_register_names[++i].name) {
+ if (hfc_register_names[i].reg == reg)
+ strcat(regname, hfc_register_names[i].name);
+ }
+ if (regname[0] == '\0')
+ strcpy(regname, "register");
+
+ bits[7] = '0'+(!!(val&1));
+ bits[6] = '0'+(!!(val&2));
+ bits[5] = '0'+(!!(val&4));
+ bits[4] = '0'+(!!(val&8));
+ bits[3] = '0'+(!!(val&16));
+ bits[2] = '0'+(!!(val&32));
+ bits[1] = '0'+(!!(val&64));
+ bits[0] = '0'+(!!(val&128));
+ printk(KERN_DEBUG
+ "HFC_inb(chip %d, %02x=%s) = 0x%02x=%s; in %s() line %d\n",
+ hc->id, reg, regname, val, bits, function, line);
+ return val;
+}
+static u_short
+HFC_inw_debug(struct hfc_multi *hc, u_char reg, const char *function, int line)
+{
+ char regname[256] = "";
+ u_short val = HFC_inw_nodebug(hc, reg);
+ int i;
+
+ i = 0;
+ while (hfc_register_names[i++].name)
+ ;
+ while (hfc_register_names[++i].name) {
+ if (hfc_register_names[i].reg == reg)
+ strcat(regname, hfc_register_names[i].name);
+ }
+ if (regname[0] == '\0')
+ strcpy(regname, "register");
+
+ printk(KERN_DEBUG
+ "HFC_inw(chip %d, %02x=%s) = 0x%04x; in %s() line %d\n",
+ hc->id, reg, regname, val, function, line);
+ return val;
+}
+static void
+HFC_wait_debug(struct hfc_multi *hc, const char *function, int line)
+{
+ printk(KERN_DEBUG "HFC_wait(chip %d); in %s() line %d\n",
+ hc->id, function, line);
+ HFC_wait_nodebug(hc);
+}
+#endif
+
+/* write fifo data (REGIO) */
+void
+write_fifo_regio(struct hfc_multi *hc, u_char *data, int len)
+{
+ outb(A_FIFO_DATA0, (hc->pci_iobase)+4);
+ while (len>>2) {
+ outl(cpu_to_le32(*(u32 *)data), hc->pci_iobase);
+ data += 4;
+ len -= 4;
+ }
+ while (len>>1) {
+ outw(cpu_to_le16(*(u16 *)data), hc->pci_iobase);
+ data += 2;
+ len -= 2;
+ }
+ while (len) {
+ outb(*data, hc->pci_iobase);
+ data++;
+ len--;
+ }
+}
+/* write fifo data (PCIMEM) */
+void
+write_fifo_pcimem(struct hfc_multi *hc, u_char *data, int len)
+{
+ while (len>>2) {
+ writel(cpu_to_le32(*(u32 *)data),
+ hc->pci_membase + A_FIFO_DATA0);
+ data += 4;
+ len -= 4;
+ }
+ while (len>>1) {
+ writew(cpu_to_le16(*(u16 *)data),
+ hc->pci_membase + A_FIFO_DATA0);
+ data += 2;
+ len -= 2;
+ }
+ while (len) {
+ writeb(*data, hc->pci_membase + A_FIFO_DATA0);
+ data++;
+ len--;
+ }
+}
+/* read fifo data (REGIO) */
+void
+read_fifo_regio(struct hfc_multi *hc, u_char *data, int len)
+{
+ outb(A_FIFO_DATA0, (hc->pci_iobase)+4);
+ while (len>>2) {
+ *(u32 *)data = le32_to_cpu(inl(hc->pci_iobase));
+ data += 4;
+ len -= 4;
+ }
+ while (len>>1) {
+ *(u16 *)data = le16_to_cpu(inw(hc->pci_iobase));
+ data += 2;
+ len -= 2;
+ }
+ while (len) {
+ *data = inb(hc->pci_iobase);
+ data++;
+ len--;
+ }
+}
+
+/* read fifo data (PCIMEM) */
+void
+read_fifo_pcimem(struct hfc_multi *hc, u_char *data, int len)
+{
+ while (len>>2) {
+ *(u32 *)data =
+ le32_to_cpu(readl(hc->pci_membase + A_FIFO_DATA0));
+ data += 4;
+ len -= 4;
+ }
+ while (len>>1) {
+ *(u16 *)data =
+ le16_to_cpu(readw(hc->pci_membase + A_FIFO_DATA0));
+ data += 2;
+ len -= 2;
+ }
+ while (len) {
+ *data = readb(hc->pci_membase + A_FIFO_DATA0);
+ data++;
+ len--;
+ }
+}
+
+
+static void
+enable_hwirq(struct hfc_multi *hc)
+{
+ hc->hw.r_irq_ctrl |= V_GLOB_IRQ_EN;
+ HFC_outb(hc, R_IRQ_CTRL, hc->hw.r_irq_ctrl);
+}
+
+static void
+disable_hwirq(struct hfc_multi *hc)
+{
+ hc->hw.r_irq_ctrl &= ~((u_char)V_GLOB_IRQ_EN);
+ HFC_outb(hc, R_IRQ_CTRL, hc->hw.r_irq_ctrl);
+}
+
+#define NUM_EC 2
+#define MAX_TDM_CHAN 32
+
+
+inline void
+enablepcibridge(struct hfc_multi *c)
+{
+ HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x3); /* was _io before */
+}
+
+inline void
+disablepcibridge(struct hfc_multi *c)
+{
+ HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x2); /* was _io before */
+}
+
+inline unsigned char
+readpcibridge(struct hfc_multi *hc, unsigned char address)
+{
+ unsigned short cipv;
+ unsigned char data;
+
+ if (!hc->pci_iobase)
+ return 0;
+
+ /* slow down a PCI read access by 1 PCI clock cycle */
+ HFC_outb(hc, R_CTRL, 0x4); /*was _io before*/
+
+ if (address == 0)
+ cipv = 0x4000;
+ else
+ cipv = 0x5800;
+
+ /* select local bridge port address by writing to CIP port */
+ /* data = HFC_inb(c, cipv); * was _io before */
+ outw(cipv, hc->pci_iobase + 4);
+ data = inb(hc->pci_iobase);
+
+ /* restore R_CTRL for normal PCI read cycle speed */
+ HFC_outb(hc, R_CTRL, 0x0); /* was _io before */
+
+ return data;
+}
+
+inline void
+writepcibridge(struct hfc_multi *hc, unsigned char address, unsigned char data)
+{
+ unsigned short cipv;
+ unsigned int datav;
+
+ if (!hc->pci_iobase)
+ return;
+
+ if (address == 0)
+ cipv = 0x4000;
+ else
+ cipv = 0x5800;
+
+ /* select local bridge port address by writing to CIP port */
+ outw(cipv, hc->pci_iobase + 4);
+ /* define a 32 bit dword with 4 identical bytes for write sequence */
+ datav = data | ((__u32) data << 8) | ((__u32) data << 16) |
+ ((__u32) data << 24);
+
+ /*
+ * write this 32 bit dword to the bridge data port
+ * this will initiate a write sequence of up to 4 writes to the same
+ * address on the local bus interface the number of write accesses
+ * is undefined but >=1 and depends on the next PCI transaction
+ * during write sequence on the local bus
+ */
+ outl(datav, hc->pci_iobase);
+}
+
+inline void
+cpld_set_reg(struct hfc_multi *hc, unsigned char reg)
+{
+ /* Do data pin read low byte */
+ HFC_outb(hc, R_GPIO_OUT1, reg);
+}
+
+inline void
+cpld_write_reg(struct hfc_multi *hc, unsigned char reg, unsigned char val)
+{
+ cpld_set_reg(hc, reg);
+
+ enablepcibridge(hc);
+ writepcibridge(hc, 1, val);
+ disablepcibridge(hc);
+
+ return;
+}
+
+inline unsigned char
+cpld_read_reg(struct hfc_multi *hc, unsigned char reg)
+{
+ unsigned char bytein;
+
+ cpld_set_reg(hc, reg);
+
+ /* Do data pin read low byte */
+ HFC_outb(hc, R_GPIO_OUT1, reg);
+
+ enablepcibridge(hc);
+ bytein = readpcibridge(hc, 1);
+ disablepcibridge(hc);
+
+ return bytein;
+}
+
+inline void
+vpm_write_address(struct hfc_multi *hc, unsigned short addr)
+{
+ cpld_write_reg(hc, 0, 0xff & addr);
+ cpld_write_reg(hc, 1, 0x01 & (addr >> 8));
+}
+
+inline unsigned short
+vpm_read_address(struct hfc_multi *c)
+{
+ unsigned short addr;
+ unsigned short highbit;
+
+ addr = cpld_read_reg(c, 0);
+ highbit = cpld_read_reg(c, 1);
+
+ addr = addr | (highbit << 8);
+
+ return addr & 0x1ff;
+}
+
+inline unsigned char
+vpm_in(struct hfc_multi *c, int which, unsigned short addr)
+{
+ unsigned char res;
+
+ vpm_write_address(c, addr);
+
+ if (!which)
+ cpld_set_reg(c, 2);
+ else
+ cpld_set_reg(c, 3);
+
+ enablepcibridge(c);
+ res = readpcibridge(c, 1);
+ disablepcibridge(c);
+
+ cpld_set_reg(c, 0);
+
+ return res;
+}
+
+inline void
+vpm_out(struct hfc_multi *c, int which, unsigned short addr,
+ unsigned char data)
+{
+ vpm_write_address(c, addr);
+
+ enablepcibridge(c);
+
+ if (!which)
+ cpld_set_reg(c, 2);
+ else
+ cpld_set_reg(c, 3);
+
+ writepcibridge(c, 1, data);
+
+ cpld_set_reg(c, 0);
+
+ disablepcibridge(c);
+
+ {
+ unsigned char regin;
+ regin = vpm_in(c, which, addr);
+ if (regin != data)
+ printk(KERN_DEBUG "Wrote 0x%x to register 0x%x but got back "
+ "0x%x\n", data, addr, regin);
+ }
+
+}
+
+
+void
+vpm_init(struct hfc_multi *wc)
+{
+ unsigned char reg;
+ unsigned int mask;
+ unsigned int i, x, y;
+ unsigned int ver;
+
+ for (x = 0; x < NUM_EC; x++) {
+ /* Setup GPIO's */
+ if (!x) {
+ ver = vpm_in(wc, x, 0x1a0);
+ printk(KERN_DEBUG "VPM: Chip %d: ver %02x\n", x, ver);
+ }
+
+ for (y = 0; y < 4; y++) {
+ vpm_out(wc, x, 0x1a8 + y, 0x00); /* GPIO out */
+ vpm_out(wc, x, 0x1ac + y, 0x00); /* GPIO dir */
+ vpm_out(wc, x, 0x1b0 + y, 0x00); /* GPIO sel */
+ }
+
+ /* Setup TDM path - sets fsync and tdm_clk as inputs */
+ reg = vpm_in(wc, x, 0x1a3); /* misc_con */
+ vpm_out(wc, x, 0x1a3, reg & ~2);
+
+ /* Setup Echo length (256 taps) */
+ vpm_out(wc, x, 0x022, 1);
+ vpm_out(wc, x, 0x023, 0xff);
+
+ /* Setup timeslots */
+ vpm_out(wc, x, 0x02f, 0x00);
+ mask = 0x02020202 << (x * 4);
+
+ /* Setup the tdm channel masks for all chips */
+ for (i = 0; i < 4; i++)
+ vpm_out(wc, x, 0x33 - i, (mask >> (i << 3)) & 0xff);
+
+ /* Setup convergence rate */
+ printk(KERN_DEBUG "VPM: A-law mode\n");
+ reg = 0x00 | 0x10 | 0x01;
+ vpm_out(wc, x, 0x20, reg);
+ printk(KERN_DEBUG "VPM reg 0x20 is %x\n", reg);
+ /*vpm_out(wc, x, 0x20, (0x00 | 0x08 | 0x20 | 0x10)); */
+
+ vpm_out(wc, x, 0x24, 0x02);
+ reg = vpm_in(wc, x, 0x24);
+ printk(KERN_DEBUG "NLP Thresh is set to %d (0x%x)\n", reg, reg);
+
+ /* Initialize echo cans */
+ for (i = 0; i < MAX_TDM_CHAN; i++) {
+ if (mask & (0x00000001 << i))
+ vpm_out(wc, x, i, 0x00);
+ }
+
+ /*
+ * ARM arch at least disallows a udelay of
+ * more than 2ms... it gives a fake "__bad_udelay"
+ * reference at link-time.
+ * long delays in kernel code are pretty sucky anyway
+ * for now work around it using 5 x 2ms instead of 1 x 10ms
+ */
+
+ udelay(2000);
+ udelay(2000);
+ udelay(2000);
+ udelay(2000);
+ udelay(2000);
+
+ /* Put in bypass mode */
+ for (i = 0; i < MAX_TDM_CHAN; i++) {
+ if (mask & (0x00000001 << i))
+ vpm_out(wc, x, i, 0x01);
+ }
+
+ /* Enable bypass */
+ for (i = 0; i < MAX_TDM_CHAN; i++) {
+ if (mask & (0x00000001 << i))
+ vpm_out(wc, x, 0x78 + i, 0x01);
+ }
+
+ }
+}
+
+void
+vpm_check(struct hfc_multi *hctmp)
+{
+ unsigned char gpi2;
+
+ gpi2 = HFC_inb(hctmp, R_GPI_IN2);
+
+ if ((gpi2 & 0x3) != 0x3)
+ printk(KERN_DEBUG "Got interrupt 0x%x from VPM!\n", gpi2);
+}
+
+
+/*
+ * Interface to enable/disable the HW Echocan
+ *
+ * these functions are called within a spin_lock_irqsave on
+ * the channel instance lock, so we are not disturbed by irqs
+ *
+ * we can later easily change the interface to make other
+ * things configurable, for now we configure the taps
+ *
+ */
+
+void
+vpm_echocan_on(struct hfc_multi *hc, int ch, int taps)
+{
+ unsigned int timeslot;
+ unsigned int unit;
+ struct bchannel *bch = hc->chan[ch].bch;
+#ifdef TXADJ
+ int txadj = -4;
+ struct sk_buff *skb;
+#endif
+ if (hc->chan[ch].protocol != ISDN_P_B_RAW)
+ return;
+
+ if (!bch)
+ return;
+
+#ifdef TXADJ
+ skb = _alloc_mISDN_skb(PH_CONTROL_IND, HFC_VOL_CHANGE_TX,
+ sizeof(int), &txadj, GFP_ATOMIC);
+ if (skb)
+ recv_Bchannel_skb(bch, skb);
+#endif
+
+ timeslot = ((ch/4)*8) + ((ch%4)*4) + 1;
+ unit = ch % 4;
+
+ printk(KERN_NOTICE "vpm_echocan_on called taps [%d] on timeslot %d\n",
+ taps, timeslot);
+
+ vpm_out(hc, unit, timeslot, 0x7e);
+}
+
+void
+vpm_echocan_off(struct hfc_multi *hc, int ch)
+{
+ unsigned int timeslot;
+ unsigned int unit;
+ struct bchannel *bch = hc->chan[ch].bch;
+#ifdef TXADJ
+ int txadj = 0;
+ struct sk_buff *skb;
+#endif
+
+ if (hc->chan[ch].protocol != ISDN_P_B_RAW)
+ return;
+
+ if (!bch)
+ return;
+
+#ifdef TXADJ
+ skb = _alloc_mISDN_skb(PH_CONTROL_IND, HFC_VOL_CHANGE_TX,
+ sizeof(int), &txadj, GFP_ATOMIC);
+ if (skb)
+ recv_Bchannel_skb(bch, skb);
+#endif
+
+ timeslot = ((ch/4)*8) + ((ch%4)*4) + 1;
+ unit = ch % 4;
+
+ printk(KERN_NOTICE "vpm_echocan_off called on timeslot %d\n",
+ timeslot);
+ /* FILLME */
+ vpm_out(hc, unit, timeslot, 0x01);
+}
+
+
+/*
+ * Speech Design resync feature
+ * NOTE: This is called sometimes outside interrupt handler.
+ * We must lock irqsave, so no other interrupt (other card) will occurr!
+ * Also multiple interrupts may nest, so must lock each access (lists, card)!
+ */
+static inline void
+hfcmulti_resync(struct hfc_multi *locked, struct hfc_multi *newmaster, int rm)
+{
+ struct hfc_multi *hc, *next, *pcmmaster = 0;
+ u_int *plx_acc_32, pv;
+ u_long flags;
+
+ spin_lock_irqsave(&HFClock, flags);
+ spin_lock(&plx_lock); /* must be locked inside other locks */
+
+ if (debug & DEBUG_HFCMULTI_PLXSD)
+ printk(KERN_DEBUG "%s: RESYNC(syncmaster=0x%p)\n",
+ __func__, syncmaster);
+
+ /* select new master */
+ if (newmaster) {
+ if (debug & DEBUG_HFCMULTI_PLXSD)
+ printk(KERN_DEBUG "using provided controller\n");
+ } else {
+ list_for_each_entry_safe(hc, next, &HFClist, list) {
+ if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+ if (hc->syncronized) {
+ newmaster = hc;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Disable sync of all cards */
+ list_for_each_entry_safe(hc, next, &HFClist, list) {
+ if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+ plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+ pv = readl(plx_acc_32);
+ pv &= ~PLX_SYNC_O_EN;
+ writel(pv, plx_acc_32);
+ if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) {
+ pcmmaster = hc;
+ if (hc->type == 1) {
+ if (debug & DEBUG_HFCMULTI_PLXSD)
+ printk(KERN_DEBUG
+ "Schedule SYNC_I\n");
+ hc->e1_resync |= 1; /* get SYNC_I */
+ }
+ }
+ }
+ }
+
+ if (newmaster) {
+ hc = newmaster;
+ if (debug & DEBUG_HFCMULTI_PLXSD)
+ printk(KERN_DEBUG "id=%d (0x%p) = syncronized with "
+ "interface.\n", hc->id, hc);
+ /* Enable new sync master */
+ plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+ pv = readl(plx_acc_32);
+ pv |= PLX_SYNC_O_EN;
+ writel(pv, plx_acc_32);
+ /* switch to jatt PLL, if not disabled by RX_SYNC */
+ if (hc->type == 1 && !test_bit(HFC_CHIP_RX_SYNC, &hc->chip)) {
+ if (debug & DEBUG_HFCMULTI_PLXSD)
+ printk(KERN_DEBUG "Schedule jatt PLL\n");
+ hc->e1_resync |= 2; /* switch to jatt */
+ }
+ } else {
+ if (pcmmaster) {
+ hc = pcmmaster;
+ if (debug & DEBUG_HFCMULTI_PLXSD)
+ printk(KERN_DEBUG
+ "id=%d (0x%p) = PCM master syncronized "
+ "with QUARTZ\n", hc->id, hc);
+ if (hc->type == 1) {
+ /* Use the crystal clock for the PCM
+ master card */
+ if (debug & DEBUG_HFCMULTI_PLXSD)
+ printk(KERN_DEBUG
+ "Schedule QUARTZ for HFC-E1\n");
+ hc->e1_resync |= 4; /* switch quartz */
+ } else {
+ if (debug & DEBUG_HFCMULTI_PLXSD)
+ printk(KERN_DEBUG
+ "QUARTZ is automatically "
+ "enabled by HFC-%dS\n", hc->type);
+ }
+ plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+ pv = readl(plx_acc_32);
+ pv |= PLX_SYNC_O_EN;
+ writel(pv, plx_acc_32);
+ } else
+ if (!rm)
+ printk(KERN_ERR "%s no pcm master, this MUST "
+ "not happen!\n", __func__);
+ }
+ syncmaster = newmaster;
+
+ spin_unlock(&plx_lock);
+ spin_unlock_irqrestore(&HFClock, flags);
+}
+
+/* This must be called AND hc must be locked irqsave!!! */
+inline void
+plxsd_checksync(struct hfc_multi *hc, int rm)
+{
+ if (hc->syncronized) {
+ if (syncmaster == NULL) {
+ if (debug & DEBUG_HFCMULTI_PLXSD)
+ printk(KERN_WARNING "%s: GOT sync on card %d"
+ " (id=%d)\n", __func__, hc->id + 1,
+ hc->id);
+ hfcmulti_resync(hc, hc, rm);
+ }
+ } else {
+ if (syncmaster == hc) {
+ if (debug & DEBUG_HFCMULTI_PLXSD)
+ printk(KERN_WARNING "%s: LOST sync on card %d"
+ " (id=%d)\n", __func__, hc->id + 1,
+ hc->id);
+ hfcmulti_resync(hc, NULL, rm);
+ }
+ }
+}
+
+
+/*
+ * free hardware resources used by driver
+ */
+static void
+release_io_hfcmulti(struct hfc_multi *hc)
+{
+ u_int *plx_acc_32, pv;
+ u_long plx_flags;
+
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG "%s: entered\n", __func__);
+
+ /* soft reset also masks all interrupts */
+ hc->hw.r_cirm |= V_SRES;
+ HFC_outb(hc, R_CIRM, hc->hw.r_cirm);
+ udelay(1000);
+ hc->hw.r_cirm &= ~V_SRES;
+ HFC_outb(hc, R_CIRM, hc->hw.r_cirm);
+ udelay(1000); /* instead of 'wait' that may cause locking */
+
+ /* release Speech Design card, if PLX was initialized */
+ if (test_bit(HFC_CHIP_PLXSD, &hc->chip) && hc->plx_membase) {
+ if (debug & DEBUG_HFCMULTI_PLXSD)
+ printk(KERN_DEBUG "%s: release PLXSD card %d\n",
+ __func__, hc->id + 1);
+ spin_lock_irqsave(&plx_lock, plx_flags);
+ plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+ writel(PLX_GPIOC_INIT, plx_acc_32);
+ pv = readl(plx_acc_32);
+ /* Termination off */
+ pv &= ~PLX_TERM_ON;
+ /* Disconnect the PCM */
+ pv |= PLX_SLAVE_EN_N;
+ pv &= ~PLX_MASTER_EN;
+ pv &= ~PLX_SYNC_O_EN;
+ /* Put the DSP in Reset */
+ pv &= ~PLX_DSP_RES_N;
+ writel(pv, plx_acc_32);
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_WARNING "%s: PCM off: PLX_GPIO=%x\n",
+ __func__, pv);
+ spin_unlock_irqrestore(&plx_lock, plx_flags);
+ }
+
+ /* disable memory mapped ports / io ports */
+ test_and_clear_bit(HFC_CHIP_PLXSD, &hc->chip); /* prevent resync */
+ pci_write_config_word(hc->pci_dev, PCI_COMMAND, 0);
+ if (hc->pci_membase)
+ iounmap((void *)hc->pci_membase);
+ if (hc->plx_membase)
+ iounmap((void *)hc->plx_membase);
+ if (hc->pci_iobase)
+ release_region(hc->pci_iobase, 8);
+
+ if (hc->pci_dev) {
+ pci_disable_device(hc->pci_dev);
+ pci_set_drvdata(hc->pci_dev, NULL);
+ }
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG "%s: done\n", __func__);
+}
+
+/*
+ * 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(struct hfc_multi *hc)
+{
+ u_long flags, val, val2 = 0, rev;
+ int i, err = 0;
+ u_char r_conf_en, rval;
+ u_int *plx_acc_32, pv;
+ u_long plx_flags, hfc_flags;
+ int plx_count;
+ struct hfc_multi *pos, *next, *plx_last_hc;
+
+ spin_lock_irqsave(&hc->lock, flags);
+ /* reset all registers */
+ memset(&hc->hw, 0, sizeof(struct hfcm_hw));
+
+ /* revision check */
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG "%s: entered\n", __func__);
+ val = HFC_inb(hc, R_CHIP_ID)>>4;
+ if (val != 0x8 && val != 0xc && val != 0xe) {
+ printk(KERN_INFO "HFC_multi: unknown CHIP_ID:%x\n", (u_int)val);
+ err = -EIO;
+ goto out;
+ }
+ rev = HFC_inb(hc, R_CHIP_RV);
+ printk(KERN_INFO
+ "HFC_multi: detected HFC with chip ID=0x%lx revision=%ld%s\n",
+ val, 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",
+ __func__);
+ 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",
+ __func__);
+ 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;
+ }
+ hc->max_trans = poll << 1;
+ if (hc->max_trans > hc->Zlen)
+ hc->max_trans = hc->Zlen;
+
+ /* Speech Design PLX bridge */
+ if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+ if (debug & DEBUG_HFCMULTI_PLXSD)
+ printk(KERN_DEBUG "%s: initializing PLXSD card %d\n",
+ __func__, hc->id + 1);
+ spin_lock_irqsave(&plx_lock, plx_flags);
+ plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+ writel(PLX_GPIOC_INIT, plx_acc_32);
+ pv = readl(plx_acc_32);
+ /* The first and the last cards are terminating the PCM bus */
+ pv |= PLX_TERM_ON; /* hc is currently the last */
+ /* Disconnect the PCM */
+ pv |= PLX_SLAVE_EN_N;
+ pv &= ~PLX_MASTER_EN;
+ pv &= ~PLX_SYNC_O_EN;
+ /* Put the DSP in Reset */
+ pv &= ~PLX_DSP_RES_N;
+ writel(pv, plx_acc_32);
+ spin_unlock_irqrestore(&plx_lock, plx_flags);
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_WARNING "%s: slave/term: PLX_GPIO=%x\n",
+ __func__, pv);
+ /*
+ * If we are the 3rd PLXSD card or higher, we must turn
+ * termination of last PLXSD card off.
+ */
+ spin_lock_irqsave(&HFClock, hfc_flags);
+ plx_count = 0;
+ plx_last_hc = NULL;
+ list_for_each_entry_safe(pos, next, &HFClist, list) {
+ if (test_bit(HFC_CHIP_PLXSD, &pos->chip)) {
+ plx_count++;
+ if (pos != hc)
+ plx_last_hc = pos;
+ }
+ }
+ if (plx_count >= 3) {
+ if (debug & DEBUG_HFCMULTI_PLXSD)
+ printk(KERN_DEBUG "%s: card %d is between, so "
+ "we disable termination\n",
+ __func__, plx_last_hc->id + 1);
+ spin_lock_irqsave(&plx_lock, plx_flags);
+ plx_acc_32 = (u_int *)(plx_last_hc->plx_membase
+ + PLX_GPIOC);
+ pv = readl(plx_acc_32);
+ pv &= ~PLX_TERM_ON;
+ writel(pv, plx_acc_32);
+ spin_unlock_irqrestore(&plx_lock, plx_flags);
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_WARNING "%s: term off: PLX_GPIO=%x\n",
+ __func__, pv);
+ }
+ spin_unlock_irqrestore(&HFClock, hfc_flags);
+ hc->hw.r_pcm_md0 = V_F0_LEN; /* shift clock for DSP */
+ }
+
+ /* 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;
+
+ /* select pcm mode */
+ if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) {
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG "%s: setting PCM into slave mode\n",
+ __func__);
+ } else
+ if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip) && !plxsd_master) {
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG "%s: setting PCM into master mode\n",
+ __func__);
+ hc->hw.r_pcm_md0 |= V_PCM_MD;
+ } else {
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG "%s: performing PCM auto detect\n",
+ __func__);
+ }
+
+ /* 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(100);
+ hc->hw.r_cirm = 0;
+ HFC_outb(hc, R_CIRM, hc->hw.r_cirm);
+ udelay(100);
+ HFC_outb(hc, R_RAM_SZ, hc->hw.r_ram_sz);
+
+ /* Speech Design PLX bridge pcm and sync mode */
+ if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+ spin_lock_irqsave(&plx_lock, plx_flags);
+ plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+ pv = readl(plx_acc_32);
+ /* Connect PCM */
+ if (hc->hw.r_pcm_md0 & V_PCM_MD) {
+ pv |= PLX_MASTER_EN | PLX_SLAVE_EN_N;
+ pv |= PLX_SYNC_O_EN;
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_WARNING "%s: master: PLX_GPIO=%x\n",
+ __func__, pv);
+ } else {
+ pv &= ~(PLX_MASTER_EN | PLX_SLAVE_EN_N);
+ pv &= ~PLX_SYNC_O_EN;
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_WARNING "%s: slave: PLX_GPIO=%x\n",
+ __func__, pv);
+ }
+ writel(pv, plx_acc_32);
+ spin_unlock_irqrestore(&plx_lock, plx_flags);
+ }
+
+ /* PCM setup */
+ HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x90);
+ if (hc->slots == 32)
+ HFC_outb(hc, R_PCM_MD1, 0x00);
+ if (hc->slots == 64)
+ HFC_outb(hc, R_PCM_MD1, 0x10);
+ if (hc->slots == 128)
+ HFC_outb(hc, R_PCM_MD1, 0x20);
+ HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0xa0);
+ if (test_bit(HFC_CHIP_PLXSD, &hc->chip))
+ HFC_outb(hc, R_PCM_MD2, V_SYNC_SRC); /* sync via SYNC_I / O */
+ else
+ HFC_outb(hc, R_PCM_MD2, 0x00); /* sync from interface */
+ HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x00);
+ for (i = 0; i < 256; i++) {
+ HFC_outb_nodebug(hc, R_SLOT, i);
+ HFC_outb_nodebug(hc, A_SL_CFG, 0);
+ HFC_outb_nodebug(hc, A_CONF, 0);
+ hc->slot_owner[i] = -1;
+ }
+
+ /* set clock speed */
+ if (test_bit(HFC_CHIP_CLOCK2, &hc->chip)) {
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG
+ "%s: setting double clock\n", __func__);
+ HFC_outb(hc, R_BRG_PCM_CFG, V_PCM_CLK);
+ }
+
+ /* B410P GPIO */
+ if (test_bit(HFC_CHIP_B410P, &hc->chip)) {
+ printk(KERN_NOTICE "Setting GPIOs\n");
+ HFC_outb(hc, R_GPIO_SEL, 0x30);
+ HFC_outb(hc, R_GPIO_EN1, 0x3);
+ udelay(1000);
+ printk(KERN_NOTICE "calling vpm_init\n");
+ vpm_init(hc);
+ }
+
+ /* check if R_F0_CNT counts (8 kHz frame count) */
+ 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 reset\n", val);
+ spin_unlock_irqrestore(&hc->lock, flags);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout((HZ/100)?:1); /* Timeout minimum 10ms */
+ spin_lock_irqsave(&hc->lock, flags);
+ val2 = HFC_inb(hc, R_F0_CNTL);
+ val2 += HFC_inb(hc, R_F0_CNTH) << 8;
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG
+ "HFC_multi F0_CNT %ld after 10 ms (1st try)\n",
+ val2);
+ if (val2 >= val+8) { /* 1 ms */
+ /* it counts, so we keep the pcm mode */
+ if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip))
+ printk(KERN_INFO "controller is PCM bus MASTER\n");
+ else
+ if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip))
+ printk(KERN_INFO "controller is PCM bus SLAVE\n");
+ else {
+ test_and_set_bit(HFC_CHIP_PCM_SLAVE, &hc->chip);
+ printk(KERN_INFO "controller is PCM bus SLAVE "
+ "(auto detected)\n");
+ }
+ } else {
+ /* does not count */
+ if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) {
+controller_fail:
+ printk(KERN_ERR "HFC_multi ERROR, getting no 125us "
+ "pulse. Seems that controller fails.\n");
+ err = -EIO;
+ goto out;
+ }
+ if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) {
+ printk(KERN_INFO "controller is PCM bus SLAVE "
+ "(ignoring missing PCM clock)\n");
+ } else {
+ /* only one pcm master */
+ if (test_bit(HFC_CHIP_PLXSD, &hc->chip)
+ && plxsd_master) {
+ printk(KERN_ERR "HFC_multi ERROR, no clock "
+ "on another Speech Design card found. "
+ "Please be sure to connect PCM cable.\n");
+ err = -EIO;
+ goto out;
+ }
+ /* retry with master clock */
+ if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+ spin_lock_irqsave(&plx_lock, plx_flags);
+ plx_acc_32 = (u_int *)(hc->plx_membase +
+ PLX_GPIOC);
+ pv = readl(plx_acc_32);
+ pv |= PLX_MASTER_EN | PLX_SLAVE_EN_N;
+ pv |= PLX_SYNC_O_EN;
+ writel(pv, plx_acc_32);
+ spin_unlock_irqrestore(&plx_lock, plx_flags);
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_WARNING "%s: master: PLX_GPIO"
+ "=%x\n", __func__, pv);
+ }
+ hc->hw.r_pcm_md0 |= V_PCM_MD;
+ HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x00);
+ spin_unlock_irqrestore(&hc->lock, flags);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout((HZ/100)?:1); /* Timeout min. 10ms */
+ spin_lock_irqsave(&hc->lock, flags);
+ val2 = HFC_inb(hc, R_F0_CNTL);
+ val2 += HFC_inb(hc, R_F0_CNTH) << 8;
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG "HFC_multi F0_CNT %ld after "
+ "10 ms (2nd try)\n", val2);
+ if (val2 >= val+8) { /* 1 ms */
+ test_and_set_bit(HFC_CHIP_PCM_MASTER,
+ &hc->chip);
+ printk(KERN_INFO "controller is PCM bus MASTER "
+ "(auto detected)\n");
+ } else
+ goto controller_fail;
+ }
+ }
+
+ /* Release the DSP Reset */
+ if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+ if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip))
+ plxsd_master = 1;
+ spin_lock_irqsave(&plx_lock, plx_flags);
+ plx_acc_32 = (u_int *)(hc->plx_membase+PLX_GPIOC);
+ pv = readl(plx_acc_32);
+ pv |= PLX_DSP_RES_N;
+ writel(pv, plx_acc_32);
+ spin_unlock_irqrestore(&plx_lock, plx_flags);
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_WARNING "%s: reset off: PLX_GPIO=%x\n",
+ __func__, pv);
+ }
+
+ /* pcm id */
+ if (hc->pcm)
+ printk(KERN_INFO "controller has given PCM BUS ID %d\n",
+ hc->pcm);
+ else {
+ if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)
+ || test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+ PCM_cnt++; /* SD has proprietary bridging */
+ }
+ hc->pcm = PCM_cnt;
+ printk(KERN_INFO "controller has PCM BUS ID %d "
+ "(auto selected)\n", hc->pcm);
+ }
+
+ /* set up timer */
+ HFC_outb(hc, R_TI_WD, poll_timer);
+ hc->hw.r_irqmsk_misc |= V_TI_IRQMSK;
+
+ /*
+ * set up 125us interrupt, only if function pointer is available
+ * and module parameter timer is set
+ */
+ if (timer && hfc_interrupt && register_interrupt) {
+ /* only one chip should use this interrupt */
+ timer = 0;
+ interrupt_registered = 1;
+ hc->hw.r_irqmsk_misc |= V_PROC_IRQMSK;
+ /* deactivate other interrupts in ztdummy */
+ register_interrupt();
+ }
+
+ /* 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", __func__);
+ 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 */
+ if (test_bit(HFC_CHIP_WATCHDOG, &hc->chip))
+ HFC_outb(hc, R_GPIO_SEL, 0x32);
+ else
+ HFC_outb(hc, R_GPIO_SEL, 0x30);
+
+ HFC_outb(hc, R_GPIO_EN1, 0x0f);
+ HFC_outb(hc, R_GPIO_OUT1, 0x00);
+
+ HFC_outb(hc, R_GPIO_EN0, V_GPIO_EN2 | V_GPIO_EN3);
+ break;
+
+ case 2: /* HFC-4S OEM */
+ case 3:
+ 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",
+ __func__, hc->masterclk, hc->ports-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 "r_irqmsk_misc.2: 0x%x\n",
+ hc->hw.r_irqmsk_misc);
+
+ /* RAM access test */
+ HFC_outb(hc, R_RAM_ADDR0, 0);
+ HFC_outb(hc, R_RAM_ADDR1, 0);
+ HFC_outb(hc, R_RAM_ADDR2, 0);
+ for (i = 0; i < 256; i++) {
+ HFC_outb_nodebug(hc, R_RAM_ADDR0, i);
+ HFC_outb_nodebug(hc, R_RAM_DATA, ((i*3)&0xff));
+ }
+ for (i = 0; i < 256; i++) {
+ HFC_outb_nodebug(hc, R_RAM_ADDR0, i);
+ HFC_inb_nodebug(hc, R_RAM_DATA);
+ rval = HFC_inb_nodebug(hc, R_INT_DATA);
+ if (rval != ((i * 3) & 0xff)) {
+ printk(KERN_DEBUG
+ "addr:%x val:%x should:%x\n", i, rval,
+ (i * 3) & 0xff);
+ err++;
+ }
+ }
+ if (err) {
+ printk(KERN_DEBUG "aborting - %d RAM access errors\n", err);
+ err = -EIO;
+ goto out;
+ }
+
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG "%s: done\n", __func__);
+out:
+ spin_unlock_irqrestore(&hc->lock, flags);
+ return err;
+}
+
+
+/*
+ * control the watchdog
+ */
+static void
+hfcmulti_watchdog(struct hfc_multi *hc)
+{
+ hc->wdcount++;
+
+ if (hc->wdcount > 10) {
+ hc->wdcount = 0;
+ hc->wdbyte = hc->wdbyte == V_GPIO_OUT2 ?
+ V_GPIO_OUT3 : V_GPIO_OUT2;
+
+ /* printk("Sending Watchdog Kill %x\n",hc->wdbyte); */
+ HFC_outb(hc, R_GPIO_EN0, V_GPIO_EN2 | V_GPIO_EN3);
+ HFC_outb(hc, R_GPIO_OUT0, hc->wdbyte);
+ }
+}
+
+
+
+/*
+ * output leds
+ */
+static void
+hfcmulti_leds(struct hfc_multi *hc)
+{
+ unsigned long lled;
+ unsigned long leddw;
+ int i, state, active, leds;
+ struct dchannel *dch;
+ int led[4];
+
+ hc->ledcount += poll;
+ if (hc->ledcount > 4096) {
+ hc->ledcount -= 4096;
+ hc->ledstate = 0xAFFEAFFE;
+ }
+
+ switch (hc->leds) {
+ case 1: /* HFC-E1 OEM */
+ /* 2 red blinking: NT mode deactivate
+ * 2 red steady: TE mode deactivate
+ * left green: L1 active
+ * left red: frame sync, but no L1
+ * right green: L2 active
+ */
+ if (hc->chan[hc->dslot].sync != 2) { /* no frame sync */
+ if (hc->chan[hc->dslot].dch->dev.D.protocol
+ != ISDN_P_NT_E1) {
+ led[0] = 1;
+ led[1] = 1;
+ } else if (hc->ledcount>>11) {
+ led[0] = 1;
+ led[1] = 1;
+ } else {
+ led[0] = 0;
+ led[1] = 0;
+ }
+ led[2] = 0;
+ led[3] = 0;
+ } else { /* with frame sync */
+ /* TODO make it work */
+ led[0] = 0;
+ led[1] = 0;
+ led[2] = 0;
+ led[3] = 1;
+ }
+ leds = (led[0] | (led[1]<<2) | (led[2]<<1) | (led[3]<<3))^0xF;
+ /* leds are inverted */
+ if (leds != (int)hc->ledstate) {
+ HFC_outb_nodebug(hc, R_GPIO_OUT1, leds);
+ hc->ledstate = leds;
+ }
+ break;
+
+ case 2: /* HFC-4S OEM */
+ /* red blinking = PH_DEACTIVATE NT Mode
+ * red steady = PH_DEACTIVATE TE Mode
+ * green steady = PH_ACTIVATE
+ */
+ for (i = 0; i < 4; i++) {
+ state = 0;
+ active = -1;
+ dch = hc->chan[(i << 2) | 2].dch;
+ if (dch) {
+ state = dch->state;
+ if (dch->dev.D.protocol == ISDN_P_NT_S0)
+ active = 3;
+ else
+ active = 7;
+ }
+ if (state) {
+ if (state == active) {
+ led[i] = 1; /* led green */
+ } else
+ if (dch->dev.D.protocol == ISDN_P_TE_S0)
+ /* TE mode: led red */
+ led[i] = 2;
+ else
+ if (hc->ledcount>>11)
+ /* led red */
+ led[i] = 2;
+ else
+ /* led off */
+ led[i] = 0;
+ } else
+ led[i] = 0; /* led off */
+ }
+ if (test_bit(HFC_CHIP_B410P, &hc->chip)) {
+ leds = 0;
+ for (i = 0; i < 4; i++) {
+ if (led[i] == 1) {
+ /*green*/
+ leds |= (0x2 << (i * 2));
+ } else if (led[i] == 2) {
+ /*red*/
+ leds |= (0x1 << (i * 2));
+ }
+ }
+ if (leds != (int)hc->ledstate) {
+ vpm_out(hc, 0, 0x1a8 + 3, leds);
+ hc->ledstate = leds;
+ }
+ } else {
+ leds = ((led[3] > 0) << 0) | ((led[1] > 0) << 1) |
+ ((led[0] > 0) << 2) | ((led[2] > 0) << 3) |
+ ((led[3] & 1) << 4) | ((led[1] & 1) << 5) |
+ ((led[0] & 1) << 6) | ((led[2] & 1) << 7);
+ if (leds != (int)hc->ledstate) {
+ HFC_outb_nodebug(hc, R_GPIO_EN1, leds & 0x0F);
+ HFC_outb_nodebug(hc, R_GPIO_OUT1, leds >> 4);
+ hc->ledstate = leds;
+ }
+ }
+ break;
+
+ case 3: /* HFC 1S/2S Beronet */
+ /* red blinking = PH_DEACTIVATE NT Mode
+ * red steady = PH_DEACTIVATE TE Mode
+ * green steady = PH_ACTIVATE
+ */
+ for (i = 0; i < 2; i++) {
+ state = 0;
+ active = -1;
+ dch = hc->chan[(i << 2) | 2].dch;
+ if (dch) {
+ state = dch->state;
+ if (dch->dev.D.protocol == ISDN_P_NT_S0)
+ active = 3;
+ else
+ active = 7;
+ }
+ if (state) {
+ if (state == active) {
+ led[i] = 1; /* led green */
+ } else
+ if (dch->dev.D.protocol == ISDN_P_TE_S0)
+ /* TE mode: led red */
+ led[i] = 2;
+ else
+ if (hc->ledcount >> 11)
+ /* led red */
+ led[i] = 2;
+ else
+ /* led off */
+ led[i] = 0;
+ } else
+ led[i] = 0; /* led off */
+ }
+
+
+ leds = (led[0] > 0) | ((led[1] > 0)<<1) | ((led[0]&1)<<2)
+ | ((led[1]&1)<<3);
+ if (leds != (int)hc->ledstate) {
+ HFC_outb_nodebug(hc, R_GPIO_EN1,
+ ((led[0] > 0) << 2) | ((led[1] > 0) << 3));
+ HFC_outb_nodebug(hc, R_GPIO_OUT1,
+ ((led[0] & 1) << 2) | ((led[1] & 1) << 3));
+ hc->ledstate = leds;
+ }
+ break;
+ case 8: /* HFC 8S+ Beronet */
+ lled = 0;
+
+ for (i = 0; i < 8; i++) {
+ state = 0;
+ active = -1;
+ dch = hc->chan[(i << 2) | 2].dch;
+ if (dch) {
+ state = dch->state;
+ if (dch->dev.D.protocol == ISDN_P_NT_S0)
+ active = 3;
+ else
+ active = 7;
+ }
+ if (state) {
+ if (state == active) {
+ lled |= 0 << i;
+ } else
+ if (hc->ledcount >> 11)
+ lled |= 0 << i;
+ else
+ lled |= 1 << i;
+ } else
+ lled |= 1 << i;
+ }
+ leddw = lled << 24 | lled << 16 | lled << 8 | lled;
+ if (leddw != hc->ledstate) {
+ /* HFC_outb(hc, R_BRG_PCM_CFG, 1);
+ HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x3); */
+ /* was _io before */
+ HFC_outb_nodebug(hc, R_BRG_PCM_CFG, 1 | V_PCM_CLK);
+ outw(0x4000, hc->pci_iobase + 4);
+ outl(leddw, hc->pci_iobase);
+ HFC_outb_nodebug(hc, R_BRG_PCM_CFG, V_PCM_CLK);
+ hc->ledstate = leddw;
+ }
+ break;
+ }
+}
+/*
+ * read dtmf coefficients
+ */
+
+static void
+hfcmulti_dtmf(struct hfc_multi *hc)
+{
+ s32 *coeff;
+ u_int mantissa;
+ int co, ch;
+ struct bchannel *bch = NULL;
+ u8 exponent;
+ int dtmf = 0;
+ int addr;
+ u16 w_float;
+ struct sk_buff *skb;
+ struct mISDNhead *hh;
+
+ if (debug & DEBUG_HFCMULTI_DTMF)
+ printk(KERN_DEBUG "%s: dtmf detection irq\n", __func__);
+ for (ch = 0; ch <= 31; ch++) {
+ /* only process enabled B-channels */
+ bch = hc->chan[ch].bch;
+ if (!bch)
+ continue;
+ if (!hc->created[hc->chan[ch].port])
+ continue;
+ if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
+ continue;
+ if (debug & DEBUG_HFCMULTI_DTMF)
+ printk(KERN_DEBUG "%s: dtmf channel %d:",
+ __func__, ch);
+ coeff = &(hc->chan[ch].coeff[hc->chan[ch].coeff_count * 16]);
+ dtmf = 1;
+ for (co = 0; co < 8; co++) {
+ /* read W(n-1) coefficient */
+ addr = hc->DTMFbase + ((co<<7) | (ch<<2));
+ HFC_outb_nodebug(hc, R_RAM_ADDR0, addr);
+ HFC_outb_nodebug(hc, R_RAM_ADDR1, addr>>8);
+ HFC_outb_nodebug(hc, R_RAM_ADDR2, (addr>>16)
+ | V_ADDR_INC);
+ w_float = HFC_inb_nodebug(hc, R_RAM_DATA);
+ w_float |= (HFC_inb_nodebug(hc, R_RAM_DATA) << 8);
+ 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_nodebug(hc, R_RAM_DATA);
+ w_float |= (HFC_inb_nodebug(hc, R_RAM_DATA) << 8);
+ 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;
+ }
+ if (debug & DEBUG_HFCMULTI_DTMF)
+ printk("%s: DTMF ready %08x %08x %08x %08x "
+ "%08x %08x %08x %08x\n", __func__,
+ coeff[0], coeff[1], coeff[2], coeff[3],
+ coeff[4], coeff[5], coeff[6], coeff[7]);
+ hc->chan[ch].coeff_count++;
+ if (hc->chan[ch].coeff_count == 8) {
+ hc->chan[ch].coeff_count = 0;
+ skb = mI_alloc_skb(512, GFP_ATOMIC);
+ if (!skb) {
+ printk(KERN_WARNING "%s: No memory for skb\n",
+ __func__);
+ continue;
+ }
+ hh = mISDN_HEAD_P(skb);
+ hh->prim = PH_CONTROL_IND;
+ hh->id = DTMF_HFC_COEF;
+ memcpy(skb_put(skb, 512), hc->chan[ch].coeff, 512);
+ recv_Bchannel_skb(bch, skb);
+ }
+ }
+
+ /* restart DTMF processing */
+ hc->dtmf = dtmf;
+ if (dtmf)
+ HFC_outb_nodebug(hc, R_DTMF, hc->hw.r_dtmf | V_RST_DTMF);
+}
+
+
+/*
+ * fill fifo as much as possible
+ */
+
+static void
+hfcmulti_tx(struct hfc_multi *hc, int ch)
+{
+ int i, ii, temp, len = 0;
+ int Zspace, z1, z2; /* must be int for calculation */
+ int Fspace, f1, f2;
+ u_char *d;
+ int *txpending, slot_tx;
+ struct bchannel *bch;
+ struct dchannel *dch;
+ struct sk_buff **sp = NULL;
+ int *idxp;
+
+ bch = hc->chan[ch].bch;
+ dch = hc->chan[ch].dch;
+ if ((!dch) && (!bch))
+ return;
+
+ txpending = &hc->chan[ch].txpending;
+ slot_tx = hc->chan[ch].slot_tx;
+ if (dch) {
+ if (!test_bit(FLG_ACTIVE, &dch->Flags))
+ return;
+ sp = &dch->tx_skb;
+ idxp = &dch->tx_idx;
+ } else {
+ if (!test_bit(FLG_ACTIVE, &bch->Flags))
+ return;
+ sp = &bch->tx_skb;
+ idxp = &bch->tx_idx;
+ }
+ if (*sp)
+ len = (*sp)->len;
+
+ if ((!len) && *txpending != 1)
+ return; /* no data */
+
+ if (test_bit(HFC_CHIP_B410P, &hc->chip) &&
+ (hc->chan[ch].protocol == ISDN_P_B_RAW) &&
+ (hc->chan[ch].slot_rx < 0) &&
+ (hc->chan[ch].slot_tx < 0))
+ HFC_outb_nodebug(hc, R_FIFO, 0x20 | (ch << 1));
+ else
+ HFC_outb_nodebug(hc, R_FIFO, ch << 1);
+ HFC_wait_nodebug(hc);
+
+ if (*txpending == 2) {
+ /* reset fifo */
+ HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F);
+ HFC_wait_nodebug(hc);
+ HFC_outb(hc, A_SUBCH_CFG, 0);
+ *txpending = 1;
+ }
+next_frame:
+ if (dch || test_bit(FLG_HDLC, &bch->Flags)) {
+ f1 = HFC_inb_nodebug(hc, A_F1);
+ f2 = HFC_inb_nodebug(hc, A_F2);
+ while (f2 != (temp = HFC_inb_nodebug(hc, A_F2))) {
+ if (debug & DEBUG_HFCMULTI_FIFO)
+ printk(KERN_DEBUG
+ "%s(card %d): reread f2 because %d!=%d\n",
+ __func__, hc->id + 1, 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_nodebug(hc, A_Z1) - hc->Zmin;
+ z2 = HFC_inw_nodebug(hc, A_Z2) - hc->Zmin;
+ while (z2 != (temp = (HFC_inw_nodebug(hc, A_Z2) - hc->Zmin))) {
+ if (debug & DEBUG_HFCMULTI_FIFO)
+ printk(KERN_DEBUG "%s(card %d): reread z2 because "
+ "%d!=%d\n", __func__, hc->id + 1, temp, z2);
+ z2 = temp; /* repeat unti Z2 is equal */
+ }
+ Zspace = z2 - z1;
+ if (Zspace <= 0)
+ Zspace += hc->Zlen;
+ Zspace -= 4; /* keep not too full, so pointers will not overrun */
+ /* fill transparent data only to maxinum transparent load (minus 4) */
+ if (bch && test_bit(FLG_TRANSPARENT, &bch->Flags))
+ Zspace = Zspace - hc->Zlen + hc->max_trans;
+ if (Zspace <= 0) /* no space of 4 bytes */
+ return;
+
+ /* if no data */
+ if (!len) {
+ if (z1 == z2) { /* empty */
+ /* if done with FIFO audio data during PCM connection */
+ if (bch && (!test_bit(FLG_HDLC, &bch->Flags)) &&
+ *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",
+ __func__, ch, slot_tx);
+ /* connect slot */
+ HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 |
+ V_HDLC_TRP | V_IFF);
+ HFC_outb_nodebug(hc, R_FIFO, ch<<1 | 1);
+ HFC_wait_nodebug(hc);
+ HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 |
+ V_HDLC_TRP | V_IFF);
+ HFC_outb_nodebug(hc, R_FIFO, ch<<1);
+ HFC_wait_nodebug(hc);
+ }
+ *txpending = 0;
+ }
+ return; /* no data */
+ }
+
+ /* if audio data and connected slot */
+ if (bch && (!test_bit(FLG_HDLC, &bch->Flags)) && (!*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",
+ __func__, ch, slot_tx);
+ /* disconnect slot */
+ HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | V_HDLC_TRP | V_IFF);
+ HFC_outb_nodebug(hc, R_FIFO, ch<<1 | 1);
+ HFC_wait_nodebug(hc);
+ HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | V_HDLC_TRP | V_IFF);
+ HFC_outb_nodebug(hc, R_FIFO, ch<<1);
+ HFC_wait_nodebug(hc);
+ }
+ *txpending = 1;
+
+ /* show activity */
+ hc->activity[hc->chan[ch].port] = 1;
+
+ /* fill fifo to what we have left */
+ ii = len;
+ if (dch || test_bit(FLG_HDLC, &bch->Flags))
+ temp = 1;
+ else
+ temp = 0;
+ i = *idxp;
+ d = (*sp)->data + i;
+ if (ii - i > Zspace)
+ ii = Zspace + i;
+ if (debug & DEBUG_HFCMULTI_FIFO)
+ printk(KERN_DEBUG "%s(card %d): fifo(%d) has %d bytes space "
+ "left (z1=%04x, z2=%04x) sending %d of %d bytes %s\n",
+ __func__, hc->id + 1, ch, Zspace, z1, z2, ii-i, len-i,
+ temp ? "HDLC":"TRANS");
+
+
+ /* Have to prep the audio data */
+ hc->write_fifo(hc, d, ii - i);
+ *idxp = 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, terminate frame */
+ if (dch || test_bit(FLG_HDLC, &bch->Flags)) {
+ /* increment f-counter */
+ HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_INC_F);
+ HFC_wait_nodebug(hc);
+ }
+
+ /* send confirm, since get_net_bframe will not do it with trans */
+ if (bch && test_bit(FLG_TRANSPARENT, &bch->Flags))
+ confirm_Bsend(bch);
+
+ /* check for next frame */
+ dev_kfree_skb(*sp);
+ if (bch && get_next_bframe(bch)) { /* hdlc is confirmed here */
+ len = (*sp)->len;
+ goto next_frame;
+ }
+ if (dch && get_next_dframe(dch)) {
+ len = (*sp)->len;
+ goto next_frame;
+ }
+
+ /*
+ * 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 (bch && test_bit(FLG_TRANSPARENT, &bch->Flags))
+ HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, silence);
+}
+
+
+/* NOTE: only called if E1 card is in active state */
+static void
+hfcmulti_rx(struct hfc_multi *hc, int ch)
+{
+ int temp;
+ int Zsize, z1, z2 = 0; /* = 0, to make GCC happy */
+ int f1 = 0, f2 = 0; /* = 0, to make GCC happy */
+ int again = 0;
+ struct bchannel *bch;
+ struct dchannel *dch;
+ struct sk_buff *skb, **sp = NULL;
+ int maxlen;
+
+ bch = hc->chan[ch].bch;
+ dch = hc->chan[ch].dch;
+ if ((!dch) && (!bch))
+ return;
+ if (dch) {
+ if (!test_bit(FLG_ACTIVE, &dch->Flags))
+ return;
+ sp = &dch->rx_skb;
+ maxlen = dch->maxlen;
+ } else {
+ if (!test_bit(FLG_ACTIVE, &bch->Flags))
+ return;
+ sp = &bch->rx_skb;
+ maxlen = bch->maxlen;
+ }
+next_frame:
+ /* on first AND before getting next valid frame, R_FIFO must be written
+ to. */
+ if (test_bit(HFC_CHIP_B410P, &hc->chip) &&
+ (hc->chan[ch].protocol == ISDN_P_B_RAW) &&
+ (hc->chan[ch].slot_rx < 0) &&
+ (hc->chan[ch].slot_tx < 0))
+ HFC_outb_nodebug(hc, R_FIFO, 0x20 | (ch<<1) | 1);
+ else
+ HFC_outb_nodebug(hc, R_FIFO, (ch<<1)|1);
+ HFC_wait_nodebug(hc);
+
+ /* ignore if rx is off BUT change fifo (above) to start pending TX */
+ if (hc->chan[ch].rx_off)
+ return;
+
+ if (dch || test_bit(FLG_HDLC, &bch->Flags)) {
+ f1 = HFC_inb_nodebug(hc, A_F1);
+ while (f1 != (temp = HFC_inb_nodebug(hc, A_F1))) {
+ if (debug & DEBUG_HFCMULTI_FIFO)
+ printk(KERN_DEBUG
+ "%s(card %d): reread f1 because %d!=%d\n",
+ __func__, hc->id + 1, temp, f1);
+ f1 = temp; /* repeat until F1 is equal */
+ }
+ f2 = HFC_inb_nodebug(hc, A_F2);
+ }
+ z1 = HFC_inw_nodebug(hc, A_Z1) - hc->Zmin;
+ while (z1 != (temp = (HFC_inw_nodebug(hc, A_Z1) - hc->Zmin))) {
+ if (debug & DEBUG_HFCMULTI_FIFO)
+ printk(KERN_DEBUG "%s(card %d): reread z2 because "
+ "%d!=%d\n", __func__, hc->id + 1, temp, z2);
+ z1 = temp; /* repeat until Z1 is equal */
+ }
+ z2 = HFC_inw_nodebug(hc, A_Z2) - hc->Zmin;
+ Zsize = z1 - z2;
+ if ((dch || test_bit(FLG_HDLC, &bch->Flags)) && f1 != f2)
+ /* complete hdlc frame */
+ Zsize++;
+ if (Zsize < 0)
+ Zsize += hc->Zlen;
+ /* if buffer is empty */
+ if (Zsize <= 0)
+ return;
+
+ if (*sp == NULL) {
+ *sp = mI_alloc_skb(maxlen + 3, GFP_ATOMIC);
+ if (*sp == NULL) {
+ printk(KERN_DEBUG "%s: No mem for rx_skb\n",
+ __func__);
+ return;
+ }
+ }
+ /* show activity */
+ hc->activity[hc->chan[ch].port] = 1;
+
+ /* empty fifo with what we have */
+ if (dch || test_bit(FLG_HDLC, &bch->Flags)) {
+ if (debug & DEBUG_HFCMULTI_FIFO)
+ printk(KERN_DEBUG "%s(card %d): fifo(%d) reading %d "
+ "bytes (z1=%04x, z2=%04x) HDLC %s (f1=%d, f2=%d) "
+ "got=%d (again %d)\n", __func__, hc->id + 1, ch,
+ Zsize, z1, z2, (f1 == f2) ? "fragment" : "COMPLETE",
+ f1, f2, Zsize + (*sp)->len, again);
+ /* HDLC */
+ if ((Zsize + (*sp)->len) > (maxlen + 3)) {
+ if (debug & DEBUG_HFCMULTI_FIFO)
+ printk(KERN_DEBUG
+ "%s(card %d): hdlc-frame too large.\n",
+ __func__, hc->id + 1);
+ skb_trim(*sp, 0);
+ HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F);
+ HFC_wait_nodebug(hc);
+ return;
+ }
+
+ hc->read_fifo(hc, skb_put(*sp, Zsize), Zsize);
+
+ if (f1 != f2) {
+ /* increment Z2,F2-counter */
+ HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_INC_F);
+ HFC_wait_nodebug(hc);
+ /* check size */
+ if ((*sp)->len < 4) {
+ if (debug & DEBUG_HFCMULTI_FIFO)
+ printk(KERN_DEBUG
+ "%s(card %d): Frame below minimum "
+ "size\n", __func__, hc->id + 1);
+ skb_trim(*sp, 0);
+ goto next_frame;
+ }
+ /* there is at least one complete frame, check crc */
+ if ((*sp)->data[(*sp)->len - 1]) {
+ if (debug & DEBUG_HFCMULTI_CRC)
+ printk(KERN_DEBUG
+ "%s: CRC-error\n", __func__);
+ skb_trim(*sp, 0);
+ goto next_frame;
+ }
+ skb_trim(*sp, (*sp)->len - 3);
+ if ((*sp)->len < MISDN_COPY_SIZE) {
+ skb = *sp;
+ *sp = mI_alloc_skb(skb->len, GFP_ATOMIC);
+ if (*sp) {
+ memcpy(skb_put(*sp, skb->len),
+ skb->data, skb->len);
+ skb_trim(skb, 0);
+ } else {
+ printk(KERN_DEBUG "%s: No mem\n",
+ __func__);
+ *sp = skb;
+ skb = NULL;
+ }
+ } else {
+ skb = NULL;
+ }
+ if (debug & DEBUG_HFCMULTI_FIFO) {
+ printk(KERN_DEBUG "%s(card %d):",
+ __func__, hc->id + 1);
+ temp = 0;
+ while (temp < (*sp)->len)
+ printk(" %02x", (*sp)->data[temp++]);
+ printk("\n");
+ }
+ if (dch)
+ recv_Dchannel(dch);
+ else
+ recv_Bchannel(bch);
+ *sp = skb;
+ again++;
+ goto next_frame;
+ }
+ /* there is an incomplete frame */
+ } else {
+ /* transparent */
+ if (Zsize > skb_tailroom(*sp))
+ Zsize = skb_tailroom(*sp);
+ hc->read_fifo(hc, skb_put(*sp, Zsize), Zsize);
+ if (((*sp)->len) < MISDN_COPY_SIZE) {
+ skb = *sp;
+ *sp = mI_alloc_skb(skb->len, GFP_ATOMIC);
+ if (*sp) {
+ memcpy(skb_put(*sp, skb->len),
+ skb->data, skb->len);
+ skb_trim(skb, 0);
+ } else {
+ printk(KERN_DEBUG "%s: No mem\n", __func__);
+ *sp = skb;
+ skb = NULL;
+ }
+ } else {
+ skb = NULL;
+ }
+ if (debug & DEBUG_HFCMULTI_FIFO)
+ printk(KERN_DEBUG
+ "%s(card %d): fifo(%d) reading %d bytes "
+ "(z1=%04x, z2=%04x) TRANS\n",
+ __func__, hc->id + 1, ch, Zsize, z1, z2);
+ /* only bch is transparent */
+ recv_Bchannel(bch);
+ *sp = skb;
+ }
+}
+
+
+/*
+ * Interrupt handler
+ */
+static void
+signal_state_up(struct dchannel *dch, int info, char *msg)
+{
+ struct sk_buff *skb;
+ int id, data = info;
+
+ if (debug & DEBUG_HFCMULTI_STATE)
+ printk(KERN_DEBUG "%s: %s\n", __func__, msg);
+
+ id = TEI_SAPI | (GROUP_TEI << 8); /* manager address */
+
+ skb = _alloc_mISDN_skb(MPH_INFORMATION_IND, id, sizeof(data), &data,
+ GFP_ATOMIC);
+ if (!skb)
+ return;
+ recv_Dchannel_skb(dch, skb);
+}
+
+static inline void
+handle_timer_irq(struct hfc_multi *hc)
+{
+ int ch, temp;
+ struct dchannel *dch;
+ u_long flags;
+
+ /* process queued resync jobs */
+ if (hc->e1_resync) {
+ /* lock, so e1_resync gets not changed */
+ spin_lock_irqsave(&HFClock, flags);
+ if (hc->e1_resync & 1) {
+ if (debug & DEBUG_HFCMULTI_PLXSD)
+ printk(KERN_DEBUG "Enable SYNC_I\n");
+ HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC);
+ /* disable JATT, if RX_SYNC is set */
+ if (test_bit(HFC_CHIP_RX_SYNC, &hc->chip))
+ HFC_outb(hc, R_SYNC_OUT, V_SYNC_E1_RX);
+ }
+ if (hc->e1_resync & 2) {
+ if (debug & DEBUG_HFCMULTI_PLXSD)
+ printk(KERN_DEBUG "Enable jatt PLL\n");
+ HFC_outb(hc, R_SYNC_CTRL, V_SYNC_OFFS);
+ }
+ if (hc->e1_resync & 4) {
+ if (debug & DEBUG_HFCMULTI_PLXSD)
+ printk(KERN_DEBUG
+ "Enable QUARTZ for HFC-E1\n");
+ /* set jatt to quartz */
+ HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC
+ | V_JATT_OFF);
+ /* switch to JATT, in case it is not already */
+ HFC_outb(hc, R_SYNC_OUT, 0);
+ }
+ hc->e1_resync = 0;
+ spin_unlock_irqrestore(&HFClock, flags);
+ }
+
+ if (hc->type != 1 || hc->e1_state == 1)
+ for (ch = 0; ch <= 31; ch++) {
+ if (hc->created[hc->chan[ch].port]) {
+ hfcmulti_tx(hc, ch);
+ /* fifo is started when switching to rx-fifo */
+ hfcmulti_rx(hc, ch);
+ if (hc->chan[ch].dch &&
+ hc->chan[ch].nt_timer > -1) {
+ dch = hc->chan[ch].dch;
+ if (!(--hc->chan[ch].nt_timer)) {
+ schedule_event(dch,
+ FLG_PHCHANGE);
+ if (debug &
+ DEBUG_HFCMULTI_STATE)
+ printk(KERN_DEBUG
+ "%s: nt_timer at "
+ "state %x\n",
+ __func__,
+ dch->state);
+ }
+ }
+ }
+ }
+ if (hc->type == 1 && hc->created[0]) {
+ dch = hc->chan[hc->dslot].dch;
+ if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[hc->dslot].cfg)) {
+ /* LOS */
+ temp = HFC_inb_nodebug(hc, R_SYNC_STA) & V_SIG_LOS;
+ if (!temp && hc->chan[hc->dslot].los)
+ signal_state_up(dch, L1_SIGNAL_LOS_ON,
+ "LOS detected");
+ if (temp && !hc->chan[hc->dslot].los)
+ signal_state_up(dch, L1_SIGNAL_LOS_OFF,
+ "LOS gone");
+ hc->chan[hc->dslot].los = temp;
+ }
+ if (test_bit(HFC_CFG_REPORT_AIS, &hc->chan[hc->dslot].cfg)) {
+ /* AIS */
+ temp = HFC_inb_nodebug(hc, R_SYNC_STA) & V_AIS;
+ if (!temp && hc->chan[hc->dslot].ais)
+ signal_state_up(dch, L1_SIGNAL_AIS_ON,
+ "AIS detected");
+ if (temp && !hc->chan[hc->dslot].ais)
+ signal_state_up(dch, L1_SIGNAL_AIS_OFF,
+ "AIS gone");
+ hc->chan[hc->dslot].ais = temp;
+ }
+ if (test_bit(HFC_CFG_REPORT_SLIP, &hc->chan[hc->dslot].cfg)) {
+ /* SLIP */
+ temp = HFC_inb_nodebug(hc, R_SLIP) & V_FOSLIP_RX;
+ if (!temp && hc->chan[hc->dslot].slip_rx)
+ signal_state_up(dch, L1_SIGNAL_SLIP_RX,
+ " bit SLIP detected RX");
+ hc->chan[hc->dslot].slip_rx = temp;
+ temp = HFC_inb_nodebug(hc, R_SLIP) & V_FOSLIP_TX;
+ if (!temp && hc->chan[hc->dslot].slip_tx)
+ signal_state_up(dch, L1_SIGNAL_SLIP_TX,
+ " bit SLIP detected TX");
+ hc->chan[hc->dslot].slip_tx = temp;
+ }
+ if (test_bit(HFC_CFG_REPORT_RDI, &hc->chan[hc->dslot].cfg)) {
+ /* RDI */
+ temp = HFC_inb_nodebug(hc, R_RX_SL0_0) & V_A;
+ if (!temp && hc->chan[hc->dslot].rdi)
+ signal_state_up(dch, L1_SIGNAL_RDI_ON,
+ "RDI detected");
+ if (temp && !hc->chan[hc->dslot].rdi)
+ signal_state_up(dch, L1_SIGNAL_RDI_OFF,
+ "RDI gone");
+ hc->chan[hc->dslot].rdi = temp;
+ }
+ temp = HFC_inb_nodebug(hc, R_JATT_DIR);
+ switch (hc->chan[hc->dslot].sync) {
+ case 0:
+ if ((temp & 0x60) == 0x60) {
+ if (debug & DEBUG_HFCMULTI_SYNC)
+ printk(KERN_DEBUG
+ "%s: (id=%d) E1 now "
+ "in clock sync\n",
+ __func__, hc->id);
+ HFC_outb(hc, R_RX_OFF,
+ hc->chan[hc->dslot].jitter | V_RX_INIT);
+ HFC_outb(hc, R_TX_OFF,
+ hc->chan[hc->dslot].jitter | V_RX_INIT);
+ hc->chan[hc->dslot].sync = 1;
+ goto check_framesync;
+ }
+ break;
+ case 1:
+ if ((temp & 0x60) != 0x60) {
+ if (debug & DEBUG_HFCMULTI_SYNC)
+ printk(KERN_DEBUG
+ "%s: (id=%d) E1 "
+ "lost clock sync\n",
+ __func__, hc->id);
+ hc->chan[hc->dslot].sync = 0;
+ break;
+ }
+check_framesync:
+ temp = HFC_inb_nodebug(hc, R_SYNC_STA);
+ if (temp == 0x27) {
+ if (debug & DEBUG_HFCMULTI_SYNC)
+ printk(KERN_DEBUG
+ "%s: (id=%d) E1 "
+ "now in frame sync\n",
+ __func__, hc->id);
+ hc->chan[hc->dslot].sync = 2;
+ }
+ break;
+ case 2:
+ if ((temp & 0x60) != 0x60) {
+ if (debug & DEBUG_HFCMULTI_SYNC)
+ printk(KERN_DEBUG
+ "%s: (id=%d) E1 lost "
+ "clock & frame sync\n",
+ __func__, hc->id);
+ hc->chan[hc->dslot].sync = 0;
+ break;
+ }
+ temp = HFC_inb_nodebug(hc, R_SYNC_STA);
+ if (temp != 0x27) {
+ if (debug & DEBUG_HFCMULTI_SYNC)
+ printk(KERN_DEBUG
+ "%s: (id=%d) E1 "
+ "lost frame sync\n",
+ __func__, hc->id);
+ hc->chan[hc->dslot].sync = 1;
+ }
+ break;
+ }
+ }
+
+ if (test_bit(HFC_CHIP_WATCHDOG, &hc->chip))
+ hfcmulti_watchdog(hc);
+
+ if (hc->leds)
+ hfcmulti_leds(hc);
+}
+
+static void
+ph_state_irq(struct hfc_multi *hc, u_char r_irq_statech)
+{
+ struct dchannel *dch;
+ int ch;
+ int active;
+ u_char st_status, temp;
+
+ /* state machine */
+ for (ch = 0; ch <= 31; ch++) {
+ if (hc->chan[ch].dch) {
+ dch = hc->chan[ch].dch;
+ if (r_irq_statech & 1) {
+ HFC_outb_nodebug(hc, R_ST_SEL,
+ hc->chan[ch].port);
+ /* undocumented: delay after R_ST_SEL */
+ udelay(1);
+ /* undocumented: status changes during read */
+ st_status = HFC_inb_nodebug(hc, A_ST_RD_STATE);
+ while (st_status != (temp =
+ HFC_inb_nodebug(hc, A_ST_RD_STATE))) {
+ if (debug & DEBUG_HFCMULTI_STATE)
+ printk(KERN_DEBUG "%s: reread "
+ "STATE because %d!=%d\n",
+ __func__, temp,
+ st_status);
+ st_status = temp; /* repeat */
+ }
+
+ /* Speech Design TE-sync indication */
+ if (test_bit(HFC_CHIP_PLXSD, &hc->chip) &&
+ dch->dev.D.protocol == ISDN_P_TE_S0) {
+ if (st_status & V_FR_SYNC_ST)
+ hc->syncronized |=
+ (1 << hc->chan[ch].port);
+ else
+ hc->syncronized &=
+ ~(1 << hc->chan[ch].port);
+ }
+ dch->state = st_status & 0x0f;
+ if (dch->dev.D.protocol == ISDN_P_NT_S0)
+ active = 3;
+ else
+ active = 7;
+ if (dch->state == active) {
+ HFC_outb_nodebug(hc, R_FIFO,
+ (ch << 1) | 1);
+ HFC_wait_nodebug(hc);
+ HFC_outb_nodebug(hc,
+ R_INC_RES_FIFO, V_RES_F);
+ HFC_wait_nodebug(hc);
+ dch->tx_idx = 0;
+ }
+ schedule_event(dch, FLG_PHCHANGE);
+ if (debug & DEBUG_HFCMULTI_STATE)
+ printk(KERN_DEBUG
+ "%s: S/T newstate %x port %d\n",
+ __func__, dch->state,
+ hc->chan[ch].port);
+ }
+ r_irq_statech >>= 1;
+ }
+ }
+ if (test_bit(HFC_CHIP_PLXSD, &hc->chip))
+ plxsd_checksync(hc, 0);
+}
+
+static void
+fifo_irq(struct hfc_multi *hc, int block)
+{
+ int ch, j;
+ struct dchannel *dch;
+ struct bchannel *bch;
+ u_char r_irq_fifo_bl;
+
+ r_irq_fifo_bl = HFC_inb_nodebug(hc, R_IRQ_FIFO_BL0 + block);
+ j = 0;
+ while (j < 8) {
+ ch = (block << 2) + (j >> 1);
+ dch = hc->chan[ch].dch;
+ bch = hc->chan[ch].bch;
+ if (((!dch) && (!bch)) || (!hc->created[hc->chan[ch].port])) {
+ j += 2;
+ continue;
+ }
+ if (dch && (r_irq_fifo_bl & (1 << j)) &&
+ test_bit(FLG_ACTIVE, &dch->Flags)) {
+ hfcmulti_tx(hc, ch);
+ /* start fifo */
+ HFC_outb_nodebug(hc, R_FIFO, 0);
+ HFC_wait_nodebug(hc);
+ }
+ if (bch && (r_irq_fifo_bl & (1 << j)) &&
+ test_bit(FLG_ACTIVE, &bch->Flags)) {
+ hfcmulti_tx(hc, ch);
+ /* start fifo */
+ HFC_outb_nodebug(hc, R_FIFO, 0);
+ HFC_wait_nodebug(hc);
+ }
+ j++;
+ if (dch && (r_irq_fifo_bl & (1 << j)) &&
+ test_bit(FLG_ACTIVE, &dch->Flags)) {
+ hfcmulti_rx(hc, ch);
+ }
+ if (bch && (r_irq_fifo_bl & (1 << j)) &&
+ test_bit(FLG_ACTIVE, &bch->Flags)) {
+ hfcmulti_rx(hc, ch);
+ }
+ j++;
+ }
+}
+
+#ifdef IRQ_DEBUG
+int irqsem;
+#endif
+static irqreturn_t
+hfcmulti_interrupt(int intno, void *dev_id)
+{
+#ifdef IRQCOUNT_DEBUG
+ static int iq1 = 0, iq2 = 0, iq3 = 0, iq4 = 0,
+ iq5 = 0, iq6 = 0, iqcnt = 0;
+#endif
+ static int count;
+ struct hfc_multi *hc = dev_id;
+ struct dchannel *dch;
+ u_char r_irq_statech, status, r_irq_misc, r_irq_oview;
+ int i;
+ u_short *plx_acc, wval;
+ u_char e1_syncsta, temp;
+ u_long flags;
+
+ if (!hc) {
+ printk(KERN_ERR "HFC-multi: Spurious interrupt!\n");
+ return IRQ_NONE;
+ }
+
+ spin_lock(&hc->lock);
+
+#ifdef IRQ_DEBUG
+ if (irqsem)
+ printk(KERN_ERR "irq for card %d during irq from "
+ "card %d, this is no bug.\n", hc->id + 1, irqsem);
+ irqsem = hc->id + 1;
+#endif
+
+ if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+ spin_lock_irqsave(&plx_lock, flags);
+ plx_acc = (u_short *)(hc->plx_membase + PLX_INTCSR);
+ wval = readw(plx_acc);
+ spin_unlock_irqrestore(&plx_lock, flags);
+ if (!(wval & PLX_INTCSR_LINTI1_STATUS))
+ goto irq_notforus;
+ }
+
+ status = HFC_inb_nodebug(hc, R_STATUS);
+ r_irq_statech = HFC_inb_nodebug(hc, R_IRQ_STATECH);
+#ifdef IRQCOUNT_DEBUG
+ if (r_irq_statech)
+ iq1++;
+ if (status & V_DTMF_STA)
+ iq2++;
+ if (status & V_LOST_STA)
+ iq3++;
+ if (status & V_EXT_IRQSTA)
+ iq4++;
+ if (status & V_MISC_IRQSTA)
+ iq5++;
+ if (status & V_FR_IRQSTA)
+ iq6++;
+ if (iqcnt++ > 5000) {
+ printk(KERN_ERR "iq1:%x iq2:%x iq3:%x iq4:%x iq5:%x iq6:%x\n",
+ iq1, iq2, iq3, iq4, iq5, iq6);
+ iqcnt = 0;
+ }
+#endif
+ 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 (r_irq_statech) {
+ if (hc->type != 1)
+ ph_state_irq(hc, r_irq_statech);
+ }
+ 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_nodebug(hc, R_IRQ_MISC);
+ if (r_irq_misc & V_STA_IRQ) {
+ if (hc->type == 1) {
+ /* state machine */
+ dch = hc->chan[hc->dslot].dch;
+ e1_syncsta = HFC_inb_nodebug(hc, R_SYNC_STA);
+ if (test_bit(HFC_CHIP_PLXSD, &hc->chip)
+ && hc->e1_getclock) {
+ if (e1_syncsta & V_FR_SYNC_E1)
+ hc->syncronized = 1;
+ else
+ hc->syncronized = 0;
+ }
+ /* undocumented: status changes during read */
+ dch->state = HFC_inb_nodebug(hc, R_E1_RD_STA);
+ while (dch->state != (temp =
+ HFC_inb_nodebug(hc, R_E1_RD_STA))) {
+ if (debug & DEBUG_HFCMULTI_STATE)
+ printk(KERN_DEBUG "%s: reread "
+ "STATE because %d!=%d\n",
+ __func__, temp,
+ dch->state);
+ dch->state = temp; /* repeat */
+ }
+ dch->state = HFC_inb_nodebug(hc, R_E1_RD_STA)
+ & 0x7;
+ schedule_event(dch, FLG_PHCHANGE);
+ if (debug & DEBUG_HFCMULTI_STATE)
+ printk(KERN_DEBUG
+ "%s: E1 (id=%d) newstate %x\n",
+ __func__, hc->id, dch->state);
+ if (test_bit(HFC_CHIP_PLXSD, &hc->chip))
+ plxsd_checksync(hc, 0);
+ }
+ }
+ if (r_irq_misc & V_TI_IRQ)
+ handle_timer_irq(hc);
+
+ if (r_irq_misc & V_DTMF_IRQ) {
+ /* -> DTMF IRQ */
+ hfcmulti_dtmf(hc);
+ }
+ /* TODO: REPLACE !!!! 125 us Interrupts are not acceptable */
+ if (r_irq_misc & V_IRQ_PROC) {
+ /* IRQ every 125us */
+ count++;
+ /* generate 1kHz signal */
+ if (count == 8) {
+ if (hfc_interrupt)
+ hfc_interrupt();
+ count = 0;
+ }
+ }
+
+ }
+ if (status & V_FR_IRQSTA) {
+ /* FIFO IRQ */
+ r_irq_oview = HFC_inb_nodebug(hc, R_IRQ_OVIEW);
+ for (i = 0; i < 8; i++) {
+ if (r_irq_oview & (1 << i))
+ fifo_irq(hc, i);
+ }
+ }
+
+#ifdef IRQ_DEBUG
+ irqsem = 0;
+#endif
+ spin_unlock(&hc->lock);
+ return IRQ_HANDLED;
+
+irq_notforus:
+#ifdef IRQ_DEBUG
+ irqsem = 0;
+#endif
+ spin_unlock(&hc->lock);
+ return IRQ_NONE;
+}
+
+
+/*
+ * timer callback for D-chan busy resolution. Currently no function
+ */
+
+static void
+hfcmulti_dbusy_timer(struct hfc_multi *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(struct hfc_multi *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, oslot_rx;
+ int conf;
+
+ if (ch < 0 || ch > 31)
+ return EINVAL;
+ oslot_tx = hc->chan[ch].slot_tx;
+ oslot_rx = hc->chan[ch].slot_rx;
+ conf = hc->chan[ch].conf;
+
+ if (debug & DEBUG_HFCMULTI_MODE)
+ printk(KERN_DEBUG
+ "%s: card %d channel %d protocol %x slot old=%d new=%d "
+ "bank new=%d (TX) slot old=%d new=%d bank new=%d (RX)\n",
+ __func__, hc->id, ch, protocol, oslot_tx, slot_tx,
+ bank_tx, oslot_rx, 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",
+ __func__, 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 tx slot "
+ "anymore, channel %d is.\n",
+ __func__, 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",
+ __func__, 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 rx slot "
+ "anymore, channel %d is.\n",
+ __func__,
+ 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 channel %d to slot %d bank"
+ " %d flow %02x routing %02x conf %d (TX)\n",
+ __func__, ch, 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 channel %d to slot %d bank"
+ " %d flow %02x routing %02x conf %d (RX)\n",
+ __func__, ch, 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_P_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->chan[ch].bch && 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);
+ /* undocumented: delay after R_ST_SEL */
+ udelay(1);
+ HFC_outb(hc, A_ST_CTRL0,
+ hc->hw.a_st_ctrl0[hc->chan[ch].port]);
+ }
+ if (hc->chan[ch].bch) {
+ test_and_clear_bit(FLG_HDLC, &hc->chan[ch].bch->Flags);
+ test_and_clear_bit(FLG_TRANSPARENT,
+ &hc->chan[ch].bch->Flags);
+ }
+ break;
+ case (ISDN_P_B_RAW): /* B-channel */
+
+ if (test_bit(HFC_CHIP_B410P, &hc->chip) &&
+ (hc->chan[ch].slot_rx < 0) &&
+ (hc->chan[ch].slot_tx < 0)) {
+
+ printk(KERN_DEBUG
+ "Setting B-channel %d to echo cancelable "
+ "state on PCM slot %d\n", ch,
+ ((ch / 4) * 8) + ((ch % 4) * 4) + 1);
+ printk(KERN_DEBUG
+ "Enabling pass through for channel\n");
+ vpm_out(hc, ch, ((ch / 4) * 8) +
+ ((ch % 4) * 4) + 1, 0x01);
+ /* rx path */
+ /* S/T -> PCM */
+ HFC_outb(hc, R_FIFO, (ch << 1));
+ HFC_wait(hc);
+ HFC_outb(hc, A_CON_HDLC, 0xc0 | V_HDLC_TRP | V_IFF);
+ HFC_outb(hc, R_SLOT, (((ch / 4) * 8) +
+ ((ch % 4) * 4) + 1) << 1);
+ HFC_outb(hc, A_SL_CFG, 0x80 | (ch << 1));
+
+ /* PCM -> FIFO */
+ HFC_outb(hc, R_FIFO, 0x20 | (ch << 1) | 1);
+ HFC_wait(hc);
+ HFC_outb(hc, A_CON_HDLC, 0x20 | 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, R_SLOT, ((((ch / 4) * 8) +
+ ((ch % 4) * 4) + 1) << 1) | 1);
+ HFC_outb(hc, A_SL_CFG, 0x80 | 0x20 | (ch << 1) | 1);
+
+ /* tx path */
+ /* PCM -> S/T */
+ HFC_outb(hc, R_FIFO, (ch << 1) | 1);
+ HFC_wait(hc);
+ HFC_outb(hc, A_CON_HDLC, 0xc0 | V_HDLC_TRP | V_IFF);
+ HFC_outb(hc, R_SLOT, ((((ch / 4) * 8) +
+ ((ch % 4) * 4)) << 1) | 1);
+ HFC_outb(hc, A_SL_CFG, 0x80 | 0x40 | (ch << 1) | 1);
+
+ /* FIFO -> PCM */
+ HFC_outb(hc, R_FIFO, 0x20 | (ch << 1));
+ HFC_wait(hc);
+ HFC_outb(hc, A_CON_HDLC, 0x20 | 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);
+ /* tx silence */
+ HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, silence);
+ HFC_outb(hc, R_SLOT, (((ch / 4) * 8) +
+ ((ch % 4) * 4)) << 1);
+ HFC_outb(hc, A_SL_CFG, 0x80 | 0x20 | (ch << 1));
+ } else {
+ /* 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);
+ /* tx silence */
+ HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, 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);
+ /* undocumented: delay after R_ST_SEL */
+ udelay(1);
+ HFC_outb(hc, A_ST_CTRL0,
+ hc->hw.a_st_ctrl0[hc->chan[ch].port]);
+ }
+ if (hc->chan[ch].bch)
+ test_and_set_bit(FLG_TRANSPARENT,
+ &hc->chan[ch].bch->Flags);
+ break;
+ case (ISDN_P_B_HDLC): /* B-channel */
+ case (ISDN_P_TE_S0): /* D-channel */
+ case (ISDN_P_NT_S0):
+ case (ISDN_P_TE_E1):
+ case (ISDN_P_NT_E1):
+ /* enable TX fifo */
+ HFC_outb(hc, R_FIFO, ch<<1);
+ HFC_wait(hc);
+ if (hc->type == 1 || hc->chan[ch].bch) {
+ /* E1 or B-channel */
+ HFC_outb(hc, A_CON_HDLC, flow_tx | 0x04);
+ HFC_outb(hc, A_SUBCH_CFG, 0);
+ } else {
+ /* D-Channel without HDLC fill flags */
+ 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);
+ if (hc->type == 1 || hc->chan[ch].bch)
+ HFC_outb(hc, A_SUBCH_CFG, 0); /* full 8 bits */
+ else
+ HFC_outb(hc, A_SUBCH_CFG, 2); /* 2 bits dchannel */
+ HFC_outb(hc, A_IRQ_MSK, V_IRQ);
+ HFC_outb(hc, R_INC_RES_FIFO, V_RES_F);
+ HFC_wait(hc);
+ if (hc->chan[ch].bch) {
+ test_and_set_bit(FLG_HDLC, &hc->chan[ch].bch->Flags);
+ 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);
+ /* undocumented: delay after R_ST_SEL */
+ udelay(1);
+ HFC_outb(hc, A_ST_CTRL0,
+ hc->hw.a_st_ctrl0[hc->chan[ch].port]);
+ }
+ }
+ break;
+ default:
+ printk(KERN_DEBUG "%s: protocol not known %x\n",
+ __func__, protocol);
+ hc->chan[ch].protocol = ISDN_P_NONE;
+ return -ENOPROTOOPT;
+ }
+ hc->chan[ch].protocol = protocol;
+ return 0;
+}
+
+
+/*
+ * connect/disconnect PCM
+ */
+
+static void
+hfcmulti_pcm(struct hfc_multi *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(struct hfc_multi *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 */
+
+/*
+ * Layer 1 callback function
+ */
+static int
+hfcm_l1callback(struct dchannel *dch, u_int cmd)
+{
+ struct hfc_multi *hc = dch->hw;
+ u_long flags;
+
+ switch (cmd) {
+ case INFO3_P8:
+ case INFO3_P10:
+ break;
+ case HW_RESET_REQ:
+ /* start activation */
+ spin_lock_irqsave(&hc->lock, flags);
+ if (hc->type == 1) {
+ if (debug & DEBUG_HFCMULTI_MSG)
+ printk(KERN_DEBUG
+ "%s: HW_RESET_REQ no BRI\n",
+ __func__);
+ } else {
+ HFC_outb(hc, R_ST_SEL, hc->chan[dch->slot].port);
+ /* undocumented: delay after R_ST_SEL */
+ udelay(1);
+ HFC_outb(hc, A_ST_WR_STATE, V_ST_LD_STA | 3); /* F3 */
+ 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 */
+ }
+ spin_unlock_irqrestore(&hc->lock, flags);
+ l1_event(dch->l1, HW_POWERUP_IND);
+ break;
+ case HW_DEACT_REQ:
+ /* start deactivation */
+ spin_lock_irqsave(&hc->lock, flags);
+ if (hc->type == 1) {
+ if (debug & DEBUG_HFCMULTI_MSG)
+ printk(KERN_DEBUG
+ "%s: HW_DEACT_REQ no BRI\n",
+ __func__);
+ } else {
+ HFC_outb(hc, R_ST_SEL, hc->chan[dch->slot].port);
+ /* undocumented: delay after R_ST_SEL */
+ udelay(1);
+ HFC_outb(hc, A_ST_WR_STATE, V_ST_ACT*2);
+ /* deactivate */
+ if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+ hc->syncronized &=
+ ~(1 << hc->chan[dch->slot].port);
+ plxsd_checksync(hc, 0);
+ }
+ }
+ skb_queue_purge(&dch->squeue);
+ if (dch->tx_skb) {
+ dev_kfree_skb(dch->tx_skb);
+ dch->tx_skb = NULL;
+ }
+ dch->tx_idx = 0;
+ if (dch->rx_skb) {
+ dev_kfree_skb(dch->rx_skb);
+ dch->rx_skb = NULL;
+ }
+ test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
+ if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
+ del_timer(&dch->timer);
+ spin_unlock_irqrestore(&hc->lock, flags);
+ break;
+ case HW_POWERUP_REQ:
+ spin_lock_irqsave(&hc->lock, flags);
+ if (hc->type == 1) {
+ if (debug & DEBUG_HFCMULTI_MSG)
+ printk(KERN_DEBUG
+ "%s: HW_POWERUP_REQ no BRI\n",
+ __func__);
+ } else {
+ HFC_outb(hc, R_ST_SEL, hc->chan[dch->slot].port);
+ /* undocumented: delay after R_ST_SEL */
+ udelay(1);
+ HFC_outb(hc, A_ST_WR_STATE, 3 | 0x10); /* activate */
+ udelay(6); /* wait at least 5,21us */
+ HFC_outb(hc, A_ST_WR_STATE, 3); /* activate */
+ }
+ spin_unlock_irqrestore(&hc->lock, flags);
+ break;
+ case PH_ACTIVATE_IND:
+ test_and_set_bit(FLG_ACTIVE, &dch->Flags);
+ _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
+ GFP_ATOMIC);
+ break;
+ case PH_DEACTIVATE_IND:
+ test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
+ _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
+ GFP_ATOMIC);
+ break;
+ default:
+ if (dch->debug & DEBUG_HW)
+ printk(KERN_DEBUG "%s: unknown command %x\n",
+ __func__, cmd);
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Layer2 -> Layer 1 Transfer
+ */
+
+static int
+handle_dmsg(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+ struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D);
+ struct dchannel *dch = container_of(dev, struct dchannel, dev);
+ struct hfc_multi *hc = dch->hw;
+ struct mISDNhead *hh = mISDN_HEAD_P(skb);
+ int ret = -EINVAL;
+ unsigned int id;
+ u_long flags;
+
+ switch (hh->prim) {
+ case PH_DATA_REQ:
+ if (skb->len < 1)
+ break;
+ spin_lock_irqsave(&hc->lock, flags);
+ ret = dchannel_senddata(dch, skb);
+ if (ret > 0) { /* direct TX */
+ id = hh->id; /* skb can be freed */
+ hfcmulti_tx(hc, dch->slot);
+ ret = 0;
+ /* start fifo */
+ HFC_outb(hc, R_FIFO, 0);
+ HFC_wait(hc);
+ spin_unlock_irqrestore(&hc->lock, flags);
+ queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+ } else
+ spin_unlock_irqrestore(&hc->lock, flags);
+ return ret;
+ case PH_ACTIVATE_REQ:
+ if (dch->dev.D.protocol != ISDN_P_TE_S0) {
+ spin_lock_irqsave(&hc->lock, flags);
+ ret = 0;
+ if (debug & DEBUG_HFCMULTI_MSG)
+ printk(KERN_DEBUG
+ "%s: PH_ACTIVATE port %d (0..%d)\n",
+ __func__, hc->chan[dch->slot].port,
+ hc->ports-1);
+ /* start activation */
+ if (hc->type == 1) {
+ ph_state_change(dch);
+ if (debug & DEBUG_HFCMULTI_STATE)
+ printk(KERN_DEBUG
+ "%s: E1 report state %x \n",
+ __func__, dch->state);
+ } else {
+ HFC_outb(hc, R_ST_SEL,
+ hc->chan[dch->slot].port);
+ /* undocumented: delay after R_ST_SEL */
+ udelay(1);
+ 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->state = 1;
+ }
+ spin_unlock_irqrestore(&hc->lock, flags);
+ } else
+ ret = l1_event(dch->l1, hh->prim);
+ break;
+ case PH_DEACTIVATE_REQ:
+ test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags);
+ if (dch->dev.D.protocol != ISDN_P_TE_S0) {
+ spin_lock_irqsave(&hc->lock, flags);
+ if (debug & DEBUG_HFCMULTI_MSG)
+ printk(KERN_DEBUG
+ "%s: PH_DEACTIVATE port %d (0..%d)\n",
+ __func__, hc->chan[dch->slot].port,
+ hc->ports-1);
+ /* start deactivation */
+ if (hc->type == 1) {
+ if (debug & DEBUG_HFCMULTI_MSG)
+ printk(KERN_DEBUG
+ "%s: PH_DEACTIVATE no BRI\n",
+ __func__);
+ } else {
+ HFC_outb(hc, R_ST_SEL,
+ hc->chan[dch->slot].port);
+ /* undocumented: delay after R_ST_SEL */
+ udelay(1);
+ HFC_outb(hc, A_ST_WR_STATE, V_ST_ACT * 2);
+ /* deactivate */
+ dch->state = 1;
+ }
+ skb_queue_purge(&dch->squeue);
+ if (dch->tx_skb) {
+ dev_kfree_skb(dch->tx_skb);
+ dch->tx_skb = NULL;
+ }
+ dch->tx_idx = 0;
+ if (dch->rx_skb) {
+ dev_kfree_skb(dch->rx_skb);
+ dch->rx_skb = NULL;
+ }
+ test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
+ if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
+ del_timer(&dch->timer);
+#ifdef FIXME
+ if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags))
+ dchannel_sched_event(&hc->dch, D_CLEARBUSY);
+#endif
+ ret = 0;
+ spin_unlock_irqrestore(&hc->lock, flags);
+ } else
+ ret = l1_event(dch->l1, hh->prim);
+ break;
+ }
+ if (!ret)
+ dev_kfree_skb(skb);
+ return ret;
+}
+
+static void
+deactivate_bchannel(struct bchannel *bch)
+{
+ struct hfc_multi *hc = bch->hw;
+ u_long flags;
+
+ spin_lock_irqsave(&hc->lock, flags);
+ if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) {
+ dev_kfree_skb(bch->next_skb);
+ bch->next_skb = NULL;
+ }
+ if (bch->tx_skb) {
+ dev_kfree_skb(bch->tx_skb);
+ bch->tx_skb = NULL;
+ }
+ bch->tx_idx = 0;
+ if (bch->rx_skb) {
+ dev_kfree_skb(bch->rx_skb);
+ bch->rx_skb = NULL;
+ }
+ hc->chan[bch->slot].coeff_count = 0;
+ test_and_clear_bit(FLG_ACTIVE, &bch->Flags);
+ test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
+ hc->chan[bch->slot].rx_off = 0;
+ hc->chan[bch->slot].conf = -1;
+ mode_hfcmulti(hc, bch->slot, ISDN_P_NONE, -1, 0, -1, 0);
+ spin_unlock_irqrestore(&hc->lock, flags);
+}
+
+static int
+handle_bmsg(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+ struct bchannel *bch = container_of(ch, struct bchannel, ch);
+ struct hfc_multi *hc = bch->hw;
+ int ret = -EINVAL;
+ struct mISDNhead *hh = mISDN_HEAD_P(skb);
+ unsigned int id;
+ u_long flags;
+
+ switch (hh->prim) {
+ case PH_DATA_REQ:
+ if (!skb->len)
+ break;
+ spin_lock_irqsave(&hc->lock, flags);
+ ret = bchannel_senddata(bch, skb);
+ if (ret > 0) { /* direct TX */
+ id = hh->id; /* skb can be freed */
+ hfcmulti_tx(hc, bch->slot);
+ ret = 0;
+ /* start fifo */
+ HFC_outb_nodebug(hc, R_FIFO, 0);
+ HFC_wait_nodebug(hc);
+ if (!test_bit(FLG_TRANSPARENT, &bch->Flags)) {
+ spin_unlock_irqrestore(&hc->lock, flags);
+ queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+ } else
+ spin_unlock_irqrestore(&hc->lock, flags);
+ } else
+ spin_unlock_irqrestore(&hc->lock, flags);
+ return ret;
+ case PH_ACTIVATE_REQ:
+ if (debug & DEBUG_HFCMULTI_MSG)
+ printk(KERN_DEBUG "%s: PH_ACTIVATE ch %d (0..32)\n",
+ __func__, bch->slot);
+ spin_lock_irqsave(&hc->lock, flags);
+ /* activate B-channel if not already activated */
+ if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) {
+ hc->chan[bch->slot].txpending = 0;
+ ret = mode_hfcmulti(hc, bch->slot,
+ ch->protocol,
+ hc->chan[bch->slot].slot_tx,
+ hc->chan[bch->slot].bank_tx,
+ hc->chan[bch->slot].slot_rx,
+ hc->chan[bch->slot].bank_rx);
+ if (!ret) {
+ if (ch->protocol == ISDN_P_B_RAW && !hc->dtmf
+ && test_bit(HFC_CHIP_DTMF, &hc->chip)) {
+ /* start decoder */
+ hc->dtmf = 1;
+ if (debug & DEBUG_HFCMULTI_DTMF)
+ printk(KERN_DEBUG
+ "%s: start dtmf decoder\n",
+ __func__);
+ HFC_outb(hc, R_DTMF, hc->hw.r_dtmf |
+ V_RST_DTMF);
+ }
+ }
+ } else
+ ret = 0;
+ spin_unlock_irqrestore(&hc->lock, flags);
+ if (!ret)
+ _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, NULL,
+ GFP_KERNEL);
+ break;
+ case PH_CONTROL_REQ:
+ spin_lock_irqsave(&hc->lock, flags);
+ switch (hh->id) {
+ case HFC_SPL_LOOP_ON: /* set sample loop */
+ if (debug & DEBUG_HFCMULTI_MSG)
+ printk(KERN_DEBUG
+ "%s: HFC_SPL_LOOP_ON (len = %d)\n",
+ __func__, skb->len);
+ ret = 0;
+ break;
+ case HFC_SPL_LOOP_OFF: /* set silence */
+ if (debug & DEBUG_HFCMULTI_MSG)
+ printk(KERN_DEBUG "%s: HFC_SPL_LOOP_OFF\n",
+ __func__);
+ ret = 0;
+ break;
+ default:
+ printk(KERN_ERR
+ "%s: unknown PH_CONTROL_REQ info %x\n",
+ __func__, hh->id);
+ ret = -EINVAL;
+ }
+ spin_unlock_irqrestore(&hc->lock, flags);
+ break;
+ case PH_DEACTIVATE_REQ:
+ deactivate_bchannel(bch); /* locked there */
+ _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, NULL,
+ GFP_KERNEL);
+ ret = 0;
+ break;
+ }
+ if (!ret)
+ dev_kfree_skb(skb);
+ return ret;
+}
+
+/*
+ * bchannel control function
+ */
+static int
+channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
+{
+ int ret = 0;
+ struct dsp_features *features =
+ (struct dsp_features *)(*((u_long *)&cq->p1));
+ struct hfc_multi *hc = bch->hw;
+ int slot_tx;
+ int bank_tx;
+ int slot_rx;
+ int bank_rx;
+ int num;
+
+ switch (cq->op) {
+ case MISDN_CTRL_GETOP:
+ cq->op = MISDN_CTRL_HFC_OP | MISDN_CTRL_HW_FEATURES_OP
+ | MISDN_CTRL_RX_OFF;
+ break;
+ case MISDN_CTRL_RX_OFF: /* turn off / on rx stream */
+ hc->chan[bch->slot].rx_off = !!cq->p1;
+ if (!hc->chan[bch->slot].rx_off) {
+ /* reset fifo on rx on */
+ HFC_outb_nodebug(hc, R_FIFO, (bch->slot << 1) | 1);
+ HFC_wait_nodebug(hc);
+ HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F);
+ HFC_wait_nodebug(hc);
+ }
+ if (debug & DEBUG_HFCMULTI_MSG)
+ printk(KERN_DEBUG "%s: RX_OFF request (nr=%d off=%d)\n",
+ __func__, bch->nr, hc->chan[bch->slot].rx_off);
+ break;
+ case MISDN_CTRL_HW_FEATURES: /* fill features structure */
+ if (debug & DEBUG_HFCMULTI_MSG)
+ printk(KERN_DEBUG "%s: HW_FEATURE request\n",
+ __func__);
+ /* create confirm */
+ features->hfc_id = hc->id;
+ if (test_bit(HFC_CHIP_DTMF, &hc->chip))
+ features->hfc_dtmf = 1;
+ features->hfc_loops = 0;
+ if (test_bit(HFC_CHIP_B410P, &hc->chip)) {
+ features->hfc_echocanhw = 1;
+ } else {
+ features->pcm_id = hc->pcm;
+ features->pcm_slots = hc->slots;
+ features->pcm_banks = 2;
+ }
+ break;
+ case MISDN_CTRL_HFC_PCM_CONN: /* connect to pcm timeslot (0..N) */
+ slot_tx = cq->p1 & 0xff;
+ bank_tx = cq->p1 >> 8;
+ slot_rx = cq->p2 & 0xff;
+ bank_rx = cq->p2 >> 8;
+ if (debug & DEBUG_HFCMULTI_MSG)
+ printk(KERN_DEBUG
+ "%s: HFC_PCM_CONN slot %d bank %d (TX) "
+ "slot %d bank %d (RX)\n",
+ __func__, 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->slot,
+ slot_tx, bank_tx, slot_rx, bank_rx);
+ else {
+ printk(KERN_WARNING
+ "%s: HFC_PCM_CONN slot %d bank %d (TX) "
+ "slot %d bank %d (RX) out of range\n",
+ __func__, slot_tx, bank_tx,
+ slot_rx, bank_rx);
+ ret = -EINVAL;
+ }
+ break;
+ case MISDN_CTRL_HFC_PCM_DISC: /* release interface from pcm timeslot */
+ if (debug & DEBUG_HFCMULTI_MSG)
+ printk(KERN_DEBUG "%s: HFC_PCM_DISC\n",
+ __func__);
+ hfcmulti_pcm(hc, bch->slot, -1, 0, -1, 0);
+ break;
+ case MISDN_CTRL_HFC_CONF_JOIN: /* join conference (0..7) */
+ num = cq->p1 & 0xff;
+ if (debug & DEBUG_HFCMULTI_MSG)
+ printk(KERN_DEBUG "%s: HFC_CONF_JOIN conf %d\n",
+ __func__, num);
+ if (num <= 7)
+ hfcmulti_conf(hc, bch->slot, num);
+ else {
+ printk(KERN_WARNING
+ "%s: HW_CONF_JOIN conf %d out of range\n",
+ __func__, num);
+ ret = -EINVAL;
+ }
+ break;
+ case MISDN_CTRL_HFC_CONF_SPLIT: /* split conference */
+ if (debug & DEBUG_HFCMULTI_MSG)
+ printk(KERN_DEBUG "%s: HFC_CONF_SPLIT\n", __func__);
+ hfcmulti_conf(hc, bch->slot, -1);
+ break;
+ case MISDN_CTRL_HFC_ECHOCAN_ON:
+ if (debug & DEBUG_HFCMULTI_MSG)
+ printk(KERN_DEBUG "%s: HFC_ECHOCAN_ON\n", __func__);
+ if (test_bit(HFC_CHIP_B410P, &hc->chip))
+ vpm_echocan_on(hc, bch->slot, cq->p1);
+ else
+ ret = -EINVAL;
+ break;
+
+ case MISDN_CTRL_HFC_ECHOCAN_OFF:
+ if (debug & DEBUG_HFCMULTI_MSG)
+ printk(KERN_DEBUG "%s: HFC_ECHOCAN_OFF\n",
+ __func__);
+ if (test_bit(HFC_CHIP_B410P, &hc->chip))
+ vpm_echocan_off(hc, bch->slot);
+ else
+ ret = -EINVAL;
+ break;
+ default:
+ printk(KERN_WARNING "%s: unknown Op %x\n",
+ __func__, cq->op);
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int
+hfcm_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+ struct bchannel *bch = container_of(ch, struct bchannel, ch);
+ struct hfc_multi *hc = bch->hw;
+ int err = -EINVAL;
+ u_long flags;
+
+ if (bch->debug & DEBUG_HW)
+ printk(KERN_DEBUG "%s: cmd:%x %p\n",
+ __func__, cmd, arg);
+ switch (cmd) {
+ case CLOSE_CHANNEL:
+ test_and_clear_bit(FLG_OPEN, &bch->Flags);
+ if (test_bit(FLG_ACTIVE, &bch->Flags))
+ deactivate_bchannel(bch); /* locked there */
+ ch->protocol = ISDN_P_NONE;
+ ch->peer = NULL;
+ module_put(THIS_MODULE);
+ err = 0;
+ break;
+ case CONTROL_CHANNEL:
+ spin_lock_irqsave(&hc->lock, flags);
+ err = channel_bctrl(bch, arg);
+ spin_unlock_irqrestore(&hc->lock, flags);
+ break;
+ default:
+ printk(KERN_WARNING "%s: unknown prim(%x)\n",
+ __func__, cmd);
+ }
+ return err;
+}
+
+/*
+ * handle D-channel events
+ *
+ * handle state change event
+ */
+static void
+ph_state_change(struct dchannel *dch)
+{
+ struct hfc_multi *hc = dch->hw;
+ int ch, i;
+
+ if (!dch) {
+ printk(KERN_WARNING "%s: ERROR given dch is NULL\n",
+ __func__);
+ return;
+ }
+ ch = dch->slot;
+
+ if (hc->type == 1) {
+ if (dch->dev.D.protocol == ISDN_P_TE_E1) {
+ if (debug & DEBUG_HFCMULTI_STATE)
+ printk(KERN_DEBUG
+ "%s: E1 TE (id=%d) newstate %x\n",
+ __func__, hc->id, dch->state);
+ } else {
+ if (debug & DEBUG_HFCMULTI_STATE)
+ printk(KERN_DEBUG
+ "%s: E1 NT (id=%d) newstate %x\n",
+ __func__, hc->id, dch->state);
+ }
+ switch (dch->state) {
+ case (1):
+ if (hc->e1_state != 1) {
+ for (i = 1; i <= 31; i++) {
+ /* reset fifos on e1 activation */
+ HFC_outb_nodebug(hc, R_FIFO, (i << 1) | 1);
+ HFC_wait_nodebug(hc);
+ HFC_outb_nodebug(hc,
+ R_INC_RES_FIFO, V_RES_F);
+ HFC_wait_nodebug(hc);
+ }
+ }
+ test_and_set_bit(FLG_ACTIVE, &dch->Flags);
+ _queue_data(&dch->dev.D, PH_ACTIVATE_IND,
+ MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
+ break;
+
+ default:
+ if (hc->e1_state != 1)
+ return;
+ test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
+ _queue_data(&dch->dev.D, PH_DEACTIVATE_IND,
+ MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
+ }
+ hc->e1_state = dch->state;
+ } else {
+ if (dch->dev.D.protocol == ISDN_P_TE_S0) {
+ if (debug & DEBUG_HFCMULTI_STATE)
+ printk(KERN_DEBUG
+ "%s: S/T TE newstate %x\n",
+ __func__, dch->state);
+ switch (dch->state) {
+ case (0):
+ l1_event(dch->l1, HW_RESET_IND);
+ break;
+ case (3):
+ l1_event(dch->l1, HW_DEACT_IND);
+ break;
+ case (5):
+ case (8):
+ l1_event(dch->l1, ANYSIGNAL);
+ break;
+ case (6):
+ l1_event(dch->l1, INFO2);
+ break;
+ case (7):
+ l1_event(dch->l1, INFO4_P8);
+ break;
+ }
+ } else {
+ if (debug & DEBUG_HFCMULTI_STATE)
+ printk(KERN_DEBUG "%s: S/T NT newstate %x\n",
+ __func__, dch->state);
+ switch (dch->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);
+ /* undocumented: delay after R_ST_SEL */
+ udelay(1);
+ 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->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);
+ /* undocumented: delay after R_ST_SEL */
+ udelay(1);
+ /* allow G2 -> G3 transition */
+ HFC_outb(hc, A_ST_WR_STATE, 2 |
+ V_SET_G2_G3);
+ }
+ break;
+ case (1):
+ hc->chan[ch].nt_timer = -1;
+ test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
+ _queue_data(&dch->dev.D, PH_DEACTIVATE_IND,
+ MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
+ break;
+ case (4):
+ hc->chan[ch].nt_timer = -1;
+ break;
+ case (3):
+ hc->chan[ch].nt_timer = -1;
+ test_and_set_bit(FLG_ACTIVE, &dch->Flags);
+ _queue_data(&dch->dev.D, PH_ACTIVATE_IND,
+ MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * called for card mode init message
+ */
+
+static void
+hfcmulti_initmode(struct dchannel *dch)
+{
+ struct hfc_multi *hc = dch->hw;
+ u_char a_st_wr_state, r_e1_wr_sta;
+ int i, pt;
+
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG "%s: entered\n", __func__);
+
+ if (hc->type == 1) {
+ hc->chan[hc->dslot].slot_tx = -1;
+ hc->chan[hc->dslot].slot_rx = -1;
+ hc->chan[hc->dslot].conf = -1;
+ if (hc->dslot) {
+ mode_hfcmulti(hc, hc->dslot, dch->dev.D.protocol,
+ -1, 0, -1, 0);
+ dch->timer.function = (void *) hfcmulti_dbusy_timer;
+ dch->timer.data = (long) dch;
+ init_timer(&dch->timer);
+ }
+ for (i = 1; i <= 31; i++) {
+ if (i == hc->dslot)
+ continue;
+ hc->chan[i].slot_tx = -1;
+ hc->chan[i].slot_rx = -1;
+ hc->chan[i].conf = -1;
+ mode_hfcmulti(hc, i, ISDN_P_NONE, -1, 0, -1, 0);
+ }
+ /* E1 */
+ if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[hc->dslot].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[hc->dslot].cfg)) {
+ HFC_outb(hc, R_RX0, 0);
+ hc->hw.r_tx0 = 0 | V_OUT_EN;
+ } else {
+ HFC_outb(hc, R_RX0, 1);
+ hc->hw.r_tx0 = 1 | V_OUT_EN;
+ }
+ hc->hw.r_tx1 = V_ATX | V_NTRI;
+ HFC_outb(hc, R_TX0, hc->hw.r_tx0);
+ HFC_outb(hc, R_TX1, hc->hw.r_tx1);
+ HFC_outb(hc, R_TX_FR0, 0x00);
+ HFC_outb(hc, R_TX_FR1, 0xf8);
+
+ if (test_bit(HFC_CFG_CRC4, &hc->chan[hc->dslot].cfg))
+ 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);
+
+ if (test_bit(HFC_CFG_CRC4, &hc->chan[hc->dslot].cfg))
+ HFC_outb(hc, R_RX_FR1, V_RX_MF | V_RX_MF_SYNC);
+
+ if (dch->dev.D.protocol == ISDN_P_NT_E1) {
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG "%s: E1 port is NT-mode\n",
+ __func__);
+ r_e1_wr_sta = 0; /* G0 */
+ hc->e1_getclock = 0;
+ } else {
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG "%s: E1 port is TE-mode\n",
+ __func__);
+ r_e1_wr_sta = 0; /* F0 */
+ hc->e1_getclock = 1;
+ }
+ if (test_bit(HFC_CHIP_RX_SYNC, &hc->chip))
+ HFC_outb(hc, R_SYNC_OUT, V_SYNC_E1_RX);
+ else
+ HFC_outb(hc, R_SYNC_OUT, 0);
+ if (test_bit(HFC_CHIP_E1CLOCK_GET, &hc->chip))
+ hc->e1_getclock = 1;
+ if (test_bit(HFC_CHIP_E1CLOCK_PUT, &hc->chip))
+ hc->e1_getclock = 0;
+ if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) {
+ /* SLAVE (clock master) */
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG
+ "%s: E1 port is clock master "
+ "(clock from PCM)\n", __func__);
+ HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC | V_PCM_SYNC);
+ } else {
+ if (hc->e1_getclock) {
+ /* MASTER (clock slave) */
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG
+ "%s: E1 port is clock slave "
+ "(clock to PCM)\n", __func__);
+ HFC_outb(hc, R_SYNC_CTRL, V_SYNC_OFFS);
+ } else {
+ /* MASTER (clock master) */
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG "%s: E1 port is "
+ "clock master "
+ "(clock from QUARTZ)\n",
+ __func__);
+ HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC |
+ V_PCM_SYNC | V_JATT_OFF);
+ HFC_outb(hc, R_SYNC_OUT, 0);
+ }
+ }
+ HFC_outb(hc, R_JATT_ATT, 0x9c); /* undoc register */
+ 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);
+ if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+ hc->syncronized = 0;
+ plxsd_checksync(hc, 0);
+ }
+ } else {
+ i = dch->slot;
+ hc->chan[i].slot_tx = -1;
+ hc->chan[i].slot_rx = -1;
+ hc->chan[i].conf = -1;
+ mode_hfcmulti(hc, i, dch->dev.D.protocol, -1, 0, -1, 0);
+ dch->timer.function = (void *)hfcmulti_dbusy_timer;
+ dch->timer.data = (long) dch;
+ init_timer(&dch->timer);
+ 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_P_NONE, -1, 0, -1, 0);
+ hc->chan[i - 1].slot_tx = -1;
+ hc->chan[i - 1].slot_rx = -1;
+ hc->chan[i - 1].conf = -1;
+ mode_hfcmulti(hc, i - 1, ISDN_P_NONE, -1, 0, -1, 0);
+ /* ST */
+ pt = hc->chan[i].port;
+ /* select interface */
+ HFC_outb(hc, R_ST_SEL, pt);
+ /* undocumented: delay after R_ST_SEL */
+ udelay(1);
+ if (dch->dev.D.protocol == ISDN_P_NT_S0) {
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG
+ "%s: ST port %d is NT-mode\n",
+ __func__, pt);
+ /* clock delay */
+ HFC_outb(hc, A_ST_CLK_DLY, clockdelay_nt);
+ a_st_wr_state = 1; /* G1 */
+ hc->hw.a_st_ctrl0[pt] = V_ST_MD;
+ } else {
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG
+ "%s: ST port %d is TE-mode\n",
+ __func__, pt);
+ /* clock delay */
+ HFC_outb(hc, A_ST_CLK_DLY, clockdelay_te);
+ a_st_wr_state = 2; /* F2 */
+ hc->hw.a_st_ctrl0[pt] = 0;
+ }
+ if (!test_bit(HFC_CFG_NONCAP_TX, &hc->chan[i].cfg))
+ hc->hw.a_st_ctrl0[pt] |= V_TX_LI;
+ /* line setup */
+ HFC_outb(hc, A_ST_CTRL0, hc->hw.a_st_ctrl0[pt]);
+ /* disable E-channel */
+ if ((dch->dev.D.protocol == ISDN_P_NT_S0) ||
+ test_bit(HFC_CFG_DIS_ECHANNEL, &hc->chan[i].cfg))
+ HFC_outb(hc, A_ST_CTRL1, V_E_IGNO);
+ else
+ HFC_outb(hc, A_ST_CTRL1, 0);
+ /* 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);
+ hc->hw.r_sci_msk |= 1 << pt;
+ /* state machine interrupts */
+ HFC_outb(hc, R_SCI_MSK, hc->hw.r_sci_msk);
+ /* unset sync on port */
+ if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+ hc->syncronized &=
+ ~(1 << hc->chan[dch->slot].port);
+ plxsd_checksync(hc, 0);
+ }
+ }
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk("%s: done\n", __func__);
+}
+
+
+static int
+open_dchannel(struct hfc_multi *hc, struct dchannel *dch,
+ struct channel_req *rq)
+{
+ int err = 0;
+ u_long flags;
+
+ if (debug & DEBUG_HW_OPEN)
+ printk(KERN_DEBUG "%s: dev(%d) open from %p\n", __func__,
+ dch->dev.id, __builtin_return_address(0));
+ if (rq->protocol == ISDN_P_NONE)
+ return -EINVAL;
+ if ((dch->dev.D.protocol != ISDN_P_NONE) &&
+ (dch->dev.D.protocol != rq->protocol)) {
+ if (debug & DEBUG_HFCMULTI_MODE)
+ printk(KERN_WARNING "%s: change protocol %x to %x\n",
+ __func__, dch->dev.D.protocol, rq->protocol);
+ }
+ if ((dch->dev.D.protocol == ISDN_P_TE_S0)
+ && (rq->protocol != ISDN_P_TE_S0))
+ l1_event(dch->l1, CLOSE_CHANNEL);
+ if (dch->dev.D.protocol != rq->protocol) {
+ if (rq->protocol == ISDN_P_TE_S0) {
+ err = create_l1(dch, hfcm_l1callback);
+ if (err)
+ return err;
+ }
+ dch->dev.D.protocol = rq->protocol;
+ spin_lock_irqsave(&hc->lock, flags);
+ hfcmulti_initmode(dch);
+ spin_unlock_irqrestore(&hc->lock, flags);
+ }
+
+ if (((rq->protocol == ISDN_P_NT_S0) && (dch->state == 3)) ||
+ ((rq->protocol == ISDN_P_TE_S0) && (dch->state == 7)) ||
+ ((rq->protocol == ISDN_P_NT_E1) && (dch->state == 1)) ||
+ ((rq->protocol == ISDN_P_TE_E1) && (dch->state == 1))) {
+ _queue_data(&dch->dev.D, PH_ACTIVATE_IND, MISDN_ID_ANY,
+ 0, NULL, GFP_KERNEL);
+ }
+ rq->ch = &dch->dev.D;
+ if (!try_module_get(THIS_MODULE))
+ printk(KERN_WARNING "%s:cannot get module\n", __func__);
+ return 0;
+}
+
+static int
+open_bchannel(struct hfc_multi *hc, struct dchannel *dch,
+ struct channel_req *rq)
+{
+ struct bchannel *bch;
+ int ch;
+
+ if (!test_channelmap(rq->adr.channel, dch->dev.channelmap))
+ return -EINVAL;
+ if (rq->protocol == ISDN_P_NONE)
+ return -EINVAL;
+ if (hc->type == 1)
+ ch = rq->adr.channel;
+ else
+ ch = (rq->adr.channel - 1) + (dch->slot - 2);
+ bch = hc->chan[ch].bch;
+ if (!bch) {
+ printk(KERN_ERR "%s:internal error ch %d has no bch\n",
+ __func__, ch);
+ return -EINVAL;
+ }
+ if (test_and_set_bit(FLG_OPEN, &bch->Flags))
+ return -EBUSY; /* b-channel can be only open once */
+ bch->ch.protocol = rq->protocol;
+ hc->chan[ch].rx_off = 0;
+ rq->ch = &bch->ch;
+ if (!try_module_get(THIS_MODULE))
+ printk(KERN_WARNING "%s:cannot get module\n", __func__);
+ return 0;
+}
+
+/*
+ * device control function
+ */
+static int
+channel_dctrl(struct dchannel *dch, struct mISDN_ctrl_req *cq)
+{
+ int ret = 0;
+
+ switch (cq->op) {
+ case MISDN_CTRL_GETOP:
+ cq->op = 0;
+ break;
+ default:
+ printk(KERN_WARNING "%s: unknown Op %x\n",
+ __func__, cq->op);
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int
+hfcm_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+ struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D);
+ struct dchannel *dch = container_of(dev, struct dchannel, dev);
+ struct hfc_multi *hc = dch->hw;
+ struct channel_req *rq;
+ int err = 0;
+ u_long flags;
+
+ if (dch->debug & DEBUG_HW)
+ printk(KERN_DEBUG "%s: cmd:%x %p\n",
+ __func__, cmd, arg);
+ switch (cmd) {
+ case OPEN_CHANNEL:
+ rq = arg;
+ switch (rq->protocol) {
+ case ISDN_P_TE_S0:
+ case ISDN_P_NT_S0:
+ if (hc->type == 1) {
+ err = -EINVAL;
+ break;
+ }
+ err = open_dchannel(hc, dch, rq); /* locked there */
+ break;
+ case ISDN_P_TE_E1:
+ case ISDN_P_NT_E1:
+ if (hc->type != 1) {
+ err = -EINVAL;
+ break;
+ }
+ err = open_dchannel(hc, dch, rq); /* locked there */
+ break;
+ default:
+ spin_lock_irqsave(&hc->lock, flags);
+ err = open_bchannel(hc, dch, rq);
+ spin_unlock_irqrestore(&hc->lock, flags);
+ }
+ break;
+ case CLOSE_CHANNEL:
+ if (debug & DEBUG_HW_OPEN)
+ printk(KERN_DEBUG "%s: dev(%d) close from %p\n",
+ __func__, dch->dev.id,
+ __builtin_return_address(0));
+ module_put(THIS_MODULE);
+ break;
+ case CONTROL_CHANNEL:
+ spin_lock_irqsave(&hc->lock, flags);
+ err = channel_dctrl(dch, arg);
+ spin_unlock_irqrestore(&hc->lock, flags);
+ break;
+ default:
+ if (dch->debug & DEBUG_HW)
+ printk(KERN_DEBUG "%s: unknown command %x\n",
+ __func__, cmd);
+ err = -EINVAL;
+ }
+ return err;
+}
+
+/*
+ * 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(struct hfc_multi *hc)
+{
+ int err = -EIO;
+ u_long flags;
+ u_short *plx_acc;
+ u_long plx_flags;
+
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG "%s: entered\n", __func__);
+
+ spin_lock_irqsave(&hc->lock, flags);
+ /* set interrupts but leave global interrupt disabled */
+ hc->hw.r_irq_ctrl = V_FIFO_IRQ;
+ disable_hwirq(hc);
+ spin_unlock_irqrestore(&hc->lock, flags);
+
+ if (request_irq(hc->pci_dev->irq, hfcmulti_interrupt, IRQF_SHARED,
+ "HFC-multi", hc)) {
+ printk(KERN_WARNING "mISDN: Could not get interrupt %d.\n",
+ hc->pci_dev->irq);
+ return -EIO;
+ }
+ hc->irq = hc->pci_dev->irq;
+
+ if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+ spin_lock_irqsave(&plx_lock, plx_flags);
+ plx_acc = (u_short *)(hc->plx_membase+PLX_INTCSR);
+ writew((PLX_INTCSR_PCIINT_ENABLE | PLX_INTCSR_LINTI1_ENABLE),
+ plx_acc); /* enable PCI & LINT1 irq */
+ spin_unlock_irqrestore(&plx_lock, plx_flags);
+ }
+
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG "%s: IRQ %d count %d\n",
+ __func__, hc->irq, hc->irqcnt);
+ err = init_chip(hc);
+ if (err)
+ 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
+ */
+ spin_lock_irqsave(&hc->lock, flags);
+ enable_hwirq(hc);
+ spin_unlock_irqrestore(&hc->lock, flags);
+ /* printk(KERN_DEBUG "no master irq set!!!\n"); */
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout((100*HZ)/1000); /* Timeout 100ms */
+ /* turn IRQ off until chip is completely initialized */
+ spin_lock_irqsave(&hc->lock, flags);
+ disable_hwirq(hc);
+ spin_unlock_irqrestore(&hc->lock, flags);
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG "%s: IRQ %d count %d\n",
+ __func__, hc->irq, hc->irqcnt);
+ if (hc->irqcnt) {
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG "%s: done\n", __func__);
+
+ return 0;
+ }
+ if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) {
+ printk(KERN_INFO "ignoring missing interrupts\n");
+ return 0;
+ }
+
+ printk(KERN_ERR "HFC PCI: IRQ(%d) getting no interrupts during init.\n",
+ hc->irq);
+
+ err = -EIO;
+
+error:
+ if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+ spin_lock_irqsave(&plx_lock, plx_flags);
+ plx_acc = (u_short *)(hc->plx_membase+PLX_INTCSR);
+ writew(0x00, plx_acc); /*disable IRQs*/
+ spin_unlock_irqrestore(&plx_lock, plx_flags);
+ }
+
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_WARNING "%s: free irq %d\n", __func__, hc->irq);
+ if (hc->irq) {
+ free_irq(hc->irq, hc);
+ hc->irq = 0;
+ }
+
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG "%s: done (err=%d)\n", __func__, err);
+ return err;
+}
+
+/*
+ * find pci device and set it up
+ */
+
+static int
+setup_pci(struct hfc_multi *hc, struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct hm_map *m = (struct hm_map *)ent->driver_data;
+
+ printk(KERN_INFO
+ "HFC-multi: card manufacturer: '%s' card name: '%s' clock: %s\n",
+ m->vendor_name, m->card_name, m->clock2 ? "double" : "normal");
+
+ hc->pci_dev = pdev;
+ if (m->clock2)
+ test_and_set_bit(HFC_CHIP_CLOCK2, &hc->chip);
+
+ if (ent->device == 0xB410) {
+ test_and_set_bit(HFC_CHIP_B410P, &hc->chip);
+ test_and_set_bit(HFC_CHIP_PCM_MASTER, &hc->chip);
+ test_and_clear_bit(HFC_CHIP_PCM_SLAVE, &hc->chip);
+ hc->slots = 32;
+ }
+
+ if (hc->pci_dev->irq <= 0) {
+ printk(KERN_WARNING "HFC-multi: No IRQ for PCI card found.\n");
+ return -EIO;
+ }
+ if (pci_enable_device(hc->pci_dev)) {
+ printk(KERN_WARNING "HFC-multi: Error enabling PCI card.\n");
+ return -EIO;
+ }
+ hc->leds = m->leds;
+ hc->ledstate = 0xAFFEAFFE;
+ hc->opticalsupport = m->opticalsupport;
+
+ /* set memory access methods */
+ if (m->io_mode) /* use mode from card config */
+ hc->io_mode = m->io_mode;
+ switch (hc->io_mode) {
+ case HFC_IO_MODE_PLXSD:
+ test_and_set_bit(HFC_CHIP_PLXSD, &hc->chip);
+ hc->slots = 128; /* required */
+ /* fall through */
+ case HFC_IO_MODE_PCIMEM:
+ hc->HFC_outb = HFC_outb_pcimem;
+ hc->HFC_inb = HFC_inb_pcimem;
+ hc->HFC_inw = HFC_inw_pcimem;
+ hc->HFC_wait = HFC_wait_pcimem;
+ hc->read_fifo = read_fifo_pcimem;
+ hc->write_fifo = write_fifo_pcimem;
+ break;
+ case HFC_IO_MODE_REGIO:
+ hc->HFC_outb = HFC_outb_regio;
+ hc->HFC_inb = HFC_inb_regio;
+ hc->HFC_inw = HFC_inw_regio;
+ hc->HFC_wait = HFC_wait_regio;
+ hc->read_fifo = read_fifo_regio;
+ hc->write_fifo = write_fifo_regio;
+ break;
+ default:
+ printk(KERN_WARNING "HFC-multi: Invalid IO mode.\n");
+ pci_disable_device(hc->pci_dev);
+ return -EIO;
+ }
+ hc->HFC_outb_nodebug = hc->HFC_outb;
+ hc->HFC_inb_nodebug = hc->HFC_inb;
+ hc->HFC_inw_nodebug = hc->HFC_inw;
+ hc->HFC_wait_nodebug = hc->HFC_wait;
+#ifdef HFC_REGISTER_DEBUG
+ hc->HFC_outb = HFC_outb_debug;
+ hc->HFC_inb = HFC_inb_debug;
+ hc->HFC_inw = HFC_inw_debug;
+ hc->HFC_wait = HFC_wait_debug;
+#endif
+ hc->pci_iobase = 0;
+ hc->pci_membase = NULL;
+ hc->plx_membase = NULL;
+
+ switch (hc->io_mode) {
+ case HFC_IO_MODE_PLXSD:
+ hc->plx_origmembase = hc->pci_dev->resource[0].start;
+ /* MEMBASE 1 is PLX PCI Bridge */
+
+ if (!hc->plx_origmembase) {
+ printk(KERN_WARNING
+ "HFC-multi: No IO-Memory for PCI PLX bridge found\n");
+ pci_disable_device(hc->pci_dev);
+ return -EIO;
+ }
+
+ hc->plx_membase = ioremap(hc->plx_origmembase, 0x80);
+ if (!hc->plx_membase) {
+ printk(KERN_WARNING
+ "HFC-multi: failed to remap plx address space. "
+ "(internal error)\n");
+ pci_disable_device(hc->pci_dev);
+ return -EIO;
+ }
+ printk(KERN_INFO
+ "HFC-multi: plx_membase:%#lx plx_origmembase:%#lx\n",
+ (u_long)hc->plx_membase, hc->plx_origmembase);
+
+ hc->pci_origmembase = hc->pci_dev->resource[2].start;
+ /* MEMBASE 1 is PLX PCI Bridge */
+ if (!hc->pci_origmembase) {
+ printk(KERN_WARNING
+ "HFC-multi: No IO-Memory for PCI card found\n");
+ pci_disable_device(hc->pci_dev);
+ return -EIO;
+ }
+
+ hc->pci_membase = ioremap(hc->pci_origmembase, 0x400);
+ if (!hc->pci_membase) {
+ printk(KERN_WARNING "HFC-multi: failed to remap io "
+ "address space. (internal error)\n");
+ pci_disable_device(hc->pci_dev);
+ return -EIO;
+ }
+
+ printk(KERN_INFO
+ "card %d: defined at MEMBASE %#lx (%#lx) IRQ %d HZ %d "
+ "leds-type %d\n",
+ hc->id, (u_long)hc->pci_membase, hc->pci_origmembase,
+ hc->pci_dev->irq, HZ, hc->leds);
+ pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_MEMIO);
+ break;
+ case HFC_IO_MODE_PCIMEM:
+ hc->pci_origmembase = hc->pci_dev->resource[1].start;
+ if (!hc->pci_origmembase) {
+ printk(KERN_WARNING
+ "HFC-multi: No IO-Memory for PCI card found\n");
+ pci_disable_device(hc->pci_dev);
+ return -EIO;
+ }
+
+ hc->pci_membase = ioremap(hc->pci_origmembase, 256);
+ if (!hc->pci_membase) {
+ printk(KERN_WARNING
+ "HFC-multi: failed to remap io address space. "
+ "(internal error)\n");
+ pci_disable_device(hc->pci_dev);
+ return -EIO;
+ }
+ printk(KERN_INFO "card %d: defined at MEMBASE %#lx (%#lx) IRQ %d "
+ "HZ %d leds-type %d\n", hc->id, (u_long)hc->pci_membase,
+ hc->pci_origmembase, hc->pci_dev->irq, HZ, hc->leds);
+ pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_MEMIO);
+ break;
+ case HFC_IO_MODE_REGIO:
+ hc->pci_iobase = (u_int) hc->pci_dev->resource[0].start;
+ if (!hc->pci_iobase) {
+ printk(KERN_WARNING
+ "HFC-multi: No IO for PCI card found\n");
+ pci_disable_device(hc->pci_dev);
+ return -EIO;
+ }
+
+ if (!request_region(hc->pci_iobase, 8, "hfcmulti")) {
+ printk(KERN_WARNING "HFC-multi: failed to request "
+ "address space at 0x%08lx (internal error)\n",
+ hc->pci_iobase);
+ pci_disable_device(hc->pci_dev);
+ return -EIO;
+ }
+
+ printk(KERN_INFO
+ "%s %s: defined at IOBASE %#x IRQ %d HZ %d leds-type %d\n",
+ m->vendor_name, m->card_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);
+ break;
+ default:
+ printk(KERN_WARNING "HFC-multi: Invalid IO mode.\n");
+ pci_disable_device(hc->pci_dev);
+ return -EIO;
+ }
+
+ pci_set_drvdata(hc->pci_dev, hc);
+
+ /* At this point the needed PCI config is done */
+ /* fifos are still not enabled */
+ return 0;
+}
+
+
+/*
+ * remove port
+ */
+
+static void
+release_port(struct hfc_multi *hc, struct dchannel *dch)
+{
+ int pt, ci, i = 0;
+ u_long flags;
+ struct bchannel *pb;
+
+ ci = dch->slot;
+ pt = hc->chan[ci].port;
+
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG "%s: entered for port %d\n",
+ __func__, pt + 1);
+
+ if (pt >= hc->ports) {
+ printk(KERN_WARNING "%s: ERROR port out of range (%d).\n",
+ __func__, pt + 1);
+ return;
+ }
+
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG "%s: releasing port=%d\n",
+ __func__, pt + 1);
+
+ if (dch->dev.D.protocol == ISDN_P_TE_S0)
+ l1_event(dch->l1, CLOSE_CHANNEL);
+
+ hc->chan[ci].dch = NULL;
+
+ if (hc->created[pt]) {
+ hc->created[pt] = 0;
+ mISDN_unregister_device(&dch->dev);
+ }
+
+ spin_lock_irqsave(&hc->lock, flags);
+
+ if (dch->timer.function) {
+ del_timer(&dch->timer);
+ dch->timer.function = NULL;
+ }
+
+ if (hc->type == 1) { /* E1 */
+ /* remove sync */
+ if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+ hc->syncronized = 0;
+ plxsd_checksync(hc, 1);
+ }
+ /* free channels */
+ for (i = 0; i <= 31; i++) {
+ if (hc->chan[i].bch) {
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG
+ "%s: free port %d channel %d\n",
+ __func__, hc->chan[i].port+1, i);
+ pb = hc->chan[i].bch;
+ hc->chan[i].bch = NULL;
+ spin_unlock_irqrestore(&hc->lock, flags);
+ mISDN_freebchannel(pb);
+ kfree(pb);
+ kfree(hc->chan[i].coeff);
+ spin_lock_irqsave(&hc->lock, flags);
+ }
+ }
+ } else {
+ /* remove sync */
+ if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) {
+ hc->syncronized &=
+ ~(1 << hc->chan[ci].port);
+ plxsd_checksync(hc, 1);
+ }
+ /* free channels */
+ if (hc->chan[ci - 2].bch) {
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG
+ "%s: free port %d channel %d\n",
+ __func__, hc->chan[ci - 2].port+1,
+ ci - 2);
+ pb = hc->chan[ci - 2].bch;
+ hc->chan[ci - 2].bch = NULL;
+ spin_unlock_irqrestore(&hc->lock, flags);
+ mISDN_freebchannel(pb);
+ kfree(pb);
+ kfree(hc->chan[ci - 2].coeff);
+ spin_lock_irqsave(&hc->lock, flags);
+ }
+ if (hc->chan[ci - 1].bch) {
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG
+ "%s: free port %d channel %d\n",
+ __func__, hc->chan[ci - 1].port+1,
+ ci - 1);
+ pb = hc->chan[ci - 1].bch;
+ hc->chan[ci - 1].bch = NULL;
+ spin_unlock_irqrestore(&hc->lock, flags);
+ mISDN_freebchannel(pb);
+ kfree(pb);
+ kfree(hc->chan[ci - 1].coeff);
+ spin_lock_irqsave(&hc->lock, flags);
+ }
+ }
+
+ spin_unlock_irqrestore(&hc->lock, flags);
+
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG "%s: free port %d channel D\n", __func__, pt);
+ mISDN_freedchannel(dch);
+ kfree(dch);
+
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG "%s: done!\n", __func__);
+}
+
+static void
+release_card(struct hfc_multi *hc)
+{
+ u_long flags;
+ int ch;
+
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_WARNING "%s: release card (%d) entered\n",
+ __func__, hc->id);
+
+ spin_lock_irqsave(&hc->lock, flags);
+ disable_hwirq(hc);
+ spin_unlock_irqrestore(&hc->lock, flags);
+
+ udelay(1000);
+
+ /* dimm leds */
+ if (hc->leds)
+ hfcmulti_leds(hc);
+
+ /* disable D-channels & B-channels */
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG "%s: disable all channels (d and b)\n",
+ __func__);
+ for (ch = 0; ch <= 31; ch++) {
+ if (hc->chan[ch].dch)
+ release_port(hc, hc->chan[ch].dch);
+ }
+
+ /* release hardware & irq */
+ if (hc->irq) {
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_WARNING "%s: free irq %d\n",
+ __func__, hc->irq);
+ free_irq(hc->irq, hc);
+ hc->irq = 0;
+
+ }
+ release_io_hfcmulti(hc);
+
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_WARNING "%s: remove instance from list\n",
+ __func__);
+ list_del(&hc->list);
+
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_WARNING "%s: delete instance\n", __func__);
+ if (hc == syncmaster)
+ syncmaster = NULL;
+ kfree(hc);
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_WARNING "%s: card successfully removed\n",
+ __func__);
+}
+
+static int
+init_e1_port(struct hfc_multi *hc, struct hm_map *m)
+{
+ struct dchannel *dch;
+ struct bchannel *bch;
+ int ch, ret = 0;
+ char name[MISDN_MAX_IDLEN];
+
+ dch = kzalloc(sizeof(struct dchannel), GFP_KERNEL);
+ if (!dch)
+ return -ENOMEM;
+ dch->debug = debug;
+ mISDN_initdchannel(dch, MAX_DFRAME_LEN_L1, ph_state_change);
+ dch->hw = hc;
+ dch->dev.Dprotocols = (1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1);
+ dch->dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
+ (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
+ dch->dev.D.send = handle_dmsg;
+ dch->dev.D.ctrl = hfcm_dctrl;
+ dch->dev.nrbchan = (hc->dslot)?30:31;
+ dch->slot = hc->dslot;
+ hc->chan[hc->dslot].dch = dch;
+ hc->chan[hc->dslot].port = 0;
+ hc->chan[hc->dslot].nt_timer = -1;
+ for (ch = 1; ch <= 31; ch++) {
+ if (ch == hc->dslot) /* skip dchannel */
+ continue;
+ bch = kzalloc(sizeof(struct bchannel), GFP_KERNEL);
+ if (!bch) {
+ printk(KERN_ERR "%s: no memory for bchannel\n",
+ __func__);
+ ret = -ENOMEM;
+ goto free_chan;
+ }
+ hc->chan[ch].coeff = kzalloc(512, GFP_KERNEL);
+ if (!hc->chan[ch].coeff) {
+ printk(KERN_ERR "%s: no memory for coeffs\n",
+ __func__);
+ ret = -ENOMEM;
+ goto free_chan;
+ }
+ bch->nr = ch;
+ bch->slot = ch;
+ bch->debug = debug;
+ mISDN_initbchannel(bch, MAX_DATA_MEM);
+ bch->hw = hc;
+ bch->ch.send = handle_bmsg;
+ bch->ch.ctrl = hfcm_bctrl;
+ bch->ch.nr = ch;
+ list_add(&bch->ch.list, &dch->dev.bchannels);
+ hc->chan[ch].bch = bch;
+ hc->chan[ch].port = 0;
+ set_channelmap(bch->nr, dch->dev.channelmap);
+ }
+ /* set optical line type */
+ if (port[Port_cnt] & 0x001) {
+ if (!m->opticalsupport) {
+ printk(KERN_INFO
+ "This board has no optical "
+ "support\n");
+ } else {
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG
+ "%s: PORT set optical "
+ "interfacs: card(%d) "
+ "port(%d)\n",
+ __func__,
+ HFC_cnt + 1, 1);
+ test_and_set_bit(HFC_CFG_OPTICAL,
+ &hc->chan[hc->dslot].cfg);
+ }
+ }
+ /* set LOS report */
+ if (port[Port_cnt] & 0x004) {
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG "%s: PORT set "
+ "LOS report: card(%d) port(%d)\n",
+ __func__, HFC_cnt + 1, 1);
+ test_and_set_bit(HFC_CFG_REPORT_LOS,
+ &hc->chan[hc->dslot].cfg);
+ }
+ /* set AIS report */
+ if (port[Port_cnt] & 0x008) {
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG "%s: PORT set "
+ "AIS report: card(%d) port(%d)\n",
+ __func__, HFC_cnt + 1, 1);
+ test_and_set_bit(HFC_CFG_REPORT_AIS,
+ &hc->chan[hc->dslot].cfg);
+ }
+ /* set SLIP report */
+ if (port[Port_cnt] & 0x010) {
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG
+ "%s: PORT set SLIP report: "
+ "card(%d) port(%d)\n",
+ __func__, HFC_cnt + 1, 1);
+ test_and_set_bit(HFC_CFG_REPORT_SLIP,
+ &hc->chan[hc->dslot].cfg);
+ }
+ /* set RDI report */
+ if (port[Port_cnt] & 0x020) {
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG
+ "%s: PORT set RDI report: "
+ "card(%d) port(%d)\n",
+ __func__, HFC_cnt + 1, 1);
+ test_and_set_bit(HFC_CFG_REPORT_RDI,
+ &hc->chan[hc->dslot].cfg);
+ }
+ /* set CRC-4 Mode */
+ if (!(port[Port_cnt] & 0x100)) {
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG "%s: PORT turn on CRC4 report:"
+ " card(%d) port(%d)\n",
+ __func__, HFC_cnt + 1, 1);
+ test_and_set_bit(HFC_CFG_CRC4,
+ &hc->chan[hc->dslot].cfg);
+ } else {
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG "%s: PORT turn off CRC4"
+ " report: card(%d) port(%d)\n",
+ __func__, HFC_cnt + 1, 1);
+ }
+ /* set forced clock */
+ if (port[Port_cnt] & 0x0200) {
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG "%s: PORT force getting clock from "
+ "E1: card(%d) port(%d)\n",
+ __func__, HFC_cnt + 1, 1);
+ test_and_set_bit(HFC_CHIP_E1CLOCK_GET, &hc->chip);
+ } else
+ if (port[Port_cnt] & 0x0400) {
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG "%s: PORT force putting clock to "
+ "E1: card(%d) port(%d)\n",
+ __func__, HFC_cnt + 1, 1);
+ test_and_set_bit(HFC_CHIP_E1CLOCK_PUT, &hc->chip);
+ }
+ /* set JATT PLL */
+ if (port[Port_cnt] & 0x0800) {
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG "%s: PORT disable JATT PLL on "
+ "E1: card(%d) port(%d)\n",
+ __func__, HFC_cnt + 1, 1);
+ test_and_set_bit(HFC_CHIP_RX_SYNC, &hc->chip);
+ }
+ /* set elastic jitter buffer */
+ if (port[Port_cnt] & 0x3000) {
+ hc->chan[hc->dslot].jitter = (port[Port_cnt]>>12) & 0x3;
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG
+ "%s: PORT set elastic "
+ "buffer to %d: card(%d) port(%d)\n",
+ __func__, hc->chan[hc->dslot].jitter,
+ HFC_cnt + 1, 1);
+ } else
+ hc->chan[hc->dslot].jitter = 2; /* default */
+ snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-e1.%d", HFC_cnt + 1);
+ ret = mISDN_register_device(&dch->dev, name);
+ if (ret)
+ goto free_chan;
+ hc->created[0] = 1;
+ return ret;
+free_chan:
+ release_port(hc, dch);
+ return ret;
+}
+
+static int
+init_multi_port(struct hfc_multi *hc, int pt)
+{
+ struct dchannel *dch;
+ struct bchannel *bch;
+ int ch, i, ret = 0;
+ char name[MISDN_MAX_IDLEN];
+
+ dch = kzalloc(sizeof(struct dchannel), GFP_KERNEL);
+ if (!dch)
+ return -ENOMEM;
+ dch->debug = debug;
+ mISDN_initdchannel(dch, MAX_DFRAME_LEN_L1, ph_state_change);
+ dch->hw = hc;
+ dch->dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0);
+ dch->dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
+ (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
+ dch->dev.D.send = handle_dmsg;
+ dch->dev.D.ctrl = hfcm_dctrl;
+ dch->dev.nrbchan = 2;
+ i = pt << 2;
+ dch->slot = i + 2;
+ hc->chan[i + 2].dch = dch;
+ hc->chan[i + 2].port = pt;
+ hc->chan[i + 2].nt_timer = -1;
+ for (ch = 0; ch < dch->dev.nrbchan; ch++) {
+ bch = kzalloc(sizeof(struct bchannel), GFP_KERNEL);
+ if (!bch) {
+ printk(KERN_ERR "%s: no memory for bchannel\n",
+ __func__);
+ ret = -ENOMEM;
+ goto free_chan;
+ }
+ hc->chan[i + ch].coeff = kzalloc(512, GFP_KERNEL);
+ if (!hc->chan[i + ch].coeff) {
+ printk(KERN_ERR "%s: no memory for coeffs\n",
+ __func__);
+ ret = -ENOMEM;
+ goto free_chan;
+ }
+ bch->nr = ch + 1;
+ bch->slot = i + ch;
+ bch->debug = debug;
+ mISDN_initbchannel(bch, MAX_DATA_MEM);
+ bch->hw = hc;
+ bch->ch.send = handle_bmsg;
+ bch->ch.ctrl = hfcm_bctrl;
+ bch->ch.nr = ch + 1;
+ list_add(&bch->ch.list, &dch->dev.bchannels);
+ hc->chan[i + ch].bch = bch;
+ hc->chan[i + ch].port = pt;
+ set_channelmap(bch->nr, dch->dev.channelmap);
+ }
+ /* set master clock */
+ if (port[Port_cnt] & 0x001) {
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG
+ "%s: PROTOCOL set master clock: "
+ "card(%d) port(%d)\n",
+ __func__, HFC_cnt + 1, pt + 1);
+ if (dch->dev.D.protocol != ISDN_P_TE_S0) {
+ printk(KERN_ERR "Error: Master clock "
+ "for port(%d) of card(%d) is only"
+ " possible with TE-mode\n",
+ pt + 1, HFC_cnt + 1);
+ ret = -EINVAL;
+ goto free_chan;
+ }
+ 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);
+ ret = -EINVAL;
+ goto free_chan;
+ }
+ hc->masterclk = pt;
+ }
+ /* set transmitter line to non capacitive */
+ if (port[Port_cnt] & 0x002) {
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG
+ "%s: PROTOCOL set non capacitive "
+ "transmitter: card(%d) port(%d)\n",
+ __func__, HFC_cnt + 1, pt + 1);
+ test_and_set_bit(HFC_CFG_NONCAP_TX,
+ &hc->chan[i + 2].cfg);
+ }
+ /* disable E-channel */
+ if (port[Port_cnt] & 0x004) {
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG
+ "%s: PROTOCOL disable E-channel: "
+ "card(%d) port(%d)\n",
+ __func__, HFC_cnt + 1, pt + 1);
+ test_and_set_bit(HFC_CFG_DIS_ECHANNEL,
+ &hc->chan[i + 2].cfg);
+ }
+ snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-%ds.%d/%d",
+ hc->type, HFC_cnt + 1, pt + 1);
+ ret = mISDN_register_device(&dch->dev, name);
+ if (ret)
+ goto free_chan;
+ hc->created[pt] = 1;
+ return ret;
+free_chan:
+ release_port(hc, dch);
+ return ret;
+}
+
+static int
+hfcmulti_init(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ struct hm_map *m = (struct hm_map *)ent->driver_data;
+ int ret_err = 0;
+ int pt;
+ struct hfc_multi *hc;
+ u_long flags;
+ u_char dips = 0, pmj = 0; /* dip settings, port mode Jumpers */
+
+ if (HFC_cnt >= MAX_CARDS) {
+ printk(KERN_ERR "too many cards (max=%d).\n",
+ MAX_CARDS);
+ return -EINVAL;
+ }
+ if ((type[HFC_cnt] & 0xff) && (type[HFC_cnt] & 0xff) != m->type) {
+ printk(KERN_WARNING "HFC-MULTI: Card '%s:%s' type %d found but "
+ "type[%d] %d was supplied as module parameter\n",
+ m->vendor_name, m->card_name, m->type, HFC_cnt,
+ type[HFC_cnt] & 0xff);
+ printk(KERN_WARNING "HFC-MULTI: Load module without parameters "
+ "first, to see cards and their types.");
+ return -EINVAL;
+ }
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG "%s: Registering %s:%s chip type %d (0x%x)\n",
+ __func__, m->vendor_name, m->card_name, m->type,
+ type[HFC_cnt]);
+
+ /* allocate card+fifo structure */
+ hc = kzalloc(sizeof(struct hfc_multi), GFP_KERNEL);
+ if (!hc) {
+ printk(KERN_ERR "No kmem for HFC-Multi card\n");
+ return -ENOMEM;
+ }
+ spin_lock_init(&hc->lock);
+ hc->mtyp = m;
+ hc->type = m->type;
+ hc->ports = m->ports;
+ hc->id = HFC_cnt;
+ hc->pcm = pcm[HFC_cnt];
+ hc->io_mode = iomode[HFC_cnt];
+ if (dslot[HFC_cnt] < 0) {
+ hc->dslot = 0;
+ printk(KERN_INFO "HFC-E1 card has disabled D-channel, but "
+ "31 B-channels\n");
+ } if (dslot[HFC_cnt] > 0 && dslot[HFC_cnt] < 32) {
+ hc->dslot = dslot[HFC_cnt];
+ printk(KERN_INFO "HFC-E1 card has alternating D-channel on "
+ "time slot %d\n", dslot[HFC_cnt]);
+ } else
+ hc->dslot = 16;
+
+ /* set chip specific features */
+ hc->masterclk = -1;
+ 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] & 0x800)
+ test_and_set_bit(HFC_CHIP_PCM_SLAVE, &hc->chip);
+ if (type[HFC_cnt] & 0x1000) {
+ test_and_set_bit(HFC_CHIP_PCM_MASTER, &hc->chip);
+ test_and_clear_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 (type[HFC_cnt] & 0x80000) {
+ test_and_set_bit(HFC_CHIP_WATCHDOG, &hc->chip);
+ hc->wdcount = 0;
+ hc->wdbyte = V_GPIO_OUT2;
+ printk(KERN_NOTICE "Watchdog enabled\n");
+ }
+
+ /* setup pci, hc->slots may change due to PLXSD */
+ ret_err = setup_pci(hc, pdev, ent);
+ if (ret_err) {
+ if (hc == syncmaster)
+ syncmaster = NULL;
+ kfree(hc);
+ return ret_err;
+ }
+
+ /* crate channels */
+ for (pt = 0; pt < hc->ports; pt++) {
+ if (Port_cnt >= MAX_PORTS) {
+ printk(KERN_ERR "too many ports (max=%d).\n",
+ MAX_PORTS);
+ ret_err = -EINVAL;
+ goto free_card;
+ }
+ if (hc->type == 1)
+ ret_err = init_e1_port(hc, m);
+ else
+ ret_err = init_multi_port(hc, pt);
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG
+ "%s: Registering D-channel, card(%d) port(%d)"
+ "result %d\n",
+ __func__, HFC_cnt + 1, pt, ret_err);
+
+ if (ret_err) {
+ while (pt) { /* release already registered ports */
+ pt--;
+ release_port(hc, hc->chan[(pt << 2) + 2].dch);
+ }
+ goto free_card;
+ }
+ Port_cnt++;
+ }
+
+ /* disp switches */
+ switch (m->dip_type) {
+ case DIP_4S:
+ /*
+ * get DIP Setting for beroNet 1S/2S/4S cards
+ * check if Port Jumper config matches
+ * module param 'protocol'
+ * DIP Setting: (collect GPIO 13/14/15 (R_GPIO_IN1) +
+ * GPI 19/23 (R_GPI_IN2))
+ */
+ dips = ((~HFC_inb(hc, R_GPIO_IN1) & 0xE0) >> 5) |
+ ((~HFC_inb(hc, R_GPI_IN2) & 0x80) >> 3) |
+ (~HFC_inb(hc, R_GPI_IN2) & 0x08);
+
+ /* Port mode (TE/NT) jumpers */
+ pmj = ((HFC_inb(hc, R_GPI_IN3) >> 4) & 0xf);
+
+ if (test_bit(HFC_CHIP_B410P, &hc->chip))
+ pmj = ~pmj & 0xf;
+
+ printk(KERN_INFO "%s: %s DIPs(0x%x) jumpers(0x%x)\n",
+ m->vendor_name, m->card_name, dips, pmj);
+ break;
+ case DIP_8S:
+ /*
+ * get DIP Setting for beroNet 8S0+ cards
+ *
+ * enable PCI auxbridge function
+ */
+ HFC_outb(hc, R_BRG_PCM_CFG, 1 | V_PCM_CLK);
+ /* prepare access to auxport */
+ outw(0x4000, hc->pci_iobase + 4);
+ /*
+ * some dummy reads are required to
+ * read valid DIP switch data
+ */
+ dips = inb(hc->pci_iobase);
+ dips = inb(hc->pci_iobase);
+ dips = inb(hc->pci_iobase);
+ dips = ~inb(hc->pci_iobase) & 0x3F;
+ outw(0x0, hc->pci_iobase + 4);
+ /* disable PCI auxbridge function */
+ HFC_outb(hc, R_BRG_PCM_CFG, V_PCM_CLK);
+ printk(KERN_INFO "%s: %s DIPs(0x%x)\n",
+ m->vendor_name, m->card_name, dips);
+ break;
+ case DIP_E1:
+ /*
+ * get DIP Setting for beroNet E1 cards
+ * DIP Setting: collect GPI 4/5/6/7 (R_GPI_IN0)
+ */
+ dips = (~HFC_inb(hc, R_GPI_IN0) & 0xF0)>>4;
+ printk(KERN_INFO "%s: %s DIPs(0x%x)\n",
+ m->vendor_name, m->card_name, dips);
+ break;
+ }
+
+ /* add to list */
+ spin_lock_irqsave(&HFClock, flags);
+ list_add_tail(&hc->list, &HFClist);
+ spin_unlock_irqrestore(&HFClock, flags);
+
+ /* initialize hardware */
+ ret_err = init_card(hc);
+ if (ret_err) {
+ printk(KERN_ERR "init card returns %d\n", ret_err);
+ release_card(hc);
+ return ret_err;
+ }
+
+ /* start IRQ and return */
+ spin_lock_irqsave(&hc->lock, flags);
+ enable_hwirq(hc);
+ spin_unlock_irqrestore(&hc->lock, flags);
+ return 0;
+
+free_card:
+ release_io_hfcmulti(hc);
+ if (hc == syncmaster)
+ syncmaster = NULL;
+ kfree(hc);
+ return ret_err;
+}
+
+static void __devexit hfc_remove_pci(struct pci_dev *pdev)
+{
+ struct hfc_multi *card = pci_get_drvdata(pdev);
+ u_long flags;
+
+ if (debug)
+ printk(KERN_INFO "removing hfc_multi card vendor:%x "
+ "device:%x subvendor:%x subdevice:%x\n",
+ pdev->vendor, pdev->device,
+ pdev->subsystem_vendor, pdev->subsystem_device);
+
+ if (card) {
+ spin_lock_irqsave(&HFClock, flags);
+ release_card(card);
+ spin_unlock_irqrestore(&HFClock, flags);
+ } else {
+ if (debug)
+ printk(KERN_WARNING "%s: drvdata allready removed\n",
+ __func__);
+ }
+}
+
+#define VENDOR_CCD "Cologne Chip AG"
+#define VENDOR_BN "beroNet GmbH"
+#define VENDOR_DIG "Digium Inc."
+#define VENDOR_JH "Junghanns.NET GmbH"
+#define VENDOR_PRIM "PrimuX"
+
+static const struct hm_map hfcm_map[] = {
+/*0*/ {VENDOR_BN, "HFC-1S Card (mini PCI)", 4, 1, 1, 3, 0, DIP_4S, 0},
+/*1*/ {VENDOR_BN, "HFC-2S Card", 4, 2, 1, 3, 0, DIP_4S, 0},
+/*2*/ {VENDOR_BN, "HFC-2S Card (mini PCI)", 4, 2, 1, 3, 0, DIP_4S, 0},
+/*3*/ {VENDOR_BN, "HFC-4S Card", 4, 4, 1, 2, 0, DIP_4S, 0},
+/*4*/ {VENDOR_BN, "HFC-4S Card (mini PCI)", 4, 4, 1, 2, 0, 0, 0},
+/*5*/ {VENDOR_CCD, "HFC-4S Eval (old)", 4, 4, 0, 0, 0, 0, 0},
+/*6*/ {VENDOR_CCD, "HFC-4S IOB4ST", 4, 4, 1, 2, 0, DIP_4S, 0},
+/*7*/ {VENDOR_CCD, "HFC-4S", 4, 4, 1, 2, 0, 0, 0},
+/*8*/ {VENDOR_DIG, "HFC-4S Card", 4, 4, 0, 2, 0, 0, HFC_IO_MODE_REGIO},
+/*9*/ {VENDOR_CCD, "HFC-4S Swyx 4xS0 SX2 QuadBri", 4, 4, 1, 2, 0, 0, 0},
+/*10*/ {VENDOR_JH, "HFC-4S (junghanns 2.0)", 4, 4, 1, 2, 0, 0, 0},
+/*11*/ {VENDOR_PRIM, "HFC-2S Primux Card", 4, 2, 0, 0, 0, 0, 0},
+
+/*12*/ {VENDOR_BN, "HFC-8S Card", 8, 8, 1, 0, 0, 0, 0},
+/*13*/ {VENDOR_BN, "HFC-8S Card (+)", 8, 8, 1, 8, 0, DIP_8S,
+ HFC_IO_MODE_REGIO},
+/*14*/ {VENDOR_CCD, "HFC-8S Eval (old)", 8, 8, 0, 0, 0, 0, 0},
+/*15*/ {VENDOR_CCD, "HFC-8S IOB4ST Recording", 8, 8, 1, 0, 0, 0, 0},
+
+/*16*/ {VENDOR_CCD, "HFC-8S IOB8ST", 8, 8, 1, 0, 0, 0, 0},
+/*17*/ {VENDOR_CCD, "HFC-8S", 8, 8, 1, 0, 0, 0, 0},
+/*18*/ {VENDOR_CCD, "HFC-8S", 8, 8, 1, 0, 0, 0, 0},
+
+/*19*/ {VENDOR_BN, "HFC-E1 Card", 1, 1, 0, 1, 0, DIP_E1, 0},
+/*20*/ {VENDOR_BN, "HFC-E1 Card (mini PCI)", 1, 1, 0, 1, 0, 0, 0},
+/*21*/ {VENDOR_BN, "HFC-E1+ Card (Dual)", 1, 1, 0, 1, 0, DIP_E1, 0},
+/*22*/ {VENDOR_BN, "HFC-E1 Card (Dual)", 1, 1, 0, 1, 0, DIP_E1, 0},
+
+/*23*/ {VENDOR_CCD, "HFC-E1 Eval (old)", 1, 1, 0, 0, 0, 0, 0},
+/*24*/ {VENDOR_CCD, "HFC-E1 IOB1E1", 1, 1, 0, 1, 0, 0, 0},
+/*25*/ {VENDOR_CCD, "HFC-E1", 1, 1, 0, 1, 0, 0, 0},
+
+/*26*/ {VENDOR_CCD, "HFC-4S Speech Design", 4, 4, 0, 0, 0, 0,
+ HFC_IO_MODE_PLXSD},
+/*27*/ {VENDOR_CCD, "HFC-E1 Speech Design", 1, 1, 0, 0, 0, 0,
+ HFC_IO_MODE_PLXSD},
+/*28*/ {VENDOR_CCD, "HFC-4S OpenVox", 4, 4, 1, 0, 0, 0, 0},
+/*29*/ {VENDOR_CCD, "HFC-2S OpenVox", 4, 2, 1, 0, 0, 0, 0},
+/*30*/ {VENDOR_CCD, "HFC-8S OpenVox", 8, 8, 1, 0, 0, 0, 0},
+};
+
+#undef H
+#define H(x) ((unsigned long)&hfcm_map[x])
+static struct pci_device_id hfmultipci_ids[] __devinitdata = {
+
+ /* Cards with HFC-4S Chip */
+ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+ PCI_SUBDEVICE_ID_CCD_BN1SM, 0, 0, H(0)}, /* BN1S mini PCI */
+ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+ PCI_SUBDEVICE_ID_CCD_BN2S, 0, 0, H(1)}, /* BN2S */
+ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+ PCI_SUBDEVICE_ID_CCD_BN2SM, 0, 0, H(2)}, /* BN2S mini PCI */
+ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+ PCI_SUBDEVICE_ID_CCD_BN4S, 0, 0, H(3)}, /* BN4S */
+ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+ PCI_SUBDEVICE_ID_CCD_BN4SM, 0, 0, H(4)}, /* BN4S mini PCI */
+ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+ PCI_DEVICE_ID_CCD_HFC4S, 0, 0, H(5)}, /* Old Eval */
+ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+ PCI_SUBDEVICE_ID_CCD_IOB4ST, 0, 0, H(6)}, /* IOB4ST */
+ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+ PCI_SUBDEVICE_ID_CCD_HFC4S, 0, 0, H(7)}, /* 4S */
+ { PCI_VENDOR_ID_DIGIUM, PCI_DEVICE_ID_DIGIUM_HFC4S,
+ PCI_VENDOR_ID_DIGIUM, PCI_DEVICE_ID_DIGIUM_HFC4S, 0, 0, H(8)},
+ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+ PCI_SUBDEVICE_ID_CCD_SWYX4S, 0, 0, H(9)}, /* 4S Swyx */
+ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+ PCI_SUBDEVICE_ID_CCD_JH4S20, 0, 0, H(10)},
+ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+ PCI_SUBDEVICE_ID_CCD_PMX2S, 0, 0, H(11)}, /* Primux */
+ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+ PCI_SUBDEVICE_ID_CCD_OV4S, 0, 0, H(28)}, /* OpenVox 4 */
+ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD,
+ PCI_SUBDEVICE_ID_CCD_OV2S, 0, 0, H(29)}, /* OpenVox 2 */
+
+ /* Cards with HFC-8S Chip */
+ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
+ PCI_SUBDEVICE_ID_CCD_BN8S, 0, 0, H(12)}, /* BN8S */
+ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
+ PCI_SUBDEVICE_ID_CCD_BN8SP, 0, 0, H(13)}, /* BN8S+ */
+ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
+ PCI_DEVICE_ID_CCD_HFC8S, 0, 0, H(14)}, /* old Eval */
+ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
+ PCI_SUBDEVICE_ID_CCD_IOB8STR, 0, 0, H(15)},
+ /* IOB8ST Recording */
+ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
+ PCI_SUBDEVICE_ID_CCD_IOB8ST, 0, 0, H(16)}, /* IOB8ST */
+ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
+ PCI_SUBDEVICE_ID_CCD_IOB8ST_1, 0, 0, H(17)}, /* IOB8ST */
+ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
+ PCI_SUBDEVICE_ID_CCD_HFC8S, 0, 0, H(18)}, /* 8S */
+ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD,
+ PCI_SUBDEVICE_ID_CCD_OV8S, 0, 0, H(30)}, /* OpenVox 8 */
+
+
+ /* Cards with HFC-E1 Chip */
+ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD,
+ PCI_SUBDEVICE_ID_CCD_BNE1, 0, 0, H(19)}, /* BNE1 */
+ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD,
+ PCI_SUBDEVICE_ID_CCD_BNE1M, 0, 0, H(20)}, /* BNE1 mini PCI */
+ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD,
+ PCI_SUBDEVICE_ID_CCD_BNE1DP, 0, 0, H(21)}, /* BNE1 + (Dual) */
+ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD,
+ PCI_SUBDEVICE_ID_CCD_BNE1D, 0, 0, H(22)}, /* BNE1 (Dual) */
+
+ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD,
+ PCI_DEVICE_ID_CCD_HFCE1, 0, 0, H(23)}, /* Old Eval */
+ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD,
+ PCI_SUBDEVICE_ID_CCD_IOB1E1, 0, 0, H(24)}, /* IOB1E1 */
+ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD,
+ PCI_SUBDEVICE_ID_CCD_HFCE1, 0, 0, H(25)}, /* E1 */
+
+ { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_CCD,
+ PCI_SUBDEVICE_ID_CCD_SPD4S, 0, 0, H(26)}, /* PLX PCI Bridge */
+ { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_CCD,
+ PCI_SUBDEVICE_ID_CCD_SPDE1, 0, 0, H(27)}, /* PLX PCI Bridge */
+ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_ANY_ID, PCI_ANY_ID,
+ 0, 0, 0},
+ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_ANY_ID, PCI_ANY_ID,
+ 0, 0, 0},
+ { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_ANY_ID, PCI_ANY_ID,
+ 0, 0, 0},
+ {0, }
+};
+#undef H
+
+MODULE_DEVICE_TABLE(pci, hfmultipci_ids);
+
+static int
+hfcmulti_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ struct hm_map *m = (struct hm_map *)ent->driver_data;
+ int ret;
+
+ if (m == NULL) {
+ if (ent->vendor == PCI_VENDOR_ID_CCD)
+ if (ent->device == PCI_DEVICE_ID_CCD_HFC4S ||
+ ent->device == PCI_DEVICE_ID_CCD_HFC8S ||
+ ent->device == PCI_DEVICE_ID_CCD_HFCE1)
+ printk(KERN_ERR
+ "unknown HFC multiport controller "
+ "(vendor:%x device:%x subvendor:%x "
+ "subdevice:%x) Please contact the "
+ "driver maintainer for support.\n",
+ ent->vendor, ent->device,
+ ent->subvendor, ent->subdevice);
+ return -ENODEV;
+ }
+ ret = hfcmulti_init(pdev, ent);
+ if (ret)
+ return ret;
+ HFC_cnt++;
+ printk(KERN_INFO "%d devices registered\n", HFC_cnt);
+ return 0;
+}
+
+static struct pci_driver hfcmultipci_driver = {
+ .name = "hfc_multi",
+ .probe = hfcmulti_probe,
+ .remove = __devexit_p(hfc_remove_pci),
+ .id_table = hfmultipci_ids,
+};
+
+static void __exit
+HFCmulti_cleanup(void)
+{
+ struct hfc_multi *card, *next;
+
+ /* unload interrupt function symbol */
+ if (hfc_interrupt)
+ symbol_put(ztdummy_extern_interrupt);
+ if (register_interrupt)
+ symbol_put(ztdummy_register_interrupt);
+ if (unregister_interrupt) {
+ if (interrupt_registered) {
+ interrupt_registered = 0;
+ unregister_interrupt();
+ }
+ symbol_put(ztdummy_unregister_interrupt);
+ }
+
+ list_for_each_entry_safe(card, next, &HFClist, list)
+ release_card(card);
+ /* get rid of all devices of this driver */
+ pci_unregister_driver(&hfcmultipci_driver);
+}
+
+static int __init
+HFCmulti_init(void)
+{
+ int err;
+
+#ifdef IRQ_DEBUG
+ printk(KERN_ERR "%s: IRQ_DEBUG IS ENABLED!\n", __func__);
+#endif
+
+ spin_lock_init(&HFClock);
+ spin_lock_init(&plx_lock);
+
+ if (debug & DEBUG_HFCMULTI_INIT)
+ printk(KERN_DEBUG "%s: init entered\n", __func__);
+
+ hfc_interrupt = symbol_get(ztdummy_extern_interrupt);
+ register_interrupt = symbol_get(ztdummy_register_interrupt);
+ unregister_interrupt = symbol_get(ztdummy_unregister_interrupt);
+ printk(KERN_INFO "mISDN: HFC-multi driver %s\n",
+ hfcmulti_revision);
+
+ switch (poll) {
+ case 0:
+ poll_timer = 6;
+ poll = 128;
+ break;
+ /*
+ * wenn dieses break nochmal verschwindet,
+ * gibt es heisse ohren :-)
+ * "without the break you will get hot ears ???"
+ */
+ 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:
+ poll_timer = 6;
+ break;
+ case 256:
+ poll_timer = 7;
+ break;
+ default:
+ printk(KERN_ERR
+ "%s: Wrong poll value (%d).\n", __func__, poll);
+ err = -EINVAL;
+ return err;
+
+ }
+
+ err = pci_register_driver(&hfcmultipci_driver);
+ if (err < 0) {
+ printk(KERN_ERR "error registering pci driver: %x\n", err);
+ if (hfc_interrupt)
+ symbol_put(ztdummy_extern_interrupt);
+ if (register_interrupt)
+ symbol_put(ztdummy_register_interrupt);
+ if (unregister_interrupt) {
+ if (interrupt_registered) {
+ interrupt_registered = 0;
+ unregister_interrupt();
+ }
+ symbol_put(ztdummy_unregister_interrupt);
+ }
+ return err;
+ }
+ return 0;
+}
+
+
+module_init(HFCmulti_init);
+module_exit(HFCmulti_cleanup);
diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c
new file mode 100644
index 0000000..cd8302a
--- /dev/null
+++ b/drivers/isdn/hardware/mISDN/hfcpci.c
@@ -0,0 +1,2254 @@
+/*
+ *
+ * hfcpci.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 2008 by Karsten Keil <kkeil@novell.com>
+ *
+ * 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 <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/mISDNhw.h>
+
+#include "hfc_pci.h"
+
+static const char *hfcpci_revision = "2.0";
+
+#define MAX_CARDS 8
+static int HFC_cnt;
+static uint debug;
+
+MODULE_AUTHOR("Karsten Keil");
+MODULE_LICENSE("GPL");
+module_param(debug, uint, 0);
+
+static LIST_HEAD(HFClist);
+static DEFINE_RWLOCK(HFClock);
+
+enum {
+ HFC_CCD_2BD0,
+ HFC_CCD_B000,
+ HFC_CCD_B006,
+ HFC_CCD_B007,
+ HFC_CCD_B008,
+ HFC_CCD_B009,
+ HFC_CCD_B00A,
+ HFC_CCD_B00B,
+ HFC_CCD_B00C,
+ HFC_CCD_B100,
+ HFC_CCD_B700,
+ HFC_CCD_B701,
+ HFC_ASUS_0675,
+ HFC_BERKOM_A1T,
+ HFC_BERKOM_TCONCEPT,
+ HFC_ANIGMA_MC145575,
+ HFC_ZOLTRIX_2BD0,
+ HFC_DIGI_DF_M_IOM2_E,
+ HFC_DIGI_DF_M_E,
+ HFC_DIGI_DF_M_IOM2_A,
+ HFC_DIGI_DF_M_A,
+ HFC_ABOCOM_2BD1,
+ HFC_SITECOM_DC105V2,
+};
+
+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 protocol;
+ int nt_timer;
+ unsigned char __iomem *pci_io; /* start of PCI IO memory */
+ dma_addr_t dmahandle;
+ void *fifos; /* FIFO memory */
+ int last_bfifo_cnt[2];
+ /* marker saving last b-fifo frame count */
+ struct timer_list timer;
+};
+
+#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
+
+#define FLG_HFC_TIMER_T1 16
+#define FLG_HFC_TIMER_T3 17
+
+#define NT_T1_COUNT 1120 /* number of 3.125ms interrupts (3.5s) */
+#define NT_T3_COUNT 31 /* number of 3.125ms interrupts (97 ms) */
+#define CLKDEL_TE 0x0e /* CLKDEL in TE mode */
+#define CLKDEL_NT 0x6c /* CLKDEL in NT mode */
+
+
+struct hfc_pci {
+ struct list_head list;
+ u_char subtype;
+ u_char chanlimit;
+ u_char initdone;
+ u_long cfg;
+ u_int irq;
+ u_int irqcnt;
+ struct pci_dev *pdev;
+ struct hfcPCI_hw hw;
+ spinlock_t lock; /* card lock */
+ struct dchannel dch;
+ struct bchannel bch[2];
+};
+
+/* Interface functions */
+static void
+enable_hwirq(struct hfc_pci *hc)
+{
+ hc->hw.int_m2 |= HFCPCI_IRQ_ENABLE;
+ Write_hfc(hc, HFCPCI_INT_M2, hc->hw.int_m2);
+}
+
+static void
+disable_hwirq(struct hfc_pci *hc)
+{
+ hc->hw.int_m2 &= ~((u_char)HFCPCI_IRQ_ENABLE);
+ Write_hfc(hc, HFCPCI_INT_M2, hc->hw.int_m2);
+}
+
+/*
+ * free hardware resources used by driver
+ */
+static void
+release_io_hfcpci(struct hfc_pci *hc)
+{
+ /* disable memory mapped ports + busmaster */
+ pci_write_config_word(hc->pdev, PCI_COMMAND, 0);
+ del_timer(&hc->hw.timer);
+ pci_free_consistent(hc->pdev, 0x8000, hc->hw.fifos, hc->hw.dmahandle);
+ iounmap(hc->hw.pci_io);
+}
+
+/*
+ * set mode (NT or TE)
+ */
+static void
+hfcpci_setmode(struct hfc_pci *hc)
+{
+ if (hc->hw.protocol == ISDN_P_NT_S0) {
+ 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.sctrl &= ~SCTRL_MODE_NT; /* TE-MODE */
+ hc->hw.states = 2; /* F2 */
+ }
+ Write_hfc(hc, HFCPCI_CLKDEL, hc->hw.clkdel);
+ Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | hc->hw.states);
+ udelay(10);
+ Write_hfc(hc, HFCPCI_STATES, hc->hw.states | 0x40); /* Deactivate */
+ Write_hfc(hc, HFCPCI_SCTRL, hc->hw.sctrl);
+}
+
+/*
+ * function called to reset the HFC PCI chip. A complete software reset of chip
+ * and fifos is done.
+ */
+static void
+reset_hfcpci(struct hfc_pci *hc)
+{
+ u_char val;
+ int cnt = 0;
+
+ printk(KERN_DEBUG "reset_hfcpci: entered\n");
+ val = Read_hfc(hc, HFCPCI_CHIP_ID);
+ printk(KERN_INFO "HFC_PCI: resetting HFC ChipId(%x)\n", val);
+ /* enable memory mapped ports, disable busmaster */
+ pci_write_config_word(hc->pdev, PCI_COMMAND, PCI_ENA_MEMIO);
+ disable_hwirq(hc);
+ /* enable memory ports + busmaster */
+ pci_write_config_word(hc->pdev, PCI_COMMAND,
+ PCI_ENA_MEMIO + PCI_ENA_MASTER);
+ 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;
+ Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en);
+ Write_hfc(hc, HFCPCI_TRM, hc->hw.trm);
+ 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));
+
+ /* set NT/TE mode */
+ hfcpci_setmode(hc);
+
+ Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
+ 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(struct hfc_pci *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 struct bchannel *
+Sel_BCS(struct hfc_pci *hc, int channel)
+{
+ if (test_bit(FLG_ACTIVE, &hc->bch[0].Flags) &&
+ (hc->bch[0].nr & channel))
+ return &hc->bch[0];
+ else if (test_bit(FLG_ACTIVE, &hc->bch[1].Flags) &&
+ (hc->bch[1].nr & channel))
+ return &hc->bch[1];
+ else
+ return NULL;
+}
+
+/*
+ * clear the desired B-channel rx fifo
+ */
+static void
+hfcpci_clear_fifo_rx(struct hfc_pci *hc, int fifo)
+{
+ u_char fifo_state;
+ struct bzfifo *bzr;
+
+ if (fifo) {
+ bzr = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b2;
+ fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B2RX;
+ } else {
+ bzr = &((union 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 = cpu_to_le16(B_FIFO_SIZE + B_SUB_VAL - 1);
+ bzr->za[MAX_B_FRAMES].z2 = cpu_to_le16(
+ le16_to_cpu(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(struct hfc_pci *hc, int fifo)
+{
+ u_char fifo_state;
+ struct bzfifo *bzt;
+
+ if (fifo) {
+ bzt = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b2;
+ fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B2TX;
+ } else {
+ bzt = &((union 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 & DEBUG_HW_BCHANNEL)
+ printk(KERN_DEBUG "hfcpci_clear_fifo_tx%d f1(%x) f2(%x) "
+ "z1(%x) z2(%x) state(%x)\n",
+ fifo, bzt->f1, bzt->f2,
+ le16_to_cpu(bzt->za[MAX_B_FRAMES].z1),
+ le16_to_cpu(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 = cpu_to_le16(B_FIFO_SIZE + B_SUB_VAL - 1);
+ bzt->za[MAX_B_FRAMES].z2 = cpu_to_le16(B_FIFO_SIZE + B_SUB_VAL - 2);
+ if (fifo_state)
+ hc->hw.fifo_en |= fifo_state;
+ Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en);
+ if (hc->bch[fifo].debug & DEBUG_HW_BCHANNEL)
+ printk(KERN_DEBUG
+ "hfcpci_clear_fifo_tx%d f1(%x) f2(%x) z1(%x) z2(%x)\n",
+ fifo, bzt->f1, bzt->f2,
+ le16_to_cpu(bzt->za[MAX_B_FRAMES].z1),
+ le16_to_cpu(bzt->za[MAX_B_FRAMES].z2));
+}
+
+/*
+ * read a complete B-frame out of the buffer
+ */
+static void
+hfcpci_empty_bfifo(struct bchannel *bch, struct bzfifo *bz,
+ u_char *bdata, int count)
+{
+ u_char *ptr, *ptr1, new_f2;
+ int total, maxlen, new_z2;
+ struct zt *zp;
+
+ if ((bch->debug & DEBUG_HW_BCHANNEL) && !(bch->debug & DEBUG_HW_BFIFO))
+ printk(KERN_DEBUG "hfcpci_empty_fifo\n");
+ zp = &bz->za[bz->f2]; /* point to Z-Regs */
+ new_z2 = le16_to_cpu(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 + (le16_to_cpu(zp->z1) - B_SUB_VAL)))) {
+ if (bch->debug & DEBUG_HW)
+ printk(KERN_DEBUG "hfcpci_empty_fifo: incoming packet "
+ "invalid length %d or crc\n", count);
+#ifdef ERROR_STATISTIC
+ bch->err_inv++;
+#endif
+ bz->za[new_f2].z2 = cpu_to_le16(new_z2);
+ bz->f2 = new_f2; /* next buffer */
+ } else {
+ bch->rx_skb = mI_alloc_skb(count - 3, GFP_ATOMIC);
+ if (!bch->rx_skb) {
+ printk(KERN_WARNING "HFCPCI: receive out of memory\n");
+ return;
+ }
+ total = count;
+ count -= 3;
+ ptr = skb_put(bch->rx_skb, count);
+
+ if (le16_to_cpu(zp->z2) + count <= B_FIFO_SIZE + B_SUB_VAL)
+ maxlen = count; /* complete transfer */
+ else
+ maxlen = B_FIFO_SIZE + B_SUB_VAL -
+ le16_to_cpu(zp->z2); /* maximum */
+
+ ptr1 = bdata + (le16_to_cpu(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 = cpu_to_le16(new_z2);
+ bz->f2 = new_f2; /* next buffer */
+ recv_Bchannel(bch);
+ }
+}
+
+/*
+ * D-channel receive procedure
+ */
+static int
+receive_dmsg(struct hfc_pci *hc)
+{
+ struct dchannel *dch = &hc->dch;
+ int maxlen;
+ int rcnt, total;
+ int count = 5;
+ u_char *ptr, *ptr1;
+ struct dfifo *df;
+ struct zt *zp;
+
+ df = &((union 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 = le16_to_cpu(zp->z1) - le16_to_cpu(zp->z2);
+ if (rcnt < 0)
+ rcnt += D_FIFO_SIZE;
+ rcnt++;
+ if (dch->debug & DEBUG_HW_DCHANNEL)
+ printk(KERN_DEBUG
+ "hfcpci recd f1(%d) f2(%d) z1(%x) z2(%x) cnt(%d)\n",
+ df->f1, df->f2,
+ le16_to_cpu(zp->z1),
+ le16_to_cpu(zp->z2),
+ rcnt);
+
+ if ((rcnt > MAX_DFRAME_LEN + 3) || (rcnt < 4) ||
+ (df->data[le16_to_cpu(zp->z1)])) {
+ if (dch->debug & DEBUG_HW)
+ printk(KERN_DEBUG
+ "empty_fifo hfcpci paket inv. len "
+ "%d or crc %d\n",
+ rcnt,
+ df->data[le16_to_cpu(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 =
+ cpu_to_le16((le16_to_cpu(zp->z2) + rcnt) & (D_FIFO_SIZE - 1));
+ } else {
+ dch->rx_skb = mI_alloc_skb(rcnt - 3, GFP_ATOMIC);
+ if (!dch->rx_skb) {
+ printk(KERN_WARNING
+ "HFC-PCI: D receive out of memory\n");
+ break;
+ }
+ total = rcnt;
+ rcnt -= 3;
+ ptr = skb_put(dch->rx_skb, rcnt);
+
+ if (le16_to_cpu(zp->z2) + rcnt <= D_FIFO_SIZE)
+ maxlen = rcnt; /* complete transfer */
+ else
+ maxlen = D_FIFO_SIZE - le16_to_cpu(zp->z2);
+ /* maximum */
+
+ ptr1 = df->data + le16_to_cpu(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 = cpu_to_le16((
+ le16_to_cpu(zp->z2) + total) & (D_FIFO_SIZE - 1));
+ recv_Dchannel(dch);
+ }
+ }
+ return 1;
+}
+
+/*
+ * check for transparent receive data and read max one threshold size if avail
+ */
+static int
+hfcpci_empty_fifo_trans(struct bchannel *bch, struct bzfifo *bz, u_char *bdata)
+{
+ __le16 *z1r, *z2r;
+ int new_z2, fcnt, maxlen;
+ u_char *ptr, *ptr1;
+
+ z1r = &bz->za[MAX_B_FRAMES].z1; /* pointer to z reg */
+ z2r = z1r + 1;
+
+ fcnt = le16_to_cpu(*z1r) - le16_to_cpu(*z2r);
+ if (!fcnt)
+ 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 = le16_to_cpu(*z2r) + fcnt; /* new position in fifo */
+ if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL))
+ new_z2 -= B_FIFO_SIZE; /* buffer wrap */
+
+ bch->rx_skb = mI_alloc_skb(fcnt, GFP_ATOMIC);
+ if (bch->rx_skb) {
+ ptr = skb_put(bch->rx_skb, fcnt);
+ if (le16_to_cpu(*z2r) + fcnt <= B_FIFO_SIZE + B_SUB_VAL)
+ maxlen = fcnt; /* complete transfer */
+ else
+ maxlen = B_FIFO_SIZE + B_SUB_VAL - le16_to_cpu(*z2r);
+ /* maximum */
+
+ ptr1 = bdata + (le16_to_cpu(*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 */
+ }
+ recv_Bchannel(bch);
+ } else
+ printk(KERN_WARNING "HFCPCI: receive out of memory\n");
+
+ *z2r = cpu_to_le16(new_z2); /* new position */
+ return 1;
+}
+
+/*
+ * B-channel main receive routine
+ */
+static void
+main_rec_hfcpci(struct bchannel *bch)
+{
+ struct hfc_pci *hc = bch->hw;
+ int rcnt, real_fifo;
+ int receive, count = 5;
+ struct bzfifo *bz;
+ u_char *bdata;
+ struct zt *zp;
+
+
+ if ((bch->nr & 2) && (!hc->hw.bswapped)) {
+ bz = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b2;
+ bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.rxdat_b2;
+ real_fifo = 1;
+ } else {
+ bz = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b1;
+ bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.rxdat_b1;
+ real_fifo = 0;
+ }
+Begin:
+ count--;
+ if (bz->f1 != bz->f2) {
+ if (bch->debug & DEBUG_HW_BCHANNEL)
+ printk(KERN_DEBUG "hfcpci rec ch(%x) f1(%d) f2(%d)\n",
+ bch->nr, bz->f1, bz->f2);
+ zp = &bz->za[bz->f2];
+
+ rcnt = le16_to_cpu(zp->z1) - le16_to_cpu(zp->z2);
+ if (rcnt < 0)
+ rcnt += B_FIFO_SIZE;
+ rcnt++;
+ if (bch->debug & DEBUG_HW_BCHANNEL)
+ printk(KERN_DEBUG
+ "hfcpci rec ch(%x) z1(%x) z2(%x) cnt(%d)\n",
+ bch->nr, le16_to_cpu(zp->z1),
+ le16_to_cpu(zp->z2), rcnt);
+ hfcpci_empty_bfifo(bch, bz, bdata, rcnt);
+ 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 (test_bit(FLG_TRANSPARENT, &bch->Flags))
+ receive = hfcpci_empty_fifo_trans(bch, bz, bdata);
+ else
+ receive = 0;
+ if (count && receive)
+ goto Begin;
+
+}
+
+/*
+ * D-channel send routine
+ */
+static void
+hfcpci_fill_dfifo(struct hfc_pci *hc)
+{
+ struct dchannel *dch = &hc->dch;
+ int fcnt;
+ int count, new_z1, maxlen;
+ struct dfifo *df;
+ u_char *src, *dst, new_f1;
+
+ if ((dch->debug & DEBUG_HW_DCHANNEL) && !(dch->debug & DEBUG_HW_DFIFO))
+ printk(KERN_DEBUG "%s\n", __func__);
+
+ if (!dch->tx_skb)
+ return;
+ count = dch->tx_skb->len - dch->tx_idx;
+ if (count <= 0)
+ return;
+ df = &((union fifo_area *) (hc->hw.fifos))->d_chan.d_tx;
+
+ if (dch->debug & DEBUG_HW_DFIFO)
+ printk(KERN_DEBUG "%s:f1(%d) f2(%d) z1(f1)(%x)\n", __func__,
+ df->f1, df->f2,
+ le16_to_cpu(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 & DEBUG_HW_DCHANNEL)
+ printk(KERN_DEBUG
+ "hfcpci_fill_Dfifo more as 14 frames\n");
+#ifdef ERROR_STATISTIC
+ cs->err_tx++;
+#endif
+ return;
+ }
+ /* now determine free bytes in FIFO buffer */
+ maxlen = le16_to_cpu(df->za[df->f2 & D_FREG_MASK].z2) -
+ le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1) - 1;
+ if (maxlen <= 0)
+ maxlen += D_FIFO_SIZE; /* count now contains available bytes */
+
+ if (dch->debug & DEBUG_HW_DCHANNEL)
+ printk(KERN_DEBUG "hfcpci_fill_Dfifo count(%d/%d)\n",
+ count, maxlen);
+ if (count > maxlen) {
+ if (dch->debug & DEBUG_HW_DCHANNEL)
+ printk(KERN_DEBUG "hfcpci_fill_Dfifo no fifo mem\n");
+ return;
+ }
+ new_z1 = (le16_to_cpu(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_skb->data + dch->tx_idx; /* source pointer */
+ dst = df->data + le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1);
+ maxlen = D_FIFO_SIZE - le16_to_cpu(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 = cpu_to_le16(new_z1);
+ /* for next buffer */
+ df->za[df->f1 & D_FREG_MASK].z1 = cpu_to_le16(new_z1);
+ /* new pos actual buffer */
+ df->f1 = new_f1; /* next frame */
+ dch->tx_idx = dch->tx_skb->len;
+}
+
+/*
+ * B-channel send routine
+ */
+static void
+hfcpci_fill_fifo(struct bchannel *bch)
+{
+ struct hfc_pci *hc = bch->hw;
+ int maxlen, fcnt;
+ int count, new_z1;
+ struct bzfifo *bz;
+ u_char *bdata;
+ u_char new_f1, *src, *dst;
+ __le16 *z1t, *z2t;
+
+ if ((bch->debug & DEBUG_HW_BCHANNEL) && !(bch->debug & DEBUG_HW_BFIFO))
+ printk(KERN_DEBUG "%s\n", __func__);
+ if ((!bch->tx_skb) || bch->tx_skb->len <= 0)
+ return;
+ count = bch->tx_skb->len - bch->tx_idx;
+ if ((bch->nr & 2) && (!hc->hw.bswapped)) {
+ bz = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b2;
+ bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.txdat_b2;
+ } else {
+ bz = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b1;
+ bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.txdat_b1;
+ }
+
+ if (test_bit(FLG_TRANSPARENT, &bch->Flags)) {
+ z1t = &bz->za[MAX_B_FRAMES].z1;
+ z2t = z1t + 1;
+ if (bch->debug & DEBUG_HW_BCHANNEL)
+ printk(KERN_DEBUG "hfcpci_fill_fifo_trans ch(%x) "
+ "cnt(%d) z1(%x) z2(%x)\n", bch->nr, count,
+ le16_to_cpu(*z1t), le16_to_cpu(*z2t));
+ fcnt = le16_to_cpu(*z2t) - le16_to_cpu(*z1t);
+ if (fcnt <= 0)
+ fcnt += B_FIFO_SIZE;
+ /* fcnt contains available bytes in fifo */
+ fcnt = B_FIFO_SIZE - fcnt;
+ /* remaining bytes to send (bytes in fifo) */
+next_t_frame:
+ count = bch->tx_skb->len - bch->tx_idx;
+ /* maximum fill shall be HFCPCI_BTRANS_MAX */
+ if (count > HFCPCI_BTRANS_MAX - fcnt)
+ count = HFCPCI_BTRANS_MAX - fcnt;
+ if (count <= 0)
+ return;
+ /* data is suitable for fifo */
+ new_z1 = le16_to_cpu(*z1t) + count;
+ /* new buffer Position */
+ if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL))
+ new_z1 -= B_FIFO_SIZE; /* buffer wrap */
+ src = bch->tx_skb->data + bch->tx_idx;
+ /* source pointer */
+ dst = bdata + (le16_to_cpu(*z1t) - B_SUB_VAL);
+ maxlen = (B_FIFO_SIZE + B_SUB_VAL) - le16_to_cpu(*z1t);
+ /* end of fifo */
+ if (bch->debug & DEBUG_HW_BFIFO)
+ printk(KERN_DEBUG "hfcpci_FFt fcnt(%d) "
+ "maxl(%d) nz1(%x) dst(%p)\n",
+ fcnt, maxlen, new_z1, dst);
+ fcnt += count;
+ bch->tx_idx += count;
+ 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);
+ }
+ *z1t = cpu_to_le16(new_z1); /* now send data */
+ if (bch->tx_idx < bch->tx_skb->len)
+ return;
+ /* send confirm, on trans, free on hdlc. */
+ if (test_bit(FLG_TRANSPARENT, &bch->Flags))
+ confirm_Bsend(bch);
+ dev_kfree_skb(bch->tx_skb);
+ if (get_next_bframe(bch))
+ goto next_t_frame;
+ return;
+ }
+ if (bch->debug & DEBUG_HW_BCHANNEL)
+ printk(KERN_DEBUG
+ "%s: ch(%x) f1(%d) f2(%d) z1(f1)(%x)\n",
+ __func__, bch->nr, 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 & DEBUG_HW_BCHANNEL)
+ printk(KERN_DEBUG
+ "hfcpci_fill_Bfifo more as 14 frames\n");
+ return;
+ }
+ /* now determine free bytes in FIFO buffer */
+ maxlen = le16_to_cpu(bz->za[bz->f2].z2) -
+ le16_to_cpu(bz->za[bz->f1].z1) - 1;
+ if (maxlen <= 0)
+ maxlen += B_FIFO_SIZE; /* count now contains available bytes */
+
+ if (bch->debug & DEBUG_HW_BCHANNEL)
+ printk(KERN_DEBUG "hfcpci_fill_fifo ch(%x) count(%d/%d)\n",
+ bch->nr, count, maxlen);
+
+ if (maxlen < count) {
+ if (bch->debug & DEBUG_HW_BCHANNEL)
+ printk(KERN_DEBUG "hfcpci_fill_fifo no fifo mem\n");
+ return;
+ }
+ new_z1 = le16_to_cpu(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_skb->data + bch->tx_idx; /* source pointer */
+ dst = bdata + (le16_to_cpu(bz->za[bz->f1].z1) - B_SUB_VAL);
+ maxlen = (B_FIFO_SIZE + B_SUB_VAL) - le16_to_cpu(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 = cpu_to_le16(new_z1); /* for next buffer */
+ bz->f1 = new_f1; /* next frame */
+ dev_kfree_skb(bch->tx_skb);
+ get_next_bframe(bch);
+}
+
+
+
+/*
+ * handle L1 state changes TE
+ */
+
+static void
+ph_state_te(struct dchannel *dch)
+{
+ if (dch->debug)
+ printk(KERN_DEBUG "%s: TE newstate %x\n",
+ __func__, dch->state);
+ switch (dch->state) {
+ case 0:
+ l1_event(dch->l1, HW_RESET_IND);
+ break;
+ case 3:
+ l1_event(dch->l1, HW_DEACT_IND);
+ break;
+ case 5:
+ case 8:
+ l1_event(dch->l1, ANYSIGNAL);
+ break;
+ case 6:
+ l1_event(dch->l1, INFO2);
+ break;
+ case 7:
+ l1_event(dch->l1, INFO4_P8);
+ break;
+ }
+}
+
+/*
+ * handle L1 state changes NT
+ */
+
+static void
+handle_nt_timer3(struct dchannel *dch) {
+ struct hfc_pci *hc = dch->hw;
+
+ test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags);
+ hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER;
+ Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+ hc->hw.nt_timer = 0;
+ test_and_set_bit(FLG_ACTIVE, &dch->Flags);
+ if (test_bit(HFC_CFG_MASTER, &hc->cfg))
+ hc->hw.mst_m |= HFCPCI_MASTER;
+ Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
+ _queue_data(&dch->dev.D, PH_ACTIVATE_IND,
+ MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
+}
+
+static void
+ph_state_nt(struct dchannel *dch)
+{
+ struct hfc_pci *hc = dch->hw;
+
+ if (dch->debug)
+ printk(KERN_DEBUG "%s: NT newstate %x\n",
+ __func__, dch->state);
+ switch (dch->state) {
+ case 2:
+ if (hc->hw.nt_timer < 0) {
+ hc->hw.nt_timer = 0;
+ test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags);
+ test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags);
+ 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->state = 4;
+ } else if (hc->hw.nt_timer == 0) {
+ hc->hw.int_m1 |= HFCPCI_INTS_TIMER;
+ Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+ hc->hw.nt_timer = NT_T1_COUNT;
+ hc->hw.ctmt &= ~HFCPCI_AUTO_TIMER;
+ hc->hw.ctmt |= HFCPCI_TIM3_125;
+ Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt |
+ HFCPCI_CLTIMER);
+ test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags);
+ test_and_set_bit(FLG_HFC_TIMER_T1, &dch->Flags);
+ /* allow G2 -> G3 transition */
+ Write_hfc(hc, HFCPCI_STATES, 2 | HFCPCI_NT_G2_G3);
+ } else {
+ Write_hfc(hc, HFCPCI_STATES, 2 | HFCPCI_NT_G2_G3);
+ }
+ break;
+ case 1:
+ hc->hw.nt_timer = 0;
+ test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags);
+ test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags);
+ hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER;
+ Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+ test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
+ hc->hw.mst_m &= ~HFCPCI_MASTER;
+ Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
+ test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags);
+ _queue_data(&dch->dev.D, PH_DEACTIVATE_IND,
+ MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
+ break;
+ case 4:
+ hc->hw.nt_timer = 0;
+ test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags);
+ test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags);
+ hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER;
+ Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+ break;
+ case 3:
+ if (!test_and_set_bit(FLG_HFC_TIMER_T3, &dch->Flags)) {
+ if (!test_and_clear_bit(FLG_L2_ACTIVATED,
+ &dch->Flags)) {
+ handle_nt_timer3(dch);
+ break;
+ }
+ test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags);
+ hc->hw.int_m1 |= HFCPCI_INTS_TIMER;
+ Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1);
+ hc->hw.nt_timer = NT_T3_COUNT;
+ hc->hw.ctmt &= ~HFCPCI_AUTO_TIMER;
+ hc->hw.ctmt |= HFCPCI_TIM3_125;
+ Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt |
+ HFCPCI_CLTIMER);
+ }
+ break;
+ }
+}
+
+static void
+ph_state(struct dchannel *dch)
+{
+ struct hfc_pci *hc = dch->hw;
+
+ if (hc->hw.protocol == ISDN_P_NT_S0) {
+ if (test_bit(FLG_HFC_TIMER_T3, &dch->Flags) &&
+ hc->hw.nt_timer < 0)
+ handle_nt_timer3(dch);
+ else
+ ph_state_nt(dch);
+ } else
+ ph_state_te(dch);
+}
+
+/*
+ * Layer 1 callback function
+ */
+static int
+hfc_l1callback(struct dchannel *dch, u_int cmd)
+{
+ struct hfc_pci *hc = dch->hw;
+
+ switch (cmd) {
+ case INFO3_P8:
+ case 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);
+ break;
+ case HW_RESET_REQ:
+ 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_event(dch->l1, HW_POWERUP_IND);
+ break;
+ case HW_DEACT_REQ:
+ hc->hw.mst_m &= ~HFCPCI_MASTER;
+ Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
+ skb_queue_purge(&dch->squeue);
+ if (dch->tx_skb) {
+ dev_kfree_skb(dch->tx_skb);
+ dch->tx_skb = NULL;
+ }
+ dch->tx_idx = 0;
+ if (dch->rx_skb) {
+ dev_kfree_skb(dch->rx_skb);
+ dch->rx_skb = NULL;
+ }
+ test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
+ if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
+ del_timer(&dch->timer);
+ break;
+ case HW_POWERUP_REQ:
+ Write_hfc(hc, HFCPCI_STATES, HFCPCI_DO_ACTION);
+ break;
+ case PH_ACTIVATE_IND:
+ test_and_set_bit(FLG_ACTIVE, &dch->Flags);
+ _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
+ GFP_ATOMIC);
+ break;
+ case PH_DEACTIVATE_IND:
+ test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
+ _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
+ GFP_ATOMIC);
+ break;
+ default:
+ if (dch->debug & DEBUG_HW)
+ printk(KERN_DEBUG "%s: unknown command %x\n",
+ __func__, cmd);
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Interrupt handler
+ */
+static inline void
+tx_birq(struct bchannel *bch)
+{
+ if (bch->tx_skb && bch->tx_idx < bch->tx_skb->len)
+ hfcpci_fill_fifo(bch);
+ else {
+ if (bch->tx_skb)
+ dev_kfree_skb(bch->tx_skb);
+ if (get_next_bframe(bch))
+ hfcpci_fill_fifo(bch);
+ }
+}
+
+static inline void
+tx_dirq(struct dchannel *dch)
+{
+ if (dch->tx_skb && dch->tx_idx < dch->tx_skb->len)
+ hfcpci_fill_dfifo(dch->hw);
+ else {
+ if (dch->tx_skb)
+ dev_kfree_skb(dch->tx_skb);
+ if (get_next_dframe(dch))
+ hfcpci_fill_dfifo(dch->hw);
+ }
+}
+
+static irqreturn_t
+hfcpci_int(int intno, void *dev_id)
+{
+ struct hfc_pci *hc = dev_id;
+ u_char exval;
+ struct bchannel *bch;
+ u_char val, stat;
+
+ spin_lock(&hc->lock);
+ if (!(hc->hw.int_m2 & 0x08)) {
+ spin_unlock(&hc->lock);
+ return IRQ_NONE; /* not initialised */
+ }
+ stat = Read_hfc(hc, HFCPCI_STATUS);
+ if (HFCPCI_ANYINT & stat) {
+ val = Read_hfc(hc, HFCPCI_INT_S1);
+ if (hc->dch.debug & DEBUG_HW_DCHANNEL)
+ printk(KERN_DEBUG
+ "HFC-PCI: stat(%02x) s1(%02x)\n", stat, val);
+ } else {
+ /* shared */
+ spin_unlock(&hc->lock);
+ return IRQ_NONE;
+ }
+ hc->irqcnt++;
+
+ if (hc->dch.debug & DEBUG_HW_DCHANNEL)
+ printk(KERN_DEBUG "HFC-PCI irq %x\n", val);
+ val &= hc->hw.int_m1;
+ if (val & 0x40) { /* state machine irq */
+ exval = Read_hfc(hc, HFCPCI_STATES) & 0xf;
+ if (hc->dch.debug & DEBUG_HW_DCHANNEL)
+ printk(KERN_DEBUG "ph_state chg %d->%d\n",
+ hc->dch.state, exval);
+ hc->dch.state = exval;
+ schedule_event(&hc->dch, FLG_PHCHANGE);
+ val &= ~0x40;
+ }
+ if (val & 0x80) { /* timer irq */
+ if (hc->hw.protocol == ISDN_P_NT_S0) {
+ if ((--hc->hw.nt_timer) < 0)
+ schedule_event(&hc->dch, FLG_PHCHANGE);
+ }
+ val &= ~0x80;
+ Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | HFCPCI_CLTIMER);
+ }
+ if (val & 0x08) {
+ bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1);
+ if (bch)
+ main_rec_hfcpci(bch);
+ else if (hc->dch.debug)
+ printk(KERN_DEBUG "hfcpci spurious 0x08 IRQ\n");
+ }
+ if (val & 0x10) {
+ bch = Sel_BCS(hc, 2);
+ if (bch)
+ main_rec_hfcpci(bch);
+ else if (hc->dch.debug)
+ printk(KERN_DEBUG "hfcpci spurious 0x10 IRQ\n");
+ }
+ if (val & 0x01) {
+ bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1);
+ if (bch)
+ tx_birq(bch);
+ else if (hc->dch.debug)
+ printk(KERN_DEBUG "hfcpci spurious 0x01 IRQ\n");
+ }
+ if (val & 0x02) {
+ bch = Sel_BCS(hc, 2);
+ if (bch)
+ tx_birq(bch);
+ else if (hc->dch.debug)
+ printk(KERN_DEBUG "hfcpci spurious 0x02 IRQ\n");
+ }
+ if (val & 0x20)
+ receive_dmsg(hc);
+ if (val & 0x04) { /* dframe transmitted */
+ if (test_and_clear_bit(FLG_BUSY_TIMER, &hc->dch.Flags))
+ del_timer(&hc->dch.timer);
+ tx_dirq(&hc->dch);
+ }
+ spin_unlock(&hc->lock);
+ return IRQ_HANDLED;
+}
+
+/*
+ * timer callback for D-chan busy resolution. Currently no function
+ */
+static void
+hfcpci_dbusy_timer(struct hfc_pci *hc)
+{
+}
+
+/*
+ * activate/deactivate hardware for selected channels and mode
+ */
+static int
+mode_hfcpci(struct bchannel *bch, int bc, int protocol)
+{
+ struct hfc_pci *hc = bch->hw;
+ int fifo2;
+ u_char rx_slot = 0, tx_slot = 0, pcm_mode;
+
+ if (bch->debug & DEBUG_HW_BCHANNEL)
+ printk(KERN_DEBUG
+ "HFCPCI bchannel protocol %x-->%x ch %x-->%x\n",
+ bch->state, protocol, bch->nr, 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",
+ __func__);
+ rx_slot = (bc>>8) & 0xff;
+ tx_slot = (bc>>16) & 0xff;
+ bc = bc & 0xff;
+ } else if (test_bit(HFC_CFG_PCM, &hc->cfg) &&
+ (protocol > ISDN_P_NONE))
+ printk(KERN_WARNING "%s: no pcm channel id but HFC_CFG_PCM\n",
+ __func__);
+ 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_P_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->state = -1;
+ bch->nr = bc;
+ case (ISDN_P_NONE):
+ if (bch->state == ISDN_P_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->nr & 2)
+ hc->hw.cirm &= 0x7f;
+ else
+ hc->hw.cirm &= 0xbf;
+#endif
+ bch->state = ISDN_P_NONE;
+ bch->nr = bc;
+ test_and_clear_bit(FLG_HDLC, &bch->Flags);
+ test_and_clear_bit(FLG_TRANSPARENT, &bch->Flags);
+ break;
+ case (ISDN_P_B_RAW):
+ bch->state = protocol;
+ bch->nr = 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;
+ }
+ test_and_set_bit(FLG_TRANSPARENT, &bch->Flags);
+ break;
+ case (ISDN_P_B_HDLC):
+ bch->state = protocol;
+ bch->nr = 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;
+ }
+ test_and_set_bit(FLG_HDLC, &bch->Flags);
+ break;
+ default:
+ printk(KERN_DEBUG "prot not known %x\n", protocol);
+ return -ENOPROTOOPT;
+ }
+ if (test_bit(HFC_CFG_PCM, &hc->cfg)) {
+ if ((protocol == ISDN_P_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",
+ __func__, tx_slot);
+ printk(KERN_DEBUG "%s: Write_hfc: B2_RSL 0x%x\n",
+ __func__, 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",
+ __func__, tx_slot);
+ printk(KERN_DEBUG "%s: Write_hfc: B1_RSL 0x%x\n",
+ __func__, 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
+ return 0;
+}
+
+static int
+set_hfcpci_rxtest(struct bchannel *bch, int protocol, int chan)
+{
+ struct hfc_pci *hc = bch->hw;
+
+ if (bch->debug & DEBUG_HW_BCHANNEL)
+ printk(KERN_DEBUG
+ "HFCPCI bchannel test rx protocol %x-->%x ch %x-->%x\n",
+ bch->state, protocol, bch->nr, chan);
+ if (bch->nr != chan) {
+ printk(KERN_DEBUG
+ "HFCPCI rxtest wrong channel parameter %x/%x\n",
+ bch->nr, chan);
+ return -EINVAL;
+ }
+ switch (protocol) {
+ case (ISDN_P_B_RAW):
+ bch->state = 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_P_B_HDLC):
+ bch->state = 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:
+ printk(KERN_DEBUG "prot not known %x\n", 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;
+}
+
+static void
+deactivate_bchannel(struct bchannel *bch)
+{
+ struct hfc_pci *hc = bch->hw;
+ u_long flags;
+
+ spin_lock_irqsave(&hc->lock, flags);
+ if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) {
+ dev_kfree_skb(bch->next_skb);
+ bch->next_skb = NULL;
+ }
+ if (bch->tx_skb) {
+ dev_kfree_skb(bch->tx_skb);
+ bch->tx_skb = NULL;
+ }
+ bch->tx_idx = 0;
+ if (bch->rx_skb) {
+ dev_kfree_skb(bch->rx_skb);
+ bch->rx_skb = NULL;
+ }
+ mode_hfcpci(bch, bch->nr, ISDN_P_NONE);
+ test_and_clear_bit(FLG_ACTIVE, &bch->Flags);
+ test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
+ spin_unlock_irqrestore(&hc->lock, flags);
+}
+
+/*
+ * Layer 1 B-channel hardware access
+ */
+static int
+channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
+{
+ int ret = 0;
+
+ switch (cq->op) {
+ case MISDN_CTRL_GETOP:
+ cq->op = 0;
+ break;
+ default:
+ printk(KERN_WARNING "%s: unknown Op %x\n", __func__, cq->op);
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+static int
+hfc_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+ struct bchannel *bch = container_of(ch, struct bchannel, ch);
+ struct hfc_pci *hc = bch->hw;
+ int ret = -EINVAL;
+ u_long flags;
+
+ if (bch->debug & DEBUG_HW)
+ printk(KERN_DEBUG "%s: cmd:%x %p\n", __func__, cmd, arg);
+ switch (cmd) {
+ case HW_TESTRX_RAW:
+ spin_lock_irqsave(&hc->lock, flags);
+ ret = set_hfcpci_rxtest(bch, ISDN_P_B_RAW, (int)(long)arg);
+ spin_unlock_irqrestore(&hc->lock, flags);
+ break;
+ case HW_TESTRX_HDLC:
+ spin_lock_irqsave(&hc->lock, flags);
+ ret = set_hfcpci_rxtest(bch, ISDN_P_B_HDLC, (int)(long)arg);
+ spin_unlock_irqrestore(&hc->lock, flags);
+ break;
+ case HW_TESTRX_OFF:
+ spin_lock_irqsave(&hc->lock, flags);
+ mode_hfcpci(bch, bch->nr, ISDN_P_NONE);
+ spin_unlock_irqrestore(&hc->lock, flags);
+ ret = 0;
+ break;
+ case CLOSE_CHANNEL:
+ test_and_clear_bit(FLG_OPEN, &bch->Flags);
+ if (test_bit(FLG_ACTIVE, &bch->Flags))
+ deactivate_bchannel(bch);
+ ch->protocol = ISDN_P_NONE;
+ ch->peer = NULL;
+ module_put(THIS_MODULE);
+ ret = 0;
+ break;
+ case CONTROL_CHANNEL:
+ ret = channel_bctrl(bch, arg);
+ break;
+ default:
+ printk(KERN_WARNING "%s: unknown prim(%x)\n",
+ __func__, cmd);
+ }
+ return ret;
+}
+
+/*
+ * Layer2 -> Layer 1 Dchannel data
+ */
+static int
+hfcpci_l2l1D(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+ struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D);
+ struct dchannel *dch = container_of(dev, struct dchannel, dev);
+ struct hfc_pci *hc = dch->hw;
+ int ret = -EINVAL;
+ struct mISDNhead *hh = mISDN_HEAD_P(skb);
+ unsigned int id;
+ u_long flags;
+
+ switch (hh->prim) {
+ case PH_DATA_REQ:
+ spin_lock_irqsave(&hc->lock, flags);
+ ret = dchannel_senddata(dch, skb);
+ if (ret > 0) { /* direct TX */
+ id = hh->id; /* skb can be freed */
+ hfcpci_fill_dfifo(dch->hw);
+ ret = 0;
+ spin_unlock_irqrestore(&hc->lock, flags);
+ queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+ } else
+ spin_unlock_irqrestore(&hc->lock, flags);
+ return ret;
+ case PH_ACTIVATE_REQ:
+ spin_lock_irqsave(&hc->lock, flags);
+ if (hc->hw.protocol == ISDN_P_NT_S0) {
+ ret = 0;
+ if (test_bit(HFC_CFG_MASTER, &hc->cfg))
+ hc->hw.mst_m |= HFCPCI_MASTER;
+ Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
+ if (test_bit(FLG_ACTIVE, &dch->Flags)) {
+ spin_unlock_irqrestore(&hc->lock, flags);
+ _queue_data(&dch->dev.D, PH_ACTIVATE_IND,
+ MISDN_ID_ANY, 0, NULL, GFP_ATOMIC);
+ break;
+ }
+ test_and_set_bit(FLG_L2_ACTIVATED, &dch->Flags);
+ Write_hfc(hc, HFCPCI_STATES, HFCPCI_ACTIVATE |
+ HFCPCI_DO_ACTION | 1);
+ } else
+ ret = l1_event(dch->l1, hh->prim);
+ spin_unlock_irqrestore(&hc->lock, flags);
+ break;
+ case PH_DEACTIVATE_REQ:
+ test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags);
+ spin_lock_irqsave(&hc->lock, flags);
+ if (hc->hw.protocol == ISDN_P_NT_S0) {
+ /* prepare deactivation */
+ Write_hfc(hc, HFCPCI_STATES, 0x40);
+ skb_queue_purge(&dch->squeue);
+ if (dch->tx_skb) {
+ dev_kfree_skb(dch->tx_skb);
+ dch->tx_skb = NULL;
+ }
+ dch->tx_idx = 0;
+ if (dch->rx_skb) {
+ dev_kfree_skb(dch->rx_skb);
+ dch->rx_skb = NULL;
+ }
+ test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
+ if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
+ del_timer(&dch->timer);
+#ifdef FIXME
+ if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags))
+ dchannel_sched_event(&hc->dch, D_CLEARBUSY);
+#endif
+ hc->hw.mst_m &= ~HFCPCI_MASTER;
+ Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m);
+ ret = 0;
+ } else {
+ ret = l1_event(dch->l1, hh->prim);
+ }
+ spin_unlock_irqrestore(&hc->lock, flags);
+ break;
+ }
+ if (!ret)
+ dev_kfree_skb(skb);
+ return ret;
+}
+
+/*
+ * Layer2 -> Layer 1 Bchannel data
+ */
+static int
+hfcpci_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+ struct bchannel *bch = container_of(ch, struct bchannel, ch);
+ struct hfc_pci *hc = bch->hw;
+ int ret = -EINVAL;
+ struct mISDNhead *hh = mISDN_HEAD_P(skb);
+ unsigned int id;
+ u_long flags;
+
+ switch (hh->prim) {
+ case PH_DATA_REQ:
+ spin_lock_irqsave(&hc->lock, flags);
+ ret = bchannel_senddata(bch, skb);
+ if (ret > 0) { /* direct TX */
+ id = hh->id; /* skb can be freed */
+ hfcpci_fill_fifo(bch);
+ ret = 0;
+ spin_unlock_irqrestore(&hc->lock, flags);
+ if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
+ queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+ } else
+ spin_unlock_irqrestore(&hc->lock, flags);
+ return ret;
+ case PH_ACTIVATE_REQ:
+ spin_lock_irqsave(&hc->lock, flags);
+ if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags))
+ ret = mode_hfcpci(bch, bch->nr, ch->protocol);
+ else
+ ret = 0;
+ spin_unlock_irqrestore(&hc->lock, flags);
+ if (!ret)
+ _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0,
+ NULL, GFP_KERNEL);
+ break;
+ case PH_DEACTIVATE_REQ:
+ deactivate_bchannel(bch);
+ _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0,
+ NULL, GFP_KERNEL);
+ ret = 0;
+ break;
+ }
+ if (!ret)
+ dev_kfree_skb(skb);
+ return ret;
+}
+
+/*
+ * called for card init message
+ */
+
+static void
+inithfcpci(struct hfc_pci *hc)
+{
+ printk(KERN_DEBUG "inithfcpci: entered\n");
+ hc->dch.timer.function = (void *) hfcpci_dbusy_timer;
+ hc->dch.timer.data = (long) &hc->dch;
+ init_timer(&hc->dch.timer);
+ hc->chanlimit = 2;
+ mode_hfcpci(&hc->bch[0], 1, -1);
+ mode_hfcpci(&hc->bch[1], 2, -1);
+}
+
+
+static int
+init_card(struct hfc_pci *hc)
+{
+ int cnt = 3;
+ u_long flags;
+
+ printk(KERN_DEBUG "init_card: entered\n");
+
+
+ spin_lock_irqsave(&hc->lock, flags);
+ disable_hwirq(hc);
+ spin_unlock_irqrestore(&hc->lock, flags);
+ if (request_irq(hc->irq, hfcpci_int, IRQF_SHARED, "HFC PCI", hc)) {
+ printk(KERN_WARNING
+ "mISDN: couldn't get interrupt %d\n", hc->irq);
+ return -EIO;
+ }
+ spin_lock_irqsave(&hc->lock, flags);
+ reset_hfcpci(hc);
+ while (cnt) {
+ inithfcpci(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
+ */
+ enable_hwirq(hc);
+ spin_unlock_irqrestore(&hc->lock, flags);
+ /* 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 */
+ spin_lock_irqsave(&hc->lock, flags);
+ 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) {
+ spin_unlock_irqrestore(&hc->lock, flags);
+ return -EIO;
+ } else {
+ reset_hfcpci(hc);
+ cnt--;
+ }
+ } else {
+ spin_unlock_irqrestore(&hc->lock, flags);
+ hc->initdone = 1;
+ return 0;
+ }
+ }
+ disable_hwirq(hc);
+ spin_unlock_irqrestore(&hc->lock, flags);
+ free_irq(hc->irq, hc);
+ return -EIO;
+}
+
+static int
+channel_ctrl(struct hfc_pci *hc, struct mISDN_ctrl_req *cq)
+{
+ int ret = 0;
+ u_char slot;
+
+ switch (cq->op) {
+ case MISDN_CTRL_GETOP:
+ cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_CONNECT |
+ MISDN_CTRL_DISCONNECT;
+ break;
+ case MISDN_CTRL_LOOP:
+ /* channel 0 disabled loop */
+ if (cq->channel < 0 || cq->channel > 2) {
+ ret = -EINVAL;
+ break;
+ }
+ if (cq->channel & 1) {
+ 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",
+ __func__, slot);
+ Write_hfc(hc, HFCPCI_B1_SSL, slot);
+ Write_hfc(hc, HFCPCI_B1_RSL, slot);
+ hc->hw.conn = (hc->hw.conn & ~7) | 6;
+ Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
+ }
+ if (cq->channel & 2) {
+ 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",
+ __func__, slot);
+ Write_hfc(hc, HFCPCI_B2_SSL, slot);
+ Write_hfc(hc, HFCPCI_B2_RSL, slot);
+ hc->hw.conn = (hc->hw.conn & ~0x38) | 0x30;
+ Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
+ }
+ if (cq->channel & 3)
+ hc->hw.trm |= 0x80; /* enable IOM-loop */
+ else {
+ hc->hw.conn = (hc->hw.conn & ~0x3f) | 0x09;
+ Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
+ hc->hw.trm &= 0x7f; /* disable IOM-loop */
+ }
+ Write_hfc(hc, HFCPCI_TRM, hc->hw.trm);
+ break;
+ case MISDN_CTRL_CONNECT:
+ if (cq->channel == cq->p1) {
+ ret = -EINVAL;
+ break;
+ }
+ if (cq->channel < 1 || cq->channel > 2 ||
+ cq->p1 < 1 || cq->p1 > 2) {
+ ret = -EINVAL;
+ break;
+ }
+ 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",
+ __func__, slot);
+ Write_hfc(hc, HFCPCI_B1_SSL, slot);
+ Write_hfc(hc, HFCPCI_B2_RSL, slot);
+ 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",
+ __func__, slot);
+ Write_hfc(hc, HFCPCI_B2_SSL, slot);
+ Write_hfc(hc, HFCPCI_B1_RSL, slot);
+ hc->hw.conn = (hc->hw.conn & ~0x3f) | 0x36;
+ Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
+ hc->hw.trm |= 0x80;
+ Write_hfc(hc, HFCPCI_TRM, hc->hw.trm);
+ break;
+ case MISDN_CTRL_DISCONNECT:
+ hc->hw.conn = (hc->hw.conn & ~0x3f) | 0x09;
+ Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn);
+ hc->hw.trm &= 0x7f; /* disable IOM-loop */
+ break;
+ default:
+ printk(KERN_WARNING "%s: unknown Op %x\n",
+ __func__, cq->op);
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int
+open_dchannel(struct hfc_pci *hc, struct mISDNchannel *ch,
+ struct channel_req *rq)
+{
+ int err = 0;
+
+ if (debug & DEBUG_HW_OPEN)
+ printk(KERN_DEBUG "%s: dev(%d) open from %p\n", __func__,
+ hc->dch.dev.id, __builtin_return_address(0));
+ if (rq->protocol == ISDN_P_NONE)
+ return -EINVAL;
+ if (!hc->initdone) {
+ if (rq->protocol == ISDN_P_TE_S0) {
+ err = create_l1(&hc->dch, hfc_l1callback);
+ if (err)
+ return err;
+ }
+ hc->hw.protocol = rq->protocol;
+ ch->protocol = rq->protocol;
+ err = init_card(hc);
+ if (err)
+ return err;
+ } else {
+ if (rq->protocol != ch->protocol) {
+ if (hc->hw.protocol == ISDN_P_TE_S0)
+ l1_event(hc->dch.l1, CLOSE_CHANNEL);
+ hc->hw.protocol = rq->protocol;
+ ch->protocol = rq->protocol;
+ hfcpci_setmode(hc);
+ }
+ }
+
+ if (((ch->protocol == ISDN_P_NT_S0) && (hc->dch.state == 3)) ||
+ ((ch->protocol == ISDN_P_TE_S0) && (hc->dch.state == 7))) {
+ _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY,
+ 0, NULL, GFP_KERNEL);
+ }
+ rq->ch = ch;
+ if (!try_module_get(THIS_MODULE))
+ printk(KERN_WARNING "%s:cannot get module\n", __func__);
+ return 0;
+}
+
+static int
+open_bchannel(struct hfc_pci *hc, struct channel_req *rq)
+{
+ struct bchannel *bch;
+
+ if (rq->adr.channel > 2)
+ return -EINVAL;
+ if (rq->protocol == ISDN_P_NONE)
+ return -EINVAL;
+ bch = &hc->bch[rq->adr.channel - 1];
+ if (test_and_set_bit(FLG_OPEN, &bch->Flags))
+ return -EBUSY; /* b-channel can be only open once */
+ bch->ch.protocol = rq->protocol;
+ rq->ch = &bch->ch; /* TODO: E-channel */
+ if (!try_module_get(THIS_MODULE))
+ printk(KERN_WARNING "%s:cannot get module\n", __func__);
+ return 0;
+}
+
+/*
+ * device control function
+ */
+static int
+hfc_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+ struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D);
+ struct dchannel *dch = container_of(dev, struct dchannel, dev);
+ struct hfc_pci *hc = dch->hw;
+ struct channel_req *rq;
+ int err = 0;
+
+ if (dch->debug & DEBUG_HW)
+ printk(KERN_DEBUG "%s: cmd:%x %p\n",
+ __func__, cmd, arg);
+ switch (cmd) {
+ case OPEN_CHANNEL:
+ rq = arg;
+ if (rq->adr.channel == 0)
+ err = open_dchannel(hc, ch, rq);
+ else
+ err = open_bchannel(hc, rq);
+ break;
+ case CLOSE_CHANNEL:
+ if (debug & DEBUG_HW_OPEN)
+ printk(KERN_DEBUG "%s: dev(%d) close from %p\n",
+ __func__, hc->dch.dev.id,
+ __builtin_return_address(0));
+ module_put(THIS_MODULE);
+ break;
+ case CONTROL_CHANNEL:
+ err = channel_ctrl(hc, arg);
+ break;
+ default:
+ if (dch->debug & DEBUG_HW)
+ printk(KERN_DEBUG "%s: unknown command %x\n",
+ __func__, cmd);
+ return -EINVAL;
+ }
+ return err;
+}
+
+static int
+setup_hw(struct hfc_pci *hc)
+{
+ void *buffer;
+
+ printk(KERN_INFO "mISDN: HFC-PCI driver %s\n", hfcpci_revision);
+ hc->hw.cirm = 0;
+ hc->dch.state = 0;
+ pci_set_master(hc->pdev);
+ if (!hc->irq) {
+ printk(KERN_WARNING "HFC-PCI: No IRQ for PCI card found\n");
+ return 1;
+ }
+ hc->hw.pci_io = (char __iomem *)(unsigned long)hc->pdev->resource[1].start;
+
+ if (!hc->hw.pci_io) {
+ printk(KERN_WARNING "HFC-PCI: No IO-Mem for PCI card found\n");
+ return 1;
+ }
+ /* Allocate memory for FIFOS */
+ /* the memory needs to be on a 32k boundary within the first 4G */
+ pci_set_dma_mask(hc->pdev, 0xFFFF8000);
+ buffer = pci_alloc_consistent(hc->pdev, 0x8000, &hc->hw.dmahandle);
+ /* We silently assume the address is okay if nonzero */
+ if (!buffer) {
+ printk(KERN_WARNING
+ "HFC-PCI: Error allocating memory for FIFO!\n");
+ return 1;
+ }
+ hc->hw.fifos = buffer;
+ pci_write_config_dword(hc->pdev, 0x80, hc->hw.dmahandle);
+ hc->hw.pci_io = ioremap((ulong) hc->hw.pci_io, 256);
+ printk(KERN_INFO
+ "HFC-PCI: defined at mem %#lx fifo %#lx(%#lx) IRQ %d HZ %d\n",
+ (u_long) hc->hw.pci_io, (u_long) hc->hw.fifos,
+ (u_long) hc->hw.dmahandle, hc->irq, HZ);
+ /* enable memory mapped ports, disable busmaster */
+ pci_write_config_word(hc->pdev, PCI_COMMAND, PCI_ENA_MEMIO);
+ hc->hw.int_m2 = 0;
+ disable_hwirq(hc);
+ 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);
+ /* default PCM master */
+ test_and_set_bit(HFC_CFG_MASTER, &hc->cfg);
+ return 0;
+}
+
+static void
+release_card(struct hfc_pci *hc) {
+ u_long flags;
+
+ spin_lock_irqsave(&hc->lock, flags);
+ hc->hw.int_m2 = 0; /* interrupt output off ! */
+ disable_hwirq(hc);
+ mode_hfcpci(&hc->bch[0], 1, ISDN_P_NONE);
+ mode_hfcpci(&hc->bch[1], 2, ISDN_P_NONE);
+ if (hc->dch.timer.function != NULL) {
+ del_timer(&hc->dch.timer);
+ hc->dch.timer.function = NULL;
+ }
+ spin_unlock_irqrestore(&hc->lock, flags);
+ if (hc->hw.protocol == ISDN_P_TE_S0)
+ l1_event(hc->dch.l1, CLOSE_CHANNEL);
+ if (hc->initdone)
+ free_irq(hc->irq, hc);
+ release_io_hfcpci(hc); /* must release after free_irq! */
+ mISDN_unregister_device(&hc->dch.dev);
+ mISDN_freebchannel(&hc->bch[1]);
+ mISDN_freebchannel(&hc->bch[0]);
+ mISDN_freedchannel(&hc->dch);
+ list_del(&hc->list);
+ pci_set_drvdata(hc->pdev, NULL);
+ kfree(hc);
+}
+
+static int
+setup_card(struct hfc_pci *card)
+{
+ int err = -EINVAL;
+ u_int i;
+ u_long flags;
+ char name[MISDN_MAX_IDLEN];
+
+ if (HFC_cnt >= MAX_CARDS)
+ return -EINVAL; /* maybe better value */
+
+ card->dch.debug = debug;
+ spin_lock_init(&card->lock);
+ mISDN_initdchannel(&card->dch, MAX_DFRAME_LEN_L1, ph_state);
+ card->dch.hw = card;
+ card->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0);
+ card->dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
+ (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
+ card->dch.dev.D.send = hfcpci_l2l1D;
+ card->dch.dev.D.ctrl = hfc_dctrl;
+ card->dch.dev.nrbchan = 2;
+ for (i = 0; i < 2; i++) {
+ card->bch[i].nr = i + 1;
+ set_channelmap(i + 1, card->dch.dev.channelmap);
+ card->bch[i].debug = debug;
+ mISDN_initbchannel(&card->bch[i], MAX_DATA_MEM);
+ card->bch[i].hw = card;
+ card->bch[i].ch.send = hfcpci_l2l1B;
+ card->bch[i].ch.ctrl = hfc_bctrl;
+ card->bch[i].ch.nr = i + 1;
+ list_add(&card->bch[i].ch.list, &card->dch.dev.bchannels);
+ }
+ err = setup_hw(card);
+ if (err)
+ goto error;
+ snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-pci.%d", HFC_cnt + 1);
+ err = mISDN_register_device(&card->dch.dev, name);
+ if (err)
+ goto error;
+ HFC_cnt++;
+ write_lock_irqsave(&HFClock, flags);
+ list_add_tail(&card->list, &HFClist);
+ write_unlock_irqrestore(&HFClock, flags);
+ printk(KERN_INFO "HFC %d cards installed\n", HFC_cnt);
+ return 0;
+error:
+ mISDN_freebchannel(&card->bch[1]);
+ mISDN_freebchannel(&card->bch[0]);
+ mISDN_freedchannel(&card->dch);
+ kfree(card);
+ return err;
+}
+
+/* private data in the PCI devices list */
+struct _hfc_map {
+ u_int subtype;
+ u_int flag;
+ char *name;
+};
+
+static const struct _hfc_map hfc_map[] =
+{
+ {HFC_CCD_2BD0, 0, "CCD/Billion/Asuscom 2BD0"},
+ {HFC_CCD_B000, 0, "Billion B000"},
+ {HFC_CCD_B006, 0, "Billion B006"},
+ {HFC_CCD_B007, 0, "Billion B007"},
+ {HFC_CCD_B008, 0, "Billion B008"},
+ {HFC_CCD_B009, 0, "Billion B009"},
+ {HFC_CCD_B00A, 0, "Billion B00A"},
+ {HFC_CCD_B00B, 0, "Billion B00B"},
+ {HFC_CCD_B00C, 0, "Billion B00C"},
+ {HFC_CCD_B100, 0, "Seyeon B100"},
+ {HFC_CCD_B700, 0, "Primux II S0 B700"},
+ {HFC_CCD_B701, 0, "Primux II S0 NT B701"},
+ {HFC_ABOCOM_2BD1, 0, "Abocom/Magitek 2BD1"},
+ {HFC_ASUS_0675, 0, "Asuscom/Askey 675"},
+ {HFC_BERKOM_TCONCEPT, 0, "German telekom T-Concept"},
+ {HFC_BERKOM_A1T, 0, "German telekom A1T"},
+ {HFC_ANIGMA_MC145575, 0, "Motorola MC145575"},
+ {HFC_ZOLTRIX_2BD0, 0, "Zoltrix 2BD0"},
+ {HFC_DIGI_DF_M_IOM2_E, 0,
+ "Digi International DataFire Micro V IOM2 (Europe)"},
+ {HFC_DIGI_DF_M_E, 0,
+ "Digi International DataFire Micro V (Europe)"},
+ {HFC_DIGI_DF_M_IOM2_A, 0,
+ "Digi International DataFire Micro V IOM2 (North America)"},
+ {HFC_DIGI_DF_M_A, 0,
+ "Digi International DataFire Micro V (North America)"},
+ {HFC_SITECOM_DC105V2, 0, "Sitecom Connectivity DC-105 ISDN TA"},
+ {},
+};
+
+static struct pci_device_id hfc_ids[] =
+{
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_2BD0,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[0]},
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B000,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[1]},
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B006,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[2]},
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B007,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[3]},
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B008,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[4]},
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B009,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[5]},
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00A,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[6]},
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00B,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[7]},
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00C,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[8]},
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B100,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[9]},
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B700,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[10]},
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B701,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[11]},
+ {PCI_VENDOR_ID_ABOCOM, PCI_DEVICE_ID_ABOCOM_2BD1,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[12]},
+ {PCI_VENDOR_ID_ASUSTEK, PCI_DEVICE_ID_ASUSTEK_0675,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[13]},
+ {PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_T_CONCEPT,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[14]},
+ {PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_A1T,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[15]},
+ {PCI_VENDOR_ID_ANIGMA, PCI_DEVICE_ID_ANIGMA_MC145575,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[16]},
+ {PCI_VENDOR_ID_ZOLTRIX, PCI_DEVICE_ID_ZOLTRIX_2BD0,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[17]},
+ {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_E,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[18]},
+ {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_E,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[19]},
+ {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_A,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[20]},
+ {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_A,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[21]},
+ {PCI_VENDOR_ID_SITECOM, PCI_DEVICE_ID_SITECOM_DC105V2,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) &hfc_map[22]},
+ {},
+};
+
+static int __devinit
+hfc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ int err = -ENOMEM;
+ struct hfc_pci *card;
+ struct _hfc_map *m = (struct _hfc_map *)ent->driver_data;
+
+ card = kzalloc(sizeof(struct hfc_pci), GFP_ATOMIC);
+ if (!card) {
+ printk(KERN_ERR "No kmem for HFC card\n");
+ return err;
+ }
+ card->pdev = pdev;
+ card->subtype = m->subtype;
+ err = pci_enable_device(pdev);
+ if (err) {
+ kfree(card);
+ return err;
+ }
+
+ printk(KERN_INFO "mISDN_hfcpci: found adapter %s at %s\n",
+ m->name, pci_name(pdev));
+
+ card->irq = pdev->irq;
+ pci_set_drvdata(pdev, card);
+ err = setup_card(card);
+ if (err)
+ pci_set_drvdata(pdev, NULL);
+ return err;
+}
+
+static void __devexit
+hfc_remove_pci(struct pci_dev *pdev)
+{
+ struct hfc_pci *card = pci_get_drvdata(pdev);
+ u_long flags;
+
+ if (card) {
+ write_lock_irqsave(&HFClock, flags);
+ release_card(card);
+ write_unlock_irqrestore(&HFClock, flags);
+ } else
+ if (debug)
+ printk(KERN_WARNING "%s: drvdata allready removed\n",
+ __func__);
+}
+
+
+static struct pci_driver hfc_driver = {
+ .name = "hfcpci",
+ .probe = hfc_probe,
+ .remove = __devexit_p(hfc_remove_pci),
+ .id_table = hfc_ids,
+};
+
+static int __init
+HFC_init(void)
+{
+ int err;
+
+ err = pci_register_driver(&hfc_driver);
+ return err;
+}
+
+static void __exit
+HFC_cleanup(void)
+{
+ struct hfc_pci *card, *next;
+
+ list_for_each_entry_safe(card, next, &HFClist, list) {
+ release_card(card);
+ }
+ pci_unregister_driver(&hfc_driver);
+}
+
+module_init(HFC_init);
+module_exit(HFC_cleanup);
diff --git a/drivers/isdn/hisax/Kconfig b/drivers/isdn/hisax/Kconfig
new file mode 100644
index 0000000..7832d8b
--- /dev/null
+++ b/drivers/isdn/hisax/Kconfig
@@ -0,0 +1,434 @@
+
+menu "Passive cards"
+
+config ISDN_DRV_HISAX
+ tristate "HiSax SiemensChipSet driver support"
+ select CRC_CCITT
+ ---help---
+ This is a driver supporting the Siemens chipset on various
+ ISDN-cards (like AVM A1, Elsa ISDN cards, Teles S0-16.0, Teles
+ S0-16.3, Teles S0-8, Teles/Creatix PnP, ITK micro ix1 and many
+ compatibles).
+
+ HiSax is just the name of this driver, not the name of any hardware.
+
+ If you have a card with such a chipset, you should say Y here and
+ also to the configuration option of the driver for your particular
+ card, below.
+
+if ISDN_DRV_HISAX!=n
+
+comment "D-channel protocol features"
+
+config HISAX_EURO
+ bool "HiSax Support for EURO/DSS1"
+ help
+ Say Y or N according to the D-channel protocol which your local
+ telephone service company provides.
+
+ The call control protocol E-DSS1 is used in most European countries.
+ If unsure, say Y.
+
+config DE_AOC
+ bool "Support for german chargeinfo"
+ depends on HISAX_EURO
+ help
+ If you want that the HiSax hardware driver sends messages to the
+ upper level of the isdn code on each AOCD (Advice Of Charge, During
+ the call -- transmission of the fee information during a call) and
+ on each AOCE (Advice Of Charge, at the End of the call --
+ transmission of fee information at the end of the call), say Y here.
+ This works only in Germany.
+
+config HISAX_NO_SENDCOMPLETE
+ bool "Disable sending complete"
+ depends on HISAX_EURO
+ help
+ If you have trouble with some ugly exchanges or you live in
+ Australia select this option.
+
+config HISAX_NO_LLC
+ bool "Disable sending low layer compatibility"
+ depends on HISAX_EURO
+ help
+ If you have trouble with some ugly exchanges try to select this
+ option.
+
+config HISAX_NO_KEYPAD
+ bool "Disable keypad protocol option"
+ depends on HISAX_EURO
+ help
+ If you like to send special dial strings including * or # without
+ using the keypad protocol, select this option.
+
+config HISAX_1TR6
+ bool "HiSax Support for german 1TR6"
+ help
+ Say Y or N according to the D-channel protocol which your local
+ telephone service company provides.
+
+ 1TR6 is an old call control protocol which was used in Germany
+ before E-DSS1 was established. Nowadays, all new lines in Germany
+ use E-DSS1.
+
+config HISAX_NI1
+ bool "HiSax Support for US NI1"
+ help
+ Enable this if you like to use ISDN in US on a NI1 basic rate
+ interface.
+
+config HISAX_MAX_CARDS
+ int "Maximum number of cards supported by HiSax"
+ default "8"
+ help
+ This option allows you to specify the maximum number of cards which
+ the HiSax driver will be able to handle.
+
+comment "HiSax supported cards"
+
+config HISAX_16_0
+ bool "Teles 16.0/8.0"
+ depends on ISA
+ help
+ This enables HiSax support for the Teles ISDN-cards S0-16.0, S0-8
+ and many compatibles.
+
+ See <file:Documentation/isdn/README.HiSax> on how to configure it
+ using the different cards, a different D-channel protocol, or
+ non-standard IRQ/port/shmem settings.
+
+config HISAX_16_3
+ bool "Teles 16.3 or PNP or PCMCIA"
+ help
+ This enables HiSax support for the Teles ISDN-cards S0-16.3 the
+ Teles/Creatix PnP and the Teles PCMCIA.
+
+ See <file:Documentation/isdn/README.HiSax> on how to configure it
+ using the different cards, a different D-channel protocol, or
+ non-standard IRQ/port settings.
+
+config HISAX_TELESPCI
+ bool "Teles PCI"
+ depends on PCI && PCI_LEGACY && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV))
+ help
+ This enables HiSax support for the Teles PCI.
+ See <file:Documentation/isdn/README.HiSax> on how to configure it.
+
+config HISAX_S0BOX
+ bool "Teles S0Box"
+ help
+ This enables HiSax support for the Teles/Creatix parallel port
+ S0BOX. See <file:Documentation/isdn/README.HiSax> on how to
+ configure it.
+
+config HISAX_AVM_A1
+ bool "AVM A1 (Fritz)"
+ depends on ISA
+ help
+ This enables HiSax support for the AVM A1 (aka "Fritz").
+
+ See <file:Documentation/isdn/README.HiSax> on how to configure it
+ using the different cards, a different D-channel protocol, or
+ non-standard IRQ/port settings.
+
+config HISAX_FRITZPCI
+ bool "AVM PnP/PCI (Fritz!PnP/PCI)"
+ depends on BROKEN || !PPC64
+ help
+ This enables HiSax support for the AVM "Fritz!PnP" and "Fritz!PCI".
+ See <file:Documentation/isdn/README.HiSax> on how to configure it.
+
+config HISAX_AVM_A1_PCMCIA
+ bool "AVM A1 PCMCIA (Fritz)"
+ help
+ This enables HiSax support for the AVM A1 "Fritz!PCMCIA").
+ See <file:Documentation/isdn/README.HiSax> on how to configure it.
+
+config HISAX_ELSA
+ bool "Elsa cards"
+ help
+ This enables HiSax support for the Elsa Mircolink ISA cards, for the
+ Elsa Quickstep series cards and Elsa PCMCIA.
+
+ See <file:Documentation/isdn/README.HiSax> on how to configure it
+ using the different cards, a different D-channel protocol, or
+ non-standard IRQ/port settings.
+
+config HISAX_IX1MICROR2
+ bool "ITK ix1-micro Revision 2"
+ depends on ISA
+ help
+ This enables HiSax support for the ITK ix1-micro Revision 2 card.
+
+ See <file:Documentation/isdn/README.HiSax> on how to configure it
+ using the different cards, a different D-channel protocol, or
+ non-standard IRQ/port settings.
+
+config HISAX_DIEHLDIVA
+ bool "Eicon.Diehl Diva cards"
+ help
+ This enables HiSax support for the Eicon.Diehl Diva none PRO
+ versions passive ISDN cards.
+
+ See <file:Documentation/isdn/README.HiSax> on how to configure it
+ using the different cards, a different D-channel protocol, or
+ non-standard IRQ/port settings.
+
+config HISAX_ASUSCOM
+ bool "ASUSCOM ISA cards"
+ depends on ISA
+ help
+ This enables HiSax support for the AsusCom and their OEM versions
+ passive ISDN ISA cards.
+
+ See <file:Documentation/isdn/README.HiSax> on how to configure it
+ using the different cards, a different D-channel protocol, or
+ non-standard IRQ/port settings.
+
+config HISAX_TELEINT
+ bool "TELEINT cards"
+ depends on ISA
+ help
+ This enables HiSax support for the TELEINT SA1 semiactiv ISDN card.
+
+ See <file:Documentation/isdn/README.HiSax> on how to configure it
+ using the different cards, a different D-channel protocol, or
+ non-standard IRQ/port settings.
+
+config HISAX_HFCS
+ bool "HFC-S based cards"
+ depends on ISA
+ help
+ This enables HiSax support for the HFC-S 2BDS0 based cards, like
+ teles 16.3c.
+
+ See <file:Documentation/isdn/README.HiSax> on how to configure it
+ using the different cards, a different D-channel protocol, or
+ non-standard IRQ/port settings.
+
+config HISAX_SEDLBAUER
+ bool "Sedlbauer cards"
+ help
+ This enables HiSax support for the Sedlbauer passive ISDN cards.
+
+ See <file:Documentation/isdn/README.HiSax> on how to configure it
+ using the different cards, a different D-channel protocol, or
+ non-standard IRQ/port settings.
+
+config HISAX_SPORTSTER
+ bool "USR Sportster internal TA"
+ depends on ISA
+ help
+ This enables HiSax support for the USR Sportster internal TA card.
+
+ See <file:Documentation/isdn/README.HiSax> on how to configure it
+ using a different D-channel protocol, or non-standard IRQ/port
+ settings.
+
+config HISAX_MIC
+ bool "MIC card"
+ depends on ISA
+ help
+ This enables HiSax support for the ITH MIC card.
+
+ See <file:Documentation/isdn/README.HiSax> on how to configure it
+ using a different D-channel protocol, or non-standard IRQ/port
+ settings.
+
+config HISAX_NETJET
+ bool "NETjet card"
+ depends on PCI && PCI_LEGACY && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV))
+ help
+ This enables HiSax support for the NetJet from Traverse
+ Technologies.
+
+ See <file:Documentation/isdn/README.HiSax> on how to configure it
+ using a different D-channel protocol, or non-standard IRQ/port
+ settings.
+
+config HISAX_NETJET_U
+ bool "NETspider U card"
+ depends on PCI && PCI_LEGACY && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV))
+ help
+ This enables HiSax support for the Netspider U interface ISDN card
+ from Traverse Technologies.
+ See <file:Documentation/isdn/README.HiSax> on how to configure it
+ using a different D-channel protocol, or non-standard IRQ/port
+ settings.
+
+config HISAX_NICCY
+ bool "Niccy PnP/PCI card"
+ help
+ This enables HiSax support for the Dr. Neuhaus Niccy PnP or PCI.
+
+ See <file:Documentation/isdn/README.HiSax> on how to configure it
+ using a different D-channel protocol, or non-standard IRQ/port
+ settings.
+
+config HISAX_ISURF
+ bool "Siemens I-Surf card"
+ depends on ISA
+ help
+ This enables HiSax support for the Siemens I-Talk/I-Surf card with
+ ISAR chip.
+ See <file:Documentation/isdn/README.HiSax> on how to configure it
+ using a different D-channel protocol, or non-standard IRQ/port
+ settings.
+
+config HISAX_HSTSAPHIR
+ bool "HST Saphir card"
+ depends on ISA
+ help
+ This enables HiSax support for the HST Saphir card.
+
+ See <file:Documentation/isdn/README.HiSax> on how to configure it
+ using a different D-channel protocol, or non-standard IRQ/port
+ settings.
+
+config HISAX_BKM_A4T
+ bool "Telekom A4T card"
+ depends on PCI && PCI_LEGACY
+ help
+ This enables HiSax support for the Telekom A4T card.
+
+ See <file:Documentation/isdn/README.HiSax> on how to configure it
+ using a different D-channel protocol, or non-standard IRQ/port
+ settings.
+
+config HISAX_SCT_QUADRO
+ bool "Scitel Quadro card"
+ depends on PCI && PCI_LEGACY
+ help
+ This enables HiSax support for the Scitel Quadro card.
+
+ See <file:Documentation/isdn/README.HiSax> on how to configure it
+ using a different D-channel protocol, or non-standard IRQ/port
+ settings.
+
+config HISAX_GAZEL
+ bool "Gazel cards"
+ help
+ This enables HiSax support for the Gazel cards.
+
+ See <file:Documentation/isdn/README.HiSax> on how to configure it
+ using a different D-channel protocol, or non-standard IRQ/port
+ settings.
+
+config HISAX_HFC_PCI
+ bool "HFC PCI-Bus cards"
+ depends on PCI && PCI_LEGACY && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV))
+ help
+ This enables HiSax support for the HFC-S PCI 2BDS0 based cards.
+
+ For more information see under
+ <file:Documentation/isdn/README.hfc-pci>.
+
+config HISAX_W6692
+ bool "Winbond W6692 based cards"
+ depends on PCI && PCI_LEGACY
+ help
+ This enables HiSax support for Winbond W6692 based PCI ISDN cards.
+
+ See <file:Documentation/isdn/README.HiSax> on how to configure it
+ using a different D-channel protocol, or non-standard IRQ/port
+ settings.
+
+config HISAX_HFC_SX
+ bool "HFC-S+, HFC-SP, HFC-PCMCIA cards"
+ help
+ This enables HiSax support for the HFC-S+, HFC-SP and HFC-PCMCIA
+ cards. This code is not finished yet.
+
+config HISAX_ENTERNOW_PCI
+ bool "Formula-n enter:now PCI card"
+ depends on HISAX_NETJET && PCI && PCI_LEGACY && (BROKEN || !(SPARC || PPC || PARISC || M68K || (MIPS && !CPU_LITTLE_ENDIAN) || FRV))
+ help
+ This enables HiSax support for the Formula-n enter:now PCI
+ ISDN card.
+
+endif
+
+if ISDN_DRV_HISAX
+
+config HISAX_DEBUG
+ bool "HiSax debugging"
+ help
+ This enables debugging code in the new-style HiSax drivers, i.e.
+ the ST5481 USB driver currently.
+ If in doubt, say yes.
+
+comment "HiSax PCMCIA card service modules"
+
+config HISAX_SEDLBAUER_CS
+ tristate "Sedlbauer PCMCIA cards"
+ depends on PCMCIA && HISAX_SEDLBAUER
+ help
+ This enables the PCMCIA client driver for the Sedlbauer Speed Star
+ and Speed Star II cards.
+
+config HISAX_ELSA_CS
+ tristate "ELSA PCMCIA MicroLink cards"
+ depends on PCMCIA && HISAX_ELSA
+ help
+ This enables the PCMCIA client driver for the Elsa PCMCIA MicroLink
+ card.
+
+config HISAX_AVM_A1_CS
+ tristate "AVM A1 PCMCIA cards"
+ depends on PCMCIA && ISDN_DRV_HISAX
+ help
+ This enables the PCMCIA client driver for the AVM A1 / Fritz!Card
+ PCMCIA cards.
+
+config HISAX_TELES_CS
+ tristate "TELES PCMCIA cards"
+ depends on PCMCIA && HISAX_16_3
+ help
+ This enables the PCMCIA client driver for the Teles PCMCIA cards.
+
+comment "HiSax sub driver modules"
+
+config HISAX_ST5481
+ tristate "ST5481 USB ISDN modem (EXPERIMENTAL)"
+ depends on USB && EXPERIMENTAL
+ select CRC_CCITT
+ select BITREVERSE
+ help
+ This enables the driver for ST5481 based USB ISDN adapters,
+ e.g. the BeWan Gazel 128 USB
+
+config HISAX_HFCUSB
+ tristate "HFC USB based ISDN modems (EXPERIMENTAL)"
+ depends on USB && EXPERIMENTAL
+ help
+ This enables the driver for HFC USB based ISDN modems.
+
+config HISAX_HFC4S8S
+ tristate "HFC-4S/8S based ISDN cards (EXPERIMENTAL)"
+ depends on EXPERIMENTAL
+ help
+ This enables the driver for HFC-4S/8S based ISDN cards.
+
+config HISAX_FRITZ_PCIPNP
+ tristate "AVM Fritz!Card PCI/PCIv2/PnP support (EXPERIMENTAL)"
+ depends on PCI && PCI_LEGACY && EXPERIMENTAL
+ help
+ This enables the driver for the AVM Fritz!Card PCI,
+ Fritz!Card PCI v2 and Fritz!Card PnP.
+ (the latter also needs you to select "ISA Plug and Play support"
+ from the menu "Plug and Play configuration")
+
+config HISAX_HDLC
+ bool
+ depends on HISAX_ST5481
+ default y
+
+config HISAX_AVM_A1_PCMCIA
+ bool
+ depends on HISAX_AVM_A1_CS
+ default y
+
+endif
+
+endmenu
+
diff --git a/drivers/isdn/hisax/Makefile b/drivers/isdn/hisax/Makefile
new file mode 100644
index 0000000..c7a3794
--- /dev/null
+++ b/drivers/isdn/hisax/Makefile
@@ -0,0 +1,63 @@
+# Makefile for the hisax ISDN device driver
+
+# The target object and module list name.
+
+# Define maximum number of cards
+
+EXTRA_CFLAGS += -DHISAX_MAX_CARDS=$(CONFIG_HISAX_MAX_CARDS)
+
+obj-$(CONFIG_ISDN_DRV_HISAX) += hisax.o
+obj-$(CONFIG_HISAX_SEDLBAUER_CS) += sedlbauer_cs.o
+obj-$(CONFIG_HISAX_ELSA_CS) += elsa_cs.o
+obj-$(CONFIG_HISAX_AVM_A1_CS) += avma1_cs.o
+obj-$(CONFIG_HISAX_TELES_CS) += teles_cs.o
+obj-$(CONFIG_HISAX_ST5481) += hisax_st5481.o
+obj-$(CONFIG_HISAX_HFCUSB) += hfc_usb.o
+obj-$(CONFIG_HISAX_HFC4S8S) += hfc4s8s_l1.o
+obj-$(CONFIG_HISAX_FRITZ_PCIPNP) += hisax_isac.o hisax_fcpcipnp.o
+
+ifdef CONFIG_HISAX_HDLC
+obj-$(CONFIG_ISDN_DRV_HISAX) += isdnhdlc.o
+endif
+
+# Multipart objects.
+
+hisax_st5481-y := st5481_init.o st5481_usb.o st5481_d.o \
+ st5481_b.o
+
+hisax-y := config.o isdnl1.o tei.o isdnl2.o isdnl3.o \
+ lmgr.o q931.o callc.o fsm.o
+hisax-$(CONFIG_HISAX_EURO) += l3dss1.o
+hisax-$(CONFIG_HISAX_NI1) += l3ni1.o
+hisax-$(CONFIG_HISAX_1TR6) += l3_1tr6.o
+
+hisax-$(CONFIG_HISAX_16_0) += teles0.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_16_3) += teles3.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_TELESPCI) += telespci.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_S0BOX) += s0box.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_AVM_A1) += avm_a1.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_AVM_A1_PCMCIA) += avm_a1p.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_FRITZPCI) += avm_pci.o isac.o arcofi.o
+hisax-$(CONFIG_HISAX_ELSA) += elsa.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_IX1MICROR2) += ix1_micro.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_DIEHLDIVA) += diva.o isac.o arcofi.o hscx.o ipacx.o
+hisax-$(CONFIG_HISAX_ASUSCOM) += asuscom.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_TELEINT) += teleint.o isac.o arcofi.o hfc_2bs0.o
+hisax-$(CONFIG_HISAX_SEDLBAUER) += sedlbauer.o isac.o arcofi.o hscx.o \
+ isar.o
+hisax-$(CONFIG_HISAX_SPORTSTER) += sportster.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_MIC) += mic.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_NETJET) += nj_s.o netjet.o isac.o arcofi.o
+hisax-$(CONFIG_HISAX_NETJET_U) += nj_u.o netjet.o icc.o
+hisax-$(CONFIG_HISAX_HFCS) += hfcscard.o hfc_2bds0.o
+hisax-$(CONFIG_HISAX_HFC_PCI) += hfc_pci.o
+hisax-$(CONFIG_HISAX_HFC_SX) += hfc_sx.o
+hisax-$(CONFIG_HISAX_NICCY) += niccy.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_ISURF) += isurf.o isac.o arcofi.o isar.o
+hisax-$(CONFIG_HISAX_HSTSAPHIR) += saphir.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_BKM_A4T) += bkm_a4t.o isac.o arcofi.o jade.o
+hisax-$(CONFIG_HISAX_SCT_QUADRO) += bkm_a8.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_GAZEL) += gazel.o isac.o arcofi.o hscx.o
+hisax-$(CONFIG_HISAX_W6692) += w6692.o
+hisax-$(CONFIG_HISAX_ENTERNOW_PCI) += enternow_pci.o amd7930_fn.o
+
diff --git a/drivers/isdn/hisax/amd7930_fn.c b/drivers/isdn/hisax/amd7930_fn.c
new file mode 100644
index 0000000..341faf5
--- /dev/null
+++ b/drivers/isdn/hisax/amd7930_fn.c
@@ -0,0 +1,796 @@
+/* gerdes_amd7930.c,v 0.99 2001/10/02
+ *
+ * gerdes_amd7930.c Amd 79C30A and 79C32A specific routines
+ * (based on HiSax driver by Karsten Keil)
+ *
+ * Author Christoph Ersfeld <info@formula-n.de>
+ * Formula-n Europe AG (www.formula-n.com)
+ * previously Gerdes AG
+ *
+ *
+ * This file is (c) under GNU PUBLIC LICENSE
+ *
+ *
+ * Notes:
+ * Version 0.99 is the first release of this driver and there are
+ * certainly a few bugs.
+ *
+ * Please don't report any malfunction to me without sending
+ * (compressed) debug-logs.
+ * It would be nearly impossible to retrace it.
+ *
+ * Log D-channel-processing as follows:
+ *
+ * 1. Load hisax with card-specific parameters, this example ist for
+ * Formula-n enter:now ISDN PCI and compatible
+ * (f.e. Gerdes Power ISDN PCI)
+ *
+ * modprobe hisax type=41 protocol=2 id=gerdes
+ *
+ * if you chose an other value for id, you need to modify the
+ * code below, too.
+ *
+ * 2. set debug-level
+ *
+ * hisaxctrl gerdes 1 0x3ff
+ * hisaxctrl gerdes 11 0x4f
+ * cat /dev/isdnctrl >> ~/log &
+ *
+ * Please take also a look into /var/log/messages if there is
+ * anything importand concerning HISAX.
+ *
+ *
+ * Credits:
+ * Programming the driver for Formula-n enter:now ISDN PCI and
+ * necessary this driver for the used Amd 7930 D-channel-controller
+ * was spnsored by Formula-n Europe AG.
+ * Thanks to Karsten Keil and Petr Novak, who gave me support in
+ * Hisax-specific questions.
+ * I want so say special thanks to Carl-Friedrich Braun, who had to
+ * answer a lot of questions about generally ISDN and about handling
+ * of the Amd-Chip.
+ *
+ */
+
+
+#include "hisax.h"
+#include "isdnl1.h"
+#include "isac.h"
+#include "amd7930_fn.h"
+#include <linux/interrupt.h>
+#include <linux/init.h>
+
+static void Amd7930_new_ph(struct IsdnCardState *cs);
+
+static WORD initAMD[] = {
+ 0x0100,
+
+ 0x00A5, 3, 0x01, 0x40, 0x58, // LPR, LMR1, LMR2
+ 0x0086, 1, 0x0B, // DMR1 (D-Buffer TH-Interrupts on)
+ 0x0087, 1, 0xFF, // DMR2
+ 0x0092, 1, 0x03, // EFCR (extended mode d-channel-fifo on)
+ 0x0090, 4, 0xFE, 0xFF, 0x02, 0x0F, // FRAR4, SRAR4, DMR3, DMR4 (address recognition )
+ 0x0084, 2, 0x80, 0x00, // DRLR
+ 0x00C0, 1, 0x47, // PPCR1
+ 0x00C8, 1, 0x01, // PPCR2
+
+ 0x0102,
+ 0x0107,
+ 0x01A1, 1,
+ 0x0121, 1,
+ 0x0189, 2,
+
+ 0x0045, 4, 0x61, 0x72, 0x00, 0x00, // MCR1, MCR2, MCR3, MCR4
+ 0x0063, 2, 0x08, 0x08, // GX
+ 0x0064, 2, 0x08, 0x08, // GR
+ 0x0065, 2, 0x99, 0x00, // GER
+ 0x0066, 2, 0x7C, 0x8B, // STG
+ 0x0067, 2, 0x00, 0x00, // FTGR1, FTGR2
+ 0x0068, 2, 0x20, 0x20, // ATGR1, ATGR2
+ 0x0069, 1, 0x4F, // MMR1
+ 0x006A, 1, 0x00, // MMR2
+ 0x006C, 1, 0x40, // MMR3
+ 0x0021, 1, 0x02, // INIT
+ 0x00A3, 1, 0x40, // LMR1
+
+ 0xFFFF
+};
+
+
+static void /* macro wWordAMD */
+WriteWordAmd7930(struct IsdnCardState *cs, BYTE reg, WORD val)
+{
+ wByteAMD(cs, 0x00, reg);
+ wByteAMD(cs, 0x01, LOBYTE(val));
+ wByteAMD(cs, 0x01, HIBYTE(val));
+}
+
+static WORD /* macro rWordAMD */
+ReadWordAmd7930(struct IsdnCardState *cs, BYTE reg)
+{
+ WORD res;
+ /* direct access register */
+ if(reg < 8) {
+ res = rByteAMD(cs, reg);
+ res += 256*rByteAMD(cs, reg);
+ }
+ /* indirect access register */
+ else {
+ wByteAMD(cs, 0x00, reg);
+ res = rByteAMD(cs, 0x01);
+ res += 256*rByteAMD(cs, 0x01);
+ }
+ return (res);
+}
+
+
+static void
+Amd7930_ph_command(struct IsdnCardState *cs, u_char command, char *s)
+{
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "AMD7930: %s: ph_command 0x%02X", s, command);
+
+ cs->dc.amd7930.lmr1 = command;
+ wByteAMD(cs, 0xA3, command);
+}
+
+
+
+static BYTE i430States[] = {
+// to reset F3 F4 F5 F6 F7 F8 AR from
+ 0x01, 0x02, 0x00, 0x00, 0x00, 0x07, 0x05, 0x00, // init
+ 0x01, 0x02, 0x00, 0x00, 0x00, 0x07, 0x05, 0x00, // reset
+ 0x01, 0x02, 0x00, 0x00, 0x00, 0x09, 0x05, 0x04, // F3
+ 0x01, 0x02, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, // F4
+ 0x01, 0x02, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, // F5
+ 0x01, 0x03, 0x00, 0x00, 0x00, 0x06, 0x05, 0x00, // F6
+ 0x11, 0x13, 0x00, 0x00, 0x1B, 0x00, 0x15, 0x00, // F7
+ 0x01, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, // F8
+ 0x01, 0x03, 0x00, 0x00, 0x00, 0x09, 0x00, 0x0A}; // AR
+
+
+/* Row init - reset F3 F4 F5 F6 F7 F8 AR */
+static BYTE stateHelper[] = { 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
+
+
+
+
+static void
+Amd7930_get_state(struct IsdnCardState *cs) {
+ BYTE lsr = rByteAMD(cs, 0xA1);
+ cs->dc.amd7930.ph_state = (lsr & 0x7) + 2;
+ Amd7930_new_ph(cs);
+}
+
+
+
+static void
+Amd7930_new_ph(struct IsdnCardState *cs)
+{
+ u_char index = stateHelper[cs->dc.amd7930.old_state]*8 + stateHelper[cs->dc.amd7930.ph_state]-1;
+ u_char message = i430States[index];
+
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "AMD7930: new_ph %d, old_ph %d, message %d, index %d",
+ cs->dc.amd7930.ph_state, cs->dc.amd7930.old_state, message & 0x0f, index);
+
+ cs->dc.amd7930.old_state = cs->dc.amd7930.ph_state;
+
+ /* abort transmit if nessesary */
+ if ((message & 0xf0) && (cs->tx_skb)) {
+ wByteAMD(cs, 0x21, 0xC2);
+ wByteAMD(cs, 0x21, 0x02);
+ }
+
+ switch (message & 0x0f) {
+
+ case (1):
+ l1_msg(cs, HW_RESET | INDICATION, NULL);
+ Amd7930_get_state(cs);
+ break;
+ case (2): /* init, Card starts in F3 */
+ l1_msg(cs, HW_DEACTIVATE | CONFIRM, NULL);
+ break;
+ case (3):
+ l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
+ break;
+ case (4):
+ l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
+ Amd7930_ph_command(cs, 0x50, "HW_ENABLE REQUEST");
+ break;
+ case (5):
+ l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+ break;
+ case (6):
+ l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
+ break;
+ case (7): /* init, Card starts in F7 */
+ l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+ l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
+ break;
+ case (8):
+ l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
+ /* fall through */
+ case (9):
+ Amd7930_ph_command(cs, 0x40, "HW_ENABLE REQ cleared if set");
+ l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+ l1_msg(cs, HW_INFO2 | INDICATION, NULL);
+ l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
+ break;
+ case (10):
+ Amd7930_ph_command(cs, 0x40, "T3 expired, HW_ENABLE REQ cleared");
+ cs->dc.amd7930.old_state = 3;
+ break;
+ case (11):
+ l1_msg(cs, HW_INFO2 | INDICATION, NULL);
+ break;
+ default:
+ break;
+ }
+}
+
+
+
+static void
+Amd7930_bh(struct work_struct *work)
+{
+ struct IsdnCardState *cs =
+ container_of(work, struct IsdnCardState, tqueue);
+ struct PStack *stptr;
+
+ if (!cs)
+ return;
+ if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) {
+ if (cs->debug)
+ debugl1(cs, "Amd7930: bh, D-Channel Busy cleared");
+ stptr = cs->stlist;
+ while (stptr != NULL) {
+ stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL);
+ stptr = stptr->next;
+ }
+ }
+ if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) {
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "AMD7930: bh, D_L1STATECHANGE");
+ Amd7930_new_ph(cs);
+ }
+
+ if (test_and_clear_bit(D_RCVBUFREADY, &cs->event)) {
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "AMD7930: bh, D_RCVBUFREADY");
+ DChannel_proc_rcv(cs);
+ }
+
+ if (test_and_clear_bit(D_XMTBUFREADY, &cs->event)) {
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "AMD7930: bh, D_XMTBUFREADY");
+ DChannel_proc_xmt(cs);
+ }
+}
+
+static void
+Amd7930_empty_Dfifo(struct IsdnCardState *cs, int flag)
+{
+
+ BYTE stat, der;
+ BYTE *ptr;
+ struct sk_buff *skb;
+
+
+ if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
+ debugl1(cs, "Amd7930: empty_Dfifo");
+
+
+ ptr = cs->rcvbuf + cs->rcvidx;
+
+ /* AMD interrupts off */
+ AmdIrqOff(cs);
+
+ /* read D-Channel-Fifo*/
+ stat = rByteAMD(cs, 0x07); // DSR2
+
+ /* while Data in Fifo ... */
+ while ( (stat & 2) && ((ptr-cs->rcvbuf) < MAX_DFRAME_LEN_L1) ) {
+ *ptr = rByteAMD(cs, 0x04); // DCRB
+ ptr++;
+ stat = rByteAMD(cs, 0x07); // DSR2
+ cs->rcvidx = ptr - cs->rcvbuf;
+
+ /* Paket ready? */
+ if (stat & 1) {
+
+ der = rWordAMD(cs, 0x03);
+
+ /* no errors, packet ok */
+ if(!der && !flag) {
+ rWordAMD(cs, 0x89); // clear DRCR
+
+ if ((cs->rcvidx) > 0) {
+ if (!(skb = alloc_skb(cs->rcvidx, GFP_ATOMIC)))
+ printk(KERN_WARNING "HiSax: Amd7930: empty_Dfifo, D receive out of memory!\n");
+ else {
+ /* Debugging */
+ if (cs->debug & L1_DEB_ISAC_FIFO) {
+ char *t = cs->dlog;
+
+ t += sprintf(t, "Amd7930: empty_Dfifo cnt: %d |", cs->rcvidx);
+ QuickHex(t, cs->rcvbuf, cs->rcvidx);
+ debugl1(cs, cs->dlog);
+ }
+ /* moves received data in sk-buffer */
+ memcpy(skb_put(skb, cs->rcvidx), cs->rcvbuf, cs->rcvidx);
+ skb_queue_tail(&cs->rq, skb);
+ }
+ }
+
+ }
+ /* throw damaged packets away, reset receive-buffer, indicate RX */
+ ptr = cs->rcvbuf;
+ cs->rcvidx = 0;
+ schedule_event(cs, D_RCVBUFREADY);
+ }
+ }
+ /* Packet to long, overflow */
+ if(cs->rcvidx >= MAX_DFRAME_LEN_L1) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "AMD7930: empty_Dfifo L2-Framelength overrun");
+ cs->rcvidx = 0;
+ return;
+ }
+ /* AMD interrupts on */
+ AmdIrqOn(cs);
+}
+
+
+static void
+Amd7930_fill_Dfifo(struct IsdnCardState *cs)
+{
+
+ WORD dtcrr, dtcrw, len, count;
+ BYTE txstat, dmr3;
+ BYTE *ptr, *deb_ptr;
+
+ if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
+ debugl1(cs, "Amd7930: fill_Dfifo");
+
+ if ((!cs->tx_skb) || (cs->tx_skb->len <= 0))
+ return;
+
+ dtcrw = 0;
+ if(!cs->dc.amd7930.tx_xmtlen)
+ /* new Frame */
+ len = dtcrw = cs->tx_skb->len;
+ /* continue frame */
+ else len = cs->dc.amd7930.tx_xmtlen;
+
+
+ /* AMD interrupts off */
+ AmdIrqOff(cs);
+
+ deb_ptr = ptr = cs->tx_skb->data;
+
+ /* while free place in tx-fifo available and data in sk-buffer */
+ txstat = 0x10;
+ while((txstat & 0x10) && (cs->tx_cnt < len)) {
+ wByteAMD(cs, 0x04, *ptr);
+ ptr++;
+ cs->tx_cnt++;
+ txstat= rByteAMD(cs, 0x07);
+ }
+ count = ptr - cs->tx_skb->data;
+ skb_pull(cs->tx_skb, count);
+
+
+ dtcrr = rWordAMD(cs, 0x85); // DTCR
+ dmr3 = rByteAMD(cs, 0x8E);
+
+ if (cs->debug & L1_DEB_ISAC) {
+ debugl1(cs, "Amd7930: fill_Dfifo, DMR3: 0x%02X, DTCR read: 0x%04X write: 0x%02X 0x%02X", dmr3, dtcrr, LOBYTE(dtcrw), HIBYTE(dtcrw));
+ }
+
+ /* writeing of dtcrw starts transmit */
+ if(!cs->dc.amd7930.tx_xmtlen) {
+ wWordAMD(cs, 0x85, dtcrw);
+ cs->dc.amd7930.tx_xmtlen = dtcrw;
+ }
+
+ if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+ debugl1(cs, "Amd7930: fill_Dfifo dbusytimer running");
+ del_timer(&cs->dbusytimer);
+ }
+ init_timer(&cs->dbusytimer);
+ cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ) / 1000);
+ add_timer(&cs->dbusytimer);
+
+ if (cs->debug & L1_DEB_ISAC_FIFO) {
+ char *t = cs->dlog;
+
+ t += sprintf(t, "Amd7930: fill_Dfifo cnt: %d |", count);
+ QuickHex(t, deb_ptr, count);
+ debugl1(cs, cs->dlog);
+ }
+ /* AMD interrupts on */
+ AmdIrqOn(cs);
+}
+
+
+void Amd7930_interrupt(struct IsdnCardState *cs, BYTE irflags)
+{
+ BYTE dsr1, dsr2, lsr;
+ WORD der;
+
+ while (irflags)
+ {
+
+ dsr1 = rByteAMD(cs, 0x02);
+ der = rWordAMD(cs, 0x03);
+ dsr2 = rByteAMD(cs, 0x07);
+ lsr = rByteAMD(cs, 0xA1);
+
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "Amd7930: interrupt: flags: 0x%02X, DSR1: 0x%02X, DSR2: 0x%02X, LSR: 0x%02X, DER=0x%04X", irflags, dsr1, dsr2, lsr, der);
+
+ /* D error -> read DER and DSR2 bit 2 */
+ if (der || (dsr2 & 4)) {
+
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "Amd7930: interrupt: D error DER=0x%04X", der);
+
+ /* RX, TX abort if collision detected */
+ if (der & 2) {
+ wByteAMD(cs, 0x21, 0xC2);
+ wByteAMD(cs, 0x21, 0x02);
+ if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+ del_timer(&cs->dbusytimer);
+ if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+ schedule_event(cs, D_CLEARBUSY);
+ /* restart frame */
+ if (cs->tx_skb) {
+ skb_push(cs->tx_skb, cs->tx_cnt);
+ cs->tx_cnt = 0;
+ cs->dc.amd7930.tx_xmtlen = 0;
+ Amd7930_fill_Dfifo(cs);
+ } else {
+ printk(KERN_WARNING "HiSax: Amd7930 D-Collision, no skb\n");
+ debugl1(cs, "Amd7930: interrupt: D-Collision, no skb");
+ }
+ }
+ /* remove damaged data from fifo */
+ Amd7930_empty_Dfifo(cs, 1);
+
+ if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+ del_timer(&cs->dbusytimer);
+ if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+ schedule_event(cs, D_CLEARBUSY);
+ /* restart TX-Frame */
+ if (cs->tx_skb) {
+ skb_push(cs->tx_skb, cs->tx_cnt);
+ cs->tx_cnt = 0;
+ cs->dc.amd7930.tx_xmtlen = 0;
+ Amd7930_fill_Dfifo(cs);
+ }
+ }
+
+ /* D TX FIFO empty -> fill */
+ if (irflags & 1) {
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "Amd7930: interrupt: clear Timer and fill D-TX-FIFO if data");
+
+ /* AMD interrupts off */
+ AmdIrqOff(cs);
+
+ if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+ del_timer(&cs->dbusytimer);
+ if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+ schedule_event(cs, D_CLEARBUSY);
+ if (cs->tx_skb) {
+ if (cs->tx_skb->len)
+ Amd7930_fill_Dfifo(cs);
+ }
+ /* AMD interrupts on */
+ AmdIrqOn(cs);
+ }
+
+
+ /* D RX FIFO full or tiny packet in Fifo -> empty */
+ if ((irflags & 2) || (dsr1 & 2)) {
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "Amd7930: interrupt: empty D-FIFO");
+ Amd7930_empty_Dfifo(cs, 0);
+ }
+
+
+ /* D-Frame transmit complete */
+ if (dsr1 & 64) {
+ if (cs->debug & L1_DEB_ISAC) {
+ debugl1(cs, "Amd7930: interrupt: transmit packet ready");
+ }
+ /* AMD interrupts off */
+ AmdIrqOff(cs);
+
+ if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+ del_timer(&cs->dbusytimer);
+ if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+ schedule_event(cs, D_CLEARBUSY);
+
+ if (cs->tx_skb) {
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "Amd7930: interrupt: TX-Packet ready, freeing skb");
+ dev_kfree_skb_irq(cs->tx_skb);
+ cs->tx_cnt = 0;
+ cs->dc.amd7930.tx_xmtlen=0;
+ cs->tx_skb = NULL;
+ }
+ if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "Amd7930: interrupt: TX-Packet ready, next packet dequeued");
+ cs->tx_cnt = 0;
+ cs->dc.amd7930.tx_xmtlen=0;
+ Amd7930_fill_Dfifo(cs);
+ }
+ else
+ schedule_event(cs, D_XMTBUFREADY);
+ /* AMD interrupts on */
+ AmdIrqOn(cs);
+ }
+
+ /* LIU status interrupt -> read LSR, check statechanges */
+ if (lsr & 0x38) {
+ /* AMD interrupts off */
+ AmdIrqOff(cs);
+
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "Amd: interrupt: LSR=0x%02X, LIU is in state %d", lsr, ((lsr & 0x7) +2));
+
+ cs->dc.amd7930.ph_state = (lsr & 0x7) + 2;
+
+ schedule_event(cs, D_L1STATECHANGE);
+ /* AMD interrupts on */
+ AmdIrqOn(cs);
+ }
+
+ /* reads Interrupt-Register again. If there is a new interrupt-flag: restart handler */
+ irflags = rByteAMD(cs, 0x00);
+ }
+
+}
+
+static void
+Amd7930_l1hw(struct PStack *st, int pr, void *arg)
+{
+ struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+ struct sk_buff *skb = arg;
+ u_long flags;
+
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "Amd7930: l1hw called, pr: 0x%04X", pr);
+
+ switch (pr) {
+ case (PH_DATA | REQUEST):
+ if (cs->debug & DEB_DLOG_HEX)
+ LogFrame(cs, skb->data, skb->len);
+ if (cs->debug & DEB_DLOG_VERBOSE)
+ dlogframe(cs, skb, 0);
+ spin_lock_irqsave(&cs->lock, flags);
+ if (cs->tx_skb) {
+ skb_queue_tail(&cs->sq, skb);
+#ifdef L2FRAME_DEBUG /* psa */
+ if (cs->debug & L1_DEB_LAPD)
+ Logl2Frame(cs, skb, "Amd7930: l1hw: PH_DATA Queued", 0);
+#endif
+ } else {
+ cs->tx_skb = skb;
+ cs->tx_cnt = 0;
+ cs->dc.amd7930.tx_xmtlen=0;
+#ifdef L2FRAME_DEBUG /* psa */
+ if (cs->debug & L1_DEB_LAPD)
+ Logl2Frame(cs, skb, "Amd7930: l1hw: PH_DATA", 0);
+#endif
+ Amd7930_fill_Dfifo(cs);
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (PH_PULL | INDICATION):
+ spin_lock_irqsave(&cs->lock, flags);
+ if (cs->tx_skb) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "Amd7930: l1hw: l2l1 tx_skb exist this shouldn't happen");
+ skb_queue_tail(&cs->sq, skb);
+ break;
+ }
+ if (cs->debug & DEB_DLOG_HEX)
+ LogFrame(cs, skb->data, skb->len);
+ if (cs->debug & DEB_DLOG_VERBOSE)
+ dlogframe(cs, skb, 0);
+ cs->tx_skb = skb;
+ cs->tx_cnt = 0;
+ cs->dc.amd7930.tx_xmtlen=0;
+#ifdef L2FRAME_DEBUG /* psa */
+ if (cs->debug & L1_DEB_LAPD)
+ Logl2Frame(cs, skb, "Amd7930: l1hw: PH_DATA_PULLED", 0);
+#endif
+ Amd7930_fill_Dfifo(cs);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (PH_PULL | REQUEST):
+#ifdef L2FRAME_DEBUG /* psa */
+ if (cs->debug & L1_DEB_LAPD)
+ debugl1(cs, "Amd7930: l1hw: -> PH_REQUEST_PULL, skb: %s", (cs->tx_skb)? "yes":"no");
+#endif
+ if (!cs->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 (HW_RESET | REQUEST):
+ spin_lock_irqsave(&cs->lock, flags);
+ if ((cs->dc.amd7930.ph_state == 8)) {
+ /* b-channels off, PH-AR cleared
+ * change to F3 */
+ Amd7930_ph_command(cs, 0x20, "HW_RESET REQEST"); //LMR1 bit 5
+ spin_unlock_irqrestore(&cs->lock, flags);
+ } else {
+ Amd7930_ph_command(cs, 0x40, "HW_RESET REQUEST");
+ cs->dc.amd7930.ph_state = 2;
+ spin_unlock_irqrestore(&cs->lock, flags);
+ Amd7930_new_ph(cs);
+ }
+ break;
+ case (HW_ENABLE | REQUEST):
+ cs->dc.amd7930.ph_state = 9;
+ Amd7930_new_ph(cs);
+ break;
+ case (HW_INFO3 | REQUEST):
+ // automatic
+ break;
+ case (HW_TESTLOOP | REQUEST):
+ /* not implemented yet */
+ break;
+ case (HW_DEACTIVATE | RESPONSE):
+ skb_queue_purge(&cs->rq);
+ skb_queue_purge(&cs->sq);
+ if (cs->tx_skb) {
+ dev_kfree_skb(cs->tx_skb);
+ cs->tx_skb = NULL;
+ }
+ if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+ del_timer(&cs->dbusytimer);
+ if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+ schedule_event(cs, D_CLEARBUSY);
+ break;
+ default:
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "Amd7930: l1hw: unknown %04x", pr);
+ break;
+ }
+}
+
+static void
+setstack_Amd7930(struct PStack *st, struct IsdnCardState *cs)
+{
+
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "Amd7930: setstack called");
+
+ st->l1.l1hw = Amd7930_l1hw;
+}
+
+
+static void
+DC_Close_Amd7930(struct IsdnCardState *cs) {
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "Amd7930: DC_Close called");
+}
+
+
+static void
+dbusy_timer_handler(struct IsdnCardState *cs)
+{
+ u_long flags;
+ struct PStack *stptr;
+ WORD dtcr, der;
+ BYTE dsr1, dsr2;
+
+
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "Amd7930: dbusy_timer expired!");
+
+ if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+ spin_lock_irqsave(&cs->lock, flags);
+ /* D Transmit Byte Count Register:
+ * Counts down packet's number of Bytes, 0 if packet ready */
+ dtcr = rWordAMD(cs, 0x85);
+ dsr1 = rByteAMD(cs, 0x02);
+ dsr2 = rByteAMD(cs, 0x07);
+ der = rWordAMD(cs, 0x03);
+
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "Amd7930: dbusy_timer_handler: DSR1=0x%02X, DSR2=0x%02X, DER=0x%04X, cs->tx_skb->len=%u, tx_stat=%u, dtcr=%u, cs->tx_cnt=%u", dsr1, dsr2, der, cs->tx_skb->len, cs->dc.amd7930.tx_xmtlen, dtcr, cs->tx_cnt);
+
+ if ((cs->dc.amd7930.tx_xmtlen - dtcr) < cs->tx_cnt) { /* D-Channel Busy */
+ test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
+ stptr = cs->stlist;
+ spin_unlock_irqrestore(&cs->lock, flags);
+ while (stptr != NULL) {
+ stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL);
+ stptr = stptr->next;
+ }
+
+ } else {
+ /* discard frame; reset transceiver */
+ test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags);
+ if (cs->tx_skb) {
+ dev_kfree_skb_any(cs->tx_skb);
+ cs->tx_cnt = 0;
+ cs->tx_skb = NULL;
+ cs->dc.amd7930.tx_xmtlen = 0;
+ } else {
+ printk(KERN_WARNING "HiSax: Amd7930: D-Channel Busy no skb\n");
+ debugl1(cs, "Amd7930: D-Channel Busy no skb");
+
+ }
+ /* Transmitter reset, abort transmit */
+ wByteAMD(cs, 0x21, 0x82);
+ wByteAMD(cs, 0x21, 0x02);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ cs->irq_func(cs->irq, cs);
+
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "Amd7930: dbusy_timer_handler: Transmitter reset");
+ }
+ }
+}
+
+
+
+void Amd7930_init(struct IsdnCardState *cs)
+{
+ WORD *ptr;
+ BYTE cmd, cnt;
+
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "Amd7930: initamd called");
+
+ cs->dc.amd7930.tx_xmtlen = 0;
+ cs->dc.amd7930.old_state = 0;
+ cs->dc.amd7930.lmr1 = 0x40;
+ cs->dc.amd7930.ph_command = Amd7930_ph_command;
+ cs->setstack_d = setstack_Amd7930;
+ cs->DC_Close = DC_Close_Amd7930;
+
+ /* AMD Initialisation */
+ for (ptr = initAMD; *ptr != 0xFFFF; ) {
+ cmd = LOBYTE(*ptr);
+
+ /* read */
+ if (*ptr++ >= 0x100) {
+ if (cmd < 8)
+ /* reset register */
+ rByteAMD(cs, cmd);
+ else {
+ wByteAMD(cs, 0x00, cmd);
+ for (cnt = *ptr++; cnt > 0; cnt--)
+ rByteAMD(cs, 0x01);
+ }
+ }
+ /* write */
+ else if (cmd < 8)
+ wByteAMD(cs, cmd, LOBYTE(*ptr++));
+
+ else {
+ wByteAMD(cs, 0x00, cmd);
+ for (cnt = *ptr++; cnt > 0; cnt--)
+ wByteAMD(cs, 0x01, LOBYTE(*ptr++));
+ }
+ }
+}
+
+void __devinit
+setup_Amd7930(struct IsdnCardState *cs)
+{
+ INIT_WORK(&cs->tqueue, Amd7930_bh);
+ cs->dbusytimer.function = (void *) dbusy_timer_handler;
+ cs->dbusytimer.data = (long) cs;
+ init_timer(&cs->dbusytimer);
+}
diff --git a/drivers/isdn/hisax/amd7930_fn.h b/drivers/isdn/hisax/amd7930_fn.h
new file mode 100644
index 0000000..1f4d80c
--- /dev/null
+++ b/drivers/isdn/hisax/amd7930_fn.h
@@ -0,0 +1,37 @@
+/* drivers/isdn/hisax/amd7930_fn.h
+ *
+ * gerdes_amd7930.h Header-file included by
+ * gerdes_amd7930.c
+ *
+ * Author Christoph Ersfeld <info@formula-n.de>
+ * Formula-n Europe AG (www.formula-n.com)
+ * previously Gerdes AG
+ *
+ *
+ * This file is (c) under GNU PUBLIC LICENSE
+ */
+
+
+
+
+#define BYTE unsigned char
+#define WORD unsigned int
+#define rByteAMD(cs, reg) cs->readisac(cs, reg)
+#define wByteAMD(cs, reg, val) cs->writeisac(cs, reg, val)
+#define rWordAMD(cs, reg) ReadWordAmd7930(cs, reg)
+#define wWordAMD(cs, reg, val) WriteWordAmd7930(cs, reg, val)
+#define HIBYTE(w) ((unsigned char)((w & 0xff00) / 256))
+#define LOBYTE(w) ((unsigned char)(w & 0x00ff))
+
+#define AmdIrqOff(cs) cs->dc.amd7930.setIrqMask(cs, 0)
+#define AmdIrqOn(cs) cs->dc.amd7930.setIrqMask(cs, 1)
+
+#define AMD_CR 0x00
+#define AMD_DR 0x01
+
+
+#define DBUSY_TIMER_VALUE 80
+
+extern void Amd7930_interrupt(struct IsdnCardState *, unsigned char);
+extern void Amd7930_init(struct IsdnCardState *);
+extern void setup_Amd7930(struct IsdnCardState *);
diff --git a/drivers/isdn/hisax/arcofi.c b/drivers/isdn/hisax/arcofi.c
new file mode 100644
index 0000000..d30ce5b
--- /dev/null
+++ b/drivers/isdn/hisax/arcofi.c
@@ -0,0 +1,134 @@
+/* $Id: arcofi.c,v 1.14.2.3 2004/01/13 14:31:24 keil Exp $
+ *
+ * Ansteuerung ARCOFI 2165
+ *
+ * Author Karsten Keil
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include "hisax.h"
+#include "isdnl1.h"
+#include "isac.h"
+#include "arcofi.h"
+
+#define ARCOFI_TIMER_VALUE 20
+
+static void
+add_arcofi_timer(struct IsdnCardState *cs) {
+ if (test_and_set_bit(FLG_ARCOFI_TIMER, &cs->HW_Flags)) {
+ del_timer(&cs->dc.isac.arcofitimer);
+ }
+ init_timer(&cs->dc.isac.arcofitimer);
+ cs->dc.isac.arcofitimer.expires = jiffies + ((ARCOFI_TIMER_VALUE * HZ)/1000);
+ add_timer(&cs->dc.isac.arcofitimer);
+}
+
+static void
+send_arcofi(struct IsdnCardState *cs) {
+ u_char val;
+
+ add_arcofi_timer(cs);
+ cs->dc.isac.mon_txp = 0;
+ cs->dc.isac.mon_txc = cs->dc.isac.arcofi_list->len;
+ memcpy(cs->dc.isac.mon_tx, cs->dc.isac.arcofi_list->msg, cs->dc.isac.mon_txc);
+ switch(cs->dc.isac.arcofi_bc) {
+ case 0: break;
+ case 1: cs->dc.isac.mon_tx[1] |= 0x40;
+ break;
+ default: break;
+ }
+ cs->dc.isac.mocr &= 0x0f;
+ cs->dc.isac.mocr |= 0xa0;
+ cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+ val = cs->readisac(cs, ISAC_MOSR);
+ cs->writeisac(cs, ISAC_MOX1, cs->dc.isac.mon_tx[cs->dc.isac.mon_txp++]);
+ cs->dc.isac.mocr |= 0x10;
+ cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+}
+
+int
+arcofi_fsm(struct IsdnCardState *cs, int event, void *data) {
+ if (cs->debug & L1_DEB_MONITOR) {
+ debugl1(cs, "arcofi state %d event %d", cs->dc.isac.arcofi_state, event);
+ }
+ if (event == ARCOFI_TIMEOUT) {
+ cs->dc.isac.arcofi_state = ARCOFI_NOP;
+ test_and_set_bit(FLG_ARCOFI_ERROR, &cs->HW_Flags);
+ wake_up(&cs->dc.isac.arcofi_wait);
+ return(1);
+ }
+ switch (cs->dc.isac.arcofi_state) {
+ case ARCOFI_NOP:
+ if (event == ARCOFI_START) {
+ cs->dc.isac.arcofi_list = data;
+ cs->dc.isac.arcofi_state = ARCOFI_TRANSMIT;
+ send_arcofi(cs);
+ }
+ break;
+ case ARCOFI_TRANSMIT:
+ if (event == ARCOFI_TX_END) {
+ if (cs->dc.isac.arcofi_list->receive) {
+ add_arcofi_timer(cs);
+ cs->dc.isac.arcofi_state = ARCOFI_RECEIVE;
+ } else {
+ if (cs->dc.isac.arcofi_list->next) {
+ cs->dc.isac.arcofi_list =
+ cs->dc.isac.arcofi_list->next;
+ send_arcofi(cs);
+ } else {
+ if (test_and_clear_bit(FLG_ARCOFI_TIMER, &cs->HW_Flags)) {
+ del_timer(&cs->dc.isac.arcofitimer);
+ }
+ cs->dc.isac.arcofi_state = ARCOFI_NOP;
+ wake_up(&cs->dc.isac.arcofi_wait);
+ }
+ }
+ }
+ break;
+ case ARCOFI_RECEIVE:
+ if (event == ARCOFI_RX_END) {
+ if (cs->dc.isac.arcofi_list->next) {
+ cs->dc.isac.arcofi_list =
+ cs->dc.isac.arcofi_list->next;
+ cs->dc.isac.arcofi_state = ARCOFI_TRANSMIT;
+ send_arcofi(cs);
+ } else {
+ if (test_and_clear_bit(FLG_ARCOFI_TIMER, &cs->HW_Flags)) {
+ del_timer(&cs->dc.isac.arcofitimer);
+ }
+ cs->dc.isac.arcofi_state = ARCOFI_NOP;
+ wake_up(&cs->dc.isac.arcofi_wait);
+ }
+ }
+ break;
+ default:
+ debugl1(cs, "Arcofi unknown state %x", cs->dc.isac.arcofi_state);
+ return(2);
+ }
+ return(0);
+}
+
+static void
+arcofi_timer(struct IsdnCardState *cs) {
+ arcofi_fsm(cs, ARCOFI_TIMEOUT, NULL);
+}
+
+void
+clear_arcofi(struct IsdnCardState *cs) {
+ if (test_and_clear_bit(FLG_ARCOFI_TIMER, &cs->HW_Flags)) {
+ del_timer(&cs->dc.isac.arcofitimer);
+ }
+}
+
+void
+init_arcofi(struct IsdnCardState *cs) {
+ cs->dc.isac.arcofitimer.function = (void *) arcofi_timer;
+ cs->dc.isac.arcofitimer.data = (long) cs;
+ init_timer(&cs->dc.isac.arcofitimer);
+ init_waitqueue_head(&cs->dc.isac.arcofi_wait);
+ test_and_set_bit(HW_ARCOFI, &cs->HW_Flags);
+}
diff --git a/drivers/isdn/hisax/arcofi.h b/drivers/isdn/hisax/arcofi.h
new file mode 100644
index 0000000..00c44d3
--- /dev/null
+++ b/drivers/isdn/hisax/arcofi.h
@@ -0,0 +1,27 @@
+/* $Id: arcofi.h,v 1.6.6.2 2001/09/23 22:24:46 kai Exp $
+ *
+ * Ansteuerung ARCOFI 2165
+ *
+ * Author Karsten Keil
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#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
+
+extern int arcofi_fsm(struct IsdnCardState *cs, int event, void *data);
+extern void init_arcofi(struct IsdnCardState *cs);
+extern void clear_arcofi(struct IsdnCardState *cs);
diff --git a/drivers/isdn/hisax/asuscom.c b/drivers/isdn/hisax/asuscom.c
new file mode 100644
index 0000000..1f879b5
--- /dev/null
+++ b/drivers/isdn/hisax/asuscom.c
@@ -0,0 +1,424 @@
+/* $Id: asuscom.c,v 1.14.2.4 2004/01/13 23:48:39 keil Exp $
+ *
+ * low level stuff for ASUSCOM NETWORK INC. ISDNLink cards
+ *
+ * Author Karsten Keil
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to ASUSCOM NETWORK INC. Taiwan and Dynalink NL for information
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/isapnp.h>
+#include "hisax.h"
+#include "isac.h"
+#include "ipac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+static const char *Asuscom_revision = "$Revision: 1.14.2.4 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define ASUS_ISAC 0
+#define ASUS_HSCX 1
+#define ASUS_ADR 2
+#define ASUS_CTRL_U7 3
+#define ASUS_CTRL_POTS 5
+
+#define ASUS_IPAC_ALE 0
+#define ASUS_IPAC_DATA 1
+
+#define ASUS_ISACHSCX 1
+#define ASUS_IPAC 2
+
+/* CARD_ADR (Write) */
+#define ASUS_RESET 0x80 /* Bit 7 Reset-Leitung */
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+ register u_char ret;
+
+ byteout(ale, off);
+ ret = bytein(adr);
+ return (ret);
+}
+
+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(struct IsdnCardState *cs, u_char offset)
+{
+ return (readreg(cs->hw.asus.adr, cs->hw.asus.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ writereg(cs->hw.asus.adr, cs->hw.asus.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ readfifo(cs->hw.asus.adr, cs->hw.asus.isac, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ writefifo(cs->hw.asus.adr, cs->hw.asus.isac, 0, data, size);
+}
+
+static u_char
+ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset)
+{
+ return (readreg(cs->hw.asus.adr, cs->hw.asus.isac, offset|0x80));
+}
+
+static void
+WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ writereg(cs->hw.asus.adr, cs->hw.asus.isac, offset|0x80, value);
+}
+
+static void
+ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+ readfifo(cs->hw.asus.adr, cs->hw.asus.isac, 0x80, data, size);
+}
+
+static void
+WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+ writefifo(cs->hw.asus.adr, cs->hw.asus.isac, 0x80, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+ return (readreg(cs->hw.asus.adr,
+ cs->hw.asus.hscx, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+ writereg(cs->hw.asus.adr,
+ cs->hw.asus.hscx, offset + (hscx ? 0x40 : 0), value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.asus.adr, \
+ cs->hw.asus.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.asus.adr, \
+ cs->hw.asus.hscx, reg + (nr ? 0x40 : 0), data)
+
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.asus.adr, \
+ cs->hw.asus.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.asus.adr, \
+ cs->hw.asus.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+asuscom_interrupt(int intno, void *dev_id)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char val;
+ u_long flags;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40);
+ Start_HSCX:
+ if (val)
+ hscx_int_main(cs, val);
+ val = readreg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_ISTA);
+ Start_ISAC:
+ if (val)
+ isac_interrupt(cs, val);
+ val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40);
+ if (val) {
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "HSCX IntStat after IntRoutine");
+ goto Start_HSCX;
+ }
+ val = readreg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_ISTA);
+ if (val) {
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ISAC IntStat after IntRoutine");
+ goto Start_ISAC;
+ }
+ writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK, 0xFF);
+ writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK + 0x40, 0xFF);
+ writereg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_MASK, 0xFF);
+ writereg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_MASK, 0x0);
+ writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK, 0x0);
+ writereg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_MASK + 0x40, 0x0);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t
+asuscom_interrupt_ipac(int intno, void *dev_id)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char ista, val, icnt = 5;
+ u_long flags;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ ista = readreg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_ISTA);
+Start_IPAC:
+ if (cs->debug & L1_DEB_IPAC)
+ debugl1(cs, "IPAC ISTA %02X", ista);
+ if (ista & 0x0f) {
+ val = readreg(cs->hw.asus.adr, cs->hw.asus.hscx, HSCX_ISTA + 0x40);
+ if (ista & 0x01)
+ val |= 0x01;
+ if (ista & 0x04)
+ val |= 0x02;
+ if (ista & 0x08)
+ val |= 0x04;
+ if (val)
+ hscx_int_main(cs, val);
+ }
+ if (ista & 0x20) {
+ val = 0xfe & readreg(cs->hw.asus.adr, cs->hw.asus.isac, ISAC_ISTA | 0x80);
+ if (val) {
+ isac_interrupt(cs, val);
+ }
+ }
+ if (ista & 0x10) {
+ val = 0x01;
+ isac_interrupt(cs, val);
+ }
+ ista = readreg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_ISTA);
+ if ((ista & 0x3f) && icnt) {
+ icnt--;
+ goto Start_IPAC;
+ }
+ if (!icnt)
+ printk(KERN_WARNING "ASUS IRQ LOOP\n");
+ writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_MASK, 0xFF);
+ writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_MASK, 0xC0);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static void
+release_io_asuscom(struct IsdnCardState *cs)
+{
+ int bytecnt = 8;
+
+ if (cs->hw.asus.cfg_reg)
+ release_region(cs->hw.asus.cfg_reg, bytecnt);
+}
+
+static void
+reset_asuscom(struct IsdnCardState *cs)
+{
+ if (cs->subtyp == ASUS_IPAC)
+ writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_POTA2, 0x20);
+ else
+ byteout(cs->hw.asus.adr, ASUS_RESET); /* Reset On */
+ mdelay(10);
+ if (cs->subtyp == ASUS_IPAC)
+ writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_POTA2, 0x0);
+ else
+ byteout(cs->hw.asus.adr, 0); /* Reset Off */
+ mdelay(10);
+ if (cs->subtyp == ASUS_IPAC) {
+ writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_CONF, 0x0);
+ writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_ACFG, 0xff);
+ writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_AOE, 0x0);
+ writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_MASK, 0xc0);
+ writereg(cs->hw.asus.adr, cs->hw.asus.isac, IPAC_PCFG, 0x12);
+ }
+}
+
+static int
+Asus_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ u_long flags;
+
+ switch (mt) {
+ case CARD_RESET:
+ spin_lock_irqsave(&cs->lock, flags);
+ reset_asuscom(cs);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return(0);
+ case CARD_RELEASE:
+ release_io_asuscom(cs);
+ return(0);
+ case CARD_INIT:
+ spin_lock_irqsave(&cs->lock, flags);
+ cs->debug |= L1_DEB_IPAC;
+ inithscxisac(cs, 3);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return(0);
+ case CARD_TEST:
+ return(0);
+ }
+ return(0);
+}
+
+#ifdef __ISAPNP__
+static struct isapnp_device_id asus_ids[] __devinitdata = {
+ { ISAPNP_VENDOR('A', 'S', 'U'), ISAPNP_FUNCTION(0x1688),
+ ISAPNP_VENDOR('A', 'S', 'U'), ISAPNP_FUNCTION(0x1688),
+ (unsigned long) "Asus1688 PnP" },
+ { ISAPNP_VENDOR('A', 'S', 'U'), ISAPNP_FUNCTION(0x1690),
+ ISAPNP_VENDOR('A', 'S', 'U'), ISAPNP_FUNCTION(0x1690),
+ (unsigned long) "Asus1690 PnP" },
+ { ISAPNP_VENDOR('S', 'I', 'E'), ISAPNP_FUNCTION(0x0020),
+ ISAPNP_VENDOR('S', 'I', 'E'), ISAPNP_FUNCTION(0x0020),
+ (unsigned long) "Isurf2 PnP" },
+ { ISAPNP_VENDOR('E', 'L', 'F'), ISAPNP_FUNCTION(0x0000),
+ ISAPNP_VENDOR('E', 'L', 'F'), ISAPNP_FUNCTION(0x0000),
+ (unsigned long) "Iscas TE320" },
+ { 0, }
+};
+
+static struct isapnp_device_id *ipid __devinitdata = &asus_ids[0];
+static struct pnp_card *pnp_c __devinitdata = NULL;
+#endif
+
+int __devinit
+setup_asuscom(struct IsdnCard *card)
+{
+ int bytecnt;
+ struct IsdnCardState *cs = card->cs;
+ u_char val;
+ char tmp[64];
+
+ strcpy(tmp, Asuscom_revision);
+ printk(KERN_INFO "HiSax: Asuscom ISDNLink driver Rev. %s\n", HiSax_getrev(tmp));
+ if (cs->typ != ISDN_CTYPE_ASUSCOM)
+ return (0);
+#ifdef __ISAPNP__
+ if (!card->para[1] && isapnp_present()) {
+ struct pnp_dev *pnp_d;
+ while(ipid->card_vendor) {
+ if ((pnp_c = pnp_find_card(ipid->card_vendor,
+ ipid->card_device, pnp_c))) {
+ pnp_d = NULL;
+ if ((pnp_d = pnp_find_dev(pnp_c,
+ ipid->vendor, ipid->function, pnp_d))) {
+ int err;
+
+ printk(KERN_INFO "HiSax: %s detected\n",
+ (char *)ipid->driver_data);
+ pnp_disable_dev(pnp_d);
+ err = pnp_activate_dev(pnp_d);
+ if (err<0) {
+ printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
+ __func__, err);
+ return(0);
+ }
+ card->para[1] = pnp_port_start(pnp_d, 0);
+ card->para[0] = pnp_irq(pnp_d, 0);
+ if (!card->para[0] || !card->para[1]) {
+ printk(KERN_ERR "AsusPnP:some resources are missing %ld/%lx\n",
+ card->para[0], card->para[1]);
+ pnp_disable_dev(pnp_d);
+ return(0);
+ }
+ break;
+ } else {
+ printk(KERN_ERR "AsusPnP: PnP error card found, no device\n");
+ }
+ }
+ ipid++;
+ pnp_c = NULL;
+ }
+ if (!ipid->card_vendor) {
+ printk(KERN_INFO "AsusPnP: no ISAPnP card found\n");
+ return(0);
+ }
+ }
+#endif
+ bytecnt = 8;
+ cs->hw.asus.cfg_reg = card->para[1];
+ cs->irq = card->para[0];
+ if (!request_region(cs->hw.asus.cfg_reg, bytecnt, "asuscom isdn")) {
+ printk(KERN_WARNING
+ "HiSax: ISDNLink config port %x-%x already in use\n",
+ cs->hw.asus.cfg_reg,
+ cs->hw.asus.cfg_reg + bytecnt);
+ return (0);
+ }
+ printk(KERN_INFO "ISDNLink: defined at 0x%x IRQ %d\n",
+ cs->hw.asus.cfg_reg, cs->irq);
+ setup_isac(cs);
+ cs->BC_Read_Reg = &ReadHSCX;
+ cs->BC_Write_Reg = &WriteHSCX;
+ cs->BC_Send_Data = &hscx_fill_fifo;
+ cs->cardmsg = &Asus_card_msg;
+ val = readreg(cs->hw.asus.cfg_reg + ASUS_IPAC_ALE,
+ cs->hw.asus.cfg_reg + ASUS_IPAC_DATA, IPAC_ID);
+ if ((val == 1) || (val == 2)) {
+ cs->subtyp = ASUS_IPAC;
+ cs->hw.asus.adr = cs->hw.asus.cfg_reg + ASUS_IPAC_ALE;
+ cs->hw.asus.isac = cs->hw.asus.cfg_reg + ASUS_IPAC_DATA;
+ cs->hw.asus.hscx = cs->hw.asus.cfg_reg + ASUS_IPAC_DATA;
+ test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+ cs->readisac = &ReadISAC_IPAC;
+ cs->writeisac = &WriteISAC_IPAC;
+ cs->readisacfifo = &ReadISACfifo_IPAC;
+ cs->writeisacfifo = &WriteISACfifo_IPAC;
+ cs->irq_func = &asuscom_interrupt_ipac;
+ printk(KERN_INFO "Asus: IPAC version %x\n", val);
+ } else {
+ cs->subtyp = ASUS_ISACHSCX;
+ cs->hw.asus.adr = cs->hw.asus.cfg_reg + ASUS_ADR;
+ cs->hw.asus.isac = cs->hw.asus.cfg_reg + ASUS_ISAC;
+ cs->hw.asus.hscx = cs->hw.asus.cfg_reg + ASUS_HSCX;
+ cs->hw.asus.u7 = cs->hw.asus.cfg_reg + ASUS_CTRL_U7;
+ cs->hw.asus.pots = cs->hw.asus.cfg_reg + ASUS_CTRL_POTS;
+ cs->readisac = &ReadISAC;
+ cs->writeisac = &WriteISAC;
+ cs->readisacfifo = &ReadISACfifo;
+ cs->writeisacfifo = &WriteISACfifo;
+ cs->irq_func = &asuscom_interrupt;
+ ISACVersion(cs, "ISDNLink:");
+ if (HscxVersion(cs, "ISDNLink:")) {
+ printk(KERN_WARNING
+ "ISDNLink: wrong HSCX versions check IO address\n");
+ release_io_asuscom(cs);
+ return (0);
+ }
+ }
+ return (1);
+}
diff --git a/drivers/isdn/hisax/avm_a1.c b/drivers/isdn/hisax/avm_a1.c
new file mode 100644
index 0000000..eb6b432
--- /dev/null
+++ b/drivers/isdn/hisax/avm_a1.c
@@ -0,0 +1,308 @@
+/* $Id: avm_a1.c,v 2.15.2.4 2004/01/13 21:46:03 keil Exp $
+ *
+ * low level stuff for AVM A1 (Fritz) isdn cards
+ *
+ * Author Karsten Keil
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+static const char *avm_revision = "$Revision: 2.15.2.4 $";
+
+#define AVM_A1_STAT_ISAC 0x01
+#define AVM_A1_STAT_HSCX 0x02
+#define AVM_A1_STAT_TIMER 0x04
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+static inline u_char
+readreg(unsigned int adr, u_char off)
+{
+ return (bytein(adr + off));
+}
+
+static inline void
+writereg(unsigned int adr, u_char off, u_char data)
+{
+ byteout(adr + off, data);
+}
+
+
+static inline void
+read_fifo(unsigned int adr, u_char * data, int size)
+{
+ insb(adr, data, size);
+}
+
+static void
+write_fifo(unsigned int adr, u_char * data, int size)
+{
+ outsb(adr, data, size);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+ return (readreg(cs->hw.avm.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ writereg(cs->hw.avm.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ read_fifo(cs->hw.avm.isacfifo, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ write_fifo(cs->hw.avm.isacfifo, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+ return (readreg(cs->hw.avm.hscx[hscx], offset));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+ writereg(cs->hw.avm.hscx[hscx], offset, value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.avm.hscx[nr], reg)
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.avm.hscx[nr], reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.avm.hscxfifo[nr], ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.avm.hscxfifo[nr], ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+avm_a1_interrupt(int intno, void *dev_id)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char val, sval;
+ u_long flags;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ while (((sval = bytein(cs->hw.avm.cfg_reg)) & 0xf) != 0x7) {
+ if (!(sval & AVM_A1_STAT_TIMER)) {
+ byteout(cs->hw.avm.cfg_reg, 0x1E);
+ sval = bytein(cs->hw.avm.cfg_reg);
+ } else if (cs->debug & L1_DEB_INTSTAT)
+ debugl1(cs, "avm IntStatus %x", sval);
+ if (!(sval & AVM_A1_STAT_HSCX)) {
+ val = readreg(cs->hw.avm.hscx[1], HSCX_ISTA);
+ if (val)
+ hscx_int_main(cs, val);
+ }
+ if (!(sval & AVM_A1_STAT_ISAC)) {
+ val = readreg(cs->hw.avm.isac, ISAC_ISTA);
+ if (val)
+ isac_interrupt(cs, val);
+ }
+ }
+ writereg(cs->hw.avm.hscx[0], HSCX_MASK, 0xFF);
+ writereg(cs->hw.avm.hscx[1], HSCX_MASK, 0xFF);
+ writereg(cs->hw.avm.isac, ISAC_MASK, 0xFF);
+ writereg(cs->hw.avm.isac, ISAC_MASK, 0x0);
+ writereg(cs->hw.avm.hscx[0], HSCX_MASK, 0x0);
+ writereg(cs->hw.avm.hscx[1], HSCX_MASK, 0x0);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static inline void
+release_ioregs(struct IsdnCardState *cs, int mask)
+{
+ release_region(cs->hw.avm.cfg_reg, 8);
+ if (mask & 1)
+ release_region(cs->hw.avm.isac + 32, 32);
+ if (mask & 2)
+ release_region(cs->hw.avm.isacfifo, 1);
+ if (mask & 4)
+ release_region(cs->hw.avm.hscx[0] + 32, 32);
+ if (mask & 8)
+ release_region(cs->hw.avm.hscxfifo[0], 1);
+ if (mask & 0x10)
+ release_region(cs->hw.avm.hscx[1] + 32, 32);
+ if (mask & 0x20)
+ release_region(cs->hw.avm.hscxfifo[1], 1);
+}
+
+static int
+AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ u_long flags;
+
+ switch (mt) {
+ case CARD_RESET:
+ return(0);
+ case CARD_RELEASE:
+ release_ioregs(cs, 0x3f);
+ return(0);
+ case CARD_INIT:
+ spin_lock_irqsave(&cs->lock, flags);
+ inithscxisac(cs, 1);
+ byteout(cs->hw.avm.cfg_reg, 0x16);
+ byteout(cs->hw.avm.cfg_reg, 0x1E);
+ inithscxisac(cs, 2);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return(0);
+ case CARD_TEST:
+ return(0);
+ }
+ return(0);
+}
+
+int __devinit
+setup_avm_a1(struct IsdnCard *card)
+{
+ u_char val;
+ struct IsdnCardState *cs = card->cs;
+ char tmp[64];
+
+ strcpy(tmp, avm_revision);
+ printk(KERN_INFO "HiSax: AVM driver Rev. %s\n", HiSax_getrev(tmp));
+ if (cs->typ != ISDN_CTYPE_A1)
+ return (0);
+
+ cs->hw.avm.cfg_reg = card->para[1] + 0x1800;
+ cs->hw.avm.isac = card->para[1] + 0x1400 - 0x20;
+ cs->hw.avm.hscx[0] = card->para[1] + 0x400 - 0x20;
+ cs->hw.avm.hscx[1] = card->para[1] + 0xc00 - 0x20;
+ cs->hw.avm.isacfifo = card->para[1] + 0x1000;
+ cs->hw.avm.hscxfifo[0] = card->para[1];
+ cs->hw.avm.hscxfifo[1] = card->para[1] + 0x800;
+ cs->irq = card->para[0];
+ if (!request_region(cs->hw.avm.cfg_reg, 8, "avm cfg")) {
+ printk(KERN_WARNING
+ "HiSax: AVM A1 config port %x-%x already in use\n",
+ cs->hw.avm.cfg_reg,
+ cs->hw.avm.cfg_reg + 8);
+ return (0);
+ }
+ if (!request_region(cs->hw.avm.isac + 32, 32, "HiSax isac")) {
+ printk(KERN_WARNING
+ "HiSax: AVM A1 isac ports %x-%x already in use\n",
+ cs->hw.avm.isac + 32,
+ cs->hw.avm.isac + 64);
+ release_ioregs(cs, 0);
+ return (0);
+ }
+ if (!request_region(cs->hw.avm.isacfifo, 1, "HiSax isac fifo")) {
+ printk(KERN_WARNING
+ "HiSax: AVM A1 isac fifo port %x already in use\n",
+ cs->hw.avm.isacfifo);
+ release_ioregs(cs, 1);
+ return (0);
+ }
+ if (!request_region(cs->hw.avm.hscx[0] + 32, 32, "HiSax hscx A")) {
+ printk(KERN_WARNING
+ "HiSax: AVM A1 hscx A ports %x-%x already in use\n",
+ cs->hw.avm.hscx[0] + 32,
+ cs->hw.avm.hscx[0] + 64);
+ release_ioregs(cs, 3);
+ return (0);
+ }
+ if (!request_region(cs->hw.avm.hscxfifo[0], 1, "HiSax hscx A fifo")) {
+ printk(KERN_WARNING
+ "HiSax: AVM A1 hscx A fifo port %x already in use\n",
+ cs->hw.avm.hscxfifo[0]);
+ release_ioregs(cs, 7);
+ return (0);
+ }
+ if (!request_region(cs->hw.avm.hscx[1] + 32, 32, "HiSax hscx B")) {
+ printk(KERN_WARNING
+ "HiSax: AVM A1 hscx B ports %x-%x already in use\n",
+ cs->hw.avm.hscx[1] + 32,
+ cs->hw.avm.hscx[1] + 64);
+ release_ioregs(cs, 0xf);
+ return (0);
+ }
+ if (!request_region(cs->hw.avm.hscxfifo[1], 1, "HiSax hscx B fifo")) {
+ printk(KERN_WARNING
+ "HiSax: AVM A1 hscx B fifo port %x already in use\n",
+ cs->hw.avm.hscxfifo[1]);
+ release_ioregs(cs, 0x1f);
+ return (0);
+ }
+ byteout(cs->hw.avm.cfg_reg, 0x0);
+ HZDELAY(HZ / 5 + 1);
+ byteout(cs->hw.avm.cfg_reg, 0x1);
+ HZDELAY(HZ / 5 + 1);
+ byteout(cs->hw.avm.cfg_reg, 0x0);
+ HZDELAY(HZ / 5 + 1);
+ val = cs->irq;
+ if (val == 9)
+ val = 2;
+ byteout(cs->hw.avm.cfg_reg + 1, val);
+ HZDELAY(HZ / 5 + 1);
+ byteout(cs->hw.avm.cfg_reg, 0x0);
+ HZDELAY(HZ / 5 + 1);
+
+ val = bytein(cs->hw.avm.cfg_reg);
+ printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
+ cs->hw.avm.cfg_reg, val);
+ val = bytein(cs->hw.avm.cfg_reg + 3);
+ printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
+ cs->hw.avm.cfg_reg + 3, val);
+ val = bytein(cs->hw.avm.cfg_reg + 2);
+ printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
+ cs->hw.avm.cfg_reg + 2, val);
+ val = bytein(cs->hw.avm.cfg_reg);
+ printk(KERN_INFO "AVM A1: Byte at %x is %x\n",
+ cs->hw.avm.cfg_reg, val);
+
+ printk(KERN_INFO "HiSax: AVM A1 config irq:%d cfg:0x%X\n",
+ cs->irq,
+ cs->hw.avm.cfg_reg);
+ printk(KERN_INFO
+ "HiSax: isac:0x%X/0x%X\n",
+ cs->hw.avm.isac + 32, cs->hw.avm.isacfifo);
+ printk(KERN_INFO
+ "HiSax: hscx A:0x%X/0x%X hscx B:0x%X/0x%X\n",
+ cs->hw.avm.hscx[0] + 32, cs->hw.avm.hscxfifo[0],
+ cs->hw.avm.hscx[1] + 32, cs->hw.avm.hscxfifo[1]);
+
+ cs->readisac = &ReadISAC;
+ cs->writeisac = &WriteISAC;
+ cs->readisacfifo = &ReadISACfifo;
+ cs->writeisacfifo = &WriteISACfifo;
+ cs->BC_Read_Reg = &ReadHSCX;
+ cs->BC_Write_Reg = &WriteHSCX;
+ cs->BC_Send_Data = &hscx_fill_fifo;
+ setup_isac(cs);
+ cs->cardmsg = &AVM_card_msg;
+ cs->irq_func = &avm_a1_interrupt;
+ ISACVersion(cs, "AVM A1:");
+ if (HscxVersion(cs, "AVM A1:")) {
+ printk(KERN_WARNING
+ "AVM A1: wrong HSCX versions check IO address\n");
+ release_ioregs(cs, 0x3f);
+ return (0);
+ }
+ return (1);
+}
diff --git a/drivers/isdn/hisax/avm_a1p.c b/drivers/isdn/hisax/avm_a1p.c
new file mode 100644
index 0000000..3039c6d
--- /dev/null
+++ b/drivers/isdn/hisax/avm_a1p.c
@@ -0,0 +1,267 @@
+/* $Id: avm_a1p.c,v 2.9.2.5 2004/01/24 20:47:19 keil Exp $
+ *
+ * low level stuff for the following AVM cards:
+ * A1 PCMCIA
+ * FRITZ!Card PCMCIA
+ * FRITZ!Card PCMCIA 2.0
+ *
+ * Author Carsten Paeth
+ * Copyright by Carsten Paeth <calle@calle.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+/* register offsets */
+#define ADDRREG_OFFSET 0x02
+#define DATAREG_OFFSET 0x03
+#define ASL0_OFFSET 0x04
+#define ASL1_OFFSET 0x05
+#define MODREG_OFFSET 0x06
+#define VERREG_OFFSET 0x07
+
+/* address offsets */
+#define ISAC_FIFO_OFFSET 0x00
+#define ISAC_REG_OFFSET 0x20
+#define HSCX_CH_DIFF 0x40
+#define HSCX_FIFO_OFFSET 0x80
+#define HSCX_REG_OFFSET 0xa0
+
+/* read bits ASL0 */
+#define ASL0_R_TIMER 0x10 /* active low */
+#define ASL0_R_ISAC 0x20 /* active low */
+#define ASL0_R_HSCX 0x40 /* active low */
+#define ASL0_R_TESTBIT 0x80
+#define ASL0_R_IRQPENDING (ASL0_R_ISAC|ASL0_R_HSCX|ASL0_R_TIMER)
+
+/* write bits ASL0 */
+#define ASL0_W_RESET 0x01
+#define ASL0_W_TDISABLE 0x02
+#define ASL0_W_TRESET 0x04
+#define ASL0_W_IRQENABLE 0x08
+#define ASL0_W_TESTBIT 0x80
+
+/* write bits ASL1 */
+#define ASL1_W_LED0 0x10
+#define ASL1_W_LED1 0x20
+#define ASL1_W_ENABLE_S0 0xC0
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+static const char *avm_revision = "$Revision: 2.9.2.5 $";
+
+static inline u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+ u_char ret;
+
+ offset -= 0x20;
+ byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_REG_OFFSET+offset);
+ ret = bytein(cs->hw.avm.cfg_reg+DATAREG_OFFSET);
+ return ret;
+}
+
+static inline void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ offset -= 0x20;
+ byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_REG_OFFSET+offset);
+ byteout(cs->hw.avm.cfg_reg+DATAREG_OFFSET, value);
+}
+
+static inline void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_FIFO_OFFSET);
+ insb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size);
+}
+
+static inline void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,ISAC_FIFO_OFFSET);
+ outsb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size);
+}
+
+static inline u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+ u_char ret;
+
+ offset -= 0x20;
+ byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,
+ HSCX_REG_OFFSET+hscx*HSCX_CH_DIFF+offset);
+ ret = bytein(cs->hw.avm.cfg_reg+DATAREG_OFFSET);
+ return ret;
+}
+
+static inline void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+ offset -= 0x20;
+ byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,
+ HSCX_REG_OFFSET+hscx*HSCX_CH_DIFF+offset);
+ byteout(cs->hw.avm.cfg_reg+DATAREG_OFFSET, value);
+}
+
+static inline void
+ReadHSCXfifo(struct IsdnCardState *cs, int hscx, u_char * data, int size)
+{
+ byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,
+ HSCX_FIFO_OFFSET+hscx*HSCX_CH_DIFF);
+ insb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size);
+}
+
+static inline void
+WriteHSCXfifo(struct IsdnCardState *cs, int hscx, u_char * data, int size)
+{
+ byteout(cs->hw.avm.cfg_reg+ADDRREG_OFFSET,
+ HSCX_FIFO_OFFSET+hscx*HSCX_CH_DIFF);
+ outsb(cs->hw.avm.cfg_reg+DATAREG_OFFSET, data, size);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) ReadHSCX(cs, nr, reg)
+#define WRITEHSCX(cs, nr, reg, data) WriteHSCX(cs, nr, reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) ReadHSCXfifo(cs, nr, ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) WriteHSCXfifo(cs, nr, ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+avm_a1p_interrupt(int intno, void *dev_id)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char val, sval;
+ u_long flags;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ while ((sval = (~bytein(cs->hw.avm.cfg_reg+ASL0_OFFSET) & ASL0_R_IRQPENDING))) {
+ if (cs->debug & L1_DEB_INTSTAT)
+ debugl1(cs, "avm IntStatus %x", sval);
+ if (sval & ASL0_R_HSCX) {
+ val = ReadHSCX(cs, 1, HSCX_ISTA);
+ if (val)
+ hscx_int_main(cs, val);
+ }
+ if (sval & ASL0_R_ISAC) {
+ val = ReadISAC(cs, ISAC_ISTA);
+ if (val)
+ isac_interrupt(cs, val);
+ }
+ }
+ WriteHSCX(cs, 0, HSCX_MASK, 0xff);
+ WriteHSCX(cs, 1, HSCX_MASK, 0xff);
+ WriteISAC(cs, ISAC_MASK, 0xff);
+ WriteISAC(cs, ISAC_MASK, 0x00);
+ WriteHSCX(cs, 0, HSCX_MASK, 0x00);
+ WriteHSCX(cs, 1, HSCX_MASK, 0x00);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static int
+AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ u_long flags;
+
+ switch (mt) {
+ case CARD_RESET:
+ spin_lock_irqsave(&cs->lock, flags);
+ byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00);
+ HZDELAY(HZ / 5 + 1);
+ byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,ASL0_W_RESET);
+ HZDELAY(HZ / 5 + 1);
+ byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return 0;
+
+ case CARD_RELEASE:
+ /* free_irq is done in HiSax_closecard(). */
+ /* free_irq(cs->irq, cs); */
+ return 0;
+
+ case CARD_INIT:
+ spin_lock_irqsave(&cs->lock, flags);
+ byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,ASL0_W_TDISABLE|ASL0_W_TRESET|ASL0_W_IRQENABLE);
+ clear_pending_isac_ints(cs);
+ clear_pending_hscx_ints(cs);
+ inithscxisac(cs, 1);
+ inithscxisac(cs, 2);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return 0;
+
+ case CARD_TEST:
+ /* we really don't need it for the PCMCIA Version */
+ return 0;
+
+ default:
+ /* all card drivers ignore others, so we do the same */
+ return 0;
+ }
+ return 0;
+}
+
+int __devinit setup_avm_a1_pcmcia(struct IsdnCard *card)
+{
+ u_char model, vers;
+ struct IsdnCardState *cs = card->cs;
+ char tmp[64];
+
+
+ strcpy(tmp, avm_revision);
+ printk(KERN_INFO "HiSax: AVM A1 PCMCIA driver Rev. %s\n",
+ HiSax_getrev(tmp));
+ if (cs->typ != ISDN_CTYPE_A1_PCMCIA)
+ return (0);
+
+ cs->hw.avm.cfg_reg = card->para[1];
+ cs->irq = card->para[0];
+
+
+ byteout(cs->hw.avm.cfg_reg+ASL1_OFFSET, ASL1_W_ENABLE_S0);
+ byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00);
+ HZDELAY(HZ / 5 + 1);
+ byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,ASL0_W_RESET);
+ HZDELAY(HZ / 5 + 1);
+ byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET,0x00);
+
+ byteout(cs->hw.avm.cfg_reg+ASL0_OFFSET, ASL0_W_TDISABLE|ASL0_W_TRESET);
+
+ model = bytein(cs->hw.avm.cfg_reg+MODREG_OFFSET);
+ vers = bytein(cs->hw.avm.cfg_reg+VERREG_OFFSET);
+
+ printk(KERN_INFO "AVM A1 PCMCIA: io 0x%x irq %d model %d version %d\n",
+ cs->hw.avm.cfg_reg, cs->irq, model, vers);
+
+ setup_isac(cs);
+ cs->readisac = &ReadISAC;
+ cs->writeisac = &WriteISAC;
+ cs->readisacfifo = &ReadISACfifo;
+ cs->writeisacfifo = &WriteISACfifo;
+ cs->BC_Read_Reg = &ReadHSCX;
+ cs->BC_Write_Reg = &WriteHSCX;
+ cs->BC_Send_Data = &hscx_fill_fifo;
+ cs->cardmsg = &AVM_card_msg;
+ cs->irq_flags = IRQF_SHARED;
+ cs->irq_func = &avm_a1p_interrupt;
+
+ ISACVersion(cs, "AVM A1 PCMCIA:");
+ if (HscxVersion(cs, "AVM A1 PCMCIA:")) {
+ printk(KERN_WARNING
+ "AVM A1 PCMCIA: wrong HSCX versions check IO address\n");
+ return (0);
+ }
+ return (1);
+}
diff --git a/drivers/isdn/hisax/avm_pci.c b/drivers/isdn/hisax/avm_pci.c
new file mode 100644
index 0000000..7cabc5a
--- /dev/null
+++ b/drivers/isdn/hisax/avm_pci.c
@@ -0,0 +1,902 @@
+/* $Id: avm_pci.c,v 1.29.2.4 2004/02/11 13:21:32 keil Exp $
+ *
+ * low level stuff for AVM Fritz!PCI and ISA PnP isdn cards
+ *
+ * Author Karsten Keil
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to AVM, Berlin for information
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/isapnp.h>
+#include <linux/interrupt.h>
+
+static const char *avm_pci_rev = "$Revision: 1.29.2.4 $";
+
+#define AVM_FRITZ_PCI 1
+#define AVM_FRITZ_PNP 2
+
+#define HDLC_FIFO 0x0
+#define HDLC_STATUS 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
+
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+ register u_char idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW;
+ register u_char val;
+
+ outb(idx, cs->hw.avm.cfg_reg + 4);
+ val = inb(cs->hw.avm.isac + (offset & 0xf));
+ return (val);
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ register u_char idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW;
+
+ outb(idx, cs->hw.avm.cfg_reg + 4);
+ outb(value, cs->hw.avm.isac + (offset & 0xf));
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ outb(AVM_ISAC_FIFO, cs->hw.avm.cfg_reg + 4);
+ insb(cs->hw.avm.isac, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ outb(AVM_ISAC_FIFO, cs->hw.avm.cfg_reg + 4);
+ outsb(cs->hw.avm.isac, data, size);
+}
+
+static inline u_int
+ReadHDLCPCI(struct IsdnCardState *cs, int chan, u_char offset)
+{
+ register u_int idx = chan ? AVM_HDLC_2 : AVM_HDLC_1;
+ register u_int val;
+
+ outl(idx, cs->hw.avm.cfg_reg + 4);
+ val = inl(cs->hw.avm.isac + offset);
+ return (val);
+}
+
+static inline void
+WriteHDLCPCI(struct IsdnCardState *cs, int chan, u_char offset, u_int value)
+{
+ register u_int idx = chan ? AVM_HDLC_2 : AVM_HDLC_1;
+
+ outl(idx, cs->hw.avm.cfg_reg + 4);
+ outl(value, cs->hw.avm.isac + offset);
+}
+
+static inline u_char
+ReadHDLCPnP(struct IsdnCardState *cs, int chan, u_char offset)
+{
+ register u_char idx = chan ? AVM_HDLC_2 : AVM_HDLC_1;
+ register u_char val;
+
+ outb(idx, cs->hw.avm.cfg_reg + 4);
+ val = inb(cs->hw.avm.isac + offset);
+ return (val);
+}
+
+static inline void
+WriteHDLCPnP(struct IsdnCardState *cs, int chan, u_char offset, u_char value)
+{
+ register u_char idx = chan ? AVM_HDLC_2 : AVM_HDLC_1;
+
+ outb(idx, cs->hw.avm.cfg_reg + 4);
+ outb(value, cs->hw.avm.isac + offset);
+}
+
+static u_char
+ReadHDLC_s(struct IsdnCardState *cs, int chan, u_char offset)
+{
+ return(0xff & ReadHDLCPCI(cs, chan, offset));
+}
+
+static void
+WriteHDLC_s(struct IsdnCardState *cs, int chan, u_char offset, u_char value)
+{
+ WriteHDLCPCI(cs, chan, offset, value);
+}
+
+static inline
+struct BCState *Sel_BCS(struct IsdnCardState *cs, int channel)
+{
+ if (cs->bcs[0].mode && (cs->bcs[0].channel == channel))
+ return(&cs->bcs[0]);
+ else if (cs->bcs[1].mode && (cs->bcs[1].channel == channel))
+ return(&cs->bcs[1]);
+ else
+ return(NULL);
+}
+
+static void
+write_ctrl(struct BCState *bcs, int which) {
+
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs, "hdlc %c wr%x ctrl %x",
+ 'A' + bcs->channel, which, bcs->hw.hdlc.ctrl.ctrl);
+ if (bcs->cs->subtyp == AVM_FRITZ_PCI) {
+ WriteHDLCPCI(bcs->cs, bcs->channel, HDLC_STATUS, bcs->hw.hdlc.ctrl.ctrl);
+ } else {
+ if (which & 4)
+ WriteHDLCPnP(bcs->cs, bcs->channel, HDLC_STATUS + 2,
+ bcs->hw.hdlc.ctrl.sr.mode);
+ if (which & 2)
+ WriteHDLCPnP(bcs->cs, bcs->channel, HDLC_STATUS + 1,
+ bcs->hw.hdlc.ctrl.sr.xml);
+ if (which & 1)
+ WriteHDLCPnP(bcs->cs, bcs->channel, HDLC_STATUS,
+ bcs->hw.hdlc.ctrl.sr.cmd);
+ }
+}
+
+static void
+modehdlc(struct BCState *bcs, int mode, int bc)
+{
+ struct IsdnCardState *cs = bcs->cs;
+ int hdlc = bcs->channel;
+
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hdlc %c mode %d --> %d ichan %d --> %d",
+ 'A' + hdlc, bcs->mode, mode, hdlc, bc);
+ bcs->hw.hdlc.ctrl.ctrl = 0;
+ switch (mode) {
+ case (-1): /* used for init */
+ bcs->mode = 1;
+ bcs->channel = bc;
+ bc = 0;
+ case (L1_MODE_NULL):
+ if (bcs->mode == L1_MODE_NULL)
+ return;
+ bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS;
+ bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_TRANS;
+ write_ctrl(bcs, 5);
+ bcs->mode = L1_MODE_NULL;
+ bcs->channel = bc;
+ break;
+ case (L1_MODE_TRANS):
+ bcs->mode = mode;
+ bcs->channel = bc;
+ bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS;
+ bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_TRANS;
+ write_ctrl(bcs, 5);
+ bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS;
+ write_ctrl(bcs, 1);
+ bcs->hw.hdlc.ctrl.sr.cmd = 0;
+ schedule_event(bcs, B_XMTBUFREADY);
+ break;
+ case (L1_MODE_HDLC):
+ bcs->mode = mode;
+ bcs->channel = bc;
+ bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS;
+ bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_ITF_FLG;
+ write_ctrl(bcs, 5);
+ bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS;
+ write_ctrl(bcs, 1);
+ bcs->hw.hdlc.ctrl.sr.cmd = 0;
+ schedule_event(bcs, B_XMTBUFREADY);
+ break;
+ }
+}
+
+static inline void
+hdlc_empty_fifo(struct BCState *bcs, int count)
+{
+ register u_int *ptr;
+ u_char *p;
+ u_char idx = bcs->channel ? AVM_HDLC_2 : AVM_HDLC_1;
+ int cnt=0;
+ struct IsdnCardState *cs = bcs->cs;
+
+ if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+ debugl1(cs, "hdlc_empty_fifo %d", count);
+ if (bcs->hw.hdlc.rcvidx + count > HSCX_BUFMAX) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "hdlc_empty_fifo: incoming packet too large");
+ return;
+ }
+ p = bcs->hw.hdlc.rcvbuf + bcs->hw.hdlc.rcvidx;
+ ptr = (u_int *)p;
+ bcs->hw.hdlc.rcvidx += count;
+ if (cs->subtyp == AVM_FRITZ_PCI) {
+ outl(idx, cs->hw.avm.cfg_reg + 4);
+ while (cnt < count) {
+#ifdef __powerpc__
+ *ptr++ = in_be32((unsigned *)(cs->hw.avm.isac +_IO_BASE));
+#else
+ *ptr++ = inl(cs->hw.avm.isac);
+#endif /* __powerpc__ */
+ cnt += 4;
+ }
+ } else {
+ outb(idx, cs->hw.avm.cfg_reg + 4);
+ while (cnt < count) {
+ *p++ = inb(cs->hw.avm.isac);
+ cnt++;
+ }
+ }
+ if (cs->debug & L1_DEB_HSCX_FIFO) {
+ char *t = bcs->blog;
+
+ if (cs->subtyp == AVM_FRITZ_PNP)
+ p = (u_char *) ptr;
+ t += sprintf(t, "hdlc_empty_fifo %c cnt %d",
+ bcs->channel ? 'B' : 'A', count);
+ QuickHex(t, p, count);
+ debugl1(cs, bcs->blog);
+ }
+}
+
+static inline void
+hdlc_fill_fifo(struct BCState *bcs)
+{
+ struct IsdnCardState *cs = bcs->cs;
+ int count, cnt =0;
+ int fifo_size = 32;
+ u_char *p;
+ u_int *ptr;
+
+ if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+ debugl1(cs, "hdlc_fill_fifo");
+ if (!bcs->tx_skb)
+ return;
+ if (bcs->tx_skb->len <= 0)
+ return;
+
+ bcs->hw.hdlc.ctrl.sr.cmd &= ~HDLC_CMD_XME;
+ if (bcs->tx_skb->len > fifo_size) {
+ count = fifo_size;
+ } else {
+ count = bcs->tx_skb->len;
+ if (bcs->mode != L1_MODE_TRANS)
+ bcs->hw.hdlc.ctrl.sr.cmd |= HDLC_CMD_XME;
+ }
+ if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+ debugl1(cs, "hdlc_fill_fifo %d/%ld", count, bcs->tx_skb->len);
+ p = bcs->tx_skb->data;
+ ptr = (u_int *)p;
+ skb_pull(bcs->tx_skb, count);
+ bcs->tx_cnt -= count;
+ bcs->hw.hdlc.count += count;
+ bcs->hw.hdlc.ctrl.sr.xml = ((count == fifo_size) ? 0 : count);
+ write_ctrl(bcs, 3); /* sets the correct index too */
+ if (cs->subtyp == AVM_FRITZ_PCI) {
+ while (cnt<count) {
+#ifdef __powerpc__
+ out_be32((unsigned *)(cs->hw.avm.isac +_IO_BASE), *ptr++);
+#else
+ outl(*ptr++, cs->hw.avm.isac);
+#endif /* __powerpc__ */
+ cnt += 4;
+ }
+ } else {
+ while (cnt<count) {
+ outb(*p++, cs->hw.avm.isac);
+ cnt++;
+ }
+ }
+ if (cs->debug & L1_DEB_HSCX_FIFO) {
+ char *t = bcs->blog;
+
+ if (cs->subtyp == AVM_FRITZ_PNP)
+ p = (u_char *) ptr;
+ t += sprintf(t, "hdlc_fill_fifo %c cnt %d",
+ bcs->channel ? 'B' : 'A', count);
+ QuickHex(t, p, count);
+ debugl1(cs, bcs->blog);
+ }
+}
+
+static void
+HDLC_irq(struct BCState *bcs, u_int stat) {
+ int len;
+ struct sk_buff *skb;
+
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs, "ch%d stat %#x", bcs->channel, stat);
+ if (stat & HDLC_INT_RPR) {
+ if (stat & HDLC_STAT_RDO) {
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs, "RDO");
+ else
+ debugl1(bcs->cs, "ch%d stat %#x", bcs->channel, stat);
+ bcs->hw.hdlc.ctrl.sr.xml = 0;
+ bcs->hw.hdlc.ctrl.sr.cmd |= HDLC_CMD_RRS;
+ write_ctrl(bcs, 1);
+ bcs->hw.hdlc.ctrl.sr.cmd &= ~HDLC_CMD_RRS;
+ write_ctrl(bcs, 1);
+ bcs->hw.hdlc.rcvidx = 0;
+ } else {
+ if (!(len = (stat & HDLC_STAT_RML_MASK)>>8))
+ len = 32;
+ hdlc_empty_fifo(bcs, len);
+ if ((stat & HDLC_STAT_RME) || (bcs->mode == L1_MODE_TRANS)) {
+ if (((stat & HDLC_STAT_CRCVFRRAB)==HDLC_STAT_CRCVFR) ||
+ (bcs->mode == L1_MODE_TRANS)) {
+ if (!(skb = dev_alloc_skb(bcs->hw.hdlc.rcvidx)))
+ printk(KERN_WARNING "HDLC: receive out of memory\n");
+ else {
+ memcpy(skb_put(skb, bcs->hw.hdlc.rcvidx),
+ bcs->hw.hdlc.rcvbuf, bcs->hw.hdlc.rcvidx);
+ skb_queue_tail(&bcs->rqueue, skb);
+ }
+ bcs->hw.hdlc.rcvidx = 0;
+ schedule_event(bcs, B_RCVBUFREADY);
+ } else {
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs, "invalid frame");
+ else
+ debugl1(bcs->cs, "ch%d invalid frame %#x", bcs->channel, stat);
+ bcs->hw.hdlc.rcvidx = 0;
+ }
+ }
+ }
+ }
+ if (stat & HDLC_INT_XDU) {
+ /* Here we lost an TX interrupt, so
+ * restart transmitting the whole frame.
+ */
+ if (bcs->tx_skb) {
+ skb_push(bcs->tx_skb, bcs->hw.hdlc.count);
+ bcs->tx_cnt += bcs->hw.hdlc.count;
+ bcs->hw.hdlc.count = 0;
+ if (bcs->cs->debug & L1_DEB_WARN)
+ debugl1(bcs->cs, "ch%d XDU", bcs->channel);
+ } else if (bcs->cs->debug & L1_DEB_WARN)
+ debugl1(bcs->cs, "ch%d XDU without skb", bcs->channel);
+ bcs->hw.hdlc.ctrl.sr.xml = 0;
+ bcs->hw.hdlc.ctrl.sr.cmd |= HDLC_CMD_XRS;
+ write_ctrl(bcs, 1);
+ bcs->hw.hdlc.ctrl.sr.cmd &= ~HDLC_CMD_XRS;
+ write_ctrl(bcs, 1);
+ hdlc_fill_fifo(bcs);
+ } else if (stat & HDLC_INT_XPR) {
+ if (bcs->tx_skb) {
+ if (bcs->tx_skb->len) {
+ hdlc_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.hdlc.count;
+ spin_unlock_irqrestore(&bcs->aclock, flags);
+ schedule_event(bcs, B_ACKPENDING);
+ }
+ dev_kfree_skb_irq(bcs->tx_skb);
+ bcs->hw.hdlc.count = 0;
+ bcs->tx_skb = NULL;
+ }
+ }
+ if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+ bcs->hw.hdlc.count = 0;
+ test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+ hdlc_fill_fifo(bcs);
+ } else {
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ schedule_event(bcs, B_XMTBUFREADY);
+ }
+ }
+}
+
+static inline void
+HDLC_irq_main(struct IsdnCardState *cs)
+{
+ u_int stat;
+ struct BCState *bcs;
+
+ if (cs->subtyp == AVM_FRITZ_PCI) {
+ stat = ReadHDLCPCI(cs, 0, HDLC_STATUS);
+ } else {
+ stat = ReadHDLCPnP(cs, 0, HDLC_STATUS);
+ if (stat & HDLC_INT_RPR)
+ stat |= (ReadHDLCPnP(cs, 0, HDLC_STATUS+1))<<8;
+ }
+ if (stat & HDLC_INT_MASK) {
+ if (!(bcs = Sel_BCS(cs, 0))) {
+ if (cs->debug)
+ debugl1(cs, "hdlc spurious channel 0 IRQ");
+ } else
+ HDLC_irq(bcs, stat);
+ }
+ if (cs->subtyp == AVM_FRITZ_PCI) {
+ stat = ReadHDLCPCI(cs, 1, HDLC_STATUS);
+ } else {
+ stat = ReadHDLCPnP(cs, 1, HDLC_STATUS);
+ if (stat & HDLC_INT_RPR)
+ stat |= (ReadHDLCPnP(cs, 1, HDLC_STATUS+1))<<8;
+ }
+ if (stat & HDLC_INT_MASK) {
+ if (!(bcs = Sel_BCS(cs, 1))) {
+ if (cs->debug)
+ debugl1(cs, "hdlc spurious channel 1 IRQ");
+ } else
+ HDLC_irq(bcs, stat);
+ }
+}
+
+static void
+hdlc_l2l1(struct PStack *st, int pr, void *arg)
+{
+ struct BCState *bcs = st->l1.bcs;
+ struct sk_buff *skb = arg;
+ 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);
+ bcs->hw.hdlc.count = 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 "hdlc_l2l1: this shouldn't happen\n");
+ } else {
+ test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+ bcs->tx_skb = skb;
+ bcs->hw.hdlc.count = 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);
+ modehdlc(bcs, st->l1.mode, st->l1.bc);
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | REQUEST):
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | CONFIRM):
+ spin_lock_irqsave(&bcs->cs->lock, flags);
+ test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ modehdlc(bcs, 0, st->l1.bc);
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
+ st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+ break;
+ }
+}
+
+static void
+close_hdlcstate(struct BCState *bcs)
+{
+ modehdlc(bcs, 0, 0);
+ if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+ kfree(bcs->hw.hdlc.rcvbuf);
+ bcs->hw.hdlc.rcvbuf = NULL;
+ kfree(bcs->blog);
+ bcs->blog = 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);
+ }
+ }
+}
+
+static int
+open_hdlcstate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+ if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+ if (!(bcs->hw.hdlc.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "HiSax: No memory for hdlc.rcvbuf\n");
+ return (1);
+ }
+ if (!(bcs->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "HiSax: No memory for bcs->blog\n");
+ test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+ kfree(bcs->hw.hdlc.rcvbuf);
+ bcs->hw.hdlc.rcvbuf = NULL;
+ return (2);
+ }
+ 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);
+ bcs->event = 0;
+ bcs->hw.hdlc.rcvidx = 0;
+ bcs->tx_cnt = 0;
+ return (0);
+}
+
+static int
+setstack_hdlc(struct PStack *st, struct BCState *bcs)
+{
+ bcs->channel = st->l1.bc;
+ if (open_hdlcstate(st->l1.hardware, bcs))
+ return (-1);
+ st->l1.bcs = bcs;
+ st->l2.l2l1 = hdlc_l2l1;
+ setstack_manager(st);
+ bcs->st = st;
+ setstack_l1_B(st);
+ return (0);
+}
+
+#if 0
+void __init
+clear_pending_hdlc_ints(struct IsdnCardState *cs)
+{
+ u_int val;
+
+ if (cs->subtyp == AVM_FRITZ_PCI) {
+ val = ReadHDLCPCI(cs, 0, HDLC_STATUS);
+ debugl1(cs, "HDLC 1 STA %x", val);
+ val = ReadHDLCPCI(cs, 1, HDLC_STATUS);
+ debugl1(cs, "HDLC 2 STA %x", val);
+ } else {
+ val = ReadHDLCPnP(cs, 0, HDLC_STATUS);
+ debugl1(cs, "HDLC 1 STA %x", val);
+ val = ReadHDLCPnP(cs, 0, HDLC_STATUS + 1);
+ debugl1(cs, "HDLC 1 RML %x", val);
+ val = ReadHDLCPnP(cs, 0, HDLC_STATUS + 2);
+ debugl1(cs, "HDLC 1 MODE %x", val);
+ val = ReadHDLCPnP(cs, 0, HDLC_STATUS + 3);
+ debugl1(cs, "HDLC 1 VIN %x", val);
+ val = ReadHDLCPnP(cs, 1, HDLC_STATUS);
+ debugl1(cs, "HDLC 2 STA %x", val);
+ val = ReadHDLCPnP(cs, 1, HDLC_STATUS + 1);
+ debugl1(cs, "HDLC 2 RML %x", val);
+ val = ReadHDLCPnP(cs, 1, HDLC_STATUS + 2);
+ debugl1(cs, "HDLC 2 MODE %x", val);
+ val = ReadHDLCPnP(cs, 1, HDLC_STATUS + 3);
+ debugl1(cs, "HDLC 2 VIN %x", val);
+ }
+}
+#endif /* 0 */
+
+static void
+inithdlc(struct IsdnCardState *cs)
+{
+ cs->bcs[0].BC_SetStack = setstack_hdlc;
+ cs->bcs[1].BC_SetStack = setstack_hdlc;
+ cs->bcs[0].BC_Close = close_hdlcstate;
+ cs->bcs[1].BC_Close = close_hdlcstate;
+ modehdlc(cs->bcs, -1, 0);
+ modehdlc(cs->bcs + 1, -1, 1);
+}
+
+static irqreturn_t
+avm_pcipnp_interrupt(int intno, void *dev_id)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_long flags;
+ u_char val;
+ u_char sval;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ sval = inb(cs->hw.avm.cfg_reg + 2);
+ if ((sval & AVM_STATUS0_IRQ_MASK) == AVM_STATUS0_IRQ_MASK) {
+ /* possible a shared IRQ reqest */
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_NONE;
+ }
+ if (!(sval & AVM_STATUS0_IRQ_ISAC)) {
+ val = ReadISAC(cs, ISAC_ISTA);
+ isac_interrupt(cs, val);
+ }
+ if (!(sval & AVM_STATUS0_IRQ_HDLC)) {
+ HDLC_irq_main(cs);
+ }
+ WriteISAC(cs, ISAC_MASK, 0xFF);
+ WriteISAC(cs, ISAC_MASK, 0x0);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static void
+reset_avmpcipnp(struct IsdnCardState *cs)
+{
+ printk(KERN_INFO "AVM PCI/PnP: reset\n");
+ outb(AVM_STATUS0_RESET | AVM_STATUS0_DIS_TIMER, cs->hw.avm.cfg_reg + 2);
+ mdelay(10);
+ outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER | AVM_STATUS0_ENA_IRQ, cs->hw.avm.cfg_reg + 2);
+ outb(AVM_STATUS1_ENA_IOM | cs->irq, cs->hw.avm.cfg_reg + 3);
+ mdelay(10);
+ printk(KERN_INFO "AVM PCI/PnP: S1 %x\n", inb(cs->hw.avm.cfg_reg + 3));
+}
+
+static int
+AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ u_long flags;
+
+ switch (mt) {
+ case CARD_RESET:
+ spin_lock_irqsave(&cs->lock, flags);
+ reset_avmpcipnp(cs);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return(0);
+ case CARD_RELEASE:
+ outb(0, cs->hw.avm.cfg_reg + 2);
+ release_region(cs->hw.avm.cfg_reg, 32);
+ return(0);
+ case CARD_INIT:
+ spin_lock_irqsave(&cs->lock, flags);
+ reset_avmpcipnp(cs);
+ clear_pending_isac_ints(cs);
+ initisac(cs);
+ inithdlc(cs);
+ outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER,
+ cs->hw.avm.cfg_reg + 2);
+ WriteISAC(cs, ISAC_MASK, 0);
+ outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER |
+ AVM_STATUS0_ENA_IRQ, cs->hw.avm.cfg_reg + 2);
+ /* RESET Receiver and Transmitter */
+ WriteISAC(cs, ISAC_CMDR, 0x41);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return(0);
+ case CARD_TEST:
+ return(0);
+ }
+ return(0);
+}
+
+static int __devinit avm_setup_rest(struct IsdnCardState *cs)
+{
+ u_int val, ver;
+
+ cs->hw.avm.isac = cs->hw.avm.cfg_reg + 0x10;
+ if (!request_region(cs->hw.avm.cfg_reg, 32,
+ (cs->subtyp == AVM_FRITZ_PCI) ? "avm PCI" : "avm PnP")) {
+ printk(KERN_WARNING
+ "HiSax: Fritz!PCI/PNP config port %x-%x already in use\n",
+ cs->hw.avm.cfg_reg,
+ cs->hw.avm.cfg_reg + 31);
+ return (0);
+ }
+ switch (cs->subtyp) {
+ case AVM_FRITZ_PCI:
+ val = inl(cs->hw.avm.cfg_reg);
+ printk(KERN_INFO "AVM PCI: stat %#x\n", val);
+ printk(KERN_INFO "AVM PCI: Class %X Rev %d\n",
+ val & 0xff, (val>>8) & 0xff);
+ cs->BC_Read_Reg = &ReadHDLC_s;
+ cs->BC_Write_Reg = &WriteHDLC_s;
+ break;
+ case AVM_FRITZ_PNP:
+ val = inb(cs->hw.avm.cfg_reg);
+ ver = inb(cs->hw.avm.cfg_reg + 1);
+ printk(KERN_INFO "AVM PnP: Class %X Rev %d\n", val, ver);
+ cs->BC_Read_Reg = &ReadHDLCPnP;
+ cs->BC_Write_Reg = &WriteHDLCPnP;
+ break;
+ default:
+ printk(KERN_WARNING "AVM unknown subtype %d\n", cs->subtyp);
+ return(0);
+ }
+ printk(KERN_INFO "HiSax: %s config irq:%d base:0x%X\n",
+ (cs->subtyp == AVM_FRITZ_PCI) ? "AVM Fritz!PCI" : "AVM Fritz!PnP",
+ cs->irq, cs->hw.avm.cfg_reg);
+
+ setup_isac(cs);
+ cs->readisac = &ReadISAC;
+ cs->writeisac = &WriteISAC;
+ cs->readisacfifo = &ReadISACfifo;
+ cs->writeisacfifo = &WriteISACfifo;
+ cs->BC_Send_Data = &hdlc_fill_fifo;
+ cs->cardmsg = &AVM_card_msg;
+ cs->irq_func = &avm_pcipnp_interrupt;
+ cs->writeisac(cs, ISAC_MASK, 0xFF);
+ ISACVersion(cs, (cs->subtyp == AVM_FRITZ_PCI) ? "AVM PCI:" : "AVM PnP:");
+ return (1);
+}
+
+#ifndef __ISAPNP__
+
+static int __devinit avm_pnp_setup(struct IsdnCardState *cs)
+{
+ return(1); /* no-op: success */
+}
+
+#else
+
+static struct pnp_card *pnp_avm_c __devinitdata = NULL;
+
+static int __devinit avm_pnp_setup(struct IsdnCardState *cs)
+{
+ struct pnp_dev *pnp_avm_d = NULL;
+
+ if (!isapnp_present())
+ return(1); /* no-op: success */
+
+ if ((pnp_avm_c = pnp_find_card(
+ ISAPNP_VENDOR('A', 'V', 'M'),
+ ISAPNP_FUNCTION(0x0900), pnp_avm_c))) {
+ if ((pnp_avm_d = pnp_find_dev(pnp_avm_c,
+ ISAPNP_VENDOR('A', 'V', 'M'),
+ ISAPNP_FUNCTION(0x0900), pnp_avm_d))) {
+ int err;
+
+ pnp_disable_dev(pnp_avm_d);
+ err = pnp_activate_dev(pnp_avm_d);
+ if (err<0) {
+ printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
+ __func__, err);
+ return(0);
+ }
+ cs->hw.avm.cfg_reg =
+ pnp_port_start(pnp_avm_d, 0);
+ cs->irq = pnp_irq(pnp_avm_d, 0);
+ if (!cs->irq) {
+ printk(KERN_ERR "FritzPnP:No IRQ\n");
+ return(0);
+ }
+ if (!cs->hw.avm.cfg_reg) {
+ printk(KERN_ERR "FritzPnP:No IO address\n");
+ return(0);
+ }
+ cs->subtyp = AVM_FRITZ_PNP;
+
+ return (2); /* goto 'ready' label */
+ }
+ }
+
+ return (1);
+}
+
+#endif /* __ISAPNP__ */
+
+#ifndef CONFIG_PCI_LEGACY
+
+static int __devinit avm_pci_setup(struct IsdnCardState *cs)
+{
+ return(1); /* no-op: success */
+}
+
+#else
+
+static struct pci_dev *dev_avm __devinitdata = NULL;
+
+static int __devinit avm_pci_setup(struct IsdnCardState *cs)
+{
+ if ((dev_avm = pci_find_device(PCI_VENDOR_ID_AVM,
+ PCI_DEVICE_ID_AVM_A1, dev_avm))) {
+
+ if (pci_enable_device(dev_avm))
+ return(0);
+
+ cs->irq = dev_avm->irq;
+ if (!cs->irq) {
+ printk(KERN_ERR "FritzPCI: No IRQ for PCI card found\n");
+ return(0);
+ }
+
+ cs->hw.avm.cfg_reg = pci_resource_start(dev_avm, 1);
+ if (!cs->hw.avm.cfg_reg) {
+ printk(KERN_ERR "FritzPCI: No IO-Adr for PCI card found\n");
+ return(0);
+ }
+
+ cs->subtyp = AVM_FRITZ_PCI;
+ } else {
+ printk(KERN_WARNING "FritzPCI: No PCI card found\n");
+ return(0);
+ }
+
+ cs->irq_flags |= IRQF_SHARED;
+
+ return (1);
+}
+
+#endif /* CONFIG_PCI_LEGACY */
+
+int __devinit
+setup_avm_pcipnp(struct IsdnCard *card)
+{
+ struct IsdnCardState *cs = card->cs;
+ char tmp[64];
+ int rc;
+
+ strcpy(tmp, avm_pci_rev);
+ printk(KERN_INFO "HiSax: AVM PCI driver Rev. %s\n", HiSax_getrev(tmp));
+
+ if (cs->typ != ISDN_CTYPE_FRITZPCI)
+ return (0);
+
+ if (card->para[1]) {
+ /* old manual method */
+ cs->hw.avm.cfg_reg = card->para[1];
+ cs->irq = card->para[0];
+ cs->subtyp = AVM_FRITZ_PNP;
+ goto ready;
+ }
+
+ rc = avm_pnp_setup(cs);
+ if (rc < 1)
+ return (0);
+ if (rc == 2)
+ goto ready;
+
+ rc = avm_pci_setup(cs);
+ if (rc < 1)
+ return (0);
+
+ready:
+ return avm_setup_rest(cs);
+}
diff --git a/drivers/isdn/hisax/avma1_cs.c b/drivers/isdn/hisax/avma1_cs.c
new file mode 100644
index 0000000..23560c8
--- /dev/null
+++ b/drivers/isdn/hisax/avma1_cs.c
@@ -0,0 +1,323 @@
+/*
+ * PCMCIA client driver for AVM A1 / Fritz!PCMCIA
+ *
+ * Author Carsten Paeth
+ * Copyright 1998-2001 by Carsten Paeth <calle@calle.in-berlin.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+#include "hisax_cfg.h"
+
+MODULE_DESCRIPTION("ISDN4Linux: PCMCIA client driver for AVM A1/Fritz!PCMCIA cards");
+MODULE_AUTHOR("Carsten Paeth");
+MODULE_LICENSE("GPL");
+
+/*
+ All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If
+ you do not define PCMCIA_DEBUG at all, all the debug code will be
+ left out. If you compile with PCMCIA_DEBUG=0, the debug code will
+ be present but disabled -- but it can then be enabled for specific
+ modules at load time with a 'pc_debug=#' option to insmod.
+*/
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args);
+static char *version =
+"avma1_cs.c 1.00 1998/01/23 10:00:00 (Carsten Paeth)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+static int isdnprot = 2;
+
+module_param(isdnprot, int, 0);
+
+/*====================================================================*/
+
+/*
+ The event() function is this driver's Card Services event handler.
+ It will be called by Card Services when an appropriate card status
+ event is received. The config() and release() entry points are
+ used to configure or release a socket, in response to card insertion
+ and ejection events. They are invoked from the skeleton event
+ handler.
+*/
+
+static int avma1cs_config(struct pcmcia_device *link);
+static void avma1cs_release(struct pcmcia_device *link);
+
+/*
+ The attach() and detach() entry points are used to create and destroy
+ "instances" of the driver, where each instance represents everything
+ needed to manage one actual PCMCIA card.
+*/
+
+static void avma1cs_detach(struct pcmcia_device *p_dev);
+
+
+/*
+ A linked list of "instances" of the skeleton device. Each actual
+ PCMCIA card corresponds to one device instance, and is described
+ by one struct pcmcia_device structure (defined in ds.h).
+
+ You may not want to use a linked list for this -- for example, the
+ memory card driver uses an array of struct pcmcia_device pointers, where minor
+ device numbers are used to derive the corresponding array index.
+*/
+
+/*
+ A driver needs to provide a dev_node_t structure for each device
+ on a card. In some cases, there is only one device per card (for
+ example, ethernet cards, modems). In other cases, there may be
+ many actual or logical devices (SCSI adapters, memory cards with
+ multiple partitions). The dev_node_t structures need to be kept
+ in a linked list starting at the 'dev' field of a struct pcmcia_device
+ structure. We allocate them in the card's private data structure,
+ because they generally can't be allocated dynamically.
+*/
+
+typedef struct local_info_t {
+ dev_node_t node;
+} local_info_t;
+
+/*======================================================================
+
+ avma1cs_attach() creates an "instance" of the driver, allocating
+ local data structures for one device. The device is registered
+ with Card Services.
+
+ The dev_link structure is initialized, but we don't actually
+ configure the card at this point -- we wait until we receive a
+ card insertion event.
+
+======================================================================*/
+
+static int avma1cs_probe(struct pcmcia_device *p_dev)
+{
+ local_info_t *local;
+
+ DEBUG(0, "avma1cs_attach()\n");
+
+ /* Allocate space for private device-specific data */
+ local = kzalloc(sizeof(local_info_t), GFP_KERNEL);
+ if (!local)
+ return -ENOMEM;
+
+ p_dev->priv = local;
+
+ /* The io structure describes IO port mapping */
+ p_dev->io.NumPorts1 = 16;
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ p_dev->io.NumPorts2 = 16;
+ p_dev->io.Attributes2 = IO_DATA_PATH_WIDTH_16;
+ p_dev->io.IOAddrLines = 5;
+
+ /* Interrupt setup */
+ p_dev->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+ p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
+
+ p_dev->irq.IRQInfo1 = IRQ_LEVEL_ID;
+
+ /* General socket configuration */
+ p_dev->conf.Attributes = CONF_ENABLE_IRQ;
+ p_dev->conf.IntType = INT_MEMORY_AND_IO;
+ p_dev->conf.ConfigIndex = 1;
+ p_dev->conf.Present = PRESENT_OPTION;
+
+ return avma1cs_config(p_dev);
+} /* avma1cs_attach */
+
+/*======================================================================
+
+ This deletes a driver "instance". The device is de-registered
+ with Card Services. If it has been released, all local data
+ structures are freed. Otherwise, the structures will be freed
+ when the device is released.
+
+======================================================================*/
+
+static void avma1cs_detach(struct pcmcia_device *link)
+{
+ DEBUG(0, "avma1cs_detach(0x%p)\n", link);
+ avma1cs_release(link);
+ kfree(link->priv);
+} /* avma1cs_detach */
+
+/*======================================================================
+
+ avma1cs_config() is scheduled to run after a CARD_INSERTION event
+ is received, to configure the PCMCIA socket, and to make the
+ ethernet device available to the system.
+
+======================================================================*/
+
+static int avma1cs_configcheck(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cf,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ if (cf->io.nwin <= 0)
+ return -ENODEV;
+
+ p_dev->io.BasePort1 = cf->io.win[0].base;
+ p_dev->io.NumPorts1 = cf->io.win[0].len;
+ p_dev->io.NumPorts2 = 0;
+ printk(KERN_INFO "avma1_cs: testing i/o %#x-%#x\n",
+ p_dev->io.BasePort1,
+ p_dev->io.BasePort1+p_dev->io.NumPorts1-1);
+ return pcmcia_request_io(p_dev, &p_dev->io);
+}
+
+
+static int avma1cs_config(struct pcmcia_device *link)
+{
+ local_info_t *dev;
+ int i;
+ char devname[128];
+ IsdnCard_t icard;
+ int busy = 0;
+
+ dev = link->priv;
+
+ DEBUG(0, "avma1cs_config(0x%p)\n", link);
+
+ devname[0] = 0;
+ if (link->prod_id[1])
+ strlcpy(devname, link->prod_id[1], sizeof(devname));
+
+ if (pcmcia_loop_config(link, avma1cs_configcheck, NULL))
+ return -ENODEV;
+
+ do {
+ /*
+ * allocate an interrupt line
+ */
+ i = pcmcia_request_irq(link, &link->irq);
+ if (i != 0) {
+ cs_error(link, RequestIRQ, i);
+ /* undo */
+ pcmcia_disable_device(link);
+ break;
+ }
+
+ /*
+ * configure the PCMCIA socket
+ */
+ i = pcmcia_request_configuration(link, &link->conf);
+ if (i != 0) {
+ cs_error(link, RequestConfiguration, i);
+ pcmcia_disable_device(link);
+ break;
+ }
+
+ } while (0);
+
+ /* At this point, the dev_node_t structure(s) should be
+ initialized and arranged in a linked list at link->dev. */
+
+ strcpy(dev->node.dev_name, "A1");
+ dev->node.major = 45;
+ dev->node.minor = 0;
+ link->dev_node = &dev->node;
+
+ /* If any step failed, release any partially configured state */
+ if (i != 0) {
+ avma1cs_release(link);
+ return -ENODEV;
+ }
+
+ printk(KERN_NOTICE "avma1_cs: checking at i/o %#x, irq %d\n",
+ link->io.BasePort1, link->irq.AssignedIRQ);
+
+ icard.para[0] = link->irq.AssignedIRQ;
+ icard.para[1] = link->io.BasePort1;
+ icard.protocol = isdnprot;
+ icard.typ = ISDN_CTYPE_A1_PCMCIA;
+
+ i = hisax_init_pcmcia(link, &busy, &icard);
+ if (i < 0) {
+ printk(KERN_ERR "avma1_cs: failed to initialize AVM A1 PCMCIA %d at i/o %#x\n", i, link->io.BasePort1);
+ avma1cs_release(link);
+ return -ENODEV;
+ }
+ dev->node.minor = i;
+
+ return 0;
+} /* avma1cs_config */
+
+/*======================================================================
+
+ After a card is removed, avma1cs_release() will unregister the net
+ device, and release the PCMCIA configuration. If the device is
+ still open, this will be postponed until it is closed.
+
+======================================================================*/
+
+static void avma1cs_release(struct pcmcia_device *link)
+{
+ local_info_t *local = link->priv;
+
+ DEBUG(0, "avma1cs_release(0x%p)\n", link);
+
+ /* now unregister function with hisax */
+ HiSax_closecard(local->node.minor);
+
+ pcmcia_disable_device(link);
+} /* avma1cs_release */
+
+
+static struct pcmcia_device_id avma1cs_ids[] = {
+ PCMCIA_DEVICE_PROD_ID12("AVM", "ISDN A", 0x95d42008, 0xadc9d4bb),
+ PCMCIA_DEVICE_PROD_ID12("ISDN", "CARD", 0x8d9761c8, 0x01c5aa7b),
+ PCMCIA_DEVICE_NULL
+};
+MODULE_DEVICE_TABLE(pcmcia, avma1cs_ids);
+
+static struct pcmcia_driver avma1cs_driver = {
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = "avma1_cs",
+ },
+ .probe = avma1cs_probe,
+ .remove = avma1cs_detach,
+ .id_table = avma1cs_ids,
+};
+
+/*====================================================================*/
+
+static int __init init_avma1_cs(void)
+{
+ return(pcmcia_register_driver(&avma1cs_driver));
+}
+
+static void __exit exit_avma1_cs(void)
+{
+ pcmcia_unregister_driver(&avma1cs_driver);
+}
+
+module_init(init_avma1_cs);
+module_exit(exit_avma1_cs);
diff --git a/drivers/isdn/hisax/bkm_a4t.c b/drivers/isdn/hisax/bkm_a4t.c
new file mode 100644
index 0000000..9ca2ee5
--- /dev/null
+++ b/drivers/isdn/hisax/bkm_a4t.c
@@ -0,0 +1,362 @@
+/* $Id: bkm_a4t.c,v 1.22.2.4 2004/01/14 16:04:48 keil Exp $
+ *
+ * low level stuff for T-Berkom A4T
+ *
+ * Author Roland Klabunde
+ * Copyright by Roland Klabunde <R.Klabunde@Berkom.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "jade.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include "bkm_ax.h"
+
+static const char *bkm_a4t_revision = "$Revision: 1.22.2.4 $";
+
+
+static inline u_char
+readreg(unsigned int ale, unsigned long adr, u_char off)
+{
+ register u_int ret;
+ unsigned int *po = (unsigned int *) adr; /* Postoffice */
+
+ *po = (GCS_2 | PO_WRITE | off);
+ __WAITI20__(po);
+ *po = (ale | PO_READ);
+ __WAITI20__(po);
+ ret = *po;
+ return ((unsigned char) ret);
+}
+
+
+static inline void
+readfifo(unsigned int ale, unsigned long adr, u_char off, u_char * data, int size)
+{
+ int i;
+ for (i = 0; i < size; i++)
+ *data++ = readreg(ale, adr, off);
+}
+
+
+static inline void
+writereg(unsigned int ale, unsigned long adr, u_char off, u_char data)
+{
+ unsigned int *po = (unsigned int *) adr; /* Postoffice */
+ *po = (GCS_2 | PO_WRITE | off);
+ __WAITI20__(po);
+ *po = (ale | PO_WRITE | data);
+ __WAITI20__(po);
+}
+
+
+static inline void
+writefifo(unsigned int ale, unsigned long adr, u_char off, u_char * data, int size)
+{
+ int i;
+
+ for (i = 0; i < size; i++)
+ writereg(ale, adr, off, *data++);
+}
+
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+ return (readreg(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ writereg(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ readfifo(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ writefifo(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, 0, data, size);
+}
+
+static u_char
+ReadJADE(struct IsdnCardState *cs, int jade, u_char offset)
+{
+ return (readreg(cs->hw.ax.jade_ale, cs->hw.ax.jade_adr, offset + (jade == -1 ? 0 : (jade ? 0xC0 : 0x80))));
+}
+
+static void
+WriteJADE(struct IsdnCardState *cs, int jade, u_char offset, u_char value)
+{
+ writereg(cs->hw.ax.jade_ale, cs->hw.ax.jade_adr, offset + (jade == -1 ? 0 : (jade ? 0xC0 : 0x80)), value);
+}
+
+/*
+ * fast interrupt JADE stuff goes here
+ */
+
+#define READJADE(cs, nr, reg) readreg(cs->hw.ax.jade_ale,\
+ cs->hw.ax.jade_adr, reg + (nr == -1 ? 0 : (nr ? 0xC0 : 0x80)))
+#define WRITEJADE(cs, nr, reg, data) writereg(cs->hw.ax.jade_ale,\
+ cs->hw.ax.jade_adr, reg + (nr == -1 ? 0 : (nr ? 0xC0 : 0x80)), data)
+
+#define READJADEFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.ax.jade_ale,\
+ cs->hw.ax.jade_adr, (nr == -1 ? 0 : (nr ? 0xC0 : 0x80)), ptr, cnt)
+#define WRITEJADEFIFO(cs, nr, ptr, cnt) writefifo( cs->hw.ax.jade_ale,\
+ cs->hw.ax.jade_adr, (nr == -1 ? 0 : (nr ? 0xC0 : 0x80)), ptr, cnt)
+
+#include "jade_irq.c"
+
+static irqreturn_t
+bkm_interrupt(int intno, void *dev_id)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char val = 0;
+ u_long flags;
+ I20_REGISTER_FILE *pI20_Regs;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ pI20_Regs = (I20_REGISTER_FILE *) (cs->hw.ax.base);
+
+ /* ISDN interrupt pending? */
+ if (pI20_Regs->i20IntStatus & intISDN) {
+ /* Reset the ISDN interrupt */
+ pI20_Regs->i20IntStatus = intISDN;
+ /* Disable ISDN interrupt */
+ pI20_Regs->i20IntCtrl &= ~intISDN;
+ /* Channel A first */
+ val = readreg(cs->hw.ax.jade_ale, cs->hw.ax.jade_adr, jade_HDLC_ISR + 0x80);
+ if (val) {
+ jade_int_main(cs, val, 0);
+ }
+ /* Channel B */
+ val = readreg(cs->hw.ax.jade_ale, cs->hw.ax.jade_adr, jade_HDLC_ISR + 0xC0);
+ if (val) {
+ jade_int_main(cs, val, 1);
+ }
+ /* D-Channel */
+ val = readreg(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, ISAC_ISTA);
+ if (val) {
+ isac_interrupt(cs, val);
+ }
+ /* Reenable ISDN interrupt */
+ pI20_Regs->i20IntCtrl |= intISDN;
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+ } else {
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_NONE;
+ }
+}
+
+static void
+release_io_bkm(struct IsdnCardState *cs)
+{
+ if (cs->hw.ax.base) {
+ iounmap((void *) cs->hw.ax.base);
+ cs->hw.ax.base = 0;
+ }
+}
+
+static void
+enable_bkm_int(struct IsdnCardState *cs, unsigned bEnable)
+{
+ if (cs->typ == ISDN_CTYPE_BKM_A4T) {
+ I20_REGISTER_FILE *pI20_Regs = (I20_REGISTER_FILE *) (cs->hw.ax.base);
+ if (bEnable)
+ pI20_Regs->i20IntCtrl |= (intISDN | intPCI);
+ else
+ /* CAUTION: This disables the video capture driver too */
+ pI20_Regs->i20IntCtrl &= ~(intISDN | intPCI);
+ }
+}
+
+static void
+reset_bkm(struct IsdnCardState *cs)
+{
+ if (cs->typ == ISDN_CTYPE_BKM_A4T) {
+ I20_REGISTER_FILE *pI20_Regs = (I20_REGISTER_FILE *) (cs->hw.ax.base);
+ /* Issue the I20 soft reset */
+ pI20_Regs->i20SysControl = 0xFF; /* all in */
+ mdelay(10);
+ /* Remove the soft reset */
+ pI20_Regs->i20SysControl = sysRESET | 0xFF;
+ mdelay(10);
+ /* Set our configuration */
+ pI20_Regs->i20SysControl = sysRESET | sysCFG;
+ /* Issue ISDN reset */
+ pI20_Regs->i20GuestControl = guestWAIT_CFG |
+ g_A4T_JADE_RES |
+ g_A4T_ISAR_RES |
+ g_A4T_ISAC_RES |
+ g_A4T_JADE_BOOTR |
+ g_A4T_ISAR_BOOTR;
+ mdelay(10);
+
+ /* Remove RESET state from ISDN */
+ pI20_Regs->i20GuestControl &= ~(g_A4T_ISAC_RES |
+ g_A4T_JADE_RES |
+ g_A4T_ISAR_RES);
+ mdelay(10);
+ }
+}
+
+static int
+BKM_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ u_long flags;
+
+ switch (mt) {
+ case CARD_RESET:
+ /* Disable ints */
+ spin_lock_irqsave(&cs->lock, flags);
+ enable_bkm_int(cs, 0);
+ reset_bkm(cs);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return (0);
+ case CARD_RELEASE:
+ /* Sanity */
+ spin_lock_irqsave(&cs->lock, flags);
+ enable_bkm_int(cs, 0);
+ reset_bkm(cs);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ release_io_bkm(cs);
+ return (0);
+ case CARD_INIT:
+ spin_lock_irqsave(&cs->lock, flags);
+ clear_pending_isac_ints(cs);
+ clear_pending_jade_ints(cs);
+ initisac(cs);
+ initjade(cs);
+ /* Enable ints */
+ enable_bkm_int(cs, 1);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return (0);
+ case CARD_TEST:
+ return (0);
+ }
+ return (0);
+}
+
+static int __devinit a4t_pci_probe(struct pci_dev *dev_a4t,
+ struct IsdnCardState *cs,
+ u_int *found,
+ u_int *pci_memaddr)
+{
+ u16 sub_sys;
+ u16 sub_vendor;
+
+ sub_vendor = dev_a4t->subsystem_vendor;
+ sub_sys = dev_a4t->subsystem_device;
+ if ((sub_sys == PCI_DEVICE_ID_BERKOM_A4T) && (sub_vendor == PCI_VENDOR_ID_BERKOM)) {
+ if (pci_enable_device(dev_a4t))
+ return (0); /* end loop & function */
+ *found = 1;
+ *pci_memaddr = pci_resource_start(dev_a4t, 0);
+ cs->irq = dev_a4t->irq;
+ return (1); /* end loop */
+ }
+
+ return (-1); /* continue looping */
+}
+
+static int __devinit a4t_cs_init(struct IsdnCard *card,
+ struct IsdnCardState *cs,
+ u_int pci_memaddr)
+{
+ I20_REGISTER_FILE *pI20_Regs;
+
+ if (!cs->irq) { /* IRQ range check ?? */
+ printk(KERN_WARNING "HiSax: Telekom A4T: No IRQ\n");
+ return (0);
+ }
+ cs->hw.ax.base = (long) ioremap(pci_memaddr, 4096);
+ /* Check suspecious address */
+ pI20_Regs = (I20_REGISTER_FILE *) (cs->hw.ax.base);
+ if ((pI20_Regs->i20IntStatus & 0x8EFFFFFF) != 0) {
+ printk(KERN_WARNING "HiSax: Telekom A4T address "
+ "%lx-%lx suspicious\n",
+ cs->hw.ax.base, cs->hw.ax.base + 4096);
+ iounmap((void *) cs->hw.ax.base);
+ cs->hw.ax.base = 0;
+ return (0);
+ }
+ cs->hw.ax.isac_adr = cs->hw.ax.base + PO_OFFSET;
+ cs->hw.ax.jade_adr = cs->hw.ax.base + PO_OFFSET;
+ cs->hw.ax.isac_ale = GCS_1;
+ cs->hw.ax.jade_ale = GCS_3;
+
+ printk(KERN_INFO "HiSax: Telekom A4T: Card configured at "
+ "0x%lX IRQ %d\n",
+ cs->hw.ax.base, cs->irq);
+
+ setup_isac(cs);
+ cs->readisac = &ReadISAC;
+ cs->writeisac = &WriteISAC;
+ cs->readisacfifo = &ReadISACfifo;
+ cs->writeisacfifo = &WriteISACfifo;
+ cs->BC_Read_Reg = &ReadJADE;
+ cs->BC_Write_Reg = &WriteJADE;
+ cs->BC_Send_Data = &jade_fill_fifo;
+ cs->cardmsg = &BKM_card_msg;
+ cs->irq_func = &bkm_interrupt;
+ cs->irq_flags |= IRQF_SHARED;
+ ISACVersion(cs, "Telekom A4T:");
+ /* Jade version */
+ JadeVersion(cs, "Telekom A4T:");
+
+ return (1);
+}
+
+static struct pci_dev *dev_a4t __devinitdata = NULL;
+
+int __devinit
+setup_bkm_a4t(struct IsdnCard *card)
+{
+ struct IsdnCardState *cs = card->cs;
+ char tmp[64];
+ u_int pci_memaddr = 0, found = 0;
+ int ret;
+
+ strcpy(tmp, bkm_a4t_revision);
+ printk(KERN_INFO "HiSax: T-Berkom driver Rev. %s\n", HiSax_getrev(tmp));
+ if (cs->typ == ISDN_CTYPE_BKM_A4T) {
+ cs->subtyp = BKM_A4T;
+ } else
+ return (0);
+
+ while ((dev_a4t = pci_find_device(PCI_VENDOR_ID_ZORAN,
+ PCI_DEVICE_ID_ZORAN_36120, dev_a4t))) {
+ ret = a4t_pci_probe(dev_a4t, cs, &found, &pci_memaddr);
+ if (!ret)
+ return (0);
+ if (ret > 0)
+ break;
+ }
+ if (!found) {
+ printk(KERN_WARNING "HiSax: Telekom A4T: Card not found\n");
+ return (0);
+ }
+ if (!pci_memaddr) {
+ printk(KERN_WARNING "HiSax: Telekom A4T: "
+ "No Memory base address\n");
+ return (0);
+ }
+
+ return a4t_cs_init(card, cs, pci_memaddr);
+}
diff --git a/drivers/isdn/hisax/bkm_a8.c b/drivers/isdn/hisax/bkm_a8.c
new file mode 100644
index 0000000..e1ff471
--- /dev/null
+++ b/drivers/isdn/hisax/bkm_a8.c
@@ -0,0 +1,435 @@
+/* $Id: bkm_a8.c,v 1.22.2.4 2004/01/15 14:02:34 keil Exp $
+ *
+ * low level stuff for Scitel Quadro (4*S0, passive)
+ *
+ * Author Roland Klabunde
+ * Copyright by Roland Klabunde <R.Klabunde@Berkom.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "ipac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include "bkm_ax.h"
+
+#define ATTEMPT_PCI_REMAPPING /* Required for PLX rev 1 */
+
+static const char sct_quadro_revision[] = "$Revision: 1.22.2.4 $";
+
+static const char *sct_quadro_subtypes[] =
+{
+ "",
+ "#1",
+ "#2",
+ "#3",
+ "#4"
+};
+
+
+#define wordout(addr,val) outw(val,addr)
+#define wordin(addr) inw(addr)
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+ register u_char ret;
+ wordout(ale, off);
+ ret = wordin(adr) & 0xFF;
+ return (ret);
+}
+
+static inline void
+readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+ int i;
+ wordout(ale, off);
+ for (i = 0; i < size; i++)
+ data[i] = wordin(adr) & 0xFF;
+}
+
+
+static inline void
+writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
+{
+ wordout(ale, off);
+ wordout(adr, data);
+}
+
+static inline void
+writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+ int i;
+ wordout(ale, off);
+ for (i = 0; i < size; i++)
+ wordout(adr, data[i]);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+ return (readreg(cs->hw.ax.base, cs->hw.ax.data_adr, offset | 0x80));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ writereg(cs->hw.ax.base, cs->hw.ax.data_adr, offset | 0x80, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ readfifo(cs->hw.ax.base, cs->hw.ax.data_adr, 0x80, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ writefifo(cs->hw.ax.base, cs->hw.ax.data_adr, 0x80, data, size);
+}
+
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+ return (readreg(cs->hw.ax.base, cs->hw.ax.data_adr, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+ writereg(cs->hw.ax.base, cs->hw.ax.data_adr, offset + (hscx ? 0x40 : 0), value);
+}
+
+/* Set the specific ipac to active */
+static void
+set_ipac_active(struct IsdnCardState *cs, u_int active)
+{
+ /* set irq mask */
+ writereg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_MASK,
+ active ? 0xc0 : 0xff);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.ax.base, \
+ cs->hw.ax.data_adr, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.ax.base, \
+ cs->hw.ax.data_adr, reg + (nr ? 0x40 : 0), data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.ax.base, \
+ cs->hw.ax.data_adr, (nr ? 0x40 : 0), ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.ax.base, \
+ cs->hw.ax.data_adr, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+bkm_interrupt_ipac(int intno, void *dev_id)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char ista, val, icnt = 5;
+ u_long flags;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ ista = readreg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_ISTA);
+ if (!(ista & 0x3f)) { /* not this IPAC */
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_NONE;
+ }
+ Start_IPAC:
+ if (cs->debug & L1_DEB_IPAC)
+ debugl1(cs, "IPAC ISTA %02X", ista);
+ if (ista & 0x0f) {
+ val = readreg(cs->hw.ax.base, cs->hw.ax.data_adr, HSCX_ISTA + 0x40);
+ if (ista & 0x01)
+ val |= 0x01;
+ if (ista & 0x04)
+ val |= 0x02;
+ if (ista & 0x08)
+ val |= 0x04;
+ if (val) {
+ hscx_int_main(cs, val);
+ }
+ }
+ if (ista & 0x20) {
+ val = 0xfe & readreg(cs->hw.ax.base, cs->hw.ax.data_adr, ISAC_ISTA | 0x80);
+ if (val) {
+ isac_interrupt(cs, val);
+ }
+ }
+ if (ista & 0x10) {
+ val = 0x01;
+ isac_interrupt(cs, val);
+ }
+ ista = readreg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_ISTA);
+ if ((ista & 0x3f) && icnt) {
+ icnt--;
+ goto Start_IPAC;
+ }
+ if (!icnt)
+ printk(KERN_WARNING "HiSax: Scitel Quadro (%s) IRQ LOOP\n",
+ sct_quadro_subtypes[cs->subtyp]);
+ writereg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_MASK, 0xFF);
+ writereg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_MASK, 0xC0);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static void
+release_io_sct_quadro(struct IsdnCardState *cs)
+{
+ release_region(cs->hw.ax.base & 0xffffffc0, 128);
+ if (cs->subtyp == SCT_1)
+ release_region(cs->hw.ax.plx_adr, 64);
+}
+
+static void
+enable_bkm_int(struct IsdnCardState *cs, unsigned bEnable)
+{
+ if (cs->typ == ISDN_CTYPE_SCT_QUADRO) {
+ if (bEnable)
+ wordout(cs->hw.ax.plx_adr + 0x4C, (wordin(cs->hw.ax.plx_adr + 0x4C) | 0x41));
+ else
+ wordout(cs->hw.ax.plx_adr + 0x4C, (wordin(cs->hw.ax.plx_adr + 0x4C) & ~0x41));
+ }
+}
+
+static void
+reset_bkm(struct IsdnCardState *cs)
+{
+ if (cs->subtyp == SCT_1) {
+ wordout(cs->hw.ax.plx_adr + 0x50, (wordin(cs->hw.ax.plx_adr + 0x50) & ~4));
+ mdelay(10);
+ /* Remove the soft reset */
+ wordout(cs->hw.ax.plx_adr + 0x50, (wordin(cs->hw.ax.plx_adr + 0x50) | 4));
+ mdelay(10);
+ }
+}
+
+static int
+BKM_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ u_long flags;
+
+ switch (mt) {
+ case CARD_RESET:
+ spin_lock_irqsave(&cs->lock, flags);
+ /* Disable ints */
+ set_ipac_active(cs, 0);
+ enable_bkm_int(cs, 0);
+ reset_bkm(cs);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return (0);
+ case CARD_RELEASE:
+ /* Sanity */
+ spin_lock_irqsave(&cs->lock, flags);
+ set_ipac_active(cs, 0);
+ enable_bkm_int(cs, 0);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ release_io_sct_quadro(cs);
+ return (0);
+ case CARD_INIT:
+ spin_lock_irqsave(&cs->lock, flags);
+ cs->debug |= L1_DEB_IPAC;
+ set_ipac_active(cs, 1);
+ inithscxisac(cs, 3);
+ /* Enable ints */
+ enable_bkm_int(cs, 1);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return (0);
+ case CARD_TEST:
+ return (0);
+ }
+ return (0);
+}
+
+static int __devinit
+sct_alloc_io(u_int adr, u_int len)
+{
+ if (!request_region(adr, len, "scitel")) {
+ printk(KERN_WARNING
+ "HiSax: Scitel port %#x-%#x already in use\n",
+ adr, adr + len);
+ return (1);
+ }
+ return(0);
+}
+
+static struct pci_dev *dev_a8 __devinitdata = NULL;
+static u16 sub_vendor_id __devinitdata = 0;
+static u16 sub_sys_id __devinitdata = 0;
+static u_char pci_bus __devinitdata = 0;
+static u_char pci_device_fn __devinitdata = 0;
+static u_char pci_irq __devinitdata = 0;
+
+int __devinit
+setup_sct_quadro(struct IsdnCard *card)
+{
+ struct IsdnCardState *cs = card->cs;
+ char tmp[64];
+ u_int found = 0;
+ u_int pci_ioaddr1, pci_ioaddr2, pci_ioaddr3, pci_ioaddr4, pci_ioaddr5;
+
+ strcpy(tmp, sct_quadro_revision);
+ printk(KERN_INFO "HiSax: T-Berkom driver Rev. %s\n", HiSax_getrev(tmp));
+ if (cs->typ == ISDN_CTYPE_SCT_QUADRO) {
+ cs->subtyp = SCT_1; /* Preset */
+ } else
+ return (0);
+
+ /* Identify subtype by para[0] */
+ if (card->para[0] >= SCT_1 && card->para[0] <= SCT_4)
+ cs->subtyp = card->para[0];
+ else {
+ printk(KERN_WARNING "HiSax: Scitel Quadro: Invalid "
+ "subcontroller in configuration, default to 1\n");
+ return (0);
+ }
+ if ((cs->subtyp != SCT_1) && ((sub_sys_id != PCI_DEVICE_ID_BERKOM_SCITEL_QUADRO) ||
+ (sub_vendor_id != PCI_VENDOR_ID_BERKOM)))
+ return (0);
+ if (cs->subtyp == SCT_1) {
+ while ((dev_a8 = pci_find_device(PCI_VENDOR_ID_PLX,
+ PCI_DEVICE_ID_PLX_9050, dev_a8))) {
+
+ sub_vendor_id = dev_a8->subsystem_vendor;
+ sub_sys_id = dev_a8->subsystem_device;
+ if ((sub_sys_id == PCI_DEVICE_ID_BERKOM_SCITEL_QUADRO) &&
+ (sub_vendor_id == PCI_VENDOR_ID_BERKOM)) {
+ if (pci_enable_device(dev_a8))
+ return(0);
+ pci_ioaddr1 = pci_resource_start(dev_a8, 1);
+ pci_irq = dev_a8->irq;
+ pci_bus = dev_a8->bus->number;
+ pci_device_fn = dev_a8->devfn;
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ printk(KERN_WARNING "HiSax: Scitel Quadro (%s): "
+ "Card not found\n",
+ sct_quadro_subtypes[cs->subtyp]);
+ return (0);
+ }
+#ifdef ATTEMPT_PCI_REMAPPING
+/* HACK: PLX revision 1 bug: PLX address bit 7 must not be set */
+ if ((pci_ioaddr1 & 0x80) && (dev_a8->revision == 1)) {
+ printk(KERN_WARNING "HiSax: Scitel Quadro (%s): "
+ "PLX rev 1, remapping required!\n",
+ sct_quadro_subtypes[cs->subtyp]);
+ /* Restart PCI negotiation */
+ pci_write_config_dword(dev_a8, PCI_BASE_ADDRESS_1, (u_int) - 1);
+ /* Move up by 0x80 byte */
+ pci_ioaddr1 += 0x80;
+ pci_ioaddr1 &= PCI_BASE_ADDRESS_IO_MASK;
+ pci_write_config_dword(dev_a8, PCI_BASE_ADDRESS_1, pci_ioaddr1);
+ dev_a8->resource[ 1].start = pci_ioaddr1;
+ }
+#endif /* End HACK */
+ }
+ if (!pci_irq) { /* IRQ range check ?? */
+ printk(KERN_WARNING "HiSax: Scitel Quadro (%s): No IRQ\n",
+ sct_quadro_subtypes[cs->subtyp]);
+ return (0);
+ }
+ pci_read_config_dword(dev_a8, PCI_BASE_ADDRESS_1, &pci_ioaddr1);
+ pci_read_config_dword(dev_a8, PCI_BASE_ADDRESS_2, &pci_ioaddr2);
+ pci_read_config_dword(dev_a8, PCI_BASE_ADDRESS_3, &pci_ioaddr3);
+ pci_read_config_dword(dev_a8, PCI_BASE_ADDRESS_4, &pci_ioaddr4);
+ pci_read_config_dword(dev_a8, PCI_BASE_ADDRESS_5, &pci_ioaddr5);
+ if (!pci_ioaddr1 || !pci_ioaddr2 || !pci_ioaddr3 || !pci_ioaddr4 || !pci_ioaddr5) {
+ printk(KERN_WARNING "HiSax: Scitel Quadro (%s): "
+ "No IO base address(es)\n",
+ sct_quadro_subtypes[cs->subtyp]);
+ return (0);
+ }
+ pci_ioaddr1 &= PCI_BASE_ADDRESS_IO_MASK;
+ pci_ioaddr2 &= PCI_BASE_ADDRESS_IO_MASK;
+ pci_ioaddr3 &= PCI_BASE_ADDRESS_IO_MASK;
+ pci_ioaddr4 &= PCI_BASE_ADDRESS_IO_MASK;
+ pci_ioaddr5 &= PCI_BASE_ADDRESS_IO_MASK;
+ /* Take over */
+ cs->irq = pci_irq;
+ cs->irq_flags |= IRQF_SHARED;
+ /* pci_ioaddr1 is unique to all subdevices */
+ /* pci_ioaddr2 is for the fourth subdevice only */
+ /* pci_ioaddr3 is for the third subdevice only */
+ /* pci_ioaddr4 is for the second subdevice only */
+ /* pci_ioaddr5 is for the first subdevice only */
+ cs->hw.ax.plx_adr = pci_ioaddr1;
+ /* Enter all ipac_base addresses */
+ switch(cs->subtyp) {
+ case 1:
+ cs->hw.ax.base = pci_ioaddr5 + 0x00;
+ if (sct_alloc_io(pci_ioaddr1, 128))
+ return(0);
+ if (sct_alloc_io(pci_ioaddr5, 64))
+ return(0);
+ /* disable all IPAC */
+ writereg(pci_ioaddr5, pci_ioaddr5 + 4,
+ IPAC_MASK, 0xFF);
+ writereg(pci_ioaddr4 + 0x08, pci_ioaddr4 + 0x0c,
+ IPAC_MASK, 0xFF);
+ writereg(pci_ioaddr3 + 0x10, pci_ioaddr3 + 0x14,
+ IPAC_MASK, 0xFF);
+ writereg(pci_ioaddr2 + 0x20, pci_ioaddr2 + 0x24,
+ IPAC_MASK, 0xFF);
+ break;
+ case 2:
+ cs->hw.ax.base = pci_ioaddr4 + 0x08;
+ if (sct_alloc_io(pci_ioaddr4, 64))
+ return(0);
+ break;
+ case 3:
+ cs->hw.ax.base = pci_ioaddr3 + 0x10;
+ if (sct_alloc_io(pci_ioaddr3, 64))
+ return(0);
+ break;
+ case 4:
+ cs->hw.ax.base = pci_ioaddr2 + 0x20;
+ if (sct_alloc_io(pci_ioaddr2, 64))
+ return(0);
+ break;
+ }
+ /* For isac and hscx data path */
+ cs->hw.ax.data_adr = cs->hw.ax.base + 4;
+
+ printk(KERN_INFO "HiSax: Scitel Quadro (%s) configured at "
+ "0x%.4lX, 0x%.4lX, 0x%.4lX and IRQ %d\n",
+ sct_quadro_subtypes[cs->subtyp],
+ cs->hw.ax.plx_adr,
+ cs->hw.ax.base,
+ cs->hw.ax.data_adr,
+ cs->irq);
+
+ test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+
+ cs->readisac = &ReadISAC;
+ cs->writeisac = &WriteISAC;
+ cs->readisacfifo = &ReadISACfifo;
+ cs->writeisacfifo = &WriteISACfifo;
+
+ cs->BC_Read_Reg = &ReadHSCX;
+ cs->BC_Write_Reg = &WriteHSCX;
+ cs->BC_Send_Data = &hscx_fill_fifo;
+ cs->cardmsg = &BKM_card_msg;
+ cs->irq_func = &bkm_interrupt_ipac;
+
+ printk(KERN_INFO "HiSax: Scitel Quadro (%s): IPAC Version %d\n",
+ sct_quadro_subtypes[cs->subtyp],
+ readreg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_ID));
+ return (1);
+}
diff --git a/drivers/isdn/hisax/bkm_ax.h b/drivers/isdn/hisax/bkm_ax.h
new file mode 100644
index 0000000..029e0a2
--- /dev/null
+++ b/drivers/isdn/hisax/bkm_ax.h
@@ -0,0 +1,119 @@
+/* $Id: bkm_ax.h,v 1.5.6.3 2001/09/23 22:24:46 kai Exp $
+ *
+ * low level decls for T-Berkom cards A4T and Scitel Quadro (4*S0, passive)
+ *
+ * Author Roland Klabunde
+ * Copyright by Roland Klabunde <R.Klabunde@Berkom.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef __BKM_AX_H__
+#define __BKM_AX_H__
+
+/* Supported boards (subtypes) */
+#define SCT_1 1
+#define SCT_2 2
+#define SCT_3 3
+#define SCT_4 4
+#define BKM_A4T 5
+
+#define PLX_ADDR_PLX 0x14 /* Addr PLX configuration */
+#define PLX_ADDR_ISAC 0x18 /* Addr ISAC */
+#define PLX_ADDR_HSCX 0x1C /* Addr HSCX */
+#define PLX_ADDR_ALE 0x20 /* Addr ALE */
+#define PLX_ADDR_ALEPLUS 0x24 /* Next Addr behind ALE */
+
+#define PLX_SUBVEN 0x2C /* Offset SubVendor */
+#define PLX_SUBSYS 0x2E /* Offset SubSystem */
+
+
+/* Application specific registers I20 (Siemens SZB6120H) */
+typedef struct {
+ /* Video front end horizontal configuration register */
+ volatile u_int i20VFEHorzCfg; /* Offset 00 */
+ /* Video front end vertical configuration register */
+ volatile u_int i20VFEVertCfg; /* Offset 04 */
+ /* Video front end scaler and pixel format register */
+ volatile u_int i20VFEScaler; /* Offset 08 */
+ /* Video display top register */
+ volatile u_int i20VDispTop; /* Offset 0C */
+ /* Video display bottom register */
+ volatile u_int i20VDispBottom; /* Offset 10 */
+ /* Video stride, status and frame grab register */
+ volatile u_int i20VidFrameGrab;/* Offset 14 */
+ /* Video display configuration register */
+ volatile u_int i20VDispCfg; /* Offset 18 */
+ /* Video masking map top */
+ volatile u_int i20VMaskTop; /* Offset 1C */
+ /* Video masking map bottom */
+ volatile u_int i20VMaskBottom; /* Offset 20 */
+ /* Overlay control register */
+ volatile u_int i20OvlyControl; /* Offset 24 */
+ /* System, PCI and general purpose pins control register */
+ volatile u_int i20SysControl; /* Offset 28 */
+#define sysRESET 0x01000000 /* bit 24:Softreset (Low) */
+ /* GPIO 4...0: Output fixed for our cfg! */
+#define sysCFG 0x000000E0 /* GPIO 7,6,5: Input */
+ /* General purpose pins and guest bus control register */
+ volatile u_int i20GuestControl;/* Offset 2C */
+#define guestWAIT_CFG 0x00005555 /* 4 PCI waits for all */
+#define guestISDN_INT_E 0x01000000 /* ISDN Int en (low) */
+#define guestVID_INT_E 0x02000000 /* Video interrupt en (low) */
+#define guestADI1_INT_R 0x04000000 /* ADI #1 int req (low) */
+#define guestADI2_INT_R 0x08000000 /* ADI #2 int req (low) */
+#define guestISDN_RES 0x10000000 /* ISDN reset bit (high) */
+#define guestADI1_INT_S 0x20000000 /* ADI #1 int pending (low) */
+#define guestADI2_INT_S 0x40000000 /* ADI #2 int pending (low) */
+#define guestISDN_INT_S 0x80000000 /* ISAC int pending (low) */
+
+#define g_A4T_JADE_RES 0x01000000 /* JADE Reset (High) */
+#define g_A4T_ISAR_RES 0x02000000 /* ISAR Reset (High) */
+#define g_A4T_ISAC_RES 0x04000000 /* ISAC Reset (High) */
+#define g_A4T_JADE_BOOTR 0x08000000 /* JADE enable boot SRAM (Low) NOT USED */
+#define g_A4T_ISAR_BOOTR 0x10000000 /* ISAR enable boot SRAM (Low) NOT USED */
+#define g_A4T_JADE_INT_S 0x20000000 /* JADE interrupt pnd (Low) */
+#define g_A4T_ISAR_INT_S 0x40000000 /* ISAR interrupt pnd (Low) */
+#define g_A4T_ISAC_INT_S 0x80000000 /* ISAC interrupt pnd (Low) */
+
+ volatile u_int i20CodeSource; /* Offset 30 */
+ volatile u_int i20CodeXferCtrl;/* Offset 34 */
+ volatile u_int i20CodeMemPtr; /* Offset 38 */
+
+ volatile u_int i20IntStatus; /* Offset 3C */
+ volatile u_int i20IntCtrl; /* Offset 40 */
+#define intISDN 0x40000000 /* GIRQ1En (ISAC/ADI) (High) */
+#define intVID 0x20000000 /* GIRQ0En (VSYNC) (High) */
+#define intCOD 0x10000000 /* CodRepIrqEn (High) */
+#define intPCI 0x01000000 /* PCI IntA enable (High) */
+
+ volatile u_int i20I2CCtrl; /* Offset 44 */
+} I20_REGISTER_FILE, *PI20_REGISTER_FILE;
+
+/*
+ * Postoffice structure for A4T
+ *
+ */
+#define PO_OFFSET 0x00000200 /* Postoffice offset from base */
+
+#define GCS_0 0x00000000 /* Guest bus chip selects */
+#define GCS_1 0x00100000
+#define GCS_2 0x00200000
+#define GCS_3 0x00300000
+
+#define PO_READ 0x00000000 /* R/W from/to guest bus */
+#define PO_WRITE 0x00800000
+
+#define PO_PEND 0x02000000
+
+#define POSTOFFICE(postoffice) *(volatile unsigned int*)(postoffice)
+
+/* Wait unlimited (don't worry) */
+#define __WAITI20__(postoffice) \
+do { \
+ while ((POSTOFFICE(postoffice) & PO_PEND)) ; \
+} while (0)
+
+#endif /* __BKM_AX_H__ */
diff --git a/drivers/isdn/hisax/callc.c b/drivers/isdn/hisax/callc.c
new file mode 100644
index 0000000..7c56c44
--- /dev/null
+++ b/drivers/isdn/hisax/callc.c
@@ -0,0 +1,1793 @@
+/* $Id: callc.c,v 2.59.2.4 2004/02/11 13:21:32 keil Exp $
+ *
+ * Author Karsten Keil
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ * based on the teles driver from Jan den Ouden
+ *
+ * Thanks to Jan den Ouden
+ * Fritz Elfert
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include "hisax.h"
+#include <linux/isdn/capicmd.h>
+
+const char *lli_revision = "$Revision: 2.59.2.4 $";
+
+extern struct IsdnCard cards[];
+extern int nrcards;
+
+static int init_b_st(struct Channel *chanp, int incoming);
+static void release_b_st(struct Channel *chanp);
+
+static struct Fsm callcfsm;
+static int chancount;
+
+/* experimental REJECT after ALERTING for CALLBACK to beat the 4s delay */
+#define ALERT_REJECT 0
+
+/* Value to delay the sending of the first B-channel paket after CONNECT
+ * here is no value given by ITU, but experience shows that 300 ms will
+ * work on many networks, if you or your other side is behind local exchanges
+ * a greater value may be recommented. If the delay is to short the first paket
+ * will be lost and autodetect on many comercial routers goes wrong !
+ * You can adjust this value on runtime with
+ * hisaxctrl <id> 2 <value>
+ * value is in milliseconds
+ */
+#define DEFAULT_B_DELAY 300
+
+/* Flags for remembering action done in lli */
+
+#define FLG_START_B 0
+
+/*
+ * Find card with given driverId
+ */
+static inline struct IsdnCardState *
+hisax_findcard(int driverid)
+{
+ int i;
+
+ for (i = 0; i < nrcards; i++)
+ if (cards[i].cs)
+ if (cards[i].cs->myid == driverid)
+ return (cards[i].cs);
+ return (struct IsdnCardState *) 0;
+}
+
+static void
+link_debug(struct Channel *chanp, int direction, char *fmt, ...)
+{
+ va_list args;
+ char tmp[16];
+
+ va_start(args, fmt);
+ sprintf(tmp, "Ch%d %s ", chanp->chan,
+ direction ? "LL->HL" : "HL->LL");
+ VHiSax_putstatus(chanp->cs, tmp, fmt, args);
+ va_end(args);
+}
+
+enum {
+ ST_NULL, /* 0 inactive */
+ ST_OUT_DIAL, /* 1 outgoing, SETUP send; awaiting confirm */
+ ST_IN_WAIT_LL, /* 2 incoming call received; wait for LL confirm */
+ ST_IN_ALERT_SENT, /* 3 incoming call received; ALERT send */
+ ST_IN_WAIT_CONN_ACK, /* 4 incoming CONNECT send; awaiting CONN_ACK */
+ ST_WAIT_BCONN, /* 5 CONNECT/CONN_ACK received, awaiting b-channel prot. estbl. */
+ ST_ACTIVE, /* 6 active, b channel prot. established */
+ ST_WAIT_BRELEASE, /* 7 call clear. (initiator), awaiting b channel prot. rel. */
+ ST_WAIT_BREL_DISC, /* 8 call clear. (receiver), DISCONNECT req. received */
+ ST_WAIT_DCOMMAND, /* 9 call clear. (receiver), awaiting DCHANNEL message */
+ ST_WAIT_DRELEASE, /* 10 DISCONNECT sent, awaiting RELEASE */
+ ST_WAIT_D_REL_CNF, /* 11 RELEASE sent, awaiting RELEASE confirm */
+ ST_IN_PROCEED_SEND, /* 12 incoming call, proceeding send */
+};
+
+
+#define STATE_COUNT (ST_IN_PROCEED_SEND + 1)
+
+static char *strState[] =
+{
+ "ST_NULL",
+ "ST_OUT_DIAL",
+ "ST_IN_WAIT_LL",
+ "ST_IN_ALERT_SENT",
+ "ST_IN_WAIT_CONN_ACK",
+ "ST_WAIT_BCONN",
+ "ST_ACTIVE",
+ "ST_WAIT_BRELEASE",
+ "ST_WAIT_BREL_DISC",
+ "ST_WAIT_DCOMMAND",
+ "ST_WAIT_DRELEASE",
+ "ST_WAIT_D_REL_CNF",
+ "ST_IN_PROCEED_SEND",
+};
+
+enum {
+ EV_DIAL, /* 0 */
+ EV_SETUP_CNF, /* 1 */
+ EV_ACCEPTB, /* 2 */
+ EV_DISCONNECT_IND, /* 3 */
+ EV_RELEASE, /* 4 */
+ EV_LEASED, /* 5 */
+ EV_LEASED_REL, /* 6 */
+ EV_SETUP_IND, /* 7 */
+ EV_ACCEPTD, /* 8 */
+ EV_SETUP_CMPL_IND, /* 9 */
+ EV_BC_EST, /* 10 */
+ EV_WRITEBUF, /* 11 */
+ EV_HANGUP, /* 12 */
+ EV_BC_REL, /* 13 */
+ EV_CINF, /* 14 */
+ EV_SUSPEND, /* 15 */
+ EV_RESUME, /* 16 */
+ EV_NOSETUP_RSP, /* 17 */
+ EV_SETUP_ERR, /* 18 */
+ EV_CONNECT_ERR, /* 19 */
+ EV_PROCEED, /* 20 */
+ EV_ALERT, /* 21 */
+ EV_REDIR, /* 22 */
+};
+
+#define EVENT_COUNT (EV_REDIR + 1)
+
+static char *strEvent[] =
+{
+ "EV_DIAL",
+ "EV_SETUP_CNF",
+ "EV_ACCEPTB",
+ "EV_DISCONNECT_IND",
+ "EV_RELEASE",
+ "EV_LEASED",
+ "EV_LEASED_REL",
+ "EV_SETUP_IND",
+ "EV_ACCEPTD",
+ "EV_SETUP_CMPL_IND",
+ "EV_BC_EST",
+ "EV_WRITEBUF",
+ "EV_HANGUP",
+ "EV_BC_REL",
+ "EV_CINF",
+ "EV_SUSPEND",
+ "EV_RESUME",
+ "EV_NOSETUP_RSP",
+ "EV_SETUP_ERR",
+ "EV_CONNECT_ERR",
+ "EV_PROCEED",
+ "EV_ALERT",
+ "EV_REDIR",
+};
+
+
+static inline void
+HL_LL(struct Channel *chanp, int command)
+{
+ isdn_ctrl ic;
+
+ ic.driver = chanp->cs->myid;
+ ic.command = command;
+ ic.arg = chanp->chan;
+ chanp->cs->iif.statcallb(&ic);
+}
+
+static inline void
+lli_deliver_cause(struct Channel *chanp)
+{
+ isdn_ctrl ic;
+
+ if (!chanp->proc)
+ return;
+ if (chanp->proc->para.cause == NO_CAUSE)
+ return;
+ ic.driver = chanp->cs->myid;
+ ic.command = ISDN_STAT_CAUSE;
+ ic.arg = chanp->chan;
+ if (chanp->cs->protocol == ISDN_PTYPE_EURO)
+ sprintf(ic.parm.num, "E%02X%02X", chanp->proc->para.loc & 0x7f,
+ chanp->proc->para.cause & 0x7f);
+ else
+ sprintf(ic.parm.num, "%02X%02X", chanp->proc->para.loc & 0x7f,
+ chanp->proc->para.cause & 0x7f);
+ chanp->cs->iif.statcallb(&ic);
+}
+
+static inline void
+lli_close(struct FsmInst *fi)
+{
+ struct Channel *chanp = fi->userdata;
+
+ FsmChangeState(fi, ST_NULL);
+ chanp->Flags = 0;
+ chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
+}
+
+static void
+lli_leased_in(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+ isdn_ctrl ic;
+ int ret;
+
+ if (!chanp->leased)
+ return;
+ chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan);
+ FsmChangeState(fi, ST_IN_WAIT_LL);
+ if (chanp->debug & 1)
+ link_debug(chanp, 0, "STAT_ICALL_LEASED");
+ ic.driver = chanp->cs->myid;
+ ic.command = ((chanp->chan < 2) ? ISDN_STAT_ICALL : ISDN_STAT_ICALLW);
+ ic.arg = chanp->chan;
+ ic.parm.setup.si1 = 7;
+ ic.parm.setup.si2 = 0;
+ ic.parm.setup.plan = 0;
+ ic.parm.setup.screen = 0;
+ sprintf(ic.parm.setup.eazmsn,"%d", chanp->chan + 1);
+ sprintf(ic.parm.setup.phone,"LEASED%d", chanp->cs->myid);
+ ret = chanp->cs->iif.statcallb(&ic);
+ if (chanp->debug & 1)
+ link_debug(chanp, 1, "statcallb ret=%d", ret);
+ if (!ret) {
+ chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
+ FsmChangeState(fi, ST_NULL);
+ }
+}
+
+
+/*
+ * Dial out
+ */
+static void
+lli_init_bchan_out(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ FsmChangeState(fi, ST_WAIT_BCONN);
+ if (chanp->debug & 1)
+ link_debug(chanp, 0, "STAT_DCONN");
+ HL_LL(chanp, ISDN_STAT_DCONN);
+ init_b_st(chanp, 0);
+ chanp->b_st->lli.l4l3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL);
+}
+
+static void
+lli_prep_dialout(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ FsmDelTimer(&chanp->drel_timer, 60);
+ FsmDelTimer(&chanp->dial_timer, 73);
+ chanp->l2_active_protocol = chanp->l2_protocol;
+ chanp->incoming = 0;
+ chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan);
+ if (chanp->leased) {
+ lli_init_bchan_out(fi, event, arg);
+ } else {
+ FsmChangeState(fi, ST_OUT_DIAL);
+ chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | REQUEST, chanp);
+ }
+}
+
+static void
+lli_resume(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ FsmDelTimer(&chanp->drel_timer, 60);
+ FsmDelTimer(&chanp->dial_timer, 73);
+ chanp->l2_active_protocol = chanp->l2_protocol;
+ chanp->incoming = 0;
+ chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan);
+ if (chanp->leased) {
+ lli_init_bchan_out(fi, event, arg);
+ } else {
+ FsmChangeState(fi, ST_OUT_DIAL);
+ chanp->d_st->lli.l4l3(chanp->d_st, CC_RESUME | REQUEST, chanp);
+ }
+}
+
+static void
+lli_go_active(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+ isdn_ctrl ic;
+
+
+ FsmChangeState(fi, ST_ACTIVE);
+ chanp->data_open = !0;
+ if (chanp->bcs->conmsg)
+ strcpy(ic.parm.num, chanp->bcs->conmsg);
+ else
+ ic.parm.num[0] = 0;
+ if (chanp->debug & 1)
+ link_debug(chanp, 0, "STAT_BCONN %s", ic.parm.num);
+ ic.driver = chanp->cs->myid;
+ ic.command = ISDN_STAT_BCONN;
+ ic.arg = chanp->chan;
+ chanp->cs->iif.statcallb(&ic);
+ chanp->cs->cardmsg(chanp->cs, MDL_INFO_CONN, (void *) (long)chanp->chan);
+}
+
+
+/*
+ * RESUME
+ */
+
+/* incoming call */
+
+static void
+lli_deliver_call(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+ isdn_ctrl ic;
+ int ret;
+
+ chanp->cs->cardmsg(chanp->cs, MDL_INFO_SETUP, (void *) (long)chanp->chan);
+ /*
+ * Report incoming calls only once to linklevel, use CallFlags
+ * which is set to 3 with each broadcast message in isdnl1.c
+ * and resetted if a interface answered the STAT_ICALL.
+ */
+ if (1) { /* for only one TEI */
+ FsmChangeState(fi, ST_IN_WAIT_LL);
+ if (chanp->debug & 1)
+ link_debug(chanp, 0, (chanp->chan < 2) ? "STAT_ICALL" : "STAT_ICALLW");
+ ic.driver = chanp->cs->myid;
+ ic.command = ((chanp->chan < 2) ? ISDN_STAT_ICALL : ISDN_STAT_ICALLW);
+
+ ic.arg = chanp->chan;
+ /*
+ * No need to return "unknown" for calls without OAD,
+ * cause that's handled in linklevel now (replaced by '0')
+ */
+ memcpy(&ic.parm.setup, &chanp->proc->para.setup, sizeof(setup_parm));
+ ret = chanp->cs->iif.statcallb(&ic);
+ if (chanp->debug & 1)
+ link_debug(chanp, 1, "statcallb ret=%d", ret);
+
+ switch (ret) {
+ case 1: /* OK, someone likes this call */
+ FsmDelTimer(&chanp->drel_timer, 61);
+ FsmChangeState(fi, ST_IN_ALERT_SENT);
+ chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
+ break;
+ case 5: /* direct redirect */
+ case 4: /* Proceeding desired */
+ FsmDelTimer(&chanp->drel_timer, 61);
+ FsmChangeState(fi, ST_IN_PROCEED_SEND);
+ chanp->d_st->lli.l4l3(chanp->d_st, CC_PROCEED_SEND | REQUEST, chanp->proc);
+ if (ret == 5) {
+ memcpy(&chanp->setup, &ic.parm.setup, sizeof(setup_parm));
+ chanp->d_st->lli.l4l3(chanp->d_st, CC_REDIR | REQUEST, chanp->proc);
+ }
+ break;
+ case 2: /* Rejecting Call */
+ break;
+ case 3: /* incomplete number */
+ FsmDelTimer(&chanp->drel_timer, 61);
+ chanp->d_st->lli.l4l3(chanp->d_st, CC_MORE_INFO | REQUEST, chanp->proc);
+ break;
+ case 0: /* OK, nobody likes this call */
+ default: /* statcallb problems */
+ chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE | REQUEST, chanp->proc);
+ chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
+ FsmChangeState(fi, ST_NULL);
+ break;
+ }
+ } else {
+ chanp->d_st->lli.l4l3(chanp->d_st, CC_IGNORE | REQUEST, chanp->proc);
+ chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
+ }
+}
+
+static void
+lli_send_dconnect(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ FsmChangeState(fi, ST_IN_WAIT_CONN_ACK);
+ chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc);
+}
+
+static void
+lli_send_alert(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ FsmChangeState(fi, ST_IN_ALERT_SENT);
+ chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
+}
+
+static void
+lli_send_redir(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ chanp->d_st->lli.l4l3(chanp->d_st, CC_REDIR | REQUEST, chanp->proc);
+}
+
+static void
+lli_init_bchan_in(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ FsmChangeState(fi, ST_WAIT_BCONN);
+ if (chanp->debug & 1)
+ link_debug(chanp, 0, "STAT_DCONN");
+ HL_LL(chanp, ISDN_STAT_DCONN);
+ chanp->l2_active_protocol = chanp->l2_protocol;
+ chanp->incoming = !0;
+ init_b_st(chanp, !0);
+ chanp->b_st->lli.l4l3(chanp->b_st, DL_ESTABLISH | REQUEST, NULL);
+}
+
+static void
+lli_setup_rsp(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ if (chanp->leased) {
+ lli_init_bchan_in(fi, event, arg);
+ } else {
+ FsmChangeState(fi, ST_IN_WAIT_CONN_ACK);
+#ifdef WANT_ALERT
+ chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
+#endif
+ chanp->d_st->lli.l4l3(chanp->d_st, CC_SETUP | RESPONSE, chanp->proc);
+ }
+}
+
+/* Call suspend */
+
+static void
+lli_suspend(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ chanp->d_st->lli.l4l3(chanp->d_st, CC_SUSPEND | REQUEST, chanp->proc);
+}
+
+/* Call clearing */
+
+static void
+lli_leased_hup(struct FsmInst *fi, struct Channel *chanp)
+{
+ isdn_ctrl ic;
+
+ ic.driver = chanp->cs->myid;
+ ic.command = ISDN_STAT_CAUSE;
+ ic.arg = chanp->chan;
+ sprintf(ic.parm.num, "L0010");
+ chanp->cs->iif.statcallb(&ic);
+ if (chanp->debug & 1)
+ link_debug(chanp, 0, "STAT_DHUP");
+ HL_LL(chanp, ISDN_STAT_DHUP);
+ lli_close(fi);
+}
+
+static void
+lli_disconnect_req(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ if (chanp->leased) {
+ lli_leased_hup(fi, chanp);
+ } else {
+ FsmChangeState(fi, ST_WAIT_DRELEASE);
+ if (chanp->proc)
+ chanp->proc->para.cause = 0x10; /* Normal Call Clearing */
+ chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST,
+ chanp->proc);
+ }
+}
+
+static void
+lli_disconnect_reject(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ if (chanp->leased) {
+ lli_leased_hup(fi, chanp);
+ } else {
+ FsmChangeState(fi, ST_WAIT_DRELEASE);
+ if (chanp->proc)
+ chanp->proc->para.cause = 0x15; /* Call Rejected */
+ chanp->d_st->lli.l4l3(chanp->d_st, CC_DISCONNECT | REQUEST,
+ chanp->proc);
+ }
+}
+
+static void
+lli_dhup_close(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ if (chanp->leased) {
+ lli_leased_hup(fi, chanp);
+ } else {
+ if (chanp->debug & 1)
+ link_debug(chanp, 0, "STAT_DHUP");
+ lli_deliver_cause(chanp);
+ HL_LL(chanp, ISDN_STAT_DHUP);
+ lli_close(fi);
+ }
+}
+
+static void
+lli_reject_req(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ if (chanp->leased) {
+ lli_leased_hup(fi, chanp);
+ return;
+ }
+#ifndef ALERT_REJECT
+ if (chanp->proc)
+ chanp->proc->para.cause = 0x15; /* Call Rejected */
+ chanp->d_st->lli.l4l3(chanp->d_st, CC_REJECT | REQUEST, chanp->proc);
+ lli_dhup_close(fi, event, arg);
+#else
+ FsmRestartTimer(&chanp->drel_timer, 40, EV_HANGUP, NULL, 63);
+ FsmChangeState(fi, ST_IN_ALERT_SENT);
+ chanp->d_st->lli.l4l3(chanp->d_st, CC_ALERTING | REQUEST, chanp->proc);
+#endif
+}
+
+static void
+lli_disconn_bchan(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ chanp->data_open = 0;
+ FsmChangeState(fi, ST_WAIT_BRELEASE);
+ chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
+}
+
+static void
+lli_start_disc(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ if (chanp->leased) {
+ lli_leased_hup(fi, chanp);
+ } else {
+ lli_disconnect_req(fi, event, arg);
+ }
+}
+
+static void
+lli_rel_b_disc(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ release_b_st(chanp);
+ lli_start_disc(fi, event, arg);
+}
+
+static void
+lli_bhup_disc(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ if (chanp->debug & 1)
+ link_debug(chanp, 0, "STAT_BHUP");
+ HL_LL(chanp, ISDN_STAT_BHUP);
+ lli_rel_b_disc(fi, event, arg);
+}
+
+static void
+lli_bhup_rel_b(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ FsmChangeState(fi, ST_WAIT_DCOMMAND);
+ chanp->data_open = 0;
+ if (chanp->debug & 1)
+ link_debug(chanp, 0, "STAT_BHUP");
+ HL_LL(chanp, ISDN_STAT_BHUP);
+ release_b_st(chanp);
+}
+
+static void
+lli_release_bchan(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ chanp->data_open = 0;
+ FsmChangeState(fi, ST_WAIT_BREL_DISC);
+ chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
+}
+
+
+static void
+lli_rel_b_dhup(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ release_b_st(chanp);
+ lli_dhup_close(fi, event, arg);
+}
+
+static void
+lli_bhup_dhup(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ if (chanp->debug & 1)
+ link_debug(chanp, 0, "STAT_BHUP");
+ HL_LL(chanp, ISDN_STAT_BHUP);
+ lli_rel_b_dhup(fi, event, arg);
+}
+
+static void
+lli_abort(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ chanp->data_open = 0;
+ chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
+ lli_bhup_dhup(fi, event, arg);
+}
+
+static void
+lli_release_req(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ if (chanp->leased) {
+ lli_leased_hup(fi, chanp);
+ } else {
+ FsmChangeState(fi, ST_WAIT_D_REL_CNF);
+ chanp->d_st->lli.l4l3(chanp->d_st, CC_RELEASE | REQUEST,
+ chanp->proc);
+ }
+}
+
+static void
+lli_rel_b_release_req(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ release_b_st(chanp);
+ lli_release_req(fi, event, arg);
+}
+
+static void
+lli_bhup_release_req(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ if (chanp->debug & 1)
+ link_debug(chanp, 0, "STAT_BHUP");
+ HL_LL(chanp, ISDN_STAT_BHUP);
+ lli_rel_b_release_req(fi, event, arg);
+}
+
+
+/* processing charge info */
+static void
+lli_charge_info(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+ isdn_ctrl ic;
+
+ ic.driver = chanp->cs->myid;
+ ic.command = ISDN_STAT_CINF;
+ ic.arg = chanp->chan;
+ sprintf(ic.parm.num, "%d", chanp->proc->para.chargeinfo);
+ chanp->cs->iif.statcallb(&ic);
+}
+
+/* error procedures */
+
+static void
+lli_dchan_not_ready(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ if (chanp->debug & 1)
+ link_debug(chanp, 0, "STAT_DHUP");
+ HL_LL(chanp, ISDN_STAT_DHUP);
+}
+
+static void
+lli_no_setup_rsp(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ if (chanp->debug & 1)
+ link_debug(chanp, 0, "STAT_DHUP");
+ HL_LL(chanp, ISDN_STAT_DHUP);
+ lli_close(fi);
+}
+
+static void
+lli_error(struct FsmInst *fi, int event, void *arg)
+{
+ FsmChangeState(fi, ST_WAIT_DRELEASE);
+}
+
+static void
+lli_failure_l(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+ isdn_ctrl ic;
+
+ FsmChangeState(fi, ST_NULL);
+ ic.driver = chanp->cs->myid;
+ ic.command = ISDN_STAT_CAUSE;
+ ic.arg = chanp->chan;
+ sprintf(ic.parm.num, "L%02X%02X", 0, 0x2f);
+ chanp->cs->iif.statcallb(&ic);
+ HL_LL(chanp, ISDN_STAT_DHUP);
+ chanp->Flags = 0;
+ chanp->cs->cardmsg(chanp->cs, MDL_INFO_REL, (void *) (long)chanp->chan);
+}
+
+static void
+lli_rel_b_fail(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ release_b_st(chanp);
+ lli_failure_l(fi, event, arg);
+}
+
+static void
+lli_bhup_fail(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ if (chanp->debug & 1)
+ link_debug(chanp, 0, "STAT_BHUP");
+ HL_LL(chanp, ISDN_STAT_BHUP);
+ lli_rel_b_fail(fi, event, arg);
+}
+
+static void
+lli_failure_a(struct FsmInst *fi, int event, void *arg)
+{
+ struct Channel *chanp = fi->userdata;
+
+ chanp->data_open = 0;
+ chanp->b_st->lli.l4l3(chanp->b_st, DL_RELEASE | REQUEST, NULL);
+ lli_bhup_fail(fi, event, arg);
+}
+
+/* *INDENT-OFF* */
+static struct FsmNode fnlist[] __initdata =
+{
+ {ST_NULL, EV_DIAL, lli_prep_dialout},
+ {ST_NULL, EV_RESUME, lli_resume},
+ {ST_NULL, EV_SETUP_IND, lli_deliver_call},
+ {ST_NULL, EV_LEASED, lli_leased_in},
+ {ST_OUT_DIAL, EV_SETUP_CNF, lli_init_bchan_out},
+ {ST_OUT_DIAL, EV_HANGUP, lli_disconnect_req},
+ {ST_OUT_DIAL, EV_DISCONNECT_IND, lli_release_req},
+ {ST_OUT_DIAL, EV_RELEASE, lli_dhup_close},
+ {ST_OUT_DIAL, EV_NOSETUP_RSP, lli_no_setup_rsp},
+ {ST_OUT_DIAL, EV_SETUP_ERR, lli_error},
+ {ST_IN_WAIT_LL, EV_LEASED_REL, lli_failure_l},
+ {ST_IN_WAIT_LL, EV_ACCEPTD, lli_setup_rsp},
+ {ST_IN_WAIT_LL, EV_HANGUP, lli_reject_req},
+ {ST_IN_WAIT_LL, EV_DISCONNECT_IND, lli_release_req},
+ {ST_IN_WAIT_LL, EV_RELEASE, lli_dhup_close},
+ {ST_IN_WAIT_LL, EV_SETUP_IND, lli_deliver_call},
+ {ST_IN_WAIT_LL, EV_SETUP_ERR, lli_error},
+ {ST_IN_ALERT_SENT, EV_SETUP_CMPL_IND, lli_init_bchan_in},
+ {ST_IN_ALERT_SENT, EV_ACCEPTD, lli_send_dconnect},
+ {ST_IN_ALERT_SENT, EV_HANGUP, lli_disconnect_reject},
+ {ST_IN_ALERT_SENT, EV_DISCONNECT_IND, lli_release_req},
+ {ST_IN_ALERT_SENT, EV_RELEASE, lli_dhup_close},
+ {ST_IN_ALERT_SENT, EV_REDIR, lli_send_redir},
+ {ST_IN_PROCEED_SEND, EV_REDIR, lli_send_redir},
+ {ST_IN_PROCEED_SEND, EV_ALERT, lli_send_alert},
+ {ST_IN_PROCEED_SEND, EV_ACCEPTD, lli_send_dconnect},
+ {ST_IN_PROCEED_SEND, EV_HANGUP, lli_disconnect_reject},
+ {ST_IN_PROCEED_SEND, EV_DISCONNECT_IND, lli_dhup_close},
+ {ST_IN_ALERT_SENT, EV_RELEASE, lli_dhup_close},
+ {ST_IN_WAIT_CONN_ACK, EV_SETUP_CMPL_IND, lli_init_bchan_in},
+ {ST_IN_WAIT_CONN_ACK, EV_HANGUP, lli_disconnect_req},
+ {ST_IN_WAIT_CONN_ACK, EV_DISCONNECT_IND, lli_release_req},
+ {ST_IN_WAIT_CONN_ACK, EV_RELEASE, lli_dhup_close},
+ {ST_IN_WAIT_CONN_ACK, EV_CONNECT_ERR, lli_error},
+ {ST_WAIT_BCONN, EV_BC_EST, lli_go_active},
+ {ST_WAIT_BCONN, EV_BC_REL, lli_rel_b_disc},
+ {ST_WAIT_BCONN, EV_HANGUP, lli_rel_b_disc},
+ {ST_WAIT_BCONN, EV_DISCONNECT_IND, lli_rel_b_release_req},
+ {ST_WAIT_BCONN, EV_RELEASE, lli_rel_b_dhup},
+ {ST_WAIT_BCONN, EV_LEASED_REL, lli_rel_b_fail},
+ {ST_WAIT_BCONN, EV_CINF, lli_charge_info},
+ {ST_ACTIVE, EV_CINF, lli_charge_info},
+ {ST_ACTIVE, EV_BC_REL, lli_bhup_rel_b},
+ {ST_ACTIVE, EV_SUSPEND, lli_suspend},
+ {ST_ACTIVE, EV_HANGUP, lli_disconn_bchan},
+ {ST_ACTIVE, EV_DISCONNECT_IND, lli_release_bchan},
+ {ST_ACTIVE, EV_RELEASE, lli_abort},
+ {ST_ACTIVE, EV_LEASED_REL, lli_failure_a},
+ {ST_WAIT_BRELEASE, EV_BC_REL, lli_bhup_disc},
+ {ST_WAIT_BRELEASE, EV_DISCONNECT_IND, lli_bhup_release_req},
+ {ST_WAIT_BRELEASE, EV_RELEASE, lli_bhup_dhup},
+ {ST_WAIT_BRELEASE, EV_LEASED_REL, lli_bhup_fail},
+ {ST_WAIT_BREL_DISC, EV_BC_REL, lli_bhup_release_req},
+ {ST_WAIT_BREL_DISC, EV_RELEASE, lli_bhup_dhup},
+ {ST_WAIT_DCOMMAND, EV_HANGUP, lli_start_disc},
+ {ST_WAIT_DCOMMAND, EV_DISCONNECT_IND, lli_release_req},
+ {ST_WAIT_DCOMMAND, EV_RELEASE, lli_dhup_close},
+ {ST_WAIT_DCOMMAND, EV_LEASED_REL, lli_failure_l},
+ {ST_WAIT_DRELEASE, EV_RELEASE, lli_dhup_close},
+ {ST_WAIT_DRELEASE, EV_DIAL, lli_dchan_not_ready},
+ /* ETS 300-104 16.1 */
+ {ST_WAIT_D_REL_CNF, EV_RELEASE, lli_dhup_close},
+ {ST_WAIT_D_REL_CNF, EV_DIAL, lli_dchan_not_ready},
+};
+/* *INDENT-ON* */
+
+#define FNCOUNT (sizeof(fnlist)/sizeof(struct FsmNode))
+
+int __init
+CallcNew(void)
+{
+ callcfsm.state_count = STATE_COUNT;
+ callcfsm.event_count = EVENT_COUNT;
+ callcfsm.strEvent = strEvent;
+ callcfsm.strState = strState;
+ return FsmNew(&callcfsm, fnlist, FNCOUNT);
+}
+
+void
+CallcFree(void)
+{
+ FsmFree(&callcfsm);
+}
+
+static void
+release_b_st(struct Channel *chanp)
+{
+ struct PStack *st = chanp->b_st;
+
+ if(test_and_clear_bit(FLG_START_B, &chanp->Flags)) {
+ chanp->bcs->BC_Close(chanp->bcs);
+ switch (chanp->l2_active_protocol) {
+ case (ISDN_PROTO_L2_X75I):
+ releasestack_isdnl2(st);
+ break;
+ case (ISDN_PROTO_L2_HDLC):
+ case (ISDN_PROTO_L2_HDLC_56K):
+ case (ISDN_PROTO_L2_TRANS):
+ case (ISDN_PROTO_L2_MODEM):
+ case (ISDN_PROTO_L2_FAX):
+ releasestack_transl2(st);
+ break;
+ }
+ }
+}
+
+static struct Channel
+*selectfreechannel(struct PStack *st, int bch)
+{
+ struct IsdnCardState *cs = st->l1.hardware;
+ struct Channel *chanp = st->lli.userdata;
+ int i;
+
+ if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags))
+ i=1;
+ else
+ i=0;
+
+ if (!bch) {
+ i = 2; /* virtual channel */
+ chanp += 2;
+ }
+
+ while (i < ((bch) ? cs->chanlimit : (2 + MAX_WAITING_CALLS))) {
+ if (chanp->fi.state == ST_NULL)
+ return (chanp);
+ chanp++;
+ i++;
+ }
+
+ if (bch) /* number of channels is limited */ {
+ i = 2; /* virtual channel */
+ chanp = st->lli.userdata;
+ chanp += i;
+ while (i < (2 + MAX_WAITING_CALLS)) {
+ if (chanp->fi.state == ST_NULL)
+ return (chanp);
+ chanp++;
+ i++;
+ }
+ }
+ return (NULL);
+}
+
+static void stat_redir_result(struct IsdnCardState *cs, int chan, ulong result)
+{ isdn_ctrl ic;
+
+ ic.driver = cs->myid;
+ ic.command = ISDN_STAT_REDIR;
+ ic.arg = chan;
+ ic.parm.num[0] = result;
+ cs->iif.statcallb(&ic);
+} /* stat_redir_result */
+
+static void
+dchan_l3l4(struct PStack *st, int pr, void *arg)
+{
+ struct l3_process *pc = arg;
+ struct IsdnCardState *cs = st->l1.hardware;
+ struct Channel *chanp;
+
+ if(!pc)
+ return;
+
+ if (pr == (CC_SETUP | INDICATION)) {
+ if (!(chanp = selectfreechannel(pc->st, pc->para.bchannel))) {
+ pc->para.cause = 0x11; /* User busy */
+ pc->st->lli.l4l3(pc->st, CC_REJECT | REQUEST, pc);
+ } else {
+ chanp->proc = pc;
+ pc->chan = chanp;
+ FsmEvent(&chanp->fi, EV_SETUP_IND, NULL);
+ }
+ return;
+ }
+ if (!(chanp = pc->chan))
+ return;
+
+ switch (pr) {
+ case (CC_MORE_INFO | INDICATION):
+ FsmEvent(&chanp->fi, EV_SETUP_IND, NULL);
+ break;
+ case (CC_DISCONNECT | INDICATION):
+ FsmEvent(&chanp->fi, EV_DISCONNECT_IND, NULL);
+ break;
+ case (CC_RELEASE | CONFIRM):
+ FsmEvent(&chanp->fi, EV_RELEASE, NULL);
+ break;
+ case (CC_SUSPEND | CONFIRM):
+ FsmEvent(&chanp->fi, EV_RELEASE, NULL);
+ break;
+ case (CC_RESUME | CONFIRM):
+ FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL);
+ break;
+ case (CC_RESUME_ERR):
+ FsmEvent(&chanp->fi, EV_RELEASE, NULL);
+ break;
+ case (CC_RELEASE | INDICATION):
+ FsmEvent(&chanp->fi, EV_RELEASE, NULL);
+ break;
+ case (CC_SETUP_COMPL | INDICATION):
+ FsmEvent(&chanp->fi, EV_SETUP_CMPL_IND, NULL);
+ break;
+ case (CC_SETUP | CONFIRM):
+ FsmEvent(&chanp->fi, EV_SETUP_CNF, NULL);
+ break;
+ case (CC_CHARGE | INDICATION):
+ FsmEvent(&chanp->fi, EV_CINF, NULL);
+ break;
+ case (CC_NOSETUP_RSP):
+ FsmEvent(&chanp->fi, EV_NOSETUP_RSP, NULL);
+ break;
+ case (CC_SETUP_ERR):
+ FsmEvent(&chanp->fi, EV_SETUP_ERR, NULL);
+ break;
+ case (CC_CONNECT_ERR):
+ FsmEvent(&chanp->fi, EV_CONNECT_ERR, NULL);
+ break;
+ case (CC_RELEASE_ERR):
+ FsmEvent(&chanp->fi, EV_RELEASE, NULL);
+ break;
+ case (CC_PROCEED_SEND | INDICATION):
+ case (CC_PROCEEDING | INDICATION):
+ case (CC_ALERTING | INDICATION):
+ case (CC_PROGRESS | INDICATION):
+ case (CC_NOTIFY | INDICATION):
+ break;
+ case (CC_REDIR | INDICATION):
+ stat_redir_result(cs, chanp->chan, pc->redir_result);
+ break;
+ default:
+ if (chanp->debug & 0x800) {
+ HiSax_putstatus(chanp->cs, "Ch",
+ "%d L3->L4 unknown primitiv %#x",
+ chanp->chan, pr);
+ }
+ }
+}
+
+static void
+dummy_pstack(struct PStack *st, int pr, void *arg) {
+ printk(KERN_WARNING"call to dummy_pstack pr=%04x arg %lx\n", pr, (long)arg);
+}
+
+static int
+init_PStack(struct PStack **stp) {
+ *stp = kmalloc(sizeof(struct PStack), GFP_ATOMIC);
+ if (!*stp)
+ return -ENOMEM;
+ (*stp)->next = NULL;
+ (*stp)->l1.l1l2 = dummy_pstack;
+ (*stp)->l1.l1hw = dummy_pstack;
+ (*stp)->l1.l1tei = dummy_pstack;
+ (*stp)->l2.l2tei = dummy_pstack;
+ (*stp)->l2.l2l1 = dummy_pstack;
+ (*stp)->l2.l2l3 = dummy_pstack;
+ (*stp)->l3.l3l2 = dummy_pstack;
+ (*stp)->l3.l3ml3 = dummy_pstack;
+ (*stp)->l3.l3l4 = dummy_pstack;
+ (*stp)->lli.l4l3 = dummy_pstack;
+ (*stp)->ma.layer = dummy_pstack;
+ return 0;
+}
+
+static int
+init_d_st(struct Channel *chanp)
+{
+ struct PStack *st;
+ struct IsdnCardState *cs = chanp->cs;
+ char tmp[16];
+ int err;
+
+ err = init_PStack(&chanp->d_st);
+ if (err)
+ return err;
+ st = chanp->d_st;
+ st->next = NULL;
+ HiSax_addlist(cs, st);
+ setstack_HiSax(st, cs);
+ st->l2.sap = 0;
+ st->l2.tei = -1;
+ st->l2.flag = 0;
+ test_and_set_bit(FLG_MOD128, &st->l2.flag);
+ test_and_set_bit(FLG_LAPD, &st->l2.flag);
+ test_and_set_bit(FLG_ORIG, &st->l2.flag);
+ st->l2.maxlen = MAX_DFRAME_LEN;
+ st->l2.window = 1;
+ st->l2.T200 = 1000; /* 1000 milliseconds */
+ st->l2.N200 = 3; /* try 3 times */
+ st->l2.T203 = 10000; /* 10000 milliseconds */
+ if (test_bit(FLG_TWO_DCHAN, &cs->HW_Flags))
+ sprintf(tmp, "DCh%d Q.921 ", chanp->chan);
+ else
+ sprintf(tmp, "DCh Q.921 ");
+ setstack_isdnl2(st, tmp);
+ setstack_l3dc(st, chanp);
+ st->lli.userdata = chanp;
+ st->l3.l3l4 = dchan_l3l4;
+
+ return 0;
+}
+
+static void
+callc_debug(struct FsmInst *fi, char *fmt, ...)
+{
+ va_list args;
+ struct Channel *chanp = fi->userdata;
+ char tmp[16];
+
+ va_start(args, fmt);
+ sprintf(tmp, "Ch%d callc ", chanp->chan);
+ VHiSax_putstatus(chanp->cs, tmp, fmt, args);
+ va_end(args);
+}
+
+static int
+init_chan(int chan, struct IsdnCardState *csta)
+{
+ struct Channel *chanp = csta->channel + chan;
+ int err;
+
+ chanp->cs = csta;
+ chanp->bcs = csta->bcs + chan;
+ chanp->chan = chan;
+ chanp->incoming = 0;
+ chanp->debug = 0;
+ chanp->Flags = 0;
+ chanp->leased = 0;
+ err = init_PStack(&chanp->b_st);
+ if (err)
+ return err;
+ chanp->b_st->l1.delay = DEFAULT_B_DELAY;
+ chanp->fi.fsm = &callcfsm;
+ chanp->fi.state = ST_NULL;
+ chanp->fi.debug = 0;
+ chanp->fi.userdata = chanp;
+ chanp->fi.printdebug = callc_debug;
+ FsmInitTimer(&chanp->fi, &chanp->dial_timer);
+ FsmInitTimer(&chanp->fi, &chanp->drel_timer);
+ if (!chan || (test_bit(FLG_TWO_DCHAN, &csta->HW_Flags) && chan < 2)) {
+ err = init_d_st(chanp);
+ if (err)
+ return err;
+ } else {
+ chanp->d_st = csta->channel->d_st;
+ }
+ chanp->data_open = 0;
+ return 0;
+}
+
+int
+CallcNewChan(struct IsdnCardState *csta) {
+ int i, err;
+
+ chancount += 2;
+ err = init_chan(0, csta);
+ if (err)
+ return err;
+ err = init_chan(1, csta);
+ if (err)
+ return err;
+ printk(KERN_INFO "HiSax: 2 channels added\n");
+
+ for (i = 0; i < MAX_WAITING_CALLS; i++) {
+ err = init_chan(i+2,csta);
+ if (err)
+ return err;
+ }
+ printk(KERN_INFO "HiSax: MAX_WAITING_CALLS added\n");
+ if (test_bit(FLG_PTP, &csta->channel->d_st->l2.flag)) {
+ printk(KERN_INFO "LAYER2 WATCHING ESTABLISH\n");
+ csta->channel->d_st->lli.l4l3(csta->channel->d_st,
+ DL_ESTABLISH | REQUEST, NULL);
+ }
+ return (0);
+}
+
+static void
+release_d_st(struct Channel *chanp)
+{
+ struct PStack *st = chanp->d_st;
+
+ if (!st)
+ return;
+ releasestack_isdnl2(st);
+ releasestack_isdnl3(st);
+ HiSax_rmlist(st->l1.hardware, st);
+ kfree(st);
+ chanp->d_st = NULL;
+}
+
+void
+CallcFreeChan(struct IsdnCardState *csta)
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ FsmDelTimer(&csta->channel[i].drel_timer, 74);
+ FsmDelTimer(&csta->channel[i].dial_timer, 75);
+ if (i || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags))
+ release_d_st(csta->channel + i);
+ if (csta->channel[i].b_st) {
+ release_b_st(csta->channel + i);
+ kfree(csta->channel[i].b_st);
+ csta->channel[i].b_st = NULL;
+ } else
+ printk(KERN_WARNING "CallcFreeChan b_st ch%d allready freed\n", i);
+ if (i || test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) {
+ release_d_st(csta->channel + i);
+ } else
+ csta->channel[i].d_st = NULL;
+ }
+}
+
+static void
+lldata_handler(struct PStack *st, int pr, void *arg)
+{
+ struct Channel *chanp = (struct Channel *) st->lli.userdata;
+ struct sk_buff *skb = arg;
+
+ switch (pr) {
+ case (DL_DATA | INDICATION):
+ if (chanp->data_open) {
+ if (chanp->debug & 0x800)
+ link_debug(chanp, 0, "lldata: %d", skb->len);
+ chanp->cs->iif.rcvcallb_skb(chanp->cs->myid, chanp->chan, skb);
+ } else {
+ link_debug(chanp, 0, "lldata: channel not open");
+ dev_kfree_skb(skb);
+ }
+ break;
+ case (DL_ESTABLISH | INDICATION):
+ case (DL_ESTABLISH | CONFIRM):
+ FsmEvent(&chanp->fi, EV_BC_EST, NULL);
+ break;
+ case (DL_RELEASE | INDICATION):
+ case (DL_RELEASE | CONFIRM):
+ FsmEvent(&chanp->fi, EV_BC_REL, NULL);
+ break;
+ default:
+ printk(KERN_WARNING "lldata_handler unknown primitive %#x\n",
+ pr);
+ break;
+ }
+}
+
+static void
+lltrans_handler(struct PStack *st, int pr, void *arg)
+{
+ struct Channel *chanp = (struct Channel *) st->lli.userdata;
+ struct sk_buff *skb = arg;
+
+ switch (pr) {
+ case (PH_DATA | INDICATION):
+ if (chanp->data_open) {
+ if (chanp->debug & 0x800)
+ link_debug(chanp, 0, "lltrans: %d", skb->len);
+ chanp->cs->iif.rcvcallb_skb(chanp->cs->myid, chanp->chan, skb);
+ } else {
+ link_debug(chanp, 0, "lltrans: channel not open");
+ dev_kfree_skb(skb);
+ }
+ break;
+ case (PH_ACTIVATE | INDICATION):
+ case (PH_ACTIVATE | CONFIRM):
+ FsmEvent(&chanp->fi, EV_BC_EST, NULL);
+ break;
+ case (PH_DEACTIVATE | INDICATION):
+ case (PH_DEACTIVATE | CONFIRM):
+ FsmEvent(&chanp->fi, EV_BC_REL, NULL);
+ break;
+ default:
+ printk(KERN_WARNING "lltrans_handler unknown primitive %#x\n",
+ pr);
+ break;
+ }
+}
+
+void
+lli_writewakeup(struct PStack *st, int len)
+{
+ struct Channel *chanp = st->lli.userdata;
+ isdn_ctrl ic;
+
+ if (chanp->debug & 0x800)
+ link_debug(chanp, 0, "llwakeup: %d", len);
+ ic.driver = chanp->cs->myid;
+ ic.command = ISDN_STAT_BSENT;
+ ic.arg = chanp->chan;
+ ic.parm.length = len;
+ chanp->cs->iif.statcallb(&ic);
+}
+
+static int
+init_b_st(struct Channel *chanp, int incoming)
+{
+ struct PStack *st = chanp->b_st;
+ struct IsdnCardState *cs = chanp->cs;
+ char tmp[16];
+
+ st->l1.hardware = cs;
+ if (chanp->leased)
+ st->l1.bc = chanp->chan & 1;
+ else
+ st->l1.bc = chanp->proc->para.bchannel - 1;
+ switch (chanp->l2_active_protocol) {
+ case (ISDN_PROTO_L2_X75I):
+ case (ISDN_PROTO_L2_HDLC):
+ st->l1.mode = L1_MODE_HDLC;
+ break;
+ case (ISDN_PROTO_L2_HDLC_56K):
+ st->l1.mode = L1_MODE_HDLC_56K;
+ break;
+ case (ISDN_PROTO_L2_TRANS):
+ st->l1.mode = L1_MODE_TRANS;
+ break;
+ case (ISDN_PROTO_L2_MODEM):
+ st->l1.mode = L1_MODE_V32;
+ break;
+ case (ISDN_PROTO_L2_FAX):
+ st->l1.mode = L1_MODE_FAX;
+ break;
+ }
+ chanp->bcs->conmsg = NULL;
+ if (chanp->bcs->BC_SetStack(st, chanp->bcs))
+ return (-1);
+ st->l2.flag = 0;
+ test_and_set_bit(FLG_LAPB, &st->l2.flag);
+ st->l2.maxlen = MAX_DATA_SIZE;
+ if (!incoming)
+ test_and_set_bit(FLG_ORIG, &st->l2.flag);
+ st->l2.T200 = 1000; /* 1000 milliseconds */
+ st->l2.window = 7;
+ st->l2.N200 = 4; /* try 4 times */
+ st->l2.T203 = 5000; /* 5000 milliseconds */
+ st->l3.debug = 0;
+ switch (chanp->l2_active_protocol) {
+ case (ISDN_PROTO_L2_X75I):
+ sprintf(tmp, "Ch%d X.75", chanp->chan);
+ setstack_isdnl2(st, tmp);
+ setstack_l3bc(st, chanp);
+ st->l2.l2l3 = lldata_handler;
+ st->lli.userdata = chanp;
+ test_and_clear_bit(FLG_LLI_L1WAKEUP, &st->lli.flag);
+ test_and_set_bit(FLG_LLI_L2WAKEUP, &st->lli.flag);
+ st->l2.l2m.debug = chanp->debug & 16;
+ st->l2.debug = chanp->debug & 64;
+ break;
+ case (ISDN_PROTO_L2_HDLC):
+ case (ISDN_PROTO_L2_HDLC_56K):
+ case (ISDN_PROTO_L2_TRANS):
+ case (ISDN_PROTO_L2_MODEM):
+ case (ISDN_PROTO_L2_FAX):
+ st->l1.l1l2 = lltrans_handler;
+ st->lli.userdata = chanp;
+ test_and_set_bit(FLG_LLI_L1WAKEUP, &st->lli.flag);
+ test_and_clear_bit(FLG_LLI_L2WAKEUP, &st->lli.flag);
+ setstack_transl2(st);
+ setstack_l3bc(st, chanp);
+ break;
+ }
+ test_and_set_bit(FLG_START_B, &chanp->Flags);
+ return (0);
+}
+
+static void
+leased_l4l3(struct PStack *st, int pr, void *arg)
+{
+ struct Channel *chanp = (struct Channel *) st->lli.userdata;
+ struct sk_buff *skb = arg;
+
+ switch (pr) {
+ case (DL_DATA | REQUEST):
+ link_debug(chanp, 0, "leased line d-channel DATA");
+ dev_kfree_skb(skb);
+ break;
+ case (DL_ESTABLISH | REQUEST):
+ st->l2.l2l1(st, PH_ACTIVATE | REQUEST, NULL);
+ break;
+ case (DL_RELEASE | REQUEST):
+ break;
+ default:
+ printk(KERN_WARNING "transd_l4l3 unknown primitive %#x\n",
+ pr);
+ break;
+ }
+}
+
+static void
+leased_l1l2(struct PStack *st, int pr, void *arg)
+{
+ struct Channel *chanp = (struct Channel *) st->lli.userdata;
+ struct sk_buff *skb = arg;
+ int i,event = EV_LEASED_REL;
+
+ switch (pr) {
+ case (PH_DATA | INDICATION):
+ link_debug(chanp, 0, "leased line d-channel DATA");
+ dev_kfree_skb(skb);
+ break;
+ case (PH_ACTIVATE | INDICATION):
+ case (PH_ACTIVATE | CONFIRM):
+ event = EV_LEASED;
+ case (PH_DEACTIVATE | INDICATION):
+ case (PH_DEACTIVATE | CONFIRM):
+ if (test_bit(FLG_TWO_DCHAN, &chanp->cs->HW_Flags))
+ i = 1;
+ else
+ i = 0;
+ while (i < 2) {
+ FsmEvent(&chanp->fi, event, NULL);
+ chanp++;
+ i++;
+ }
+ break;
+ default:
+ printk(KERN_WARNING
+ "transd_l1l2 unknown primitive %#x\n", pr);
+ break;
+ }
+}
+
+static void
+distr_debug(struct IsdnCardState *csta, int debugflags)
+{
+ int i;
+ struct Channel *chanp = csta->channel;
+
+ for (i = 0; i < (2 + MAX_WAITING_CALLS) ; i++) {
+ chanp[i].debug = debugflags;
+ chanp[i].fi.debug = debugflags & 2;
+ chanp[i].d_st->l2.l2m.debug = debugflags & 8;
+ chanp[i].b_st->l2.l2m.debug = debugflags & 0x10;
+ chanp[i].d_st->l2.debug = debugflags & 0x20;
+ chanp[i].b_st->l2.debug = debugflags & 0x40;
+ chanp[i].d_st->l3.l3m.debug = debugflags & 0x80;
+ chanp[i].b_st->l3.l3m.debug = debugflags & 0x100;
+ chanp[i].b_st->ma.tei_m.debug = debugflags & 0x200;
+ chanp[i].b_st->ma.debug = debugflags & 0x200;
+ chanp[i].d_st->l1.l1m.debug = debugflags & 0x1000;
+ chanp[i].b_st->l1.l1m.debug = debugflags & 0x2000;
+ }
+ if (debugflags & 4)
+ csta->debug |= DEB_DLOG_HEX;
+ else
+ csta->debug &= ~DEB_DLOG_HEX;
+}
+
+static char tmpbuf[256];
+
+static void
+capi_debug(struct Channel *chanp, capi_msg *cm)
+{
+ char *t = tmpbuf;
+
+ t += QuickHex(t, (u_char *)cm, (cm->Length>50)? 50: cm->Length);
+ t--;
+ *t= 0;
+ HiSax_putstatus(chanp->cs, "Ch", "%d CAPIMSG %s", chanp->chan, tmpbuf);
+}
+
+static void
+lli_got_fac_req(struct Channel *chanp, capi_msg *cm) {
+ if ((cm->para[0] != 3) || (cm->para[1] != 0))
+ return;
+ if (cm->para[2]<3)
+ return;
+ if (cm->para[4] != 0)
+ return;
+ switch(cm->para[3]) {
+ case 4: /* Suspend */
+ strncpy(chanp->setup.phone, &cm->para[5], cm->para[5] +1);
+ FsmEvent(&chanp->fi, EV_SUSPEND, cm);
+ break;
+ case 5: /* Resume */
+ strncpy(chanp->setup.phone, &cm->para[5], cm->para[5] +1);
+ if (chanp->fi.state == ST_NULL) {
+ FsmEvent(&chanp->fi, EV_RESUME, cm);
+ } else {
+ FsmDelTimer(&chanp->dial_timer, 72);
+ FsmAddTimer(&chanp->dial_timer, 80, EV_RESUME, cm, 73);
+ }
+ break;
+ }
+}
+
+static void
+lli_got_manufacturer(struct Channel *chanp, struct IsdnCardState *cs, capi_msg *cm) {
+ if ((cs->typ == ISDN_CTYPE_ELSA) || (cs->typ == ISDN_CTYPE_ELSA_PNP) ||
+ (cs->typ == ISDN_CTYPE_ELSA_PCI)) {
+ if (cs->hw.elsa.MFlag) {
+ cs->cardmsg(cs, CARD_AUX_IND, cm->para);
+ }
+ }
+}
+
+
+/***************************************************************/
+/* Limit the available number of channels for the current card */
+/***************************************************************/
+static int
+set_channel_limit(struct IsdnCardState *cs, int chanmax)
+{
+ isdn_ctrl ic;
+ int i, ii;
+
+ if ((chanmax < 0) || (chanmax > 2))
+ return(-EINVAL);
+ cs->chanlimit = 0;
+ for (ii = 0; ii < 2; ii++) {
+ ic.driver = cs->myid;
+ ic.command = ISDN_STAT_DISCH;
+ ic.arg = ii;
+ if (ii >= chanmax)
+ ic.parm.num[0] = 0; /* disabled */
+ else
+ ic.parm.num[0] = 1; /* enabled */
+ i = cs->iif.statcallb(&ic);
+ if (i) return(-EINVAL);
+ if (ii < chanmax)
+ cs->chanlimit++;
+ }
+ return(0);
+} /* set_channel_limit */
+
+int
+HiSax_command(isdn_ctrl * ic)
+{
+ struct IsdnCardState *csta = hisax_findcard(ic->driver);
+ struct PStack *st;
+ struct Channel *chanp;
+ int i;
+ u_int num;
+
+ if (!csta) {
+ printk(KERN_ERR
+ "HiSax: if_command %d called with invalid driverId %d!\n",
+ ic->command, ic->driver);
+ return -ENODEV;
+ }
+ switch (ic->command) {
+ 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,
+ ic->parm.setup.si1, ic->parm.setup.si2);
+ memcpy(&chanp->setup, &ic->parm.setup, sizeof(setup_parm));
+ if (!strcmp(chanp->setup.eazmsn, "0"))
+ chanp->setup.eazmsn[0] = '\0';
+ /* this solution is dirty and may be change, if
+ * we make a callreference based callmanager */
+ if (chanp->fi.state == ST_NULL) {
+ FsmEvent(&chanp->fi, EV_DIAL, NULL);
+ } else {
+ FsmDelTimer(&chanp->dial_timer, 70);
+ FsmAddTimer(&chanp->dial_timer, 50, EV_DIAL, NULL, 71);
+ }
+ 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)
+ break;
+ switch(ic->parm.cmsg.Command) {
+ case CAPI_FACILITY:
+ if (ic->parm.cmsg.Subcommand == CAPI_REQ)
+ lli_got_fac_req(chanp, &ic->parm.cmsg);
+ break;
+ case CAPI_MANUFACTURER:
+ if (ic->parm.cmsg.Subcommand == CAPI_REQ)
+ lli_got_manufacturer(chanp, csta, &ic->parm.cmsg);
+ break;
+ default:
+ break;
+ }
+ break;
+ case (ISDN_CMD_IOCTL):
+ switch (ic->arg) {
+ case (0):
+ num = *(unsigned int *) ic->parm.num;
+ HiSax_reportcard(csta->cardnr, num);
+ break;
+ case (1):
+ num = *(unsigned int *) ic->parm.num;
+ distr_debug(csta, num);
+ printk(KERN_DEBUG "HiSax: debugging flags card %d set to %x\n",
+ csta->cardnr + 1, num);
+ HiSax_putstatus(csta, "debugging flags ",
+ "card %d set to %x", csta->cardnr + 1, num);
+ break;
+ case (2):
+ num = *(unsigned int *) ic->parm.num;
+ csta->channel[0].b_st->l1.delay = num;
+ csta->channel[1].b_st->l1.delay = num;
+ HiSax_putstatus(csta, "delay ", "card %d set to %d ms",
+ csta->cardnr + 1, num);
+ printk(KERN_DEBUG "HiSax: delay card %d set to %d ms\n",
+ csta->cardnr + 1, num);
+ break;
+ case (5): /* set card in leased mode */
+ num = *(unsigned int *) ic->parm.num;
+ if ((num <1) || (num > 2)) {
+ HiSax_putstatus(csta, "Set LEASED ",
+ "wrong channel %d", num);
+ printk(KERN_WARNING "HiSax: Set LEASED wrong channel %d\n",
+ num);
+ } else {
+ num--;
+ chanp = csta->channel +num;
+ chanp->leased = 1;
+ HiSax_putstatus(csta, "Card",
+ "%d channel %d set leased mode\n",
+ csta->cardnr + 1, num + 1);
+ chanp->d_st->l1.l1l2 = leased_l1l2;
+ chanp->d_st->lli.l4l3 = leased_l4l3;
+ chanp->d_st->lli.l4l3(chanp->d_st,
+ DL_ESTABLISH | REQUEST, NULL);
+ }
+ break;
+ case (6): /* set B-channel test loop */
+ num = *(unsigned int *) ic->parm.num;
+ if (csta->stlist)
+ csta->stlist->l2.l2l1(csta->stlist,
+ PH_TESTLOOP | REQUEST, (void *) (long)num);
+ break;
+ case (7): /* set card in PTP mode */
+ num = *(unsigned int *) ic->parm.num;
+ if (test_bit(FLG_TWO_DCHAN, &csta->HW_Flags)) {
+ printk(KERN_ERR "HiSax PTP mode only with one TEI possible\n");
+ } else if (num) {
+ test_and_set_bit(FLG_PTP, &csta->channel[0].d_st->l2.flag);
+ test_and_set_bit(FLG_FIXED_TEI, &csta->channel[0].d_st->l2.flag);
+ csta->channel[0].d_st->l2.tei = 0;
+ HiSax_putstatus(csta, "set card ", "in PTP mode");
+ printk(KERN_DEBUG "HiSax: set card in PTP mode\n");
+ printk(KERN_INFO "LAYER2 WATCHING ESTABLISH\n");
+ csta->channel[0].d_st->lli.l4l3(csta->channel[0].d_st,
+ DL_ESTABLISH | REQUEST, NULL);
+ } else {
+ test_and_clear_bit(FLG_PTP, &csta->channel[0].d_st->l2.flag);
+ test_and_clear_bit(FLG_FIXED_TEI, &csta->channel[0].d_st->l2.flag);
+ HiSax_putstatus(csta, "set card ", "in PTMP mode");
+ printk(KERN_DEBUG "HiSax: set card in PTMP mode\n");
+ }
+ break;
+ case (8): /* set card in FIXED TEI mode */
+ num = *(unsigned int *) ic->parm.num;
+ chanp = csta->channel + (num & 1);
+ num = num >>1;
+ if (num == 127) {
+ test_and_clear_bit(FLG_FIXED_TEI, &chanp->d_st->l2.flag);
+ chanp->d_st->l2.tei = -1;
+ HiSax_putstatus(csta, "set card ", "in VAR TEI mode");
+ printk(KERN_DEBUG "HiSax: set card in VAR TEI mode\n");
+ } else {
+ test_and_set_bit(FLG_FIXED_TEI, &chanp->d_st->l2.flag);
+ chanp->d_st->l2.tei = num;
+ HiSax_putstatus(csta, "set card ", "in FIXED TEI (%d) mode", num);
+ printk(KERN_DEBUG "HiSax: set card in FIXED TEI (%d) mode\n",
+ num);
+ }
+ chanp->d_st->lli.l4l3(chanp->d_st,
+ DL_ESTABLISH | REQUEST, NULL);
+ break;
+ case (11):
+ num = csta->debug & DEB_DLOG_HEX;
+ csta->debug = *(unsigned int *) ic->parm.num;
+ csta->debug |= num;
+ HiSax_putstatus(cards[0].cs, "l1 debugging ",
+ "flags card %d set to %x",
+ csta->cardnr + 1, csta->debug);
+ printk(KERN_DEBUG "HiSax: l1 debugging flags card %d set to %x\n",
+ csta->cardnr + 1, csta->debug);
+ break;
+ case (13):
+ csta->channel[0].d_st->l3.debug = *(unsigned int *) ic->parm.num;
+ csta->channel[1].d_st->l3.debug = *(unsigned int *) ic->parm.num;
+ HiSax_putstatus(cards[0].cs, "l3 debugging ",
+ "flags card %d set to %x\n", csta->cardnr + 1,
+ *(unsigned int *) ic->parm.num);
+ printk(KERN_DEBUG "HiSax: l3 debugging flags card %d set to %x\n",
+ csta->cardnr + 1, *(unsigned int *) ic->parm.num);
+ break;
+ case (10):
+ i = *(unsigned int *) ic->parm.num;
+ return(set_channel_limit(csta, i));
+ default:
+ if (csta->auxcmd)
+ return(csta->auxcmd(csta, ic));
+ printk(KERN_DEBUG "HiSax: invalid ioclt %d\n",
+ (int) ic->arg);
+ return (-EINVAL);
+ }
+ 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));
+ FsmEvent(&chanp->fi, EV_REDIR, NULL);
+ break;
+
+ /* protocol specific io commands */
+ case (ISDN_CMD_PROT_IO):
+ for (st = csta->stlist; st; st = st->next)
+ if (st->protocol == (ic->arg & 0xFF))
+ return(st->lli.l4l3_proto(st, ic));
+ return(-EINVAL);
+ break;
+ default:
+ if (csta->auxcmd)
+ return(csta->auxcmd(csta, ic));
+ return(-EINVAL);
+ }
+ return (0);
+}
+
+int
+HiSax_writebuf_skb(int id, int chan, int ack, struct sk_buff *skb)
+{
+ struct IsdnCardState *csta = hisax_findcard(id);
+ struct Channel *chanp;
+ struct PStack *st;
+ int len = skb->len;
+ struct sk_buff *nskb;
+
+ if (!csta) {
+ printk(KERN_ERR
+ "HiSax: if_sendbuf called with invalid driverId!\n");
+ return -ENODEV;
+ }
+ chanp = csta->channel + chan;
+ st = chanp->b_st;
+ if (!chanp->data_open) {
+ link_debug(chanp, 1, "writebuf: channel not open");
+ return -EIO;
+ }
+ if (len > MAX_DATA_SIZE) {
+ link_debug(chanp, 1, "writebuf: packet too large (%d bytes)", len);
+ printk(KERN_WARNING "HiSax_writebuf: packet too large (%d bytes) !\n",
+ len);
+ return -EINVAL;
+ }
+ if (len) {
+ if ((len + chanp->bcs->tx_cnt) > MAX_DATA_MEM) {
+ /* Must return 0 here, since this is not an error
+ * but a temporary lack of resources.
+ */
+ if (chanp->debug & 0x800)
+ link_debug(chanp, 1, "writebuf: no buffers for %d bytes", len);
+ return 0;
+ } else if (chanp->debug & 0x800)
+ link_debug(chanp, 1, "writebuf %d/%d/%d", len, chanp->bcs->tx_cnt,MAX_DATA_MEM);
+ nskb = skb_clone(skb, GFP_ATOMIC);
+ if (nskb) {
+ nskb->truesize = nskb->len;
+ if (!ack)
+ nskb->pkt_type = PACKET_NOACK;
+ if (chanp->l2_active_protocol == ISDN_PROTO_L2_X75I)
+ st->l3.l3l2(st, DL_DATA | REQUEST, nskb);
+ else {
+ chanp->bcs->tx_cnt += len;
+ st->l2.l2l1(st, PH_DATA | REQUEST, nskb);
+ }
+ dev_kfree_skb(skb);
+ } else
+ len = 0;
+ }
+ return (len);
+}
diff --git a/drivers/isdn/hisax/config.c b/drivers/isdn/hisax/config.c
new file mode 100644
index 0000000..ded9d0b
--- /dev/null
+++ b/drivers/isdn/hisax/config.c
@@ -0,0 +1,1995 @@
+/* $Id: config.c,v 2.84.2.5 2004/02/11 13:21:33 keil Exp $
+ *
+ * Author Karsten Keil
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ * by Kai Germaschewski <kai.germaschewski@gmx.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ * based on the teles driver from Jan den Ouden
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/timer.h>
+#include <linux/init.h>
+#include "hisax.h"
+#include <linux/module.h>
+#include <linux/kernel_stat.h>
+#include <linux/workqueue.h>
+#include <linux/interrupt.h>
+#define HISAX_STATUS_BUFSIZE 4096
+
+/*
+ * This structure array contains one entry per card. An entry looks
+ * like this:
+ *
+ * { type, protocol, p0, p1, p2, NULL }
+ *
+ * type
+ * 1 Teles 16.0 p0=irq p1=membase p2=iobase
+ * 2 Teles 8.0 p0=irq p1=membase
+ * 3 Teles 16.3 p0=irq p1=iobase
+ * 4 Creatix PNP p0=irq p1=IO0 (ISAC) p2=IO1 (HSCX)
+ * 5 AVM A1 (Fritz) p0=irq p1=iobase
+ * 6 ELSA PC [p0=iobase] or nothing (autodetect)
+ * 7 ELSA Quickstep p0=irq p1=iobase
+ * 8 Teles PCMCIA p0=irq p1=iobase
+ * 9 ITK ix1-micro p0=irq p1=iobase
+ * 10 ELSA PCMCIA p0=irq p1=iobase
+ * 11 Eicon.Diehl Diva p0=irq p1=iobase
+ * 12 Asuscom ISDNLink p0=irq p1=iobase
+ * 13 Teleint p0=irq p1=iobase
+ * 14 Teles 16.3c p0=irq p1=iobase
+ * 15 Sedlbauer speed p0=irq p1=iobase
+ * 15 Sedlbauer PC/104 p0=irq p1=iobase
+ * 15 Sedlbauer speed pci no parameter
+ * 16 USR Sportster internal p0=irq p1=iobase
+ * 17 MIC card p0=irq p1=iobase
+ * 18 ELSA Quickstep 1000PCI no parameter
+ * 19 Compaq ISDN S0 ISA card p0=irq p1=IO0 (HSCX) p2=IO1 (ISAC) p3=IO2
+ * 20 Travers Technologies NETjet-S PCI card
+ * 21 TELES PCI no parameter
+ * 22 Sedlbauer Speed Star p0=irq p1=iobase
+ * 23 reserved
+ * 24 Dr Neuhaus Niccy PnP/PCI card p0=irq p1=IO0 p2=IO1 (PnP only)
+ * 25 Teles S0Box p0=irq p1=iobase (from isapnp setup)
+ * 26 AVM A1 PCMCIA (Fritz) p0=irq p1=iobase
+ * 27 AVM PnP/PCI p0=irq p1=iobase (PCI no parameter)
+ * 28 Sedlbauer Speed Fax+ p0=irq p1=iobase (from isapnp setup)
+ * 29 Siemens I-Surf p0=irq p1=iobase p2=memory (from isapnp setup)
+ * 30 ACER P10 p0=irq p1=iobase (from isapnp setup)
+ * 31 HST Saphir p0=irq p1=iobase
+ * 32 Telekom A4T none
+ * 33 Scitel Quadro p0=subcontroller (4*S0, subctrl 1...4)
+ * 34 Gazel ISDN cards
+ * 35 HFC 2BDS0 PCI none
+ * 36 Winbond 6692 PCI none
+ * 37 HFC 2BDS0 S+/SP p0=irq p1=iobase
+ * 38 Travers Technologies NETspider-U PCI card
+ * 39 HFC 2BDS0-SP PCMCIA p0=irq p1=iobase
+ * 40 hotplug interface
+ * 41 Formula-n enter:now ISDN PCI a/b none
+ *
+ * protocol can be either ISDN_PTYPE_EURO or ISDN_PTYPE_1TR6 or ISDN_PTYPE_NI1
+ *
+ *
+ */
+
+const char *CardType[] = {
+ "No Card", "Teles 16.0", "Teles 8.0", "Teles 16.3",
+ "Creatix/Teles PnP", "AVM A1", "Elsa ML", "Elsa Quickstep",
+ "Teles PCMCIA", "ITK ix1-micro Rev.2", "Elsa PCMCIA",
+ "Eicon.Diehl Diva", "ISDNLink", "TeleInt", "Teles 16.3c",
+ "Sedlbauer Speed Card", "USR Sportster", "ith mic Linux",
+ "Elsa PCI", "Compaq ISA", "NETjet-S", "Teles PCI",
+ "Sedlbauer Speed Star (PCMCIA)", "AMD 7930", "NICCY", "S0Box",
+ "AVM A1 (PCMCIA)", "AVM Fritz PnP/PCI", "Sedlbauer Speed Fax +",
+ "Siemens I-Surf", "Acer P10", "HST Saphir", "Telekom A4T",
+ "Scitel Quadro", "Gazel", "HFC 2BDS0 PCI", "Winbond 6692",
+ "HFC 2BDS0 SX", "NETspider-U", "HFC-2BDS0-SP PCMCIA",
+ "Hotplug", "Formula-n enter:now PCI a/b",
+};
+
+#ifdef CONFIG_HISAX_ELSA
+#define DEFAULT_CARD ISDN_CTYPE_ELSA
+#define DEFAULT_CFG {0,0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_AVM_A1
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_A1
+#define DEFAULT_CFG {10,0x340,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_AVM_A1_PCMCIA
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_A1_PCMCIA
+#define DEFAULT_CFG {11,0x170,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_FRITZPCI
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_FRITZPCI
+#define DEFAULT_CFG {0,0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_16_3
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_16_3
+#define DEFAULT_CFG {15,0x180,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_S0BOX
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_S0BOX
+#define DEFAULT_CFG {7,0x378,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_16_0
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_16_0
+#define DEFAULT_CFG {15,0xd0000,0xd80,0}
+#endif
+
+#ifdef CONFIG_HISAX_TELESPCI
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_TELESPCI
+#define DEFAULT_CFG {0,0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_IX1MICROR2
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_IX1MICROR2
+#define DEFAULT_CFG {5,0x390,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_DIEHLDIVA
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_DIEHLDIVA
+#define DEFAULT_CFG {0,0x0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_ASUSCOM
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_ASUSCOM
+#define DEFAULT_CFG {5,0x200,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_TELEINT
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_TELEINT
+#define DEFAULT_CFG {5,0x300,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_SEDLBAUER
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_SEDLBAUER
+#define DEFAULT_CFG {11,0x270,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_SPORTSTER
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_SPORTSTER
+#define DEFAULT_CFG {7,0x268,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_MIC
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_MIC
+#define DEFAULT_CFG {12,0x3e0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_NETJET
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_NETJET_S
+#define DEFAULT_CFG {0,0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_HFCS
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_TELES3C
+#define DEFAULT_CFG {5,0x500,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_HFC_PCI
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_HFC_PCI
+#define DEFAULT_CFG {0,0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_HFC_SX
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_HFC_SX
+#define DEFAULT_CFG {5,0x2E0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_NICCY
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_NICCY
+#define DEFAULT_CFG {0,0x0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_ISURF
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_ISURF
+#define DEFAULT_CFG {5,0x100,0xc8000,0}
+#endif
+
+#ifdef CONFIG_HISAX_HSTSAPHIR
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_HSTSAPHIR
+#define DEFAULT_CFG {5,0x250,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_BKM_A4T
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_BKM_A4T
+#define DEFAULT_CFG {0,0x0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_SCT_QUADRO
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_SCT_QUADRO
+#define DEFAULT_CFG {1,0x0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_GAZEL
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_GAZEL
+#define DEFAULT_CFG {15,0x180,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_W6692
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_W6692
+#define DEFAULT_CFG {0,0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_NETJET_U
+#undef DEFAULT_CARD
+#undef DEFAULT_CFG
+#define DEFAULT_CARD ISDN_CTYPE_NETJET_U
+#define DEFAULT_CFG {0,0,0,0}
+#endif
+
+#ifdef CONFIG_HISAX_1TR6
+#define DEFAULT_PROTO ISDN_PTYPE_1TR6
+#define DEFAULT_PROTO_NAME "1TR6"
+#endif
+#ifdef CONFIG_HISAX_NI1
+#undef DEFAULT_PROTO
+#define DEFAULT_PROTO ISDN_PTYPE_NI1
+#undef DEFAULT_PROTO_NAME
+#define DEFAULT_PROTO_NAME "NI1"
+#endif
+#ifdef CONFIG_HISAX_EURO
+#undef DEFAULT_PROTO
+#define DEFAULT_PROTO ISDN_PTYPE_EURO
+#undef DEFAULT_PROTO_NAME
+#define DEFAULT_PROTO_NAME "EURO"
+#endif
+#ifndef DEFAULT_PROTO
+#define DEFAULT_PROTO ISDN_PTYPE_UNKNOWN
+#define DEFAULT_PROTO_NAME "UNKNOWN"
+#endif
+#ifndef DEFAULT_CARD
+#define DEFAULT_CARD 0
+#define DEFAULT_CFG {0,0,0,0}
+#endif
+
+#define FIRST_CARD { \
+ DEFAULT_CARD, \
+ DEFAULT_PROTO, \
+ DEFAULT_CFG, \
+ NULL, \
+}
+
+struct IsdnCard cards[HISAX_MAX_CARDS] = {
+ FIRST_CARD,
+};
+
+#define HISAX_IDSIZE (HISAX_MAX_CARDS*8)
+static char HiSaxID[HISAX_IDSIZE] = { 0, };
+
+static char *HiSax_id = HiSaxID;
+#ifdef MODULE
+/* Variables for insmod */
+static int type[HISAX_MAX_CARDS] = { 0, };
+static int protocol[HISAX_MAX_CARDS] = { 0, };
+static int io[HISAX_MAX_CARDS] = { 0, };
+#undef IO0_IO1
+#ifdef CONFIG_HISAX_16_3
+#define IO0_IO1
+#endif
+#ifdef CONFIG_HISAX_NICCY
+#undef IO0_IO1
+#define IO0_IO1
+#endif
+#ifdef IO0_IO1
+static int io0[HISAX_MAX_CARDS] __devinitdata = { 0, };
+static int io1[HISAX_MAX_CARDS] __devinitdata = { 0, };
+#endif
+static int irq[HISAX_MAX_CARDS] __devinitdata = { 0, };
+static int mem[HISAX_MAX_CARDS] __devinitdata = { 0, };
+static char *id = HiSaxID;
+
+MODULE_DESCRIPTION("ISDN4Linux: Driver for passive ISDN cards");
+MODULE_AUTHOR("Karsten Keil");
+MODULE_LICENSE("GPL");
+module_param_array(type, int, NULL, 0);
+module_param_array(protocol, int, NULL, 0);
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param_array(mem, int, NULL, 0);
+module_param(id, charp, 0);
+#ifdef IO0_IO1
+module_param_array(io0, int, NULL, 0);
+module_param_array(io1, int, NULL, 0);
+#endif
+#endif /* MODULE */
+
+int nrcards;
+
+extern const char *l1_revision;
+extern const char *l2_revision;
+extern const char *l3_revision;
+extern const char *lli_revision;
+extern const char *tei_revision;
+
+char *HiSax_getrev(const char *revision)
+{
+ char *rev;
+ char *p;
+
+ if ((p = strchr(revision, ':'))) {
+ rev = p + 2;
+ p = strchr(rev, '$');
+ *--p = 0;
+ } else
+ rev = "???";
+ return rev;
+}
+
+static void __init HiSaxVersion(void)
+{
+ char tmp[64];
+
+ printk(KERN_INFO "HiSax: Linux Driver for passive ISDN cards\n");
+#ifdef MODULE
+ printk(KERN_INFO "HiSax: Version 3.5 (module)\n");
+#else
+ printk(KERN_INFO "HiSax: Version 3.5 (kernel)\n");
+#endif
+ strcpy(tmp, l1_revision);
+ printk(KERN_INFO "HiSax: Layer1 Revision %s\n", HiSax_getrev(tmp));
+ strcpy(tmp, l2_revision);
+ printk(KERN_INFO "HiSax: Layer2 Revision %s\n", HiSax_getrev(tmp));
+ strcpy(tmp, tei_revision);
+ printk(KERN_INFO "HiSax: TeiMgr Revision %s\n", HiSax_getrev(tmp));
+ strcpy(tmp, l3_revision);
+ printk(KERN_INFO "HiSax: Layer3 Revision %s\n", HiSax_getrev(tmp));
+ strcpy(tmp, lli_revision);
+ printk(KERN_INFO "HiSax: LinkLayer Revision %s\n",
+ HiSax_getrev(tmp));
+}
+
+#ifndef MODULE
+#define MAX_ARG (HISAX_MAX_CARDS*5)
+static int __init HiSax_setup(char *line)
+{
+ int i, j, argc;
+ int ints[MAX_ARG + 1];
+ char *str;
+
+ str = get_options(line, MAX_ARG, ints);
+ argc = ints[0];
+ printk(KERN_DEBUG "HiSax_setup: argc(%d) str(%s)\n", argc, str);
+ i = 0;
+ j = 1;
+ while (argc && (i < HISAX_MAX_CARDS)) {
+ cards[i].protocol = DEFAULT_PROTO;
+ if (argc) {
+ cards[i].typ = ints[j];
+ j++;
+ argc--;
+ }
+ if (argc) {
+ cards[i].protocol = ints[j];
+ j++;
+ argc--;
+ }
+ if (argc) {
+ cards[i].para[0] = ints[j];
+ j++;
+ argc--;
+ }
+ if (argc) {
+ cards[i].para[1] = ints[j];
+ j++;
+ argc--;
+ }
+ if (argc) {
+ cards[i].para[2] = ints[j];
+ j++;
+ argc--;
+ }
+ i++;
+ }
+ if (str && *str) {
+ if (strlen(str) < HISAX_IDSIZE)
+ strcpy(HiSaxID, str);
+ else
+ printk(KERN_WARNING "HiSax: ID too long!");
+ } else
+ strcpy(HiSaxID, "HiSax");
+
+ HiSax_id = HiSaxID;
+ return 1;
+}
+
+__setup("hisax=", HiSax_setup);
+#endif /* MODULES */
+
+#if CARD_TELES0
+extern int setup_teles0(struct IsdnCard *card);
+#endif
+
+#if CARD_TELES3
+extern int setup_teles3(struct IsdnCard *card);
+#endif
+
+#if CARD_S0BOX
+extern int setup_s0box(struct IsdnCard *card);
+#endif
+
+#if CARD_TELESPCI
+extern int setup_telespci(struct IsdnCard *card);
+#endif
+
+#if CARD_AVM_A1
+extern int setup_avm_a1(struct IsdnCard *card);
+#endif
+
+#if CARD_AVM_A1_PCMCIA
+extern int setup_avm_a1_pcmcia(struct IsdnCard *card);
+#endif
+
+#if CARD_FRITZPCI
+extern int setup_avm_pcipnp(struct IsdnCard *card);
+#endif
+
+#if CARD_ELSA
+extern int setup_elsa(struct IsdnCard *card);
+#endif
+
+#if CARD_IX1MICROR2
+extern int setup_ix1micro(struct IsdnCard *card);
+#endif
+
+#if CARD_DIEHLDIVA
+extern int setup_diva(struct IsdnCard *card);
+#endif
+
+#if CARD_ASUSCOM
+extern int setup_asuscom(struct IsdnCard *card);
+#endif
+
+#if CARD_TELEINT
+extern int setup_TeleInt(struct IsdnCard *card);
+#endif
+
+#if CARD_SEDLBAUER
+extern int setup_sedlbauer(struct IsdnCard *card);
+#endif
+
+#if CARD_SPORTSTER
+extern int setup_sportster(struct IsdnCard *card);
+#endif
+
+#if CARD_MIC
+extern int setup_mic(struct IsdnCard *card);
+#endif
+
+#if CARD_NETJET_S
+extern int setup_netjet_s(struct IsdnCard *card);
+#endif
+
+#if CARD_HFCS
+extern int setup_hfcs(struct IsdnCard *card);
+#endif
+
+#if CARD_HFC_PCI
+extern int setup_hfcpci(struct IsdnCard *card);
+#endif
+
+#if CARD_HFC_SX
+extern int setup_hfcsx(struct IsdnCard *card);
+#endif
+
+#if CARD_NICCY
+extern int setup_niccy(struct IsdnCard *card);
+#endif
+
+#if CARD_ISURF
+extern int setup_isurf(struct IsdnCard *card);
+#endif
+
+#if CARD_HSTSAPHIR
+extern int setup_saphir(struct IsdnCard *card);
+#endif
+
+#if CARD_BKM_A4T
+extern int setup_bkm_a4t(struct IsdnCard *card);
+#endif
+
+#if CARD_SCT_QUADRO
+extern int setup_sct_quadro(struct IsdnCard *card);
+#endif
+
+#if CARD_GAZEL
+extern int setup_gazel(struct IsdnCard *card);
+#endif
+
+#if CARD_W6692
+extern int setup_w6692(struct IsdnCard *card);
+#endif
+
+#if CARD_NETJET_U
+extern int setup_netjet_u(struct IsdnCard *card);
+#endif
+
+#if CARD_FN_ENTERNOW_PCI
+extern int setup_enternow_pci(struct IsdnCard *card);
+#endif
+
+/*
+ * Find card with given driverId
+ */
+static inline struct IsdnCardState *hisax_findcard(int driverid)
+{
+ int i;
+
+ for (i = 0; i < nrcards; i++)
+ if (cards[i].cs)
+ if (cards[i].cs->myid == driverid)
+ return cards[i].cs;
+ return NULL;
+}
+
+/*
+ * Find card with given card number
+ */
+#if 0
+struct IsdnCardState *hisax_get_card(int cardnr)
+{
+ if ((cardnr <= nrcards) && (cardnr > 0))
+ if (cards[cardnr - 1].cs)
+ return cards[cardnr - 1].cs;
+ return NULL;
+}
+#endif /* 0 */
+
+static int HiSax_readstatus(u_char __user *buf, int len, int id, int channel)
+{
+ int count, cnt;
+ u_char __user *p = buf;
+ struct IsdnCardState *cs = hisax_findcard(id);
+
+ if (cs) {
+ if (len > HISAX_STATUS_BUFSIZE) {
+ printk(KERN_WARNING
+ "HiSax: status overflow readstat %d/%d\n",
+ len, HISAX_STATUS_BUFSIZE);
+ }
+ count = cs->status_end - cs->status_read + 1;
+ if (count >= len)
+ count = len;
+ if (copy_to_user(p, cs->status_read, count))
+ return -EFAULT;
+ cs->status_read += count;
+ if (cs->status_read > cs->status_end)
+ cs->status_read = cs->status_buf;
+ p += count;
+ count = len - count;
+ while (count) {
+ if (count > HISAX_STATUS_BUFSIZE)
+ cnt = HISAX_STATUS_BUFSIZE;
+ else
+ cnt = count;
+ if (copy_to_user(p, cs->status_read, cnt))
+ return -EFAULT;
+ p += cnt;
+ cs->status_read += cnt % HISAX_STATUS_BUFSIZE;
+ count -= cnt;
+ }
+ return len;
+ } else {
+ printk(KERN_ERR
+ "HiSax: if_readstatus called with invalid driverId!\n");
+ return -ENODEV;
+ }
+}
+
+int jiftime(char *s, long mark)
+{
+ s += 8;
+
+ *s-- = '\0';
+ *s-- = mark % 10 + '0';
+ mark /= 10;
+ *s-- = mark % 10 + '0';
+ mark /= 10;
+ *s-- = '.';
+ *s-- = mark % 10 + '0';
+ mark /= 10;
+ *s-- = mark % 6 + '0';
+ mark /= 6;
+ *s-- = ':';
+ *s-- = mark % 10 + '0';
+ mark /= 10;
+ *s-- = mark % 10 + '0';
+ return 8;
+}
+
+static u_char tmpbuf[HISAX_STATUS_BUFSIZE];
+
+void VHiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt,
+ va_list args)
+{
+ /* if head == NULL the fmt contains the full info */
+
+ u_long flags;
+ int count, i;
+ u_char *p;
+ isdn_ctrl ic;
+ int len;
+
+ if (!cs) {
+ printk(KERN_WARNING "HiSax: No CardStatus for message");
+ return;
+ }
+ spin_lock_irqsave(&cs->statlock, flags);
+ p = tmpbuf;
+ if (head) {
+ p += jiftime(p, jiffies);
+ p += sprintf(p, " %s", head);
+ p += vsprintf(p, fmt, args);
+ *p++ = '\n';
+ *p = 0;
+ len = p - tmpbuf;
+ p = tmpbuf;
+ } else {
+ p = fmt;
+ len = strlen(fmt);
+ }
+ if (len > HISAX_STATUS_BUFSIZE) {
+ spin_unlock_irqrestore(&cs->statlock, flags);
+ printk(KERN_WARNING "HiSax: status overflow %d/%d\n",
+ len, HISAX_STATUS_BUFSIZE);
+ return;
+ }
+ count = len;
+ i = cs->status_end - cs->status_write + 1;
+ if (i >= len)
+ i = len;
+ len -= i;
+ memcpy(cs->status_write, p, i);
+ cs->status_write += i;
+ if (cs->status_write > cs->status_end)
+ cs->status_write = cs->status_buf;
+ p += i;
+ if (len) {
+ memcpy(cs->status_write, p, len);
+ cs->status_write += len;
+ }
+#ifdef KERNELSTACK_DEBUG
+ i = (ulong) & len - current->kernel_stack_page;
+ sprintf(tmpbuf, "kstack %s %lx use %ld\n", current->comm,
+ current->kernel_stack_page, i);
+ len = strlen(tmpbuf);
+ for (p = tmpbuf, i = len; i > 0; i--, p++) {
+ *cs->status_write++ = *p;
+ if (cs->status_write > cs->status_end)
+ cs->status_write = cs->status_buf;
+ count++;
+ }
+#endif
+ spin_unlock_irqrestore(&cs->statlock, flags);
+ if (count) {
+ ic.command = ISDN_STAT_STAVAIL;
+ ic.driver = cs->myid;
+ ic.arg = count;
+ cs->iif.statcallb(&ic);
+ }
+}
+
+void HiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ VHiSax_putstatus(cs, head, fmt, args);
+ va_end(args);
+}
+
+int ll_run(struct IsdnCardState *cs, int addfeatures)
+{
+ isdn_ctrl ic;
+
+ ic.driver = cs->myid;
+ ic.command = ISDN_STAT_RUN;
+ cs->iif.features |= addfeatures;
+ cs->iif.statcallb(&ic);
+ return 0;
+}
+
+static void ll_stop(struct IsdnCardState *cs)
+{
+ isdn_ctrl ic;
+
+ ic.command = ISDN_STAT_STOP;
+ ic.driver = cs->myid;
+ cs->iif.statcallb(&ic);
+ // CallcFreeChan(cs);
+}
+
+static void ll_unload(struct IsdnCardState *cs)
+{
+ isdn_ctrl ic;
+
+ ic.command = ISDN_STAT_UNLOAD;
+ ic.driver = cs->myid;
+ cs->iif.statcallb(&ic);
+ kfree(cs->status_buf);
+ cs->status_read = NULL;
+ cs->status_write = NULL;
+ cs->status_end = NULL;
+ kfree(cs->dlog);
+ cs->dlog = NULL;
+}
+
+static void closecard(int cardnr)
+{
+ struct IsdnCardState *csta = cards[cardnr].cs;
+
+ if (csta->bcs->BC_Close != NULL) {
+ csta->bcs->BC_Close(csta->bcs + 1);
+ csta->bcs->BC_Close(csta->bcs);
+ }
+
+ skb_queue_purge(&csta->rq);
+ skb_queue_purge(&csta->sq);
+ kfree(csta->rcvbuf);
+ csta->rcvbuf = NULL;
+ if (csta->tx_skb) {
+ dev_kfree_skb(csta->tx_skb);
+ csta->tx_skb = NULL;
+ }
+ if (csta->DC_Close != NULL) {
+ csta->DC_Close(csta);
+ }
+ if (csta->cardmsg)
+ csta->cardmsg(csta, CARD_RELEASE, NULL);
+ if (csta->dbusytimer.function != NULL) // FIXME?
+ del_timer(&csta->dbusytimer);
+ ll_unload(csta);
+}
+
+static int init_card(struct IsdnCardState *cs)
+{
+ int irq_cnt, cnt = 3, ret;
+
+ if (!cs->irq) {
+ ret = cs->cardmsg(cs, CARD_INIT, NULL);
+ return(ret);
+ }
+ irq_cnt = kstat_irqs(cs->irq);
+ printk(KERN_INFO "%s: IRQ %d count %d\n", CardType[cs->typ],
+ cs->irq, irq_cnt);
+ if (request_irq(cs->irq, cs->irq_func, cs->irq_flags, "HiSax", cs)) {
+ printk(KERN_WARNING "HiSax: couldn't get interrupt %d\n",
+ cs->irq);
+ return 1;
+ }
+ while (cnt) {
+ cs->cardmsg(cs, CARD_INIT, NULL);
+ /* Timeout 10ms */
+ msleep(10);
+ printk(KERN_INFO "%s: IRQ %d count %d\n",
+ CardType[cs->typ], cs->irq, kstat_irqs(cs->irq));
+ if (kstat_irqs(cs->irq) == irq_cnt) {
+ printk(KERN_WARNING
+ "%s: IRQ(%d) getting no interrupts during init %d\n",
+ CardType[cs->typ], cs->irq, 4 - cnt);
+ if (cnt == 1) {
+ free_irq(cs->irq, cs);
+ return 2;
+ } else {
+ cs->cardmsg(cs, CARD_RESET, NULL);
+ cnt--;
+ }
+ } else {
+ cs->cardmsg(cs, CARD_TEST, NULL);
+ return 0;
+ }
+ }
+ return 3;
+}
+
+static int __devinit hisax_cs_setup_card(struct IsdnCard *card)
+{
+ int ret;
+
+ switch (card->typ) {
+#if CARD_TELES0
+ case ISDN_CTYPE_16_0:
+ case ISDN_CTYPE_8_0:
+ ret = setup_teles0(card);
+ break;
+#endif
+#if CARD_TELES3
+ case ISDN_CTYPE_16_3:
+ case ISDN_CTYPE_PNP:
+ case ISDN_CTYPE_TELESPCMCIA:
+ case ISDN_CTYPE_COMPAQ_ISA:
+ ret = setup_teles3(card);
+ break;
+#endif
+#if CARD_S0BOX
+ case ISDN_CTYPE_S0BOX:
+ ret = setup_s0box(card);
+ break;
+#endif
+#if CARD_TELESPCI
+ case ISDN_CTYPE_TELESPCI:
+ ret = setup_telespci(card);
+ break;
+#endif
+#if CARD_AVM_A1
+ case ISDN_CTYPE_A1:
+ ret = setup_avm_a1(card);
+ break;
+#endif
+#if CARD_AVM_A1_PCMCIA
+ case ISDN_CTYPE_A1_PCMCIA:
+ ret = setup_avm_a1_pcmcia(card);
+ break;
+#endif
+#if CARD_FRITZPCI
+ case ISDN_CTYPE_FRITZPCI:
+ ret = setup_avm_pcipnp(card);
+ break;
+#endif
+#if CARD_ELSA
+ case ISDN_CTYPE_ELSA:
+ case ISDN_CTYPE_ELSA_PNP:
+ case ISDN_CTYPE_ELSA_PCMCIA:
+ case ISDN_CTYPE_ELSA_PCI:
+ ret = setup_elsa(card);
+ break;
+#endif
+#if CARD_IX1MICROR2
+ case ISDN_CTYPE_IX1MICROR2:
+ ret = setup_ix1micro(card);
+ break;
+#endif
+#if CARD_DIEHLDIVA
+ case ISDN_CTYPE_DIEHLDIVA:
+ ret = setup_diva(card);
+ break;
+#endif
+#if CARD_ASUSCOM
+ case ISDN_CTYPE_ASUSCOM:
+ ret = setup_asuscom(card);
+ break;
+#endif
+#if CARD_TELEINT
+ case ISDN_CTYPE_TELEINT:
+ ret = setup_TeleInt(card);
+ break;
+#endif
+#if CARD_SEDLBAUER
+ case ISDN_CTYPE_SEDLBAUER:
+ case ISDN_CTYPE_SEDLBAUER_PCMCIA:
+ case ISDN_CTYPE_SEDLBAUER_FAX:
+ ret = setup_sedlbauer(card);
+ break;
+#endif
+#if CARD_SPORTSTER
+ case ISDN_CTYPE_SPORTSTER:
+ ret = setup_sportster(card);
+ break;
+#endif
+#if CARD_MIC
+ case ISDN_CTYPE_MIC:
+ ret = setup_mic(card);
+ break;
+#endif
+#if CARD_NETJET_S
+ case ISDN_CTYPE_NETJET_S:
+ ret = setup_netjet_s(card);
+ break;
+#endif
+#if CARD_HFCS
+ case ISDN_CTYPE_TELES3C:
+ case ISDN_CTYPE_ACERP10:
+ ret = setup_hfcs(card);
+ break;
+#endif
+#if CARD_HFC_PCI
+ case ISDN_CTYPE_HFC_PCI:
+ ret = setup_hfcpci(card);
+ break;
+#endif
+#if CARD_HFC_SX
+ case ISDN_CTYPE_HFC_SX:
+ ret = setup_hfcsx(card);
+ break;
+#endif
+#if CARD_NICCY
+ case ISDN_CTYPE_NICCY:
+ ret = setup_niccy(card);
+ break;
+#endif
+#if CARD_ISURF
+ case ISDN_CTYPE_ISURF:
+ ret = setup_isurf(card);
+ break;
+#endif
+#if CARD_HSTSAPHIR
+ case ISDN_CTYPE_HSTSAPHIR:
+ ret = setup_saphir(card);
+ break;
+#endif
+#if CARD_BKM_A4T
+ case ISDN_CTYPE_BKM_A4T:
+ ret = setup_bkm_a4t(card);
+ break;
+#endif
+#if CARD_SCT_QUADRO
+ case ISDN_CTYPE_SCT_QUADRO:
+ ret = setup_sct_quadro(card);
+ break;
+#endif
+#if CARD_GAZEL
+ case ISDN_CTYPE_GAZEL:
+ ret = setup_gazel(card);
+ break;
+#endif
+#if CARD_W6692
+ case ISDN_CTYPE_W6692:
+ ret = setup_w6692(card);
+ break;
+#endif
+#if CARD_NETJET_U
+ case ISDN_CTYPE_NETJET_U:
+ ret = setup_netjet_u(card);
+ break;
+#endif
+#if CARD_FN_ENTERNOW_PCI
+ case ISDN_CTYPE_ENTERNOW:
+ ret = setup_enternow_pci(card);
+ break;
+#endif
+ case ISDN_CTYPE_DYNAMIC:
+ ret = 2;
+ break;
+ default:
+ printk(KERN_WARNING
+ "HiSax: Support for %s Card not selected\n",
+ CardType[card->typ]);
+ ret = 0;
+ break;
+ }
+
+ return ret;
+}
+
+static int hisax_cs_new(int cardnr, char *id, struct IsdnCard *card,
+ struct IsdnCardState **cs_out, int *busy_flag,
+ struct module *lockowner)
+{
+ struct IsdnCardState *cs;
+
+ *cs_out = NULL;
+
+ cs = kzalloc(sizeof(struct IsdnCardState), GFP_ATOMIC);
+ if (!cs) {
+ printk(KERN_WARNING
+ "HiSax: No memory for IsdnCardState(card %d)\n",
+ cardnr + 1);
+ goto out;
+ }
+ card->cs = cs;
+ spin_lock_init(&cs->statlock);
+ spin_lock_init(&cs->lock);
+ cs->chanlimit = 2; /* maximum B-channel number */
+ cs->logecho = 0; /* No echo logging */
+ cs->cardnr = cardnr;
+ cs->debug = L1_DEB_WARN;
+ cs->HW_Flags = 0;
+ cs->busy_flag = busy_flag;
+ cs->irq_flags = I4L_IRQ_FLAG;
+#if TEI_PER_CARD
+ if (card->protocol == ISDN_PTYPE_NI1)
+ test_and_set_bit(FLG_TWO_DCHAN, &cs->HW_Flags);
+#else
+ test_and_set_bit(FLG_TWO_DCHAN, &cs->HW_Flags);
+#endif
+ cs->protocol = card->protocol;
+
+ if (card->typ <= 0 || card->typ > ISDN_CTYPE_COUNT) {
+ printk(KERN_WARNING
+ "HiSax: Card Type %d out of range\n", card->typ);
+ goto outf_cs;
+ }
+ if (!(cs->dlog = kmalloc(MAX_DLOG_SPACE, GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "HiSax: No memory for dlog(card %d)\n", cardnr + 1);
+ goto outf_cs;
+ }
+ if (!(cs->status_buf = kmalloc(HISAX_STATUS_BUFSIZE, GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "HiSax: No memory for status_buf(card %d)\n",
+ cardnr + 1);
+ goto outf_dlog;
+ }
+ cs->stlist = NULL;
+ cs->status_read = cs->status_buf;
+ cs->status_write = cs->status_buf;
+ cs->status_end = cs->status_buf + HISAX_STATUS_BUFSIZE - 1;
+ cs->typ = card->typ;
+#ifdef MODULE
+ cs->iif.owner = lockowner;
+#endif
+ strcpy(cs->iif.id, id);
+ cs->iif.channels = 2;
+ cs->iif.maxbufsize = MAX_DATA_SIZE;
+ cs->iif.hl_hdrlen = MAX_HEADER_LEN;
+ cs->iif.features =
+ ISDN_FEATURE_L2_X75I |
+ ISDN_FEATURE_L2_HDLC |
+ ISDN_FEATURE_L2_HDLC_56K |
+ ISDN_FEATURE_L2_TRANS |
+ ISDN_FEATURE_L3_TRANS |
+#ifdef CONFIG_HISAX_1TR6
+ ISDN_FEATURE_P_1TR6 |
+#endif
+#ifdef CONFIG_HISAX_EURO
+ ISDN_FEATURE_P_EURO |
+#endif
+#ifdef CONFIG_HISAX_NI1
+ ISDN_FEATURE_P_NI1 |
+#endif
+ 0;
+
+ cs->iif.command = HiSax_command;
+ cs->iif.writecmd = NULL;
+ cs->iif.writebuf_skb = HiSax_writebuf_skb;
+ cs->iif.readstat = HiSax_readstatus;
+ register_isdn(&cs->iif);
+ cs->myid = cs->iif.channels;
+
+ *cs_out = cs;
+ return 1; /* success */
+
+outf_dlog:
+ kfree(cs->dlog);
+outf_cs:
+ kfree(cs);
+ card->cs = NULL;
+out:
+ return 0; /* error */
+}
+
+static int hisax_cs_setup(int cardnr, struct IsdnCard *card,
+ struct IsdnCardState *cs)
+{
+ int ret;
+
+ if (!(cs->rcvbuf = kmalloc(MAX_DFRAME_LEN_L1, GFP_ATOMIC))) {
+ printk(KERN_WARNING "HiSax: No memory for isac rcvbuf\n");
+ ll_unload(cs);
+ goto outf_cs;
+ }
+ cs->rcvidx = 0;
+ cs->tx_skb = NULL;
+ cs->tx_cnt = 0;
+ cs->event = 0;
+
+ skb_queue_head_init(&cs->rq);
+ skb_queue_head_init(&cs->sq);
+
+ init_bcstate(cs, 0);
+ init_bcstate(cs, 1);
+
+ /* init_card only handles interrupts which are not */
+ /* used here for the loadable driver */
+ switch (card->typ) {
+ case ISDN_CTYPE_DYNAMIC:
+ ret = 0;
+ break;
+ default:
+ ret = init_card(cs);
+ break;
+ }
+ if (ret) {
+ closecard(cardnr);
+ goto outf_cs;
+ }
+ init_tei(cs, cs->protocol);
+ ret = CallcNewChan(cs);
+ if (ret) {
+ closecard(cardnr);
+ goto outf_cs;
+ }
+ /* ISAR needs firmware download first */
+ if (!test_bit(HW_ISAR, &cs->HW_Flags))
+ ll_run(cs, 0);
+
+ return 1;
+
+outf_cs:
+ kfree(cs);
+ card->cs = NULL;
+ return 0;
+}
+
+/* Used from an exported function but calls __devinit functions.
+ * Tell modpost not to warn (__ref)
+ */
+static int __ref checkcard(int cardnr, char *id, int *busy_flag,
+ struct module *lockowner,
+ hisax_setup_func_t card_setup)
+{
+ int ret;
+ struct IsdnCard *card = cards + cardnr;
+ struct IsdnCardState *cs;
+
+ ret = hisax_cs_new(cardnr, id, card, &cs, busy_flag, lockowner);
+ if (!ret)
+ return 0;
+
+ printk(KERN_INFO
+ "HiSax: Card %d Protocol %s Id=%s (%d)\n", cardnr + 1,
+ (card->protocol == ISDN_PTYPE_1TR6) ? "1TR6" :
+ (card->protocol == ISDN_PTYPE_EURO) ? "EDSS1" :
+ (card->protocol == ISDN_PTYPE_LEASED) ? "LEASED" :
+ (card->protocol == ISDN_PTYPE_NI1) ? "NI1" :
+ "NONE", cs->iif.id, cs->myid);
+
+ ret = card_setup(card);
+ if (!ret) {
+ ll_unload(cs);
+ goto outf_cs;
+ }
+
+ ret = hisax_cs_setup(cardnr, card, cs);
+ goto out;
+
+ outf_cs:
+ kfree(cs);
+ card->cs = NULL;
+ out:
+ return ret;
+}
+
+static void HiSax_shiftcards(int idx)
+{
+ int i;
+
+ for (i = idx; i < (HISAX_MAX_CARDS - 1); i++)
+ memcpy(&cards[i], &cards[i + 1], sizeof(cards[i]));
+}
+
+static int __init HiSax_inithardware(int *busy_flag)
+{
+ int foundcards = 0;
+ int i = 0;
+ int t = ',';
+ int flg = 0;
+ char *id;
+ char *next_id = HiSax_id;
+ char ids[20];
+
+ if (strchr(HiSax_id, ','))
+ t = ',';
+ else if (strchr(HiSax_id, '%'))
+ t = '%';
+
+ while (i < nrcards) {
+ if (cards[i].typ < 1)
+ break;
+ id = next_id;
+ if ((next_id = strchr(id, t))) {
+ *next_id++ = 0;
+ strcpy(ids, id);
+ flg = i + 1;
+ } else {
+ next_id = id;
+ if (flg >= i)
+ strcpy(ids, id);
+ else
+ sprintf(ids, "%s%d", id, i);
+ }
+ if (checkcard(i, ids, busy_flag, THIS_MODULE,
+ hisax_cs_setup_card)) {
+ foundcards++;
+ i++;
+ } else {
+ /* make sure we don't oops the module */
+ if (cards[i].typ > 0 && cards[i].typ <= ISDN_CTYPE_COUNT) {
+ printk(KERN_WARNING
+ "HiSax: Card %s not installed !\n",
+ CardType[cards[i].typ]);
+ }
+ HiSax_shiftcards(i);
+ nrcards--;
+ }
+ }
+ return foundcards;
+}
+
+void HiSax_closecard(int cardnr)
+{
+ int i, last = nrcards - 1;
+
+ if (cardnr > last || cardnr < 0)
+ return;
+ if (cards[cardnr].cs) {
+ ll_stop(cards[cardnr].cs);
+ release_tei(cards[cardnr].cs);
+ CallcFreeChan(cards[cardnr].cs);
+
+ closecard(cardnr);
+ if (cards[cardnr].cs->irq)
+ free_irq(cards[cardnr].cs->irq, cards[cardnr].cs);
+ kfree((void *) cards[cardnr].cs);
+ cards[cardnr].cs = NULL;
+ }
+ i = cardnr;
+ while (i <= last) {
+ cards[i] = cards[i + 1];
+ i++;
+ }
+ nrcards--;
+}
+
+void HiSax_reportcard(int cardnr, int sel)
+{
+ struct IsdnCardState *cs = cards[cardnr].cs;
+
+ printk(KERN_DEBUG "HiSax: reportcard No %d\n", cardnr + 1);
+ printk(KERN_DEBUG "HiSax: Type %s\n", CardType[cs->typ]);
+ printk(KERN_DEBUG "HiSax: debuglevel %x\n", cs->debug);
+ printk(KERN_DEBUG "HiSax: HiSax_reportcard address 0x%lX\n",
+ (ulong) & HiSax_reportcard);
+ printk(KERN_DEBUG "HiSax: cs 0x%lX\n", (ulong) cs);
+ printk(KERN_DEBUG "HiSax: HW_Flags %lx bc0 flg %lx bc1 flg %lx\n",
+ cs->HW_Flags, cs->bcs[0].Flag, cs->bcs[1].Flag);
+ printk(KERN_DEBUG "HiSax: bcs 0 mode %d ch%d\n",
+ cs->bcs[0].mode, cs->bcs[0].channel);
+ printk(KERN_DEBUG "HiSax: bcs 1 mode %d ch%d\n",
+ cs->bcs[1].mode, cs->bcs[1].channel);
+#ifdef ERROR_STATISTIC
+ printk(KERN_DEBUG "HiSax: dc errors(rx,crc,tx) %d,%d,%d\n",
+ cs->err_rx, cs->err_crc, cs->err_tx);
+ printk(KERN_DEBUG
+ "HiSax: bc0 errors(inv,rdo,crc,tx) %d,%d,%d,%d\n",
+ cs->bcs[0].err_inv, cs->bcs[0].err_rdo, cs->bcs[0].err_crc,
+ cs->bcs[0].err_tx);
+ printk(KERN_DEBUG
+ "HiSax: bc1 errors(inv,rdo,crc,tx) %d,%d,%d,%d\n",
+ cs->bcs[1].err_inv, cs->bcs[1].err_rdo, cs->bcs[1].err_crc,
+ cs->bcs[1].err_tx);
+ if (sel == 99) {
+ cs->err_rx = 0;
+ cs->err_crc = 0;
+ cs->err_tx = 0;
+ cs->bcs[0].err_inv = 0;
+ cs->bcs[0].err_rdo = 0;
+ cs->bcs[0].err_crc = 0;
+ cs->bcs[0].err_tx = 0;
+ cs->bcs[1].err_inv = 0;
+ cs->bcs[1].err_rdo = 0;
+ cs->bcs[1].err_crc = 0;
+ cs->bcs[1].err_tx = 0;
+ }
+#endif
+}
+
+static int __init HiSax_init(void)
+{
+ int i, retval;
+#ifdef MODULE
+ int j;
+ int nzproto = 0;
+#endif
+
+ HiSaxVersion();
+ retval = CallcNew();
+ if (retval)
+ goto out;
+ retval = Isdnl3New();
+ if (retval)
+ goto out_callc;
+ retval = Isdnl2New();
+ if (retval)
+ goto out_isdnl3;
+ retval = TeiNew();
+ if (retval)
+ goto out_isdnl2;
+ retval = Isdnl1New();
+ if (retval)
+ goto out_tei;
+
+#ifdef MODULE
+ if (!type[0]) {
+ /* We 'll register drivers later, but init basic functions */
+ for (i = 0; i < HISAX_MAX_CARDS; i++)
+ cards[i].typ = 0;
+ return 0;
+ }
+#ifdef CONFIG_HISAX_ELSA
+ if (type[0] == ISDN_CTYPE_ELSA_PCMCIA) {
+ /* we have exported and return in this case */
+ return 0;
+ }
+#endif
+#ifdef CONFIG_HISAX_SEDLBAUER
+ if (type[0] == ISDN_CTYPE_SEDLBAUER_PCMCIA) {
+ /* we have to export and return in this case */
+ return 0;
+ }
+#endif
+#ifdef CONFIG_HISAX_AVM_A1_PCMCIA
+ if (type[0] == ISDN_CTYPE_A1_PCMCIA) {
+ /* we have to export and return in this case */
+ return 0;
+ }
+#endif
+#ifdef CONFIG_HISAX_HFC_SX
+ if (type[0] == ISDN_CTYPE_HFC_SP_PCMCIA) {
+ /* we have to export and return in this case */
+ return 0;
+ }
+#endif
+#endif
+ nrcards = 0;
+#ifdef MODULE
+ if (id) /* If id= string used */
+ HiSax_id = id;
+ for (i = j = 0; j < HISAX_MAX_CARDS; i++) {
+ cards[j].typ = type[i];
+ if (protocol[i]) {
+ cards[j].protocol = protocol[i];
+ nzproto++;
+ } else {
+ cards[j].protocol = DEFAULT_PROTO;
+ }
+ switch (type[i]) {
+ case ISDN_CTYPE_16_0:
+ cards[j].para[0] = irq[i];
+ cards[j].para[1] = mem[i];
+ cards[j].para[2] = io[i];
+ break;
+
+ case ISDN_CTYPE_8_0:
+ cards[j].para[0] = irq[i];
+ cards[j].para[1] = mem[i];
+ break;
+
+#ifdef IO0_IO1
+ case ISDN_CTYPE_PNP:
+ case ISDN_CTYPE_NICCY:
+ cards[j].para[0] = irq[i];
+ cards[j].para[1] = io0[i];
+ cards[j].para[2] = io1[i];
+ break;
+ case ISDN_CTYPE_COMPAQ_ISA:
+ cards[j].para[0] = irq[i];
+ cards[j].para[1] = io0[i];
+ cards[j].para[2] = io1[i];
+ cards[j].para[3] = io[i];
+ break;
+#endif
+ case ISDN_CTYPE_ELSA:
+ case ISDN_CTYPE_HFC_PCI:
+ cards[j].para[0] = io[i];
+ break;
+ case ISDN_CTYPE_16_3:
+ case ISDN_CTYPE_TELESPCMCIA:
+ case ISDN_CTYPE_A1:
+ case ISDN_CTYPE_A1_PCMCIA:
+ case ISDN_CTYPE_ELSA_PNP:
+ case ISDN_CTYPE_ELSA_PCMCIA:
+ case ISDN_CTYPE_IX1MICROR2:
+ case ISDN_CTYPE_DIEHLDIVA:
+ case ISDN_CTYPE_ASUSCOM:
+ case ISDN_CTYPE_TELEINT:
+ case ISDN_CTYPE_SEDLBAUER:
+ case ISDN_CTYPE_SEDLBAUER_PCMCIA:
+ case ISDN_CTYPE_SEDLBAUER_FAX:
+ case ISDN_CTYPE_SPORTSTER:
+ case ISDN_CTYPE_MIC:
+ case ISDN_CTYPE_TELES3C:
+ case ISDN_CTYPE_ACERP10:
+ case ISDN_CTYPE_S0BOX:
+ case ISDN_CTYPE_FRITZPCI:
+ case ISDN_CTYPE_HSTSAPHIR:
+ case ISDN_CTYPE_GAZEL:
+ case ISDN_CTYPE_HFC_SX:
+ case ISDN_CTYPE_HFC_SP_PCMCIA:
+ cards[j].para[0] = irq[i];
+ cards[j].para[1] = io[i];
+ break;
+ case ISDN_CTYPE_ISURF:
+ cards[j].para[0] = irq[i];
+ cards[j].para[1] = io[i];
+ cards[j].para[2] = mem[i];
+ break;
+ case ISDN_CTYPE_ELSA_PCI:
+ case ISDN_CTYPE_NETJET_S:
+ case ISDN_CTYPE_TELESPCI:
+ case ISDN_CTYPE_W6692:
+ case ISDN_CTYPE_NETJET_U:
+ break;
+ case ISDN_CTYPE_BKM_A4T:
+ break;
+ case ISDN_CTYPE_SCT_QUADRO:
+ if (irq[i]) {
+ cards[j].para[0] = irq[i];
+ } else {
+ /* QUADRO is a 4 BRI card */
+ cards[j++].para[0] = 1;
+ /* we need to check if further cards can be added */
+ if (j < HISAX_MAX_CARDS) {
+ cards[j].typ = ISDN_CTYPE_SCT_QUADRO;
+ cards[j].protocol = protocol[i];
+ cards[j++].para[0] = 2;
+ }
+ if (j < HISAX_MAX_CARDS) {
+ cards[j].typ = ISDN_CTYPE_SCT_QUADRO;
+ cards[j].protocol = protocol[i];
+ cards[j++].para[0] = 3;
+ }
+ if (j < HISAX_MAX_CARDS) {
+ cards[j].typ = ISDN_CTYPE_SCT_QUADRO;
+ cards[j].protocol = protocol[i];
+ cards[j].para[0] = 4;
+ }
+ }
+ break;
+ }
+ j++;
+ }
+ if (!nzproto) {
+ printk(KERN_WARNING
+ "HiSax: Warning - no protocol specified\n");
+ printk(KERN_WARNING "HiSax: using protocol %s\n",
+ DEFAULT_PROTO_NAME);
+ }
+#endif
+ if (!HiSax_id)
+ HiSax_id = HiSaxID;
+ if (!HiSaxID[0])
+ strcpy(HiSaxID, "HiSax");
+ for (i = 0; i < HISAX_MAX_CARDS; i++)
+ if (cards[i].typ > 0)
+ nrcards++;
+ printk(KERN_DEBUG "HiSax: Total %d card%s defined\n",
+ nrcards, (nrcards > 1) ? "s" : "");
+
+ /* Install only, if at least one card found */
+ if (!HiSax_inithardware(NULL))
+ return -ENODEV;
+ return 0;
+
+ out_tei:
+ TeiFree();
+ out_isdnl2:
+ Isdnl2Free();
+ out_isdnl3:
+ Isdnl3Free();
+ out_callc:
+ CallcFree();
+ out:
+ return retval;
+}
+
+static void __exit HiSax_exit(void)
+{
+ int cardnr = nrcards - 1;
+
+ while (cardnr >= 0)
+ HiSax_closecard(cardnr--);
+ Isdnl1Free();
+ TeiFree();
+ Isdnl2Free();
+ Isdnl3Free();
+ CallcFree();
+ printk(KERN_INFO "HiSax module removed\n");
+}
+
+#ifdef CONFIG_HOTPLUG
+
+int __devinit hisax_init_pcmcia(void *pcm_iob, int *busy_flag, struct IsdnCard *card)
+{
+ u_char ids[16];
+ int ret = -1;
+
+ cards[nrcards] = *card;
+ if (nrcards)
+ sprintf(ids, "HiSax%d", nrcards);
+ else
+ sprintf(ids, "HiSax");
+ if (!checkcard(nrcards, ids, busy_flag, THIS_MODULE,
+ hisax_cs_setup_card))
+ goto error;
+
+ ret = nrcards;
+ nrcards++;
+error:
+ return ret;
+}
+
+EXPORT_SYMBOL(hisax_init_pcmcia);
+#endif
+
+EXPORT_SYMBOL(HiSax_closecard);
+
+#include "hisax_if.h"
+
+EXPORT_SYMBOL(hisax_register);
+EXPORT_SYMBOL(hisax_unregister);
+
+static void hisax_d_l1l2(struct hisax_if *ifc, int pr, void *arg);
+static void hisax_b_l1l2(struct hisax_if *ifc, int pr, void *arg);
+static void hisax_d_l2l1(struct PStack *st, int pr, void *arg);
+static void hisax_b_l2l1(struct PStack *st, int pr, void *arg);
+static int hisax_cardmsg(struct IsdnCardState *cs, int mt, void *arg);
+static int hisax_bc_setstack(struct PStack *st, struct BCState *bcs);
+static void hisax_bc_close(struct BCState *bcs);
+static void hisax_bh(struct work_struct *work);
+static void EChannel_proc_rcv(struct hisax_d_if *d_if);
+
+static int hisax_setup_card_dynamic(struct IsdnCard *card)
+{
+ return 2;
+}
+
+int hisax_register(struct hisax_d_if *hisax_d_if, struct hisax_b_if *b_if[],
+ char *name, int protocol)
+{
+ int i, retval;
+ char id[20];
+ struct IsdnCardState *cs;
+
+ for (i = 0; i < HISAX_MAX_CARDS; i++) {
+ if (!cards[i].typ)
+ break;
+ }
+
+ if (i >= HISAX_MAX_CARDS)
+ return -EBUSY;
+
+ cards[i].typ = ISDN_CTYPE_DYNAMIC;
+ cards[i].protocol = protocol;
+ sprintf(id, "%s%d", name, i);
+ nrcards++;
+ retval = checkcard(i, id, NULL, hisax_d_if->owner,
+ hisax_setup_card_dynamic);
+ if (retval == 0) { // yuck
+ cards[i].typ = 0;
+ nrcards--;
+ return -EINVAL;
+ }
+ cs = cards[i].cs;
+ hisax_d_if->cs = cs;
+ cs->hw.hisax_d_if = hisax_d_if;
+ cs->cardmsg = hisax_cardmsg;
+ INIT_WORK(&cs->tqueue, hisax_bh);
+ cs->channel[0].d_st->l2.l2l1 = hisax_d_l2l1;
+ for (i = 0; i < 2; i++) {
+ cs->bcs[i].BC_SetStack = hisax_bc_setstack;
+ cs->bcs[i].BC_Close = hisax_bc_close;
+
+ b_if[i]->ifc.l1l2 = hisax_b_l1l2;
+
+ hisax_d_if->b_if[i] = b_if[i];
+ }
+ hisax_d_if->ifc.l1l2 = hisax_d_l1l2;
+ skb_queue_head_init(&hisax_d_if->erq);
+ clear_bit(0, &hisax_d_if->ph_state);
+
+ return 0;
+}
+
+void hisax_unregister(struct hisax_d_if *hisax_d_if)
+{
+ cards[hisax_d_if->cs->cardnr].typ = 0;
+ HiSax_closecard(hisax_d_if->cs->cardnr);
+ skb_queue_purge(&hisax_d_if->erq);
+}
+
+#include "isdnl1.h"
+
+static void hisax_sched_event(struct IsdnCardState *cs, int event)
+{
+ test_and_set_bit(event, &cs->event);
+ schedule_work(&cs->tqueue);
+}
+
+static void hisax_bh(struct work_struct *work)
+{
+ struct IsdnCardState *cs =
+ container_of(work, struct IsdnCardState, tqueue);
+ struct PStack *st;
+ int pr;
+
+ if (test_and_clear_bit(D_RCVBUFREADY, &cs->event))
+ DChannel_proc_rcv(cs);
+ if (test_and_clear_bit(E_RCVBUFREADY, &cs->event))
+ EChannel_proc_rcv(cs->hw.hisax_d_if);
+ if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) {
+ if (test_bit(0, &cs->hw.hisax_d_if->ph_state))
+ pr = PH_ACTIVATE | INDICATION;
+ else
+ pr = PH_DEACTIVATE | INDICATION;
+ for (st = cs->stlist; st; st = st->next)
+ st->l1.l1l2(st, pr, NULL);
+
+ }
+}
+
+static void hisax_b_sched_event(struct BCState *bcs, int event)
+{
+ test_and_set_bit(event, &bcs->event);
+ schedule_work(&bcs->tqueue);
+}
+
+static inline void D_L2L1(struct hisax_d_if *d_if, int pr, void *arg)
+{
+ struct hisax_if *ifc = (struct hisax_if *) d_if;
+ ifc->l2l1(ifc, pr, arg);
+}
+
+static inline void B_L2L1(struct hisax_b_if *b_if, int pr, void *arg)
+{
+ struct hisax_if *ifc = (struct hisax_if *) b_if;
+ ifc->l2l1(ifc, pr, arg);
+}
+
+static void hisax_d_l1l2(struct hisax_if *ifc, int pr, void *arg)
+{
+ struct hisax_d_if *d_if = (struct hisax_d_if *) ifc;
+ struct IsdnCardState *cs = d_if->cs;
+ struct PStack *st;
+ struct sk_buff *skb;
+
+ switch (pr) {
+ case PH_ACTIVATE | INDICATION:
+ set_bit(0, &d_if->ph_state);
+ hisax_sched_event(cs, D_L1STATECHANGE);
+ break;
+ case PH_DEACTIVATE | INDICATION:
+ clear_bit(0, &d_if->ph_state);
+ hisax_sched_event(cs, D_L1STATECHANGE);
+ break;
+ case PH_DATA | INDICATION:
+ skb_queue_tail(&cs->rq, arg);
+ hisax_sched_event(cs, D_RCVBUFREADY);
+ break;
+ case PH_DATA | CONFIRM:
+ skb = skb_dequeue(&cs->sq);
+ if (skb) {
+ D_L2L1(d_if, PH_DATA | REQUEST, skb);
+ break;
+ }
+ clear_bit(FLG_L1_DBUSY, &cs->HW_Flags);
+ for (st = cs->stlist; st; st = st->next) {
+ if (test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags)) {
+ st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+ break;
+ }
+ }
+ break;
+ case PH_DATA_E | INDICATION:
+ skb_queue_tail(&d_if->erq, arg);
+ hisax_sched_event(cs, E_RCVBUFREADY);
+ break;
+ default:
+ printk("pr %#x\n", pr);
+ break;
+ }
+}
+
+static void hisax_b_l1l2(struct hisax_if *ifc, int pr, void *arg)
+{
+ struct hisax_b_if *b_if = (struct hisax_b_if *) ifc;
+ struct BCState *bcs = b_if->bcs;
+ struct PStack *st = bcs->st;
+ struct sk_buff *skb;
+
+ // FIXME use isdnl1?
+ switch (pr) {
+ case PH_ACTIVATE | INDICATION:
+ st->l1.l1l2(st, pr, NULL);
+ break;
+ case PH_DEACTIVATE | INDICATION:
+ st->l1.l1l2(st, pr, NULL);
+ clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ skb_queue_purge(&bcs->squeue);
+ bcs->hw.b_if = NULL;
+ break;
+ case PH_DATA | INDICATION:
+ skb_queue_tail(&bcs->rqueue, arg);
+ hisax_b_sched_event(bcs, B_RCVBUFREADY);
+ break;
+ case PH_DATA | CONFIRM:
+ bcs->tx_cnt -= (long)arg;
+ if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag)) {
+ u_long flags;
+ spin_lock_irqsave(&bcs->aclock, flags);
+ bcs->ackcnt += (long)arg;
+ spin_unlock_irqrestore(&bcs->aclock, flags);
+ schedule_event(bcs, B_ACKPENDING);
+ }
+ skb = skb_dequeue(&bcs->squeue);
+ if (skb) {
+ B_L2L1(b_if, PH_DATA | REQUEST, skb);
+ break;
+ }
+ clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ if (test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags)) {
+ st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+ }
+ break;
+ default:
+ printk("hisax_b_l1l2 pr %#x\n", pr);
+ break;
+ }
+}
+
+static void hisax_d_l2l1(struct PStack *st, int pr, void *arg)
+{
+ struct IsdnCardState *cs = st->l1.hardware;
+ struct hisax_d_if *hisax_d_if = cs->hw.hisax_d_if;
+ struct sk_buff *skb = arg;
+
+ switch (pr) {
+ case PH_DATA | REQUEST:
+ case PH_PULL | INDICATION:
+ if (cs->debug & DEB_DLOG_HEX)
+ LogFrame(cs, skb->data, skb->len);
+ if (cs->debug & DEB_DLOG_VERBOSE)
+ dlogframe(cs, skb, 0);
+ Logl2Frame(cs, skb, "PH_DATA_REQ", 0);
+ // FIXME lock?
+ if (!test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+ D_L2L1(hisax_d_if, PH_DATA | REQUEST, skb);
+ else
+ skb_queue_tail(&cs->sq, skb);
+ break;
+ case PH_PULL | REQUEST:
+ if (!test_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+ st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+ else
+ set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+ break;
+ default:
+ D_L2L1(hisax_d_if, pr, arg);
+ break;
+ }
+}
+
+static int hisax_cardmsg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ return 0;
+}
+
+static void hisax_b_l2l1(struct PStack *st, int pr, void *arg)
+{
+ struct BCState *bcs = st->l1.bcs;
+ struct hisax_b_if *b_if = bcs->hw.b_if;
+
+ switch (pr) {
+ case PH_ACTIVATE | REQUEST:
+ B_L2L1(b_if, pr, (void *)(unsigned long)st->l1.mode);
+ break;
+ case PH_DATA | REQUEST:
+ case PH_PULL | INDICATION:
+ // FIXME lock?
+ if (!test_and_set_bit(BC_FLG_BUSY, &bcs->Flag)) {
+ B_L2L1(b_if, PH_DATA | REQUEST, arg);
+ } else {
+ skb_queue_tail(&bcs->squeue, arg);
+ }
+ break;
+ case PH_PULL | REQUEST:
+ if (!test_bit(BC_FLG_BUSY, &bcs->Flag))
+ st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+ else
+ set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+ break;
+ case PH_DEACTIVATE | REQUEST:
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ skb_queue_purge(&bcs->squeue);
+ default:
+ B_L2L1(b_if, pr, arg);
+ break;
+ }
+}
+
+static int hisax_bc_setstack(struct PStack *st, struct BCState *bcs)
+{
+ struct IsdnCardState *cs = st->l1.hardware;
+ struct hisax_d_if *hisax_d_if = cs->hw.hisax_d_if;
+
+ bcs->channel = st->l1.bc;
+
+ bcs->hw.b_if = hisax_d_if->b_if[st->l1.bc];
+ hisax_d_if->b_if[st->l1.bc]->bcs = bcs;
+
+ st->l1.bcs = bcs;
+ st->l2.l2l1 = hisax_b_l2l1;
+ setstack_manager(st);
+ bcs->st = st;
+ setstack_l1_B(st);
+ skb_queue_head_init(&bcs->rqueue);
+ skb_queue_head_init(&bcs->squeue);
+ return 0;
+}
+
+static void hisax_bc_close(struct BCState *bcs)
+{
+ struct hisax_b_if *b_if = bcs->hw.b_if;
+
+ if (b_if)
+ B_L2L1(b_if, PH_DEACTIVATE | REQUEST, NULL);
+}
+
+static void EChannel_proc_rcv(struct hisax_d_if *d_if)
+{
+ struct IsdnCardState *cs = d_if->cs;
+ u_char *ptr;
+ struct sk_buff *skb;
+
+ while ((skb = skb_dequeue(&d_if->erq)) != NULL) {
+ if (cs->debug & DEB_DLOG_HEX) {
+ ptr = cs->dlog;
+ if ((skb->len) < MAX_DLOG_SPACE / 3 - 10) {
+ *ptr++ = 'E';
+ *ptr++ = 'C';
+ *ptr++ = 'H';
+ *ptr++ = 'O';
+ *ptr++ = ':';
+ ptr += QuickHex(ptr, skb->data, skb->len);
+ ptr--;
+ *ptr++ = '\n';
+ *ptr = 0;
+ HiSax_putstatus(cs, NULL, cs->dlog);
+ } else
+ HiSax_putstatus(cs, "LogEcho: ",
+ "warning Frame too big (%d)",
+ skb->len);
+ }
+ dev_kfree_skb_any(skb);
+ }
+}
+
+#ifdef CONFIG_PCI
+#include <linux/pci.h>
+
+static struct pci_device_id hisax_pci_tbl[] __devinitdata = {
+#ifdef CONFIG_HISAX_FRITZPCI
+ {PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1, PCI_ANY_ID, PCI_ANY_ID},
+#endif
+#ifdef CONFIG_HISAX_DIEHLDIVA
+ {PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_DIVA20, PCI_ANY_ID, PCI_ANY_ID},
+ {PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_DIVA20_U, PCI_ANY_ID, PCI_ANY_ID},
+ {PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_DIVA201, PCI_ANY_ID, PCI_ANY_ID},
+//#########################################################################################
+ {PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_DIVA202, PCI_ANY_ID, PCI_ANY_ID},
+//#########################################################################################
+#endif
+#ifdef CONFIG_HISAX_ELSA
+ {PCI_VENDOR_ID_ELSA, PCI_DEVICE_ID_ELSA_MICROLINK, PCI_ANY_ID, PCI_ANY_ID},
+ {PCI_VENDOR_ID_ELSA, PCI_DEVICE_ID_ELSA_QS3000, PCI_ANY_ID, PCI_ANY_ID},
+#endif
+#ifdef CONFIG_HISAX_GAZEL
+ {PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_R685, PCI_ANY_ID, PCI_ANY_ID},
+ {PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_R753, PCI_ANY_ID, PCI_ANY_ID},
+ {PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_DJINN_ITOO, PCI_ANY_ID, PCI_ANY_ID},
+ {PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_OLITEC, PCI_ANY_ID, PCI_ANY_ID},
+#endif
+#ifdef CONFIG_HISAX_SCT_QUADRO
+ {PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, PCI_ANY_ID, PCI_ANY_ID},
+#endif
+#ifdef CONFIG_HISAX_NICCY
+ {PCI_VENDOR_ID_SATSAGEM, PCI_DEVICE_ID_SATSAGEM_NICCY, PCI_ANY_ID,PCI_ANY_ID},
+#endif
+#ifdef CONFIG_HISAX_SEDLBAUER
+ {PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100, PCI_ANY_ID,PCI_ANY_ID},
+#endif
+#if defined(CONFIG_HISAX_NETJET) || defined(CONFIG_HISAX_NETJET_U)
+ {PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_300, PCI_ANY_ID,PCI_ANY_ID},
+#endif
+#if defined(CONFIG_HISAX_TELESPCI) || defined(CONFIG_HISAX_SCT_QUADRO)
+ {PCI_VENDOR_ID_ZORAN, PCI_DEVICE_ID_ZORAN_36120, PCI_ANY_ID,PCI_ANY_ID},
+#endif
+#ifdef CONFIG_HISAX_W6692
+ {PCI_VENDOR_ID_DYNALINK, PCI_DEVICE_ID_DYNALINK_IS64PH, PCI_ANY_ID,PCI_ANY_ID},
+ {PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692, PCI_ANY_ID,PCI_ANY_ID},
+#endif
+#ifdef CONFIG_HISAX_HFC_PCI
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_2BD0, PCI_ANY_ID, PCI_ANY_ID},
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B000, PCI_ANY_ID, PCI_ANY_ID},
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B006, PCI_ANY_ID, PCI_ANY_ID},
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B007, PCI_ANY_ID, PCI_ANY_ID},
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B008, PCI_ANY_ID, PCI_ANY_ID},
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B009, PCI_ANY_ID, PCI_ANY_ID},
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00A, PCI_ANY_ID, PCI_ANY_ID},
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00B, PCI_ANY_ID, PCI_ANY_ID},
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B00C, PCI_ANY_ID, PCI_ANY_ID},
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B100, PCI_ANY_ID, PCI_ANY_ID},
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B700, PCI_ANY_ID, PCI_ANY_ID},
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B701, PCI_ANY_ID, PCI_ANY_ID},
+ {PCI_VENDOR_ID_ABOCOM, PCI_DEVICE_ID_ABOCOM_2BD1, PCI_ANY_ID, PCI_ANY_ID},
+ {PCI_VENDOR_ID_ASUSTEK, PCI_DEVICE_ID_ASUSTEK_0675, PCI_ANY_ID, PCI_ANY_ID},
+ {PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_T_CONCEPT, PCI_ANY_ID, PCI_ANY_ID},
+ {PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_A1T, PCI_ANY_ID, PCI_ANY_ID},
+ {PCI_VENDOR_ID_ANIGMA, PCI_DEVICE_ID_ANIGMA_MC145575, PCI_ANY_ID, PCI_ANY_ID},
+ {PCI_VENDOR_ID_ZOLTRIX, PCI_DEVICE_ID_ZOLTRIX_2BD0, PCI_ANY_ID, PCI_ANY_ID},
+ {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_E, PCI_ANY_ID, PCI_ANY_ID},
+ {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_E, PCI_ANY_ID, PCI_ANY_ID},
+ {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_A, PCI_ANY_ID, PCI_ANY_ID},
+ {PCI_VENDOR_ID_DIGI, PCI_DEVICE_ID_DIGI_DF_M_A, PCI_ANY_ID, PCI_ANY_ID},
+#endif
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(pci, hisax_pci_tbl);
+#endif /* CONFIG_PCI */
+
+module_init(HiSax_init);
+module_exit(HiSax_exit);
+
+EXPORT_SYMBOL(FsmNew);
+EXPORT_SYMBOL(FsmFree);
+EXPORT_SYMBOL(FsmEvent);
+EXPORT_SYMBOL(FsmChangeState);
+EXPORT_SYMBOL(FsmInitTimer);
+EXPORT_SYMBOL(FsmDelTimer);
+EXPORT_SYMBOL(FsmRestartTimer);
diff --git a/drivers/isdn/hisax/diva.c b/drivers/isdn/hisax/diva.c
new file mode 100644
index 0000000..018bd29
--- /dev/null
+++ b/drivers/isdn/hisax/diva.c
@@ -0,0 +1,1283 @@
+/* $Id: diva.c,v 1.33.2.6 2004/02/11 13:21:33 keil Exp $
+ *
+ * low level stuff for Eicon.Diehl Diva Family ISDN cards
+ *
+ * Author Karsten Keil
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ * Thanks to Eicon Technology for documents and information
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "ipac.h"
+#include "ipacx.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/isapnp.h>
+
+static const char *Diva_revision = "$Revision: 1.33.2.6 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define DIVA_HSCX_DATA 0
+#define DIVA_HSCX_ADR 4
+#define DIVA_ISA_ISAC_DATA 2
+#define DIVA_ISA_ISAC_ADR 6
+#define DIVA_ISA_CTRL 7
+#define DIVA_IPAC_ADR 0
+#define DIVA_IPAC_DATA 1
+
+#define DIVA_PCI_ISAC_DATA 8
+#define DIVA_PCI_ISAC_ADR 0xc
+#define DIVA_PCI_CTRL 0x10
+
+/* SUB Types */
+#define DIVA_ISA 1
+#define DIVA_PCI 2
+#define DIVA_IPAC_ISA 3
+#define DIVA_IPAC_PCI 4
+#define DIVA_IPACX_PCI 5
+
+/* CTRL (Read) */
+#define DIVA_IRQ_STAT 0x01
+#define DIVA_EEPROM_SDA 0x02
+
+/* CTRL (Write) */
+#define DIVA_IRQ_REQ 0x01
+#define DIVA_RESET 0x08
+#define DIVA_EEPROM_CLK 0x40
+#define DIVA_PCI_LED_A 0x10
+#define DIVA_PCI_LED_B 0x20
+#define DIVA_ISA_LED_A 0x20
+#define DIVA_ISA_LED_B 0x40
+#define DIVA_IRQ_CLR 0x80
+
+/* Siemens PITA */
+#define PITA_MISC_REG 0x1c
+#ifdef __BIG_ENDIAN
+#define PITA_PARA_SOFTRESET 0x00000001
+#define PITA_SER_SOFTRESET 0x00000002
+#define PITA_PARA_MPX_MODE 0x00000004
+#define PITA_INT0_ENABLE 0x00000200
+#else
+#define PITA_PARA_SOFTRESET 0x01000000
+#define PITA_SER_SOFTRESET 0x02000000
+#define PITA_PARA_MPX_MODE 0x04000000
+#define PITA_INT0_ENABLE 0x00020000
+#endif
+#define PITA_INT0_STATUS 0x02
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+ register u_char ret;
+
+ byteout(ale, off);
+ ret = bytein(adr);
+ return (ret);
+}
+
+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);
+}
+
+static inline u_char
+memreadreg(unsigned long adr, u_char off)
+{
+ return(*((unsigned char *)
+ (((unsigned int *)adr) + off)));
+}
+
+static inline void
+memwritereg(unsigned long adr, u_char off, u_char data)
+{
+ register u_char *p;
+
+ p = (unsigned char *)(((unsigned int *)adr) + off);
+ *p = data;
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+ return(readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char *data, int size)
+{
+ readfifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size)
+{
+ writefifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0, data, size);
+}
+
+static u_char
+ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset)
+{
+ return (readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset+0x80));
+}
+
+static void
+WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, offset|0x80, value);
+}
+
+static void
+ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+ readfifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0x80, data, size);
+}
+
+static void
+WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+ writefifo(cs->hw.diva.isac_adr, cs->hw.diva.isac, 0x80, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+ return(readreg(cs->hw.diva.hscx_adr,
+ cs->hw.diva.hscx, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+ writereg(cs->hw.diva.hscx_adr,
+ cs->hw.diva.hscx, offset + (hscx ? 0x40 : 0), value);
+}
+
+static u_char
+MemReadISAC_IPAC(struct IsdnCardState *cs, u_char offset)
+{
+ return (memreadreg(cs->hw.diva.cfg_reg, offset+0x80));
+}
+
+static void
+MemWriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ memwritereg(cs->hw.diva.cfg_reg, offset|0x80, value);
+}
+
+static void
+MemReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+ while(size--)
+ *data++ = memreadreg(cs->hw.diva.cfg_reg, 0x80);
+}
+
+static void
+MemWriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+ while(size--)
+ memwritereg(cs->hw.diva.cfg_reg, 0x80, *data++);
+}
+
+static u_char
+MemReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+ return(memreadreg(cs->hw.diva.cfg_reg, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+MemWriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+ memwritereg(cs->hw.diva.cfg_reg, offset + (hscx ? 0x40 : 0), value);
+}
+
+/* IO-Functions for IPACX type cards */
+static u_char
+MemReadISAC_IPACX(struct IsdnCardState *cs, u_char offset)
+{
+ return (memreadreg(cs->hw.diva.cfg_reg, offset));
+}
+
+static void
+MemWriteISAC_IPACX(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ memwritereg(cs->hw.diva.cfg_reg, offset, value);
+}
+
+static void
+MemReadISACfifo_IPACX(struct IsdnCardState *cs, u_char * data, int size)
+{
+ while(size--)
+ *data++ = memreadreg(cs->hw.diva.cfg_reg, 0);
+}
+
+static void
+MemWriteISACfifo_IPACX(struct IsdnCardState *cs, u_char * data, int size)
+{
+ while(size--)
+ memwritereg(cs->hw.diva.cfg_reg, 0, *data++);
+}
+
+static u_char
+MemReadHSCX_IPACX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+ return(memreadreg(cs->hw.diva.cfg_reg, offset +
+ (hscx ? IPACX_OFF_B2 : IPACX_OFF_B1)));
+}
+
+static void
+MemWriteHSCX_IPACX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+ memwritereg(cs->hw.diva.cfg_reg, offset +
+ (hscx ? IPACX_OFF_B2 : IPACX_OFF_B1), value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.diva.hscx_adr, \
+ cs->hw.diva.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.diva.hscx_adr, \
+ cs->hw.diva.hscx, reg + (nr ? 0x40 : 0), data)
+
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.diva.hscx_adr, \
+ cs->hw.diva.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.diva.hscx_adr, \
+ cs->hw.diva.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+diva_interrupt(int intno, void *dev_id)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char val, sval;
+ u_long flags;
+ int cnt=5;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ while (((sval = bytein(cs->hw.diva.ctrl)) & DIVA_IRQ_REQ) && cnt) {
+ val = readreg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_ISTA + 0x40);
+ if (val)
+ hscx_int_main(cs, val);
+ val = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_ISTA);
+ if (val)
+ isac_interrupt(cs, val);
+ cnt--;
+ }
+ if (!cnt)
+ printk(KERN_WARNING "Diva: IRQ LOOP\n");
+ writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK, 0xFF);
+ writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK + 0x40, 0xFF);
+ writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_MASK, 0xFF);
+ writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_MASK, 0x0);
+ writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK, 0x0);
+ writereg(cs->hw.diva.hscx_adr, cs->hw.diva.hscx, HSCX_MASK + 0x40, 0x0);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t
+diva_irq_ipac_isa(int intno, void *dev_id)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char ista,val;
+ u_long flags;
+ int icnt=5;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ ista = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_ISTA);
+Start_IPACISA:
+ if (cs->debug & L1_DEB_IPAC)
+ debugl1(cs, "IPAC ISTA %02X", ista);
+ if (ista & 0x0f) {
+ val = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, HSCX_ISTA + 0x40);
+ if (ista & 0x01)
+ val |= 0x01;
+ if (ista & 0x04)
+ val |= 0x02;
+ if (ista & 0x08)
+ val |= 0x04;
+ if (val)
+ hscx_int_main(cs, val);
+ }
+ if (ista & 0x20) {
+ val = 0xfe & readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, ISAC_ISTA + 0x80);
+ if (val) {
+ isac_interrupt(cs, val);
+ }
+ }
+ if (ista & 0x10) {
+ val = 0x01;
+ isac_interrupt(cs, val);
+ }
+ ista = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_ISTA);
+ if ((ista & 0x3f) && icnt) {
+ icnt--;
+ goto Start_IPACISA;
+ }
+ if (!icnt)
+ printk(KERN_WARNING "DIVA IPAC IRQ LOOP\n");
+ writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xFF);
+ writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xC0);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static inline void
+MemwaitforCEC(struct IsdnCardState *cs, int hscx)
+{
+ int to = 50;
+
+ while ((MemReadHSCX(cs, hscx, HSCX_STAR) & 0x04) && to) {
+ udelay(1);
+ to--;
+ }
+ if (!to)
+ printk(KERN_WARNING "HiSax: waitforCEC timeout\n");
+}
+
+
+static inline void
+MemwaitforXFW(struct IsdnCardState *cs, int hscx)
+{
+ int to = 50;
+
+ while ((!(MemReadHSCX(cs, hscx, HSCX_STAR) & 0x44) == 0x40) && to) {
+ udelay(1);
+ to--;
+ }
+ if (!to)
+ printk(KERN_WARNING "HiSax: waitforXFW timeout\n");
+}
+
+static inline void
+MemWriteHSCXCMDR(struct IsdnCardState *cs, int hscx, u_char data)
+{
+ MemwaitforCEC(cs, hscx);
+ MemWriteHSCX(cs, hscx, HSCX_CMDR, data);
+}
+
+static void
+Memhscx_empty_fifo(struct BCState *bcs, int count)
+{
+ u_char *ptr;
+ struct IsdnCardState *cs = bcs->cs;
+ int cnt;
+
+ if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+ debugl1(cs, "hscx_empty_fifo");
+
+ if (bcs->hw.hscx.rcvidx + count > HSCX_BUFMAX) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "hscx_empty_fifo: incoming packet too large");
+ MemWriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x80);
+ bcs->hw.hscx.rcvidx = 0;
+ return;
+ }
+ ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx;
+ cnt = count;
+ while (cnt--)
+ *ptr++ = memreadreg(cs->hw.diva.cfg_reg, bcs->hw.hscx.hscx ? 0x40 : 0);
+ MemWriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x80);
+ ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx;
+ bcs->hw.hscx.rcvidx += count;
+ if (cs->debug & L1_DEB_HSCX_FIFO) {
+ char *t = bcs->blog;
+
+ t += sprintf(t, "hscx_empty_fifo %c cnt %d",
+ bcs->hw.hscx.hscx ? 'B' : 'A', count);
+ QuickHex(t, ptr, count);
+ debugl1(cs, bcs->blog);
+ }
+}
+
+static void
+Memhscx_fill_fifo(struct BCState *bcs)
+{
+ struct IsdnCardState *cs = bcs->cs;
+ int more, count, cnt;
+ int fifo_size = test_bit(HW_IPAC, &cs->HW_Flags)? 64: 32;
+ u_char *ptr,*p;
+
+ if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+ debugl1(cs, "hscx_fill_fifo");
+
+ if (!bcs->tx_skb)
+ return;
+ if (bcs->tx_skb->len <= 0)
+ return;
+
+ more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0;
+ if (bcs->tx_skb->len > fifo_size) {
+ more = !0;
+ count = fifo_size;
+ } else
+ count = bcs->tx_skb->len;
+ cnt = count;
+ MemwaitforXFW(cs, bcs->hw.hscx.hscx);
+ p = ptr = bcs->tx_skb->data;
+ skb_pull(bcs->tx_skb, count);
+ bcs->tx_cnt -= count;
+ bcs->hw.hscx.count += count;
+ while(cnt--)
+ memwritereg(cs->hw.diva.cfg_reg, bcs->hw.hscx.hscx ? 0x40 : 0,
+ *p++);
+ MemWriteHSCXCMDR(cs, bcs->hw.hscx.hscx, more ? 0x8 : 0xa);
+ if (cs->debug & L1_DEB_HSCX_FIFO) {
+ char *t = bcs->blog;
+
+ t += sprintf(t, "hscx_fill_fifo %c cnt %d",
+ bcs->hw.hscx.hscx ? 'B' : 'A', count);
+ QuickHex(t, ptr, count);
+ debugl1(cs, bcs->blog);
+ }
+}
+
+static void
+Memhscx_interrupt(struct IsdnCardState *cs, u_char val, u_char hscx)
+{
+ u_char r;
+ struct BCState *bcs = cs->bcs + hscx;
+ struct sk_buff *skb;
+ int fifo_size = test_bit(HW_IPAC, &cs->HW_Flags)? 64: 32;
+ int count;
+
+ if (!test_bit(BC_FLG_INIT, &bcs->Flag))
+ return;
+
+ if (val & 0x80) { /* RME */
+ r = MemReadHSCX(cs, hscx, HSCX_RSTA);
+ if ((r & 0xf0) != 0xa0) {
+ if (!(r & 0x80))
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "HSCX invalid frame");
+ if ((r & 0x40) && bcs->mode)
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "HSCX RDO mode=%d",
+ bcs->mode);
+ if (!(r & 0x20))
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "HSCX CRC error");
+ MemWriteHSCXCMDR(cs, hscx, 0x80);
+ } else {
+ count = MemReadHSCX(cs, hscx, HSCX_RBCL) & (
+ test_bit(HW_IPAC, &cs->HW_Flags)? 0x3f: 0x1f);
+ if (count == 0)
+ count = fifo_size;
+ Memhscx_empty_fifo(bcs, count);
+ if ((count = bcs->hw.hscx.rcvidx - 1) > 0) {
+ if (cs->debug & L1_DEB_HSCX_FIFO)
+ debugl1(cs, "HX Frame %d", count);
+ if (!(skb = dev_alloc_skb(count)))
+ printk(KERN_WARNING "HSCX: receive out of memory\n");
+ else {
+ memcpy(skb_put(skb, count), bcs->hw.hscx.rcvbuf, count);
+ skb_queue_tail(&bcs->rqueue, skb);
+ }
+ }
+ }
+ bcs->hw.hscx.rcvidx = 0;
+ schedule_event(bcs, B_RCVBUFREADY);
+ }
+ if (val & 0x40) { /* RPF */
+ Memhscx_empty_fifo(bcs, fifo_size);
+ if (bcs->mode == L1_MODE_TRANS) {
+ /* receive audio data */
+ if (!(skb = dev_alloc_skb(fifo_size)))
+ printk(KERN_WARNING "HiSax: receive out of memory\n");
+ else {
+ memcpy(skb_put(skb, fifo_size), bcs->hw.hscx.rcvbuf, fifo_size);
+ skb_queue_tail(&bcs->rqueue, skb);
+ }
+ bcs->hw.hscx.rcvidx = 0;
+ schedule_event(bcs, B_RCVBUFREADY);
+ }
+ }
+ if (val & 0x10) { /* XPR */
+ if (bcs->tx_skb) {
+ if (bcs->tx_skb->len) {
+ Memhscx_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.hscx.count;
+ spin_unlock_irqrestore(&bcs->aclock, flags);
+ schedule_event(bcs, B_ACKPENDING);
+ }
+ dev_kfree_skb_irq(bcs->tx_skb);
+ bcs->hw.hscx.count = 0;
+ bcs->tx_skb = NULL;
+ }
+ }
+ if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+ bcs->hw.hscx.count = 0;
+ test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+ Memhscx_fill_fifo(bcs);
+ } else {
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ schedule_event(bcs, B_XMTBUFREADY);
+ }
+ }
+}
+
+static inline void
+Memhscx_int_main(struct IsdnCardState *cs, u_char val)
+{
+
+ u_char exval;
+ struct BCState *bcs;
+
+ if (val & 0x01) { // EXB
+ bcs = cs->bcs + 1;
+ exval = MemReadHSCX(cs, 1, HSCX_EXIR);
+ if (exval & 0x40) {
+ if (bcs->mode == 1)
+ Memhscx_fill_fifo(bcs);
+ else {
+ /* Here we lost an TX interrupt, so
+ * restart transmitting the whole frame.
+ */
+ if (bcs->tx_skb) {
+ skb_push(bcs->tx_skb, bcs->hw.hscx.count);
+ bcs->tx_cnt += bcs->hw.hscx.count;
+ bcs->hw.hscx.count = 0;
+ }
+ MemWriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x01);
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "HSCX B EXIR %x Lost TX", exval);
+ }
+ } else if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "HSCX B EXIR %x", exval);
+ }
+ if (val & 0xf8) {
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "HSCX B interrupt %x", val);
+ Memhscx_interrupt(cs, val, 1);
+ }
+ if (val & 0x02) { // EXA
+ bcs = cs->bcs;
+ exval = MemReadHSCX(cs, 0, HSCX_EXIR);
+ if (exval & 0x40) {
+ if (bcs->mode == L1_MODE_TRANS)
+ Memhscx_fill_fifo(bcs);
+ else {
+ /* Here we lost an TX interrupt, so
+ * restart transmitting the whole frame.
+ */
+ if (bcs->tx_skb) {
+ skb_push(bcs->tx_skb, bcs->hw.hscx.count);
+ bcs->tx_cnt += bcs->hw.hscx.count;
+ bcs->hw.hscx.count = 0;
+ }
+ MemWriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x01);
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "HSCX A EXIR %x Lost TX", exval);
+ }
+ } else if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "HSCX A EXIR %x", exval);
+ }
+ if (val & 0x04) { // ICA
+ exval = MemReadHSCX(cs, 0, HSCX_ISTA);
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "HSCX A interrupt %x", exval);
+ Memhscx_interrupt(cs, exval, 0);
+ }
+}
+
+static irqreturn_t
+diva_irq_ipac_pci(int intno, void *dev_id)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char ista,val;
+ int icnt=5;
+ u_char *cfg;
+ u_long flags;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ cfg = (u_char *) cs->hw.diva.pci_cfg;
+ val = *cfg;
+ if (!(val & PITA_INT0_STATUS)) {
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_NONE; /* other shared IRQ */
+ }
+ *cfg = PITA_INT0_STATUS; /* Reset pending INT0 */
+ ista = memreadreg(cs->hw.diva.cfg_reg, IPAC_ISTA);
+Start_IPACPCI:
+ if (cs->debug & L1_DEB_IPAC)
+ debugl1(cs, "IPAC ISTA %02X", ista);
+ if (ista & 0x0f) {
+ val = memreadreg(cs->hw.diva.cfg_reg, HSCX_ISTA + 0x40);
+ if (ista & 0x01)
+ val |= 0x01;
+ if (ista & 0x04)
+ val |= 0x02;
+ if (ista & 0x08)
+ val |= 0x04;
+ if (val)
+ Memhscx_int_main(cs, val);
+ }
+ if (ista & 0x20) {
+ val = 0xfe & memreadreg(cs->hw.diva.cfg_reg, ISAC_ISTA + 0x80);
+ if (val) {
+ isac_interrupt(cs, val);
+ }
+ }
+ if (ista & 0x10) {
+ val = 0x01;
+ isac_interrupt(cs, val);
+ }
+ ista = memreadreg(cs->hw.diva.cfg_reg, IPAC_ISTA);
+ if ((ista & 0x3f) && icnt) {
+ icnt--;
+ goto Start_IPACPCI;
+ }
+ if (!icnt)
+ printk(KERN_WARNING "DIVA IPAC PCI IRQ LOOP\n");
+ memwritereg(cs->hw.diva.cfg_reg, IPAC_MASK, 0xFF);
+ memwritereg(cs->hw.diva.cfg_reg, IPAC_MASK, 0xC0);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t
+diva_irq_ipacx_pci(int intno, void *dev_id)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char val;
+ u_char *cfg;
+ u_long flags;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ cfg = (u_char *) cs->hw.diva.pci_cfg;
+ val = *cfg;
+ if (!(val &PITA_INT0_STATUS)) {
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_NONE; // other shared IRQ
+ }
+ interrupt_ipacx(cs); // handler for chip
+ *cfg = PITA_INT0_STATUS; // Reset PLX interrupt
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static void
+release_io_diva(struct IsdnCardState *cs)
+{
+ int bytecnt;
+
+ if ((cs->subtyp == DIVA_IPAC_PCI) ||
+ (cs->subtyp == DIVA_IPACX_PCI) ) {
+ u_int *cfg = (unsigned int *)cs->hw.diva.pci_cfg;
+
+ *cfg = 0; /* disable INT0/1 */
+ *cfg = 2; /* reset pending INT0 */
+ if (cs->hw.diva.cfg_reg)
+ iounmap((void *)cs->hw.diva.cfg_reg);
+ if (cs->hw.diva.pci_cfg)
+ iounmap((void *)cs->hw.diva.pci_cfg);
+ return;
+ } else if (cs->subtyp != DIVA_IPAC_ISA) {
+ del_timer(&cs->hw.diva.tl);
+ if (cs->hw.diva.cfg_reg)
+ byteout(cs->hw.diva.ctrl, 0); /* LED off, Reset */
+ }
+ if ((cs->subtyp == DIVA_ISA) || (cs->subtyp == DIVA_IPAC_ISA))
+ bytecnt = 8;
+ else
+ bytecnt = 32;
+ if (cs->hw.diva.cfg_reg) {
+ release_region(cs->hw.diva.cfg_reg, bytecnt);
+ }
+}
+
+static void
+iounmap_diva(struct IsdnCardState *cs)
+{
+ if ((cs->subtyp == DIVA_IPAC_PCI) || (cs->subtyp == DIVA_IPACX_PCI)) {
+ if (cs->hw.diva.cfg_reg) {
+ iounmap((void *)cs->hw.diva.cfg_reg);
+ cs->hw.diva.cfg_reg = 0;
+ }
+ if (cs->hw.diva.pci_cfg) {
+ iounmap((void *)cs->hw.diva.pci_cfg);
+ cs->hw.diva.pci_cfg = 0;
+ }
+ }
+
+ return;
+}
+
+static void
+reset_diva(struct IsdnCardState *cs)
+{
+ if (cs->subtyp == DIVA_IPAC_ISA) {
+ writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_POTA2, 0x20);
+ mdelay(10);
+ writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_POTA2, 0x00);
+ mdelay(10);
+ writereg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_MASK, 0xc0);
+ } else if (cs->subtyp == DIVA_IPAC_PCI) {
+ unsigned int *ireg = (unsigned int *)(cs->hw.diva.pci_cfg +
+ PITA_MISC_REG);
+ *ireg = PITA_PARA_SOFTRESET | PITA_PARA_MPX_MODE;
+ mdelay(10);
+ *ireg = PITA_PARA_MPX_MODE;
+ mdelay(10);
+ memwritereg(cs->hw.diva.cfg_reg, IPAC_MASK, 0xc0);
+ } else if (cs->subtyp == DIVA_IPACX_PCI) {
+ unsigned int *ireg = (unsigned int *)(cs->hw.diva.pci_cfg +
+ PITA_MISC_REG);
+ *ireg = PITA_PARA_SOFTRESET | PITA_PARA_MPX_MODE;
+ mdelay(10);
+ *ireg = PITA_PARA_MPX_MODE | PITA_SER_SOFTRESET;
+ mdelay(10);
+ MemWriteISAC_IPACX(cs, IPACX_MASK, 0xff); // Interrupts off
+ } else { /* DIVA 2.0 */
+ cs->hw.diva.ctrl_reg = 0; /* Reset On */
+ byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
+ mdelay(10);
+ cs->hw.diva.ctrl_reg |= DIVA_RESET; /* Reset Off */
+ byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
+ mdelay(10);
+ if (cs->subtyp == DIVA_ISA)
+ cs->hw.diva.ctrl_reg |= DIVA_ISA_LED_A;
+ else {
+ /* Workaround PCI9060 */
+ byteout(cs->hw.diva.pci_cfg + 0x69, 9);
+ cs->hw.diva.ctrl_reg |= DIVA_PCI_LED_A;
+ }
+ byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
+ }
+}
+
+#define DIVA_ASSIGN 1
+
+static void
+diva_led_handler(struct IsdnCardState *cs)
+{
+ int blink = 0;
+
+ if ((cs->subtyp == DIVA_IPAC_ISA) ||
+ (cs->subtyp == DIVA_IPAC_PCI) ||
+ (cs->subtyp == DIVA_IPACX_PCI) )
+ return;
+ del_timer(&cs->hw.diva.tl);
+ if (cs->hw.diva.status & DIVA_ASSIGN)
+ cs->hw.diva.ctrl_reg |= (DIVA_ISA == cs->subtyp) ?
+ DIVA_ISA_LED_A : DIVA_PCI_LED_A;
+ else {
+ cs->hw.diva.ctrl_reg ^= (DIVA_ISA == cs->subtyp) ?
+ DIVA_ISA_LED_A : DIVA_PCI_LED_A;
+ blink = 250;
+ }
+ if (cs->hw.diva.status & 0xf000)
+ cs->hw.diva.ctrl_reg |= (DIVA_ISA == cs->subtyp) ?
+ DIVA_ISA_LED_B : DIVA_PCI_LED_B;
+ else if (cs->hw.diva.status & 0x0f00) {
+ cs->hw.diva.ctrl_reg ^= (DIVA_ISA == cs->subtyp) ?
+ DIVA_ISA_LED_B : DIVA_PCI_LED_B;
+ blink = 500;
+ } else
+ cs->hw.diva.ctrl_reg &= ~((DIVA_ISA == cs->subtyp) ?
+ DIVA_ISA_LED_B : DIVA_PCI_LED_B);
+
+ byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
+ if (blink) {
+ init_timer(&cs->hw.diva.tl);
+ cs->hw.diva.tl.expires = jiffies + ((blink * HZ) / 1000);
+ add_timer(&cs->hw.diva.tl);
+ }
+}
+
+static int
+Diva_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ u_int *ireg;
+ u_long flags;
+
+ switch (mt) {
+ case CARD_RESET:
+ spin_lock_irqsave(&cs->lock, flags);
+ reset_diva(cs);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return(0);
+ case CARD_RELEASE:
+ release_io_diva(cs);
+ return(0);
+ case CARD_INIT:
+ spin_lock_irqsave(&cs->lock, flags);
+ reset_diva(cs);
+ if (cs->subtyp == DIVA_IPACX_PCI) {
+ ireg = (unsigned int *)cs->hw.diva.pci_cfg;
+ *ireg = PITA_INT0_ENABLE;
+ init_ipacx(cs, 3); // init chip and enable interrupts
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return (0);
+ }
+ if (cs->subtyp == DIVA_IPAC_PCI) {
+ ireg = (unsigned int *)cs->hw.diva.pci_cfg;
+ *ireg = PITA_INT0_ENABLE;
+ }
+ inithscxisac(cs, 3);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return(0);
+ case CARD_TEST:
+ return(0);
+ case (MDL_REMOVE | REQUEST):
+ cs->hw.diva.status = 0;
+ break;
+ case (MDL_ASSIGN | REQUEST):
+ cs->hw.diva.status |= DIVA_ASSIGN;
+ break;
+ case MDL_INFO_SETUP:
+ if ((long)arg)
+ cs->hw.diva.status |= 0x0200;
+ else
+ cs->hw.diva.status |= 0x0100;
+ break;
+ case MDL_INFO_CONN:
+ if ((long)arg)
+ cs->hw.diva.status |= 0x2000;
+ else
+ cs->hw.diva.status |= 0x1000;
+ break;
+ case MDL_INFO_REL:
+ if ((long)arg) {
+ cs->hw.diva.status &= ~0x2000;
+ cs->hw.diva.status &= ~0x0200;
+ } else {
+ cs->hw.diva.status &= ~0x1000;
+ cs->hw.diva.status &= ~0x0100;
+ }
+ break;
+ }
+ if ((cs->subtyp != DIVA_IPAC_ISA) &&
+ (cs->subtyp != DIVA_IPAC_PCI) &&
+ (cs->subtyp != DIVA_IPACX_PCI)) {
+ spin_lock_irqsave(&cs->lock, flags);
+ diva_led_handler(cs);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ }
+ return(0);
+}
+
+static int __devinit setup_diva_common(struct IsdnCardState *cs)
+{
+ int bytecnt;
+ u_char val;
+
+ if ((cs->subtyp == DIVA_ISA) || (cs->subtyp == DIVA_IPAC_ISA))
+ bytecnt = 8;
+ else
+ bytecnt = 32;
+
+ printk(KERN_INFO
+ "Diva: %s card configured at %#lx IRQ %d\n",
+ (cs->subtyp == DIVA_PCI) ? "PCI" :
+ (cs->subtyp == DIVA_ISA) ? "ISA" :
+ (cs->subtyp == DIVA_IPAC_ISA) ? "IPAC ISA" :
+ (cs->subtyp == DIVA_IPAC_PCI) ? "IPAC PCI" : "IPACX PCI",
+ cs->hw.diva.cfg_reg, cs->irq);
+ if ((cs->subtyp == DIVA_IPAC_PCI) ||
+ (cs->subtyp == DIVA_IPACX_PCI) ||
+ (cs->subtyp == DIVA_PCI) )
+ printk(KERN_INFO "Diva: %s space at %#lx\n",
+ (cs->subtyp == DIVA_PCI) ? "PCI" :
+ (cs->subtyp == DIVA_IPAC_PCI) ? "IPAC PCI" : "IPACX PCI",
+ cs->hw.diva.pci_cfg);
+ if ((cs->subtyp != DIVA_IPAC_PCI) &&
+ (cs->subtyp != DIVA_IPACX_PCI) ) {
+ if (!request_region(cs->hw.diva.cfg_reg, bytecnt, "diva isdn")) {
+ printk(KERN_WARNING
+ "HiSax: %s config port %lx-%lx already in use\n",
+ "diva",
+ cs->hw.diva.cfg_reg,
+ cs->hw.diva.cfg_reg + bytecnt);
+ iounmap_diva(cs);
+ return (0);
+ }
+ }
+ cs->BC_Read_Reg = &ReadHSCX;
+ cs->BC_Write_Reg = &WriteHSCX;
+ cs->BC_Send_Data = &hscx_fill_fifo;
+ cs->cardmsg = &Diva_card_msg;
+ setup_isac(cs);
+ if (cs->subtyp == DIVA_IPAC_ISA) {
+ cs->readisac = &ReadISAC_IPAC;
+ cs->writeisac = &WriteISAC_IPAC;
+ cs->readisacfifo = &ReadISACfifo_IPAC;
+ cs->writeisacfifo = &WriteISACfifo_IPAC;
+ cs->irq_func = &diva_irq_ipac_isa;
+ val = readreg(cs->hw.diva.isac_adr, cs->hw.diva.isac, IPAC_ID);
+ printk(KERN_INFO "Diva: IPAC version %x\n", val);
+ } else if (cs->subtyp == DIVA_IPAC_PCI) {
+ cs->readisac = &MemReadISAC_IPAC;
+ cs->writeisac = &MemWriteISAC_IPAC;
+ cs->readisacfifo = &MemReadISACfifo_IPAC;
+ cs->writeisacfifo = &MemWriteISACfifo_IPAC;
+ cs->BC_Read_Reg = &MemReadHSCX;
+ cs->BC_Write_Reg = &MemWriteHSCX;
+ cs->BC_Send_Data = &Memhscx_fill_fifo;
+ cs->irq_func = &diva_irq_ipac_pci;
+ val = memreadreg(cs->hw.diva.cfg_reg, IPAC_ID);
+ printk(KERN_INFO "Diva: IPAC version %x\n", val);
+ } else if (cs->subtyp == DIVA_IPACX_PCI) {
+ cs->readisac = &MemReadISAC_IPACX;
+ cs->writeisac = &MemWriteISAC_IPACX;
+ cs->readisacfifo = &MemReadISACfifo_IPACX;
+ cs->writeisacfifo = &MemWriteISACfifo_IPACX;
+ cs->BC_Read_Reg = &MemReadHSCX_IPACX;
+ cs->BC_Write_Reg = &MemWriteHSCX_IPACX;
+ cs->BC_Send_Data = NULL; // function located in ipacx module
+ cs->irq_func = &diva_irq_ipacx_pci;
+ printk(KERN_INFO "Diva: IPACX Design Id: %x\n",
+ MemReadISAC_IPACX(cs, IPACX_ID) &0x3F);
+ } else { /* DIVA 2.0 */
+ cs->hw.diva.tl.function = (void *) diva_led_handler;
+ cs->hw.diva.tl.data = (long) cs;
+ init_timer(&cs->hw.diva.tl);
+ cs->readisac = &ReadISAC;
+ cs->writeisac = &WriteISAC;
+ cs->readisacfifo = &ReadISACfifo;
+ cs->writeisacfifo = &WriteISACfifo;
+ cs->irq_func = &diva_interrupt;
+ ISACVersion(cs, "Diva:");
+ if (HscxVersion(cs, "Diva:")) {
+ printk(KERN_WARNING
+ "Diva: wrong HSCX versions check IO address\n");
+ release_io_diva(cs);
+ return (0);
+ }
+ }
+ return (1);
+}
+
+#ifdef CONFIG_ISA
+
+static int __devinit setup_diva_isa(struct IsdnCard *card)
+{
+ struct IsdnCardState *cs = card->cs;
+ u_char val;
+
+ if (!card->para[1])
+ return (-1); /* card not found; continue search */
+
+ cs->hw.diva.ctrl_reg = 0;
+ cs->hw.diva.cfg_reg = card->para[1];
+ val = readreg(cs->hw.diva.cfg_reg + DIVA_IPAC_ADR,
+ cs->hw.diva.cfg_reg + DIVA_IPAC_DATA, IPAC_ID);
+ printk(KERN_INFO "Diva: IPAC version %x\n", val);
+ if ((val == 1) || (val==2)) {
+ cs->subtyp = DIVA_IPAC_ISA;
+ cs->hw.diva.ctrl = 0;
+ cs->hw.diva.isac = card->para[1] + DIVA_IPAC_DATA;
+ cs->hw.diva.hscx = card->para[1] + DIVA_IPAC_DATA;
+ cs->hw.diva.isac_adr = card->para[1] + DIVA_IPAC_ADR;
+ cs->hw.diva.hscx_adr = card->para[1] + DIVA_IPAC_ADR;
+ test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+ } else {
+ cs->subtyp = DIVA_ISA;
+ cs->hw.diva.ctrl = card->para[1] + DIVA_ISA_CTRL;
+ cs->hw.diva.isac = card->para[1] + DIVA_ISA_ISAC_DATA;
+ cs->hw.diva.hscx = card->para[1] + DIVA_HSCX_DATA;
+ cs->hw.diva.isac_adr = card->para[1] + DIVA_ISA_ISAC_ADR;
+ cs->hw.diva.hscx_adr = card->para[1] + DIVA_HSCX_ADR;
+ }
+ cs->irq = card->para[0];
+
+ return (1); /* card found */
+}
+
+#else /* if !CONFIG_ISA */
+
+static int __devinit setup_diva_isa(struct IsdnCard *card)
+{
+ return (-1); /* card not found; continue search */
+}
+
+#endif /* CONFIG_ISA */
+
+#ifdef __ISAPNP__
+static struct isapnp_device_id diva_ids[] __devinitdata = {
+ { ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x51),
+ ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x51),
+ (unsigned long) "Diva picola" },
+ { ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x51),
+ ISAPNP_VENDOR('E', 'I', 'C'), ISAPNP_FUNCTION(0x51),
+ (unsigned long) "Diva picola" },
+ { ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x71),
+ ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x71),
+ (unsigned long) "Diva 2.0" },
+ { ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0x71),
+ ISAPNP_VENDOR('E', 'I', 'C'), ISAPNP_FUNCTION(0x71),
+ (unsigned long) "Diva 2.0" },
+ { ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0xA1),
+ ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0xA1),
+ (unsigned long) "Diva 2.01" },
+ { ISAPNP_VENDOR('G', 'D', 'I'), ISAPNP_FUNCTION(0xA1),
+ ISAPNP_VENDOR('E', 'I', 'C'), ISAPNP_FUNCTION(0xA1),
+ (unsigned long) "Diva 2.01" },
+ { 0, }
+};
+
+static struct isapnp_device_id *ipid __devinitdata = &diva_ids[0];
+static struct pnp_card *pnp_c __devinitdata = NULL;
+
+static int __devinit setup_diva_isapnp(struct IsdnCard *card)
+{
+ struct IsdnCardState *cs = card->cs;
+ struct pnp_dev *pnp_d;
+
+ if (!isapnp_present())
+ return (-1); /* card not found; continue search */
+
+ while(ipid->card_vendor) {
+ if ((pnp_c = pnp_find_card(ipid->card_vendor,
+ ipid->card_device, pnp_c))) {
+ pnp_d = NULL;
+ if ((pnp_d = pnp_find_dev(pnp_c,
+ ipid->vendor, ipid->function, pnp_d))) {
+ int err;
+
+ printk(KERN_INFO "HiSax: %s detected\n",
+ (char *)ipid->driver_data);
+ pnp_disable_dev(pnp_d);
+ err = pnp_activate_dev(pnp_d);
+ if (err<0) {
+ printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
+ __func__, err);
+ return(0);
+ }
+ card->para[1] = pnp_port_start(pnp_d, 0);
+ card->para[0] = pnp_irq(pnp_d, 0);
+ if (!card->para[0] || !card->para[1]) {
+ printk(KERN_ERR "Diva PnP:some resources are missing %ld/%lx\n",
+ card->para[0], card->para[1]);
+ pnp_disable_dev(pnp_d);
+ return(0);
+ }
+ cs->hw.diva.cfg_reg = card->para[1];
+ cs->irq = card->para[0];
+ if (ipid->function == ISAPNP_FUNCTION(0xA1)) {
+ cs->subtyp = DIVA_IPAC_ISA;
+ cs->hw.diva.ctrl = 0;
+ cs->hw.diva.isac =
+ card->para[1] + DIVA_IPAC_DATA;
+ cs->hw.diva.hscx =
+ card->para[1] + DIVA_IPAC_DATA;
+ cs->hw.diva.isac_adr =
+ card->para[1] + DIVA_IPAC_ADR;
+ cs->hw.diva.hscx_adr =
+ card->para[1] + DIVA_IPAC_ADR;
+ test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+ } else {
+ cs->subtyp = DIVA_ISA;
+ cs->hw.diva.ctrl =
+ card->para[1] + DIVA_ISA_CTRL;
+ cs->hw.diva.isac =
+ card->para[1] + DIVA_ISA_ISAC_DATA;
+ cs->hw.diva.hscx =
+ card->para[1] + DIVA_HSCX_DATA;
+ cs->hw.diva.isac_adr =
+ card->para[1] + DIVA_ISA_ISAC_ADR;
+ cs->hw.diva.hscx_adr =
+ card->para[1] + DIVA_HSCX_ADR;
+ }
+ return (1); /* card found */
+ } else {
+ printk(KERN_ERR "Diva PnP: PnP error card found, no device\n");
+ return(0);
+ }
+ }
+ ipid++;
+ pnp_c=NULL;
+ }
+
+ return (-1); /* card not found; continue search */
+}
+
+#else /* if !ISAPNP */
+
+static int __devinit setup_diva_isapnp(struct IsdnCard *card)
+{
+ return (-1); /* card not found; continue search */
+}
+
+#endif /* ISAPNP */
+
+#ifdef CONFIG_PCI_LEGACY
+static struct pci_dev *dev_diva __devinitdata = NULL;
+static struct pci_dev *dev_diva_u __devinitdata = NULL;
+static struct pci_dev *dev_diva201 __devinitdata = NULL;
+static struct pci_dev *dev_diva202 __devinitdata = NULL;
+
+static int __devinit setup_diva_pci(struct IsdnCard *card)
+{
+ struct IsdnCardState *cs = card->cs;
+
+ cs->subtyp = 0;
+ if ((dev_diva = pci_find_device(PCI_VENDOR_ID_EICON,
+ PCI_DEVICE_ID_EICON_DIVA20, dev_diva))) {
+ if (pci_enable_device(dev_diva))
+ return(0);
+ cs->subtyp = DIVA_PCI;
+ cs->irq = dev_diva->irq;
+ cs->hw.diva.cfg_reg = pci_resource_start(dev_diva, 2);
+ } else if ((dev_diva_u = pci_find_device(PCI_VENDOR_ID_EICON,
+ PCI_DEVICE_ID_EICON_DIVA20_U, dev_diva_u))) {
+ if (pci_enable_device(dev_diva_u))
+ return(0);
+ cs->subtyp = DIVA_PCI;
+ cs->irq = dev_diva_u->irq;
+ cs->hw.diva.cfg_reg = pci_resource_start(dev_diva_u, 2);
+ } else if ((dev_diva201 = pci_find_device(PCI_VENDOR_ID_EICON,
+ PCI_DEVICE_ID_EICON_DIVA201, dev_diva201))) {
+ if (pci_enable_device(dev_diva201))
+ return(0);
+ cs->subtyp = DIVA_IPAC_PCI;
+ cs->irq = dev_diva201->irq;
+ cs->hw.diva.pci_cfg =
+ (ulong) ioremap(pci_resource_start(dev_diva201, 0), 4096);
+ cs->hw.diva.cfg_reg =
+ (ulong) ioremap(pci_resource_start(dev_diva201, 1), 4096);
+ } else if ((dev_diva202 = pci_find_device(PCI_VENDOR_ID_EICON,
+ PCI_DEVICE_ID_EICON_DIVA202, dev_diva202))) {
+ if (pci_enable_device(dev_diva202))
+ return(0);
+ cs->subtyp = DIVA_IPACX_PCI;
+ cs->irq = dev_diva202->irq;
+ cs->hw.diva.pci_cfg =
+ (ulong) ioremap(pci_resource_start(dev_diva202, 0), 4096);
+ cs->hw.diva.cfg_reg =
+ (ulong) ioremap(pci_resource_start(dev_diva202, 1), 4096);
+ } else {
+ return (-1); /* card not found; continue search */
+ }
+
+ if (!cs->irq) {
+ printk(KERN_WARNING "Diva: No IRQ for PCI card found\n");
+ iounmap_diva(cs);
+ return(0);
+ }
+
+ if (!cs->hw.diva.cfg_reg) {
+ printk(KERN_WARNING "Diva: No IO-Adr for PCI card found\n");
+ iounmap_diva(cs);
+ return(0);
+ }
+ cs->irq_flags |= IRQF_SHARED;
+
+ if ((cs->subtyp == DIVA_IPAC_PCI) ||
+ (cs->subtyp == DIVA_IPACX_PCI) ) {
+ cs->hw.diva.ctrl = 0;
+ cs->hw.diva.isac = 0;
+ cs->hw.diva.hscx = 0;
+ cs->hw.diva.isac_adr = 0;
+ cs->hw.diva.hscx_adr = 0;
+ test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+ } else {
+ cs->hw.diva.ctrl = cs->hw.diva.cfg_reg + DIVA_PCI_CTRL;
+ cs->hw.diva.isac = cs->hw.diva.cfg_reg + DIVA_PCI_ISAC_DATA;
+ cs->hw.diva.hscx = cs->hw.diva.cfg_reg + DIVA_HSCX_DATA;
+ cs->hw.diva.isac_adr = cs->hw.diva.cfg_reg + DIVA_PCI_ISAC_ADR;
+ cs->hw.diva.hscx_adr = cs->hw.diva.cfg_reg + DIVA_HSCX_ADR;
+ }
+
+ return (1); /* card found */
+}
+
+#else /* if !CONFIG_PCI_LEGACY */
+
+static int __devinit setup_diva_pci(struct IsdnCard *card)
+{
+ return (-1); /* card not found; continue search */
+}
+
+#endif /* CONFIG_PCI_LEGACY */
+
+int __devinit
+setup_diva(struct IsdnCard *card)
+{
+ int rc, have_card = 0;
+ struct IsdnCardState *cs = card->cs;
+ char tmp[64];
+
+ strcpy(tmp, Diva_revision);
+ printk(KERN_INFO "HiSax: Eicon.Diehl Diva driver Rev. %s\n", HiSax_getrev(tmp));
+ if (cs->typ != ISDN_CTYPE_DIEHLDIVA)
+ return(0);
+ cs->hw.diva.status = 0;
+
+ rc = setup_diva_isa(card);
+ if (!rc)
+ return rc;
+ if (rc > 0) {
+ have_card = 1;
+ goto ready;
+ }
+
+ rc = setup_diva_isapnp(card);
+ if (!rc)
+ return rc;
+ if (rc > 0) {
+ have_card = 1;
+ goto ready;
+ }
+
+ rc = setup_diva_pci(card);
+ if (!rc)
+ return rc;
+ if (rc > 0)
+ have_card = 1;
+
+ready:
+ if (!have_card) {
+ printk(KERN_WARNING "Diva: No ISA, ISAPNP or PCI card found\n");
+ return(0);
+ }
+
+ return setup_diva_common(card->cs);
+}
diff --git a/drivers/isdn/hisax/elsa.c b/drivers/isdn/hisax/elsa.c
new file mode 100644
index 0000000..aa29d1c
--- /dev/null
+++ b/drivers/isdn/hisax/elsa.c
@@ -0,0 +1,1250 @@
+/* $Id: elsa.c,v 2.32.2.4 2004/01/24 20:47:21 keil Exp $
+ *
+ * low level stuff for Elsa isdn cards
+ *
+ * Author Karsten Keil
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ * Thanks to Elsa GmbH for documents and information
+ *
+ * Klaus Lichtenwalder (Klaus.Lichtenwalder@WebForum.DE)
+ * for ELSA PCMCIA support
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "arcofi.h"
+#include "isac.h"
+#include "ipac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/isapnp.h>
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+
+static const char *Elsa_revision = "$Revision: 2.32.2.4 $";
+static const char *Elsa_Types[] =
+{"None", "PC", "PCC-8", "PCC-16", "PCF", "PCF-Pro",
+ "PCMCIA", "QS 1000", "QS 3000", "Microlink PCI", "QS 3000 PCI",
+ "PCMCIA-IPAC" };
+
+static const char *ITACVer[] =
+{"?0?", "?1?", "?2?", "?3?", "?4?", "V2.2",
+ "B1", "A1"};
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define ELSA_ISAC 0
+#define ELSA_ISAC_PCM 1
+#define ELSA_ITAC 1
+#define ELSA_HSCX 2
+#define ELSA_ALE 3
+#define ELSA_ALE_PCM 4
+#define ELSA_CONTROL 4
+#define ELSA_CONFIG 5
+#define ELSA_START_TIMER 6
+#define ELSA_TRIG_IRQ 7
+
+#define ELSA_PC 1
+#define ELSA_PCC8 2
+#define ELSA_PCC16 3
+#define ELSA_PCF 4
+#define ELSA_PCFPRO 5
+#define ELSA_PCMCIA 6
+#define ELSA_QS1000 7
+#define ELSA_QS3000 8
+#define ELSA_QS1000PCI 9
+#define ELSA_QS3000PCI 10
+#define ELSA_PCMCIA_IPAC 11
+
+/* PCI stuff */
+#define ELSA_PCI_IRQ_MASK 0x04
+
+/* ITAC Registeradressen (only Microlink PC) */
+#define ITAC_SYS 0x34
+#define ITAC_ISEN 0x48
+#define ITAC_RFIE 0x4A
+#define ITAC_XFIE 0x4C
+#define ITAC_SCIE 0x4E
+#define ITAC_STIE 0x46
+
+/*** ***
+ *** Makros als Befehle fuer die Kartenregister ***
+ *** (mehrere Befehle werden durch Bit-Oderung kombiniert) ***
+ *** ***/
+
+/* Config-Register (Read) */
+#define ELIRQF_TIMER_RUN 0x02 /* Bit 1 des Config-Reg */
+#define ELIRQF_TIMER_RUN_PCC8 0x01 /* Bit 0 des Config-Reg bei PCC */
+#define ELSA_IRQ_IDX 0x38 /* Bit 3,4,5 des Config-Reg */
+#define ELSA_IRQ_IDX_PCC8 0x30 /* Bit 4,5 des Config-Reg */
+#define ELSA_IRQ_IDX_PC 0x0c /* Bit 2,3 des Config-Reg */
+
+/* Control-Register (Write) */
+#define ELSA_LINE_LED 0x02 /* Bit 1 Gelbe LED */
+#define ELSA_STAT_LED 0x08 /* Bit 3 Gruene LED */
+#define ELSA_ISDN_RESET 0x20 /* Bit 5 Reset-Leitung */
+#define ELSA_ENA_TIMER_INT 0x80 /* Bit 7 Freigabe Timer Interrupt */
+
+/* ALE-Register (Read) */
+#define ELSA_HW_RELEASE 0x07 /* Bit 0-2 Hardwarerkennung */
+#define ELSA_S0_POWER_BAD 0x08 /* Bit 3 S0-Bus Spannung fehlt */
+
+/* Status Flags */
+#define ELIRQF_TIMER_AKTIV 1
+#define ELSA_BAD_PWR 2
+#define ELSA_ASSIGN 4
+
+#define RS_ISR_PASS_LIMIT 256
+#define FLG_MODEM_ACTIVE 1
+/* IPAC AUX */
+#define ELSA_IPAC_LINE_LED 0x40 /* Bit 6 Gelbe LED */
+#define ELSA_IPAC_STAT_LED 0x80 /* Bit 7 Gruene LED */
+
+#if ARCOFI_USE
+static struct arcofi_msg ARCOFI_XOP_F =
+ {NULL,0,2,{0xa1,0x3f,0,0,0,0,0,0,0,0}}; /* Normal OP */
+static struct arcofi_msg ARCOFI_XOP_1 =
+ {&ARCOFI_XOP_F,0,2,{0xa1,0x31,0,0,0,0,0,0,0,0}}; /* PWR UP */
+static struct arcofi_msg ARCOFI_SOP_F =
+ {&ARCOFI_XOP_1,0,10,{0xa1,0x1f,0x00,0x50,0x10,0x00,0x00,0x80,0x02,0x12}};
+static struct arcofi_msg ARCOFI_COP_9 =
+ {&ARCOFI_SOP_F,0,10,{0xa1,0x29,0x80,0xcb,0xe9,0x88,0x00,0xc8,0xd8,0x80}}; /* RX */
+static struct arcofi_msg ARCOFI_COP_8 =
+ {&ARCOFI_COP_9,0,10,{0xa1,0x28,0x49,0x31,0x8,0x13,0x6e,0x88,0x2a,0x61}}; /* TX */
+static struct arcofi_msg ARCOFI_COP_7 =
+ {&ARCOFI_COP_8,0,4,{0xa1,0x27,0x80,0x80,0,0,0,0,0,0}}; /* GZ */
+static struct arcofi_msg ARCOFI_COP_6 =
+ {&ARCOFI_COP_7,0,6,{0xa1,0x26,0,0,0x82,0x7c,0,0,0,0}}; /* GRL GRH */
+static struct arcofi_msg ARCOFI_COP_5 =
+ {&ARCOFI_COP_6,0,4,{0xa1,0x25,0xbb,0x4a,0,0,0,0,0,0}}; /* GTX */
+static struct arcofi_msg ARCOFI_VERSION =
+ {NULL,1,2,{0xa0,0,0,0,0,0,0,0,0,0}};
+static struct arcofi_msg ARCOFI_XOP_0 =
+ {NULL,0,2,{0xa1,0x30,0,0,0,0,0,0,0,0}}; /* PWR Down */
+
+static void set_arcofi(struct IsdnCardState *cs, int bc);
+
+#include "elsa_ser.c"
+#endif /* ARCOFI_USE */
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+ register u_char ret;
+
+ byteout(ale, off);
+ ret = bytein(adr);
+ return (ret);
+}
+
+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(struct IsdnCardState *cs, u_char offset)
+{
+ return (readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ readfifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ writefifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0, data, size);
+}
+
+static u_char
+ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset)
+{
+ return (readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset+0x80));
+}
+
+static void
+WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, offset|0x80, value);
+}
+
+static void
+ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+ readfifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0x80, data, size);
+}
+
+static void
+WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+ writefifo(cs->hw.elsa.ale, cs->hw.elsa.isac, 0x80, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+ return (readreg(cs->hw.elsa.ale,
+ cs->hw.elsa.hscx, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+ writereg(cs->hw.elsa.ale,
+ cs->hw.elsa.hscx, offset + (hscx ? 0x40 : 0), value);
+}
+
+static inline u_char
+readitac(struct IsdnCardState *cs, u_char off)
+{
+ register u_char ret;
+
+ byteout(cs->hw.elsa.ale, off);
+ ret = bytein(cs->hw.elsa.itac);
+ return (ret);
+}
+
+static inline void
+writeitac(struct IsdnCardState *cs, u_char off, u_char data)
+{
+ byteout(cs->hw.elsa.ale, off);
+ byteout(cs->hw.elsa.itac, data);
+}
+
+static inline int
+TimerRun(struct IsdnCardState *cs)
+{
+ register u_char v;
+
+ v = bytein(cs->hw.elsa.cfg);
+ if ((cs->subtyp == ELSA_QS1000) || (cs->subtyp == ELSA_QS3000))
+ return (0 == (v & ELIRQF_TIMER_RUN));
+ else if (cs->subtyp == ELSA_PCC8)
+ return (v & ELIRQF_TIMER_RUN_PCC8);
+ return (v & ELIRQF_TIMER_RUN);
+}
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.elsa.ale, \
+ cs->hw.elsa.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.elsa.ale, \
+ cs->hw.elsa.hscx, reg + (nr ? 0x40 : 0), data)
+
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.elsa.ale, \
+ cs->hw.elsa.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.elsa.ale, \
+ cs->hw.elsa.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+elsa_interrupt(int intno, void *dev_id)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_long flags;
+ u_char val;
+ int icnt=5;
+
+ if ((cs->typ == ISDN_CTYPE_ELSA_PCMCIA) && (*cs->busy_flag == 1)) {
+ /* The card tends to generate interrupts while being removed
+ causing us to just crash the kernel. bad. */
+ printk(KERN_WARNING "Elsa: card not available!\n");
+ return IRQ_NONE;
+ }
+ spin_lock_irqsave(&cs->lock, flags);
+#if ARCOFI_USE
+ if (cs->hw.elsa.MFlag) {
+ val = serial_inp(cs, UART_IIR);
+ if (!(val & UART_IIR_NO_INT)) {
+ debugl1(cs,"IIR %02x", val);
+ rs_interrupt_elsa(cs);
+ }
+ }
+#endif
+ val = readreg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_ISTA + 0x40);
+ Start_HSCX:
+ if (val) {
+ hscx_int_main(cs, val);
+ }
+ val = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_ISTA);
+ Start_ISAC:
+ if (val) {
+ isac_interrupt(cs, val);
+ }
+ val = readreg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_ISTA + 0x40);
+ if (val && icnt) {
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "HSCX IntStat after IntRoutine");
+ icnt--;
+ goto Start_HSCX;
+ }
+ val = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_ISTA);
+ if (val && icnt) {
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ISAC IntStat after IntRoutine");
+ icnt--;
+ goto Start_ISAC;
+ }
+ if (!icnt)
+ printk(KERN_WARNING"ELSA IRQ LOOP\n");
+ writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK, 0xFF);
+ writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK + 0x40, 0xFF);
+ writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_MASK, 0xFF);
+ if (cs->hw.elsa.status & ELIRQF_TIMER_AKTIV) {
+ if (!TimerRun(cs)) {
+ /* Timer Restart */
+ byteout(cs->hw.elsa.timer, 0);
+ cs->hw.elsa.counter++;
+ }
+ }
+#if ARCOFI_USE
+ if (cs->hw.elsa.MFlag) {
+ val = serial_inp(cs, UART_MCR);
+ val ^= 0x8;
+ serial_outp(cs, UART_MCR, val);
+ val = serial_inp(cs, UART_MCR);
+ val ^= 0x8;
+ serial_outp(cs, UART_MCR, val);
+ }
+#endif
+ if (cs->hw.elsa.trig)
+ byteout(cs->hw.elsa.trig, 0x00);
+ writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK, 0x0);
+ writereg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_MASK + 0x40, 0x0);
+ writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_MASK, 0x0);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t
+elsa_interrupt_ipac(int intno, void *dev_id)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_long flags;
+ u_char ista,val;
+ int icnt=5;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ if (cs->subtyp == ELSA_QS1000PCI || cs->subtyp == ELSA_QS3000PCI) {
+ val = bytein(cs->hw.elsa.cfg + 0x4c); /* PCI IRQ */
+ if (!(val & ELSA_PCI_IRQ_MASK)) {
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_NONE;
+ }
+ }
+#if ARCOFI_USE
+ if (cs->hw.elsa.MFlag) {
+ val = serial_inp(cs, UART_IIR);
+ if (!(val & UART_IIR_NO_INT)) {
+ debugl1(cs,"IIR %02x", val);
+ rs_interrupt_elsa(cs);
+ }
+ }
+#endif
+ ista = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ISTA);
+Start_IPAC:
+ if (cs->debug & L1_DEB_IPAC)
+ debugl1(cs, "IPAC ISTA %02X", ista);
+ if (ista & 0x0f) {
+ val = readreg(cs->hw.elsa.ale, cs->hw.elsa.hscx, HSCX_ISTA + 0x40);
+ if (ista & 0x01)
+ val |= 0x01;
+ if (ista & 0x04)
+ val |= 0x02;
+ if (ista & 0x08)
+ val |= 0x04;
+ if (val)
+ hscx_int_main(cs, val);
+ }
+ if (ista & 0x20) {
+ val = 0xfe & readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, ISAC_ISTA + 0x80);
+ if (val) {
+ isac_interrupt(cs, val);
+ }
+ }
+ if (ista & 0x10) {
+ val = 0x01;
+ isac_interrupt(cs, val);
+ }
+ ista = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ISTA);
+ if ((ista & 0x3f) && icnt) {
+ icnt--;
+ goto Start_IPAC;
+ }
+ if (!icnt)
+ printk(KERN_WARNING "ELSA IRQ LOOP\n");
+ writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xFF);
+ writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xC0);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static void
+release_io_elsa(struct IsdnCardState *cs)
+{
+ int bytecnt = 8;
+
+ del_timer(&cs->hw.elsa.tl);
+#if ARCOFI_USE
+ clear_arcofi(cs);
+#endif
+ if (cs->hw.elsa.ctrl)
+ byteout(cs->hw.elsa.ctrl, 0); /* LEDs Out */
+ if (cs->subtyp == ELSA_QS1000PCI) {
+ byteout(cs->hw.elsa.cfg + 0x4c, 0x01); /* disable IRQ */
+ writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff);
+ bytecnt = 2;
+ release_region(cs->hw.elsa.cfg, 0x80);
+ }
+ if (cs->subtyp == ELSA_QS3000PCI) {
+ byteout(cs->hw.elsa.cfg + 0x4c, 0x03); /* disable ELSA PCI IRQ */
+ writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff);
+ release_region(cs->hw.elsa.cfg, 0x80);
+ }
+ if (cs->subtyp == ELSA_PCMCIA_IPAC) {
+ writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff);
+ }
+ if ((cs->subtyp == ELSA_PCFPRO) ||
+ (cs->subtyp == ELSA_QS3000) ||
+ (cs->subtyp == ELSA_PCF) ||
+ (cs->subtyp == ELSA_QS3000PCI)) {
+ bytecnt = 16;
+#if ARCOFI_USE
+ release_modem(cs);
+#endif
+ }
+ if (cs->hw.elsa.base)
+ release_region(cs->hw.elsa.base, bytecnt);
+}
+
+static void
+reset_elsa(struct IsdnCardState *cs)
+{
+ if (cs->hw.elsa.timer) {
+ /* Wait 1 Timer */
+ byteout(cs->hw.elsa.timer, 0);
+ while (TimerRun(cs));
+ cs->hw.elsa.ctrl_reg |= 0x50;
+ cs->hw.elsa.ctrl_reg &= ~ELSA_ISDN_RESET; /* Reset On */
+ byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
+ /* Wait 1 Timer */
+ byteout(cs->hw.elsa.timer, 0);
+ while (TimerRun(cs));
+ cs->hw.elsa.ctrl_reg |= ELSA_ISDN_RESET; /* Reset Off */
+ byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
+ /* Wait 1 Timer */
+ byteout(cs->hw.elsa.timer, 0);
+ while (TimerRun(cs));
+ if (cs->hw.elsa.trig)
+ byteout(cs->hw.elsa.trig, 0xff);
+ }
+ if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI) || (cs->subtyp == ELSA_PCMCIA_IPAC)) {
+ writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x20);
+ mdelay(10);
+ writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_POTA2, 0x00);
+ writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_MASK, 0xc0);
+ mdelay(10);
+ if (cs->subtyp != ELSA_PCMCIA_IPAC) {
+ writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ACFG, 0x0);
+ writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_AOE, 0x3c);
+ } else {
+ writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_PCFG, 0x10);
+ writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ACFG, 0x4);
+ writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_AOE, 0xf8);
+ }
+ writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, 0xff);
+ if (cs->subtyp == ELSA_QS1000PCI)
+ byteout(cs->hw.elsa.cfg + 0x4c, 0x41); /* enable ELSA PCI IRQ */
+ else if (cs->subtyp == ELSA_QS3000PCI)
+ byteout(cs->hw.elsa.cfg + 0x4c, 0x43); /* enable ELSA PCI IRQ */
+ }
+}
+
+#if ARCOFI_USE
+
+static void
+set_arcofi(struct IsdnCardState *cs, int bc) {
+ cs->dc.isac.arcofi_bc = bc;
+ arcofi_fsm(cs, ARCOFI_START, &ARCOFI_COP_5);
+ interruptible_sleep_on(&cs->dc.isac.arcofi_wait);
+}
+
+static int
+check_arcofi(struct IsdnCardState *cs)
+{
+ int arcofi_present = 0;
+ char tmp[40];
+ char *t;
+ u_char *p;
+
+ if (!cs->dc.isac.mon_tx)
+ if (!(cs->dc.isac.mon_tx=kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "ISAC MON TX out of buffers!");
+ return(0);
+ }
+ cs->dc.isac.arcofi_bc = 0;
+ arcofi_fsm(cs, ARCOFI_START, &ARCOFI_VERSION);
+ interruptible_sleep_on(&cs->dc.isac.arcofi_wait);
+ if (!test_and_clear_bit(FLG_ARCOFI_ERROR, &cs->HW_Flags)) {
+ debugl1(cs, "Arcofi response received %d bytes", cs->dc.isac.mon_rxp);
+ p = cs->dc.isac.mon_rx;
+ t = tmp;
+ t += sprintf(tmp, "Arcofi data");
+ QuickHex(t, p, cs->dc.isac.mon_rxp);
+ debugl1(cs, tmp);
+ if ((cs->dc.isac.mon_rxp == 2) && (cs->dc.isac.mon_rx[0] == 0xa0)) {
+ switch(cs->dc.isac.mon_rx[1]) {
+ case 0x80:
+ debugl1(cs, "Arcofi 2160 detected");
+ arcofi_present = 1;
+ break;
+ case 0x82:
+ debugl1(cs, "Arcofi 2165 detected");
+ arcofi_present = 2;
+ break;
+ case 0x84:
+ debugl1(cs, "Arcofi 2163 detected");
+ arcofi_present = 3;
+ break;
+ default:
+ debugl1(cs, "unknown Arcofi response");
+ break;
+ }
+ } else
+ debugl1(cs, "undefined Monitor response");
+ cs->dc.isac.mon_rxp = 0;
+ } else if (cs->dc.isac.mon_tx) {
+ debugl1(cs, "Arcofi not detected");
+ }
+ if (arcofi_present) {
+ if (cs->subtyp==ELSA_QS1000) {
+ cs->subtyp = ELSA_QS3000;
+ printk(KERN_INFO
+ "Elsa: %s detected modem at 0x%lx\n",
+ Elsa_Types[cs->subtyp],
+ cs->hw.elsa.base+8);
+ release_region(cs->hw.elsa.base, 8);
+ if (!request_region(cs->hw.elsa.base, 16, "elsa isdn modem")) {
+ printk(KERN_WARNING
+ "HiSax: %s config port %lx-%lx already in use\n",
+ Elsa_Types[cs->subtyp],
+ cs->hw.elsa.base + 8,
+ cs->hw.elsa.base + 16);
+ }
+ } else if (cs->subtyp==ELSA_PCC16) {
+ cs->subtyp = ELSA_PCF;
+ printk(KERN_INFO
+ "Elsa: %s detected modem at 0x%lx\n",
+ Elsa_Types[cs->subtyp],
+ cs->hw.elsa.base+8);
+ release_region(cs->hw.elsa.base, 8);
+ if (!request_region(cs->hw.elsa.base, 16, "elsa isdn modem")) {
+ printk(KERN_WARNING
+ "HiSax: %s config port %lx-%lx already in use\n",
+ Elsa_Types[cs->subtyp],
+ cs->hw.elsa.base + 8,
+ cs->hw.elsa.base + 16);
+ }
+ } else
+ printk(KERN_INFO
+ "Elsa: %s detected modem at 0x%lx\n",
+ Elsa_Types[cs->subtyp],
+ cs->hw.elsa.base+8);
+ arcofi_fsm(cs, ARCOFI_START, &ARCOFI_XOP_0);
+ interruptible_sleep_on(&cs->dc.isac.arcofi_wait);
+ return(1);
+ }
+ return(0);
+}
+#endif /* ARCOFI_USE */
+
+static void
+elsa_led_handler(struct IsdnCardState *cs)
+{
+ int blink = 0;
+
+ if (cs->subtyp == ELSA_PCMCIA || cs->subtyp == ELSA_PCMCIA_IPAC)
+ return;
+ del_timer(&cs->hw.elsa.tl);
+ if (cs->hw.elsa.status & ELSA_ASSIGN)
+ cs->hw.elsa.ctrl_reg |= ELSA_STAT_LED;
+ else if (cs->hw.elsa.status & ELSA_BAD_PWR)
+ cs->hw.elsa.ctrl_reg &= ~ELSA_STAT_LED;
+ else {
+ cs->hw.elsa.ctrl_reg ^= ELSA_STAT_LED;
+ blink = 250;
+ }
+ if (cs->hw.elsa.status & 0xf000)
+ cs->hw.elsa.ctrl_reg |= ELSA_LINE_LED;
+ else if (cs->hw.elsa.status & 0x0f00) {
+ cs->hw.elsa.ctrl_reg ^= ELSA_LINE_LED;
+ blink = 500;
+ } else
+ cs->hw.elsa.ctrl_reg &= ~ELSA_LINE_LED;
+
+ if ((cs->subtyp == ELSA_QS1000PCI) ||
+ (cs->subtyp == ELSA_QS3000PCI)) {
+ u_char led = 0xff;
+ if (cs->hw.elsa.ctrl_reg & ELSA_LINE_LED)
+ led ^= ELSA_IPAC_LINE_LED;
+ if (cs->hw.elsa.ctrl_reg & ELSA_STAT_LED)
+ led ^= ELSA_IPAC_STAT_LED;
+ writereg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ATX, led);
+ } else
+ byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
+ if (blink) {
+ init_timer(&cs->hw.elsa.tl);
+ cs->hw.elsa.tl.expires = jiffies + ((blink * HZ) / 1000);
+ add_timer(&cs->hw.elsa.tl);
+ }
+}
+
+static int
+Elsa_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ int ret = 0;
+ u_long flags;
+
+ switch (mt) {
+ case CARD_RESET:
+ spin_lock_irqsave(&cs->lock, flags);
+ reset_elsa(cs);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return(0);
+ case CARD_RELEASE:
+ release_io_elsa(cs);
+ return(0);
+ case CARD_INIT:
+ spin_lock_irqsave(&cs->lock, flags);
+ cs->debug |= L1_DEB_IPAC;
+ reset_elsa(cs);
+ inithscxisac(cs, 1);
+ if ((cs->subtyp == ELSA_QS1000) ||
+ (cs->subtyp == ELSA_QS3000))
+ {
+ byteout(cs->hw.elsa.timer, 0);
+ }
+ if (cs->hw.elsa.trig)
+ byteout(cs->hw.elsa.trig, 0xff);
+ inithscxisac(cs, 2);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return(0);
+ case CARD_TEST:
+ if ((cs->subtyp == ELSA_PCMCIA) ||
+ (cs->subtyp == ELSA_PCMCIA_IPAC) ||
+ (cs->subtyp == ELSA_QS1000PCI)) {
+ return(0);
+ } else if (cs->subtyp == ELSA_QS3000PCI) {
+ ret = 0;
+ } else {
+ spin_lock_irqsave(&cs->lock, flags);
+ cs->hw.elsa.counter = 0;
+ cs->hw.elsa.ctrl_reg |= ELSA_ENA_TIMER_INT;
+ cs->hw.elsa.status |= ELIRQF_TIMER_AKTIV;
+ byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
+ byteout(cs->hw.elsa.timer, 0);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ msleep(110);
+ spin_lock_irqsave(&cs->lock, flags);
+ cs->hw.elsa.ctrl_reg &= ~ELSA_ENA_TIMER_INT;
+ byteout(cs->hw.elsa.ctrl, cs->hw.elsa.ctrl_reg);
+ cs->hw.elsa.status &= ~ELIRQF_TIMER_AKTIV;
+ spin_unlock_irqrestore(&cs->lock, flags);
+ printk(KERN_INFO "Elsa: %d timer tics in 110 msek\n",
+ cs->hw.elsa.counter);
+ if ((cs->hw.elsa.counter > 10) &&
+ (cs->hw.elsa.counter < 16)) {
+ printk(KERN_INFO "Elsa: timer and irq OK\n");
+ ret = 0;
+ } else {
+ printk(KERN_WARNING
+ "Elsa: timer tic problem (%d/12) maybe an IRQ(%d) conflict\n",
+ cs->hw.elsa.counter, cs->irq);
+ ret = 1;
+ }
+ }
+#if ARCOFI_USE
+ if (check_arcofi(cs)) {
+ init_modem(cs);
+ }
+#endif
+ elsa_led_handler(cs);
+ return(ret);
+ case (MDL_REMOVE | REQUEST):
+ cs->hw.elsa.status &= 0;
+ break;
+ case (MDL_ASSIGN | REQUEST):
+ cs->hw.elsa.status |= ELSA_ASSIGN;
+ break;
+ case MDL_INFO_SETUP:
+ if ((long) arg)
+ cs->hw.elsa.status |= 0x0200;
+ else
+ cs->hw.elsa.status |= 0x0100;
+ break;
+ case MDL_INFO_CONN:
+ if ((long) arg)
+ cs->hw.elsa.status |= 0x2000;
+ else
+ cs->hw.elsa.status |= 0x1000;
+ break;
+ case MDL_INFO_REL:
+ if ((long) arg) {
+ cs->hw.elsa.status &= ~0x2000;
+ cs->hw.elsa.status &= ~0x0200;
+ } else {
+ cs->hw.elsa.status &= ~0x1000;
+ cs->hw.elsa.status &= ~0x0100;
+ }
+ break;
+#if ARCOFI_USE
+ case CARD_AUX_IND:
+ if (cs->hw.elsa.MFlag) {
+ int len;
+ u_char *msg;
+
+ if (!arg)
+ return(0);
+ msg = arg;
+ len = *msg;
+ msg++;
+ modem_write_cmd(cs, msg, len);
+ }
+ break;
+#endif
+ }
+ if (cs->typ == ISDN_CTYPE_ELSA) {
+ int pwr = bytein(cs->hw.elsa.ale);
+ if (pwr & 0x08)
+ cs->hw.elsa.status |= ELSA_BAD_PWR;
+ else
+ cs->hw.elsa.status &= ~ELSA_BAD_PWR;
+ }
+ elsa_led_handler(cs);
+ return(ret);
+}
+
+static unsigned char
+probe_elsa_adr(unsigned int adr, int typ)
+{
+ int i, in1, in2, p16_1 = 0, p16_2 = 0, p8_1 = 0, p8_2 = 0, pc_1 = 0,
+ pc_2 = 0, pfp_1 = 0, pfp_2 = 0;
+
+ /* In case of the elsa pcmcia card, this region is in use,
+ reserved for us by the card manager. So we do not check it
+ here, it would fail. */
+ if (typ != ISDN_CTYPE_ELSA_PCMCIA) {
+ if (request_region(adr, 8, "elsa card")) {
+ release_region(adr, 8);
+ } else {
+ printk(KERN_WARNING
+ "Elsa: Probing Port 0x%x: already in use\n", adr);
+ return (0);
+ }
+ }
+ for (i = 0; i < 16; i++) {
+ in1 = inb(adr + ELSA_CONFIG); /* 'toggelt' bei */
+ in2 = inb(adr + ELSA_CONFIG); /* jedem Zugriff */
+ p16_1 += 0x04 & in1;
+ p16_2 += 0x04 & in2;
+ p8_1 += 0x02 & in1;
+ p8_2 += 0x02 & in2;
+ pc_1 += 0x01 & in1;
+ pc_2 += 0x01 & in2;
+ pfp_1 += 0x40 & in1;
+ pfp_2 += 0x40 & in2;
+ }
+ printk(KERN_INFO "Elsa: Probing IO 0x%x", adr);
+ if (65 == ++p16_1 * ++p16_2) {
+ printk(" PCC-16/PCF found\n");
+ return (ELSA_PCC16);
+ } else if (1025 == ++pfp_1 * ++pfp_2) {
+ printk(" PCF-Pro found\n");
+ return (ELSA_PCFPRO);
+ } else if (33 == ++p8_1 * ++p8_2) {
+ printk(" PCC8 found\n");
+ return (ELSA_PCC8);
+ } else if (17 == ++pc_1 * ++pc_2) {
+ printk(" PC found\n");
+ return (ELSA_PC);
+ } else {
+ printk(" failed\n");
+ return (0);
+ }
+}
+
+static unsigned int
+probe_elsa(struct IsdnCardState *cs)
+{
+ int i;
+ unsigned int CARD_portlist[] =
+ {0x160, 0x170, 0x260, 0x360, 0};
+
+ for (i = 0; CARD_portlist[i]; i++) {
+ if ((cs->subtyp = probe_elsa_adr(CARD_portlist[i], cs->typ)))
+ break;
+ }
+ return (CARD_portlist[i]);
+}
+
+static int __devinit
+setup_elsa_isa(struct IsdnCard *card)
+{
+ struct IsdnCardState *cs = card->cs;
+ u_char val;
+
+ cs->hw.elsa.base = card->para[0];
+ printk(KERN_INFO "Elsa: Microlink IO probing\n");
+ if (cs->hw.elsa.base) {
+ if (!(cs->subtyp = probe_elsa_adr(cs->hw.elsa.base,
+ cs->typ))) {
+ printk(KERN_WARNING
+ "Elsa: no Elsa Microlink at %#lx\n",
+ cs->hw.elsa.base);
+ return (0);
+ }
+ } else
+ cs->hw.elsa.base = probe_elsa(cs);
+
+ if (!cs->hw.elsa.base) {
+ printk(KERN_WARNING
+ "No Elsa Microlink found\n");
+ return (0);
+ }
+
+ cs->hw.elsa.cfg = cs->hw.elsa.base + ELSA_CONFIG;
+ cs->hw.elsa.ctrl = cs->hw.elsa.base + ELSA_CONTROL;
+ cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE;
+ cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC;
+ cs->hw.elsa.itac = cs->hw.elsa.base + ELSA_ITAC;
+ cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX;
+ cs->hw.elsa.trig = cs->hw.elsa.base + ELSA_TRIG_IRQ;
+ cs->hw.elsa.timer = cs->hw.elsa.base + ELSA_START_TIMER;
+ val = bytein(cs->hw.elsa.cfg);
+ if (cs->subtyp == ELSA_PC) {
+ const u_char CARD_IrqTab[8] =
+ {7, 3, 5, 9, 0, 0, 0, 0};
+ cs->irq = CARD_IrqTab[(val & ELSA_IRQ_IDX_PC) >> 2];
+ } else if (cs->subtyp == ELSA_PCC8) {
+ const u_char CARD_IrqTab[8] =
+ {7, 3, 5, 9, 0, 0, 0, 0};
+ cs->irq = CARD_IrqTab[(val & ELSA_IRQ_IDX_PCC8) >> 4];
+ } else {
+ const u_char CARD_IrqTab[8] =
+ {15, 10, 15, 3, 11, 5, 11, 9};
+ cs->irq = CARD_IrqTab[(val & ELSA_IRQ_IDX) >> 3];
+ }
+ val = bytein(cs->hw.elsa.ale) & ELSA_HW_RELEASE;
+ if (val < 3)
+ val |= 8;
+ val += 'A' - 3;
+ if (val == 'B' || val == 'C')
+ val ^= 1;
+ if ((cs->subtyp == ELSA_PCFPRO) && (val == 'G'))
+ val = 'C';
+ printk(KERN_INFO
+ "Elsa: %s found at %#lx Rev.:%c IRQ %d\n",
+ Elsa_Types[cs->subtyp],
+ cs->hw.elsa.base,
+ val, cs->irq);
+ val = bytein(cs->hw.elsa.ale) & ELSA_S0_POWER_BAD;
+ if (val) {
+ printk(KERN_WARNING
+ "Elsa: Microlink S0 bus power bad\n");
+ cs->hw.elsa.status |= ELSA_BAD_PWR;
+ }
+
+ return (1);
+}
+
+#ifdef __ISAPNP__
+static struct isapnp_device_id elsa_ids[] __devinitdata = {
+ { ISAPNP_VENDOR('E', 'L', 'S'), ISAPNP_FUNCTION(0x0133),
+ ISAPNP_VENDOR('E', 'L', 'S'), ISAPNP_FUNCTION(0x0133),
+ (unsigned long) "Elsa QS1000" },
+ { ISAPNP_VENDOR('E', 'L', 'S'), ISAPNP_FUNCTION(0x0134),
+ ISAPNP_VENDOR('E', 'L', 'S'), ISAPNP_FUNCTION(0x0134),
+ (unsigned long) "Elsa QS3000" },
+ { 0, }
+};
+
+static struct isapnp_device_id *ipid __devinitdata = &elsa_ids[0];
+static struct pnp_card *pnp_c __devinitdata = NULL;
+#endif /* __ISAPNP__ */
+
+static int __devinit
+setup_elsa_isapnp(struct IsdnCard *card)
+{
+ struct IsdnCardState *cs = card->cs;
+
+#ifdef __ISAPNP__
+ if (!card->para[1] && isapnp_present()) {
+ struct pnp_dev *pnp_d;
+ while(ipid->card_vendor) {
+ if ((pnp_c = pnp_find_card(ipid->card_vendor,
+ ipid->card_device, pnp_c))) {
+ pnp_d = NULL;
+ if ((pnp_d = pnp_find_dev(pnp_c,
+ ipid->vendor, ipid->function, pnp_d))) {
+ int err;
+
+ printk(KERN_INFO "HiSax: %s detected\n",
+ (char *)ipid->driver_data);
+ pnp_disable_dev(pnp_d);
+ err = pnp_activate_dev(pnp_d);
+ if (err<0) {
+ printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
+ __func__, err);
+ return(0);
+ }
+ card->para[1] = pnp_port_start(pnp_d, 0);
+ card->para[0] = pnp_irq(pnp_d, 0);
+
+ if (!card->para[0] || !card->para[1]) {
+ printk(KERN_ERR "Elsa PnP:some resources are missing %ld/%lx\n",
+ card->para[0], card->para[1]);
+ pnp_disable_dev(pnp_d);
+ return(0);
+ }
+ if (ipid->function == ISAPNP_FUNCTION(0x133))
+ cs->subtyp = ELSA_QS1000;
+ else
+ cs->subtyp = ELSA_QS3000;
+ break;
+ } else {
+ printk(KERN_ERR "Elsa PnP: PnP error card found, no device\n");
+ return(0);
+ }
+ }
+ ipid++;
+ pnp_c=NULL;
+ }
+ if (!ipid->card_vendor) {
+ printk(KERN_INFO "Elsa PnP: no ISAPnP card found\n");
+ return(0);
+ }
+ }
+#endif /* __ISAPNP__ */
+
+ if (card->para[1] && card->para[0]) {
+ cs->hw.elsa.base = card->para[1];
+ cs->irq = card->para[0];
+ if (!cs->subtyp)
+ cs->subtyp = ELSA_QS1000;
+ } else {
+ printk(KERN_ERR "Elsa PnP: no parameter\n");
+ }
+ cs->hw.elsa.cfg = cs->hw.elsa.base + ELSA_CONFIG;
+ cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE;
+ cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC;
+ cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX;
+ cs->hw.elsa.trig = cs->hw.elsa.base + ELSA_TRIG_IRQ;
+ cs->hw.elsa.timer = cs->hw.elsa.base + ELSA_START_TIMER;
+ cs->hw.elsa.ctrl = cs->hw.elsa.base + ELSA_CONTROL;
+ printk(KERN_INFO
+ "Elsa: %s defined at %#lx IRQ %d\n",
+ Elsa_Types[cs->subtyp],
+ cs->hw.elsa.base,
+ cs->irq);
+
+ return (1);
+}
+
+static void __devinit
+setup_elsa_pcmcia(struct IsdnCard *card)
+{
+ struct IsdnCardState *cs = card->cs;
+ u_char val;
+
+ cs->hw.elsa.base = card->para[1];
+ cs->irq = card->para[0];
+ val = readreg(cs->hw.elsa.base + 0, cs->hw.elsa.base + 2, IPAC_ID);
+ if ((val == 1) || (val == 2)) { /* IPAC version 1.1/1.2 */
+ cs->subtyp = ELSA_PCMCIA_IPAC;
+ cs->hw.elsa.ale = cs->hw.elsa.base + 0;
+ cs->hw.elsa.isac = cs->hw.elsa.base + 2;
+ cs->hw.elsa.hscx = cs->hw.elsa.base + 2;
+ test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+ } else {
+ cs->subtyp = ELSA_PCMCIA;
+ cs->hw.elsa.ale = cs->hw.elsa.base + ELSA_ALE_PCM;
+ cs->hw.elsa.isac = cs->hw.elsa.base + ELSA_ISAC_PCM;
+ cs->hw.elsa.hscx = cs->hw.elsa.base + ELSA_HSCX;
+ }
+ cs->hw.elsa.timer = 0;
+ cs->hw.elsa.trig = 0;
+ cs->hw.elsa.ctrl = 0;
+ cs->irq_flags |= IRQF_SHARED;
+ printk(KERN_INFO
+ "Elsa: %s defined at %#lx IRQ %d\n",
+ Elsa_Types[cs->subtyp],
+ cs->hw.elsa.base,
+ cs->irq);
+}
+
+#ifdef CONFIG_PCI_LEGACY
+static struct pci_dev *dev_qs1000 __devinitdata = NULL;
+static struct pci_dev *dev_qs3000 __devinitdata = NULL;
+
+static int __devinit
+setup_elsa_pci(struct IsdnCard *card)
+{
+ struct IsdnCardState *cs = card->cs;
+
+ cs->subtyp = 0;
+ if ((dev_qs1000 = pci_find_device(PCI_VENDOR_ID_ELSA,
+ PCI_DEVICE_ID_ELSA_MICROLINK, dev_qs1000))) {
+ if (pci_enable_device(dev_qs1000))
+ return(0);
+ cs->subtyp = ELSA_QS1000PCI;
+ cs->irq = dev_qs1000->irq;
+ cs->hw.elsa.cfg = pci_resource_start(dev_qs1000, 1);
+ cs->hw.elsa.base = pci_resource_start(dev_qs1000, 3);
+ } else if ((dev_qs3000 = pci_find_device(PCI_VENDOR_ID_ELSA,
+ PCI_DEVICE_ID_ELSA_QS3000, dev_qs3000))) {
+ if (pci_enable_device(dev_qs3000))
+ return(0);
+ cs->subtyp = ELSA_QS3000PCI;
+ cs->irq = dev_qs3000->irq;
+ cs->hw.elsa.cfg = pci_resource_start(dev_qs3000, 1);
+ cs->hw.elsa.base = pci_resource_start(dev_qs3000, 3);
+ } else {
+ printk(KERN_WARNING "Elsa: No PCI card found\n");
+ return(0);
+ }
+ if (!cs->irq) {
+ printk(KERN_WARNING "Elsa: No IRQ for PCI card found\n");
+ return(0);
+ }
+
+ if (!(cs->hw.elsa.base && cs->hw.elsa.cfg)) {
+ printk(KERN_WARNING "Elsa: No IO-Adr for PCI card found\n");
+ return(0);
+ }
+ if ((cs->hw.elsa.cfg & 0xff) || (cs->hw.elsa.base & 0xf)) {
+ printk(KERN_WARNING "Elsa: You may have a wrong PCI bios\n");
+ printk(KERN_WARNING "Elsa: If your system hangs now, read\n");
+ printk(KERN_WARNING "Elsa: Documentation/isdn/README.HiSax\n");
+ }
+ cs->hw.elsa.ale = cs->hw.elsa.base;
+ cs->hw.elsa.isac = cs->hw.elsa.base +1;
+ cs->hw.elsa.hscx = cs->hw.elsa.base +1;
+ test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+ cs->hw.elsa.timer = 0;
+ cs->hw.elsa.trig = 0;
+ cs->irq_flags |= IRQF_SHARED;
+ printk(KERN_INFO
+ "Elsa: %s defined at %#lx/0x%x IRQ %d\n",
+ Elsa_Types[cs->subtyp],
+ cs->hw.elsa.base,
+ cs->hw.elsa.cfg,
+ cs->irq);
+
+ return (1);
+}
+
+#else
+
+static int __devinit
+setup_elsa_pci(struct IsdnCard *card)
+{
+ return (1);
+}
+#endif /* CONFIG_PCI_LEGACY */
+
+static int __devinit
+setup_elsa_common(struct IsdnCard *card)
+{
+ struct IsdnCardState *cs = card->cs;
+ u_char val;
+ int bytecnt;
+
+ switch (cs->subtyp) {
+ case ELSA_PC:
+ case ELSA_PCC8:
+ case ELSA_PCC16:
+ case ELSA_QS1000:
+ case ELSA_PCMCIA:
+ case ELSA_PCMCIA_IPAC:
+ bytecnt = 8;
+ break;
+ case ELSA_PCFPRO:
+ case ELSA_PCF:
+ case ELSA_QS3000:
+ case ELSA_QS3000PCI:
+ bytecnt = 16;
+ break;
+ case ELSA_QS1000PCI:
+ bytecnt = 2;
+ break;
+ default:
+ printk(KERN_WARNING
+ "Unknown ELSA subtype %d\n", cs->subtyp);
+ return (0);
+ }
+ /* In case of the elsa pcmcia card, this region is in use,
+ reserved for us by the card manager. So we do not check it
+ here, it would fail. */
+ if (cs->typ != ISDN_CTYPE_ELSA_PCMCIA && !request_region(cs->hw.elsa.base, bytecnt, "elsa isdn")) {
+ printk(KERN_WARNING
+ "HiSax: ELSA config port %#lx-%#lx already in use\n",
+ cs->hw.elsa.base,
+ cs->hw.elsa.base + bytecnt);
+ return (0);
+ }
+ if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI)) {
+ if (!request_region(cs->hw.elsa.cfg, 0x80, "elsa isdn pci")) {
+ printk(KERN_WARNING
+ "HiSax: ELSA pci port %x-%x already in use\n",
+ cs->hw.elsa.cfg,
+ cs->hw.elsa.cfg + 0x80);
+ release_region(cs->hw.elsa.base, bytecnt);
+ return (0);
+ }
+ }
+#if ARCOFI_USE
+ init_arcofi(cs);
+#endif
+ setup_isac(cs);
+ cs->hw.elsa.tl.function = (void *) elsa_led_handler;
+ cs->hw.elsa.tl.data = (long) cs;
+ init_timer(&cs->hw.elsa.tl);
+ /* Teste Timer */
+ if (cs->hw.elsa.timer) {
+ byteout(cs->hw.elsa.trig, 0xff);
+ byteout(cs->hw.elsa.timer, 0);
+ if (!TimerRun(cs)) {
+ byteout(cs->hw.elsa.timer, 0); /* 2. Versuch */
+ if (!TimerRun(cs)) {
+ printk(KERN_WARNING
+ "Elsa: timer do not start\n");
+ release_io_elsa(cs);
+ return (0);
+ }
+ }
+ HZDELAY((HZ/100) + 1); /* wait >=10 ms */
+ if (TimerRun(cs)) {
+ printk(KERN_WARNING "Elsa: timer do not run down\n");
+ release_io_elsa(cs);
+ return (0);
+ }
+ printk(KERN_INFO "Elsa: timer OK; resetting card\n");
+ }
+ cs->BC_Read_Reg = &ReadHSCX;
+ cs->BC_Write_Reg = &WriteHSCX;
+ cs->BC_Send_Data = &hscx_fill_fifo;
+ cs->cardmsg = &Elsa_card_msg;
+ if ((cs->subtyp == ELSA_QS1000PCI) || (cs->subtyp == ELSA_QS3000PCI) || (cs->subtyp == ELSA_PCMCIA_IPAC)) {
+ cs->readisac = &ReadISAC_IPAC;
+ cs->writeisac = &WriteISAC_IPAC;
+ cs->readisacfifo = &ReadISACfifo_IPAC;
+ cs->writeisacfifo = &WriteISACfifo_IPAC;
+ cs->irq_func = &elsa_interrupt_ipac;
+ val = readreg(cs->hw.elsa.ale, cs->hw.elsa.isac, IPAC_ID);
+ printk(KERN_INFO "Elsa: IPAC version %x\n", val);
+ } else {
+ cs->readisac = &ReadISAC;
+ cs->writeisac = &WriteISAC;
+ cs->readisacfifo = &ReadISACfifo;
+ cs->writeisacfifo = &WriteISACfifo;
+ cs->irq_func = &elsa_interrupt;
+ ISACVersion(cs, "Elsa:");
+ if (HscxVersion(cs, "Elsa:")) {
+ printk(KERN_WARNING
+ "Elsa: wrong HSCX versions check IO address\n");
+ release_io_elsa(cs);
+ return (0);
+ }
+ }
+ if (cs->subtyp == ELSA_PC) {
+ val = readitac(cs, ITAC_SYS);
+ printk(KERN_INFO "Elsa: ITAC version %s\n", ITACVer[val & 7]);
+ writeitac(cs, ITAC_ISEN, 0);
+ writeitac(cs, ITAC_RFIE, 0);
+ writeitac(cs, ITAC_XFIE, 0);
+ writeitac(cs, ITAC_SCIE, 0);
+ writeitac(cs, ITAC_STIE, 0);
+ }
+ return (1);
+}
+
+int __devinit
+setup_elsa(struct IsdnCard *card)
+{
+ int rc;
+ struct IsdnCardState *cs = card->cs;
+ char tmp[64];
+
+ strcpy(tmp, Elsa_revision);
+ printk(KERN_INFO "HiSax: Elsa driver Rev. %s\n", HiSax_getrev(tmp));
+ cs->hw.elsa.ctrl_reg = 0;
+ cs->hw.elsa.status = 0;
+ cs->hw.elsa.MFlag = 0;
+ cs->subtyp = 0;
+
+ if (cs->typ == ISDN_CTYPE_ELSA) {
+ rc = setup_elsa_isa(card);
+ if (!rc)
+ return (0);
+
+ } else if (cs->typ == ISDN_CTYPE_ELSA_PNP) {
+ rc = setup_elsa_isapnp(card);
+ if (!rc)
+ return (0);
+
+ } else if (cs->typ == ISDN_CTYPE_ELSA_PCMCIA)
+ setup_elsa_pcmcia(card);
+
+ else if (cs->typ == ISDN_CTYPE_ELSA_PCI) {
+ rc = setup_elsa_pci(card);
+ if (!rc)
+ return (0);
+
+ } else
+ return (0);
+
+ return setup_elsa_common(card);
+}
diff --git a/drivers/isdn/hisax/elsa_cs.c b/drivers/isdn/hisax/elsa_cs.c
new file mode 100644
index 0000000..f4d0fe2
--- /dev/null
+++ b/drivers/isdn/hisax/elsa_cs.c
@@ -0,0 +1,371 @@
+/*======================================================================
+
+ An elsa_cs PCMCIA client driver
+
+ This driver is for the Elsa PCM ISDN Cards, i.e. the MicroLink
+
+
+ The contents of this file are subject to the Mozilla Public
+ License Version 1.1 (the "License"); you may not use this file
+ except in compliance with the License. You may obtain a copy of
+ the License at http://www.mozilla.org/MPL/
+
+ Software distributed under the License is distributed on an "AS
+ IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ implied. See the License for the specific language governing
+ rights and limitations under the License.
+
+ The initial developer of the original code is David A. Hinds
+ <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
+ are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+
+ Modifications from dummy_cs.c are Copyright (C) 1999-2001 Klaus
+ Lichtenwalder <Lichtenwalder@ACM.org>. All Rights Reserved.
+
+ Alternatively, the contents of this file may be used under the
+ terms of the GNU General Public License version 2 (the "GPL"), in
+ which case the provisions of the GPL are applicable instead of the
+ above. If you wish to allow the use of your version of this file
+ only under the terms of the GPL and not to allow others to use
+ your version of this file under the MPL, indicate your decision
+ by deleting the provisions above and replace them with the notice
+ and other provisions required by the GPL. If you do not delete
+ the provisions above, a recipient may use your version of this
+ file under either the MPL or the GPL.
+
+======================================================================*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+#include "hisax_cfg.h"
+
+MODULE_DESCRIPTION("ISDN4Linux: PCMCIA client driver for Elsa PCM cards");
+MODULE_AUTHOR("Klaus Lichtenwalder");
+MODULE_LICENSE("Dual MPL/GPL");
+
+/*
+ All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If
+ you do not define PCMCIA_DEBUG at all, all the debug code will be
+ left out. If you compile with PCMCIA_DEBUG=0, the debug code will
+ be present but disabled -- but it can then be enabled for specific
+ modules at load time with a 'pc_debug=#' option to insmod.
+*/
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args);
+static char *version =
+"elsa_cs.c $Revision: 1.2.2.4 $ $Date: 2004/01/25 15:07:06 $ (K.Lichtenwalder)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+static int protocol = 2; /* EURO-ISDN Default */
+module_param(protocol, int, 0);
+
+/*====================================================================*/
+
+/*
+ The event() function is this driver's Card Services event handler.
+ It will be called by Card Services when an appropriate card status
+ event is received. The config() and release() entry points are
+ used to configure or release a socket, in response to card insertion
+ and ejection events. They are invoked from the elsa_cs event
+ handler.
+*/
+
+static int elsa_cs_config(struct pcmcia_device *link);
+static void elsa_cs_release(struct pcmcia_device *link);
+
+/*
+ The attach() and detach() entry points are used to create and destroy
+ "instances" of the driver, where each instance represents everything
+ needed to manage one actual PCMCIA card.
+*/
+
+static void elsa_cs_detach(struct pcmcia_device *p_dev);
+
+/*
+ A driver needs to provide a dev_node_t structure for each device
+ on a card. In some cases, there is only one device per card (for
+ example, ethernet cards, modems). In other cases, there may be
+ many actual or logical devices (SCSI adapters, memory cards with
+ multiple partitions). The dev_node_t structures need to be kept
+ in a linked list starting at the 'dev' field of a struct pcmcia_device
+ structure. We allocate them in the card's private data structure,
+ because they generally shouldn't be allocated dynamically.
+ In this case, we also provide a flag to indicate if a device is
+ "stopped" due to a power management event, or card ejection. The
+ device IO routines can use a flag like this to throttle IO to a
+ card that is not ready to accept it.
+*/
+
+typedef struct local_info_t {
+ struct pcmcia_device *p_dev;
+ dev_node_t node;
+ int busy;
+ int cardnr;
+} local_info_t;
+
+/*======================================================================
+
+ elsa_cs_attach() creates an "instance" of the driver, allocatingx
+ local data structures for one device. The device is registered
+ with Card Services.
+
+ The dev_link structure is initialized, but we don't actually
+ configure the card at this point -- we wait until we receive a
+ card insertion event.
+
+======================================================================*/
+
+static int elsa_cs_probe(struct pcmcia_device *link)
+{
+ local_info_t *local;
+
+ DEBUG(0, "elsa_cs_attach()\n");
+
+ /* Allocate space for private device-specific data */
+ local = kzalloc(sizeof(local_info_t), GFP_KERNEL);
+ if (!local) return -ENOMEM;
+
+ local->p_dev = link;
+ link->priv = local;
+
+ local->cardnr = -1;
+
+ /* Interrupt setup */
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
+ link->irq.IRQInfo1 = IRQ_LEVEL_ID|IRQ_SHARE_ID;
+ link->irq.Handler = NULL;
+
+ /*
+ General socket configuration defaults can go here. In this
+ client, we assume very little, and rely on the CIS for almost
+ everything. In most clients, many details (i.e., number, sizes,
+ and attributes of IO windows) are fixed by the nature of the
+ device, and can be hard-wired here.
+ */
+ link->io.NumPorts1 = 8;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ link->io.IOAddrLines = 3;
+
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+
+ return elsa_cs_config(link);
+} /* elsa_cs_attach */
+
+/*======================================================================
+
+ This deletes a driver "instance". The device is de-registered
+ with Card Services. If it has been released, all local data
+ structures are freed. Otherwise, the structures will be freed
+ when the device is released.
+
+======================================================================*/
+
+static void elsa_cs_detach(struct pcmcia_device *link)
+{
+ local_info_t *info = link->priv;
+
+ DEBUG(0, "elsa_cs_detach(0x%p)\n", link);
+
+ info->busy = 1;
+ elsa_cs_release(link);
+
+ kfree(info);
+} /* elsa_cs_detach */
+
+/*======================================================================
+
+ elsa_cs_config() is scheduled to run after a CARD_INSERTION event
+ is received, to configure the PCMCIA socket, and to make the
+ device available to the system.
+
+======================================================================*/
+
+static int elsa_cs_configcheck(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cf,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ int j;
+
+ if ((cf->io.nwin > 0) && cf->io.win[0].base) {
+ printk(KERN_INFO "(elsa_cs: looks like the 96 model)\n");
+ p_dev->io.BasePort1 = cf->io.win[0].base;
+ if (!pcmcia_request_io(p_dev, &p_dev->io))
+ return 0;
+ } else {
+ printk(KERN_INFO "(elsa_cs: looks like the 97 model)\n");
+ for (j = 0x2f0; j > 0x100; j -= 0x10) {
+ p_dev->io.BasePort1 = j;
+ if (!pcmcia_request_io(p_dev, &p_dev->io))
+ return 0;
+ }
+ }
+ return -ENODEV;
+}
+
+static int elsa_cs_config(struct pcmcia_device *link)
+{
+ local_info_t *dev;
+ int i, last_fn;
+ IsdnCard_t icard;
+
+ DEBUG(0, "elsa_config(0x%p)\n", link);
+ dev = link->priv;
+
+ i = pcmcia_loop_config(link, elsa_cs_configcheck, NULL);
+ if (i != 0) {
+ last_fn = RequestIO;
+ goto cs_failed;
+ }
+
+ i = pcmcia_request_irq(link, &link->irq);
+ if (i != 0) {
+ link->irq.AssignedIRQ = 0;
+ last_fn = RequestIRQ;
+ goto cs_failed;
+ }
+
+ i = pcmcia_request_configuration(link, &link->conf);
+ if (i != 0) {
+ last_fn = RequestConfiguration;
+ goto cs_failed;
+ }
+
+ /* At this point, the dev_node_t structure(s) should be
+ initialized and arranged in a linked list at link->dev. *//* */
+ sprintf(dev->node.dev_name, "elsa");
+ dev->node.major = dev->node.minor = 0x0;
+
+ link->dev_node = &dev->node;
+
+ /* Finally, report what we've done */
+ printk(KERN_INFO "%s: index 0x%02x: ",
+ dev->node.dev_name, link->conf.ConfigIndex);
+ if (link->conf.Attributes & CONF_ENABLE_IRQ)
+ printk(", irq %d", link->irq.AssignedIRQ);
+ if (link->io.NumPorts1)
+ printk(", io 0x%04x-0x%04x", link->io.BasePort1,
+ link->io.BasePort1+link->io.NumPorts1-1);
+ if (link->io.NumPorts2)
+ printk(" & 0x%04x-0x%04x", link->io.BasePort2,
+ link->io.BasePort2+link->io.NumPorts2-1);
+ printk("\n");
+
+ icard.para[0] = link->irq.AssignedIRQ;
+ icard.para[1] = link->io.BasePort1;
+ icard.protocol = protocol;
+ icard.typ = ISDN_CTYPE_ELSA_PCMCIA;
+
+ i = hisax_init_pcmcia(link, &(((local_info_t*)link->priv)->busy), &icard);
+ if (i < 0) {
+ printk(KERN_ERR "elsa_cs: failed to initialize Elsa PCMCIA %d at i/o %#x\n",
+ i, link->io.BasePort1);
+ elsa_cs_release(link);
+ } else
+ ((local_info_t*)link->priv)->cardnr = i;
+
+ return 0;
+cs_failed:
+ cs_error(link, last_fn, i);
+ elsa_cs_release(link);
+ return -ENODEV;
+} /* elsa_cs_config */
+
+/*======================================================================
+
+ After a card is removed, elsa_cs_release() will unregister the net
+ device, and release the PCMCIA configuration. If the device is
+ still open, this will be postponed until it is closed.
+
+======================================================================*/
+
+static void elsa_cs_release(struct pcmcia_device *link)
+{
+ local_info_t *local = link->priv;
+
+ DEBUG(0, "elsa_cs_release(0x%p)\n", link);
+
+ if (local) {
+ if (local->cardnr >= 0) {
+ /* no unregister function with hisax */
+ HiSax_closecard(local->cardnr);
+ }
+ }
+
+ pcmcia_disable_device(link);
+} /* elsa_cs_release */
+
+static int elsa_suspend(struct pcmcia_device *link)
+{
+ local_info_t *dev = link->priv;
+
+ dev->busy = 1;
+
+ return 0;
+}
+
+static int elsa_resume(struct pcmcia_device *link)
+{
+ local_info_t *dev = link->priv;
+
+ dev->busy = 0;
+
+ return 0;
+}
+
+static struct pcmcia_device_id elsa_ids[] = {
+ PCMCIA_DEVICE_PROD_ID12("ELSA AG (Aachen, Germany)", "MicroLink ISDN/MC ", 0x983de2c4, 0x333ba257),
+ PCMCIA_DEVICE_PROD_ID12("ELSA GmbH, Aachen", "MicroLink ISDN/MC ", 0x639e5718, 0x333ba257),
+ PCMCIA_DEVICE_NULL
+};
+MODULE_DEVICE_TABLE(pcmcia, elsa_ids);
+
+static struct pcmcia_driver elsa_cs_driver = {
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = "elsa_cs",
+ },
+ .probe = elsa_cs_probe,
+ .remove = elsa_cs_detach,
+ .id_table = elsa_ids,
+ .suspend = elsa_suspend,
+ .resume = elsa_resume,
+};
+
+static int __init init_elsa_cs(void)
+{
+ return pcmcia_register_driver(&elsa_cs_driver);
+}
+
+static void __exit exit_elsa_cs(void)
+{
+ pcmcia_unregister_driver(&elsa_cs_driver);
+}
+
+module_init(init_elsa_cs);
+module_exit(exit_elsa_cs);
diff --git a/drivers/isdn/hisax/elsa_ser.c b/drivers/isdn/hisax/elsa_ser.c
new file mode 100644
index 0000000..f181db4
--- /dev/null
+++ b/drivers/isdn/hisax/elsa_ser.c
@@ -0,0 +1,658 @@
+/* $Id: elsa_ser.c,v 2.14.2.3 2004/02/11 13:21:33 keil Exp $
+ *
+ * stuff for the serial modem on ELSA cards
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/serial.h>
+#include <linux/serial_reg.h>
+
+#define MAX_MODEM_BUF 256
+#define WAKEUP_CHARS (MAX_MODEM_BUF/2)
+#define RS_ISR_PASS_LIMIT 256
+#define BASE_BAUD ( 1843200 / 16 )
+
+//#define SERIAL_DEBUG_OPEN 1
+//#define SERIAL_DEBUG_INTR 1
+//#define SERIAL_DEBUG_FLOW 1
+#undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_FLOW
+#undef SERIAL_DEBUG_REG
+//#define SERIAL_DEBUG_REG 1
+
+#ifdef SERIAL_DEBUG_REG
+static u_char deb[32];
+const char *ModemIn[] = {"RBR","IER","IIR","LCR","MCR","LSR","MSR","SCR"};
+const char *ModemOut[] = {"THR","IER","FCR","LCR","MCR","LSR","MSR","SCR"};
+#endif
+
+static char *MInit_1 = "AT&F&C1E0&D2\r\0";
+static char *MInit_2 = "ATL2M1S64=13\r\0";
+static char *MInit_3 = "AT+FCLASS=0\r\0";
+static char *MInit_4 = "ATV1S2=128X1\r\0";
+static char *MInit_5 = "AT\\V8\\N3\r\0";
+static char *MInit_6 = "ATL0M0&G0%E1\r\0";
+static char *MInit_7 = "AT%L1%M0%C3\r\0";
+
+static char *MInit_speed28800 = "AT%G0%B28800\r\0";
+
+static char *MInit_dialout = "ATs7=60 x1 d\r\0";
+static char *MInit_dialin = "ATs7=60 x1 a\r\0";
+
+
+static inline unsigned int serial_in(struct IsdnCardState *cs, int offset)
+{
+#ifdef SERIAL_DEBUG_REG
+ u_int val = inb(cs->hw.elsa.base + 8 + offset);
+ debugl1(cs,"in %s %02x",ModemIn[offset], val);
+ return(val);
+#else
+ return inb(cs->hw.elsa.base + 8 + offset);
+#endif
+}
+
+static inline unsigned int serial_inp(struct IsdnCardState *cs, int offset)
+{
+#ifdef SERIAL_DEBUG_REG
+#ifdef ELSA_SERIAL_NOPAUSE_IO
+ u_int val = inb(cs->hw.elsa.base + 8 + offset);
+ debugl1(cs,"inp %s %02x",ModemIn[offset], val);
+#else
+ u_int val = inb_p(cs->hw.elsa.base + 8 + offset);
+ debugl1(cs,"inP %s %02x",ModemIn[offset], val);
+#endif
+ return(val);
+#else
+#ifdef ELSA_SERIAL_NOPAUSE_IO
+ return inb(cs->hw.elsa.base + 8 + offset);
+#else
+ return inb_p(cs->hw.elsa.base + 8 + offset);
+#endif
+#endif
+}
+
+static inline void serial_out(struct IsdnCardState *cs, int offset, int value)
+{
+#ifdef SERIAL_DEBUG_REG
+ debugl1(cs,"out %s %02x",ModemOut[offset], value);
+#endif
+ outb(value, cs->hw.elsa.base + 8 + offset);
+}
+
+static inline void serial_outp(struct IsdnCardState *cs, int offset,
+ int value)
+{
+#ifdef SERIAL_DEBUG_REG
+#ifdef ELSA_SERIAL_NOPAUSE_IO
+ debugl1(cs,"outp %s %02x",ModemOut[offset], value);
+#else
+ debugl1(cs,"outP %s %02x",ModemOut[offset], value);
+#endif
+#endif
+#ifdef ELSA_SERIAL_NOPAUSE_IO
+ outb(value, cs->hw.elsa.base + 8 + offset);
+#else
+ outb_p(value, cs->hw.elsa.base + 8 + offset);
+#endif
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void change_speed(struct IsdnCardState *cs, int baud)
+{
+ int quot = 0, baud_base;
+ unsigned cval, fcr = 0;
+ int bits;
+
+
+ /* byte size and parity */
+ cval = 0x03; bits = 10;
+ /* Determine divisor based on baud rate */
+ baud_base = BASE_BAUD;
+ quot = baud_base / baud;
+ /* If the quotient is ever zero, default to 9600 bps */
+ if (!quot)
+ quot = baud_base / 9600;
+
+ /* Set up FIFO's */
+ if ((baud_base / quot) < 2400)
+ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
+ else
+ fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
+ serial_outp(cs, UART_FCR, fcr);
+ /* CTS flow control flag and modem status interrupts */
+ cs->hw.elsa.IER &= ~UART_IER_MSI;
+ cs->hw.elsa.IER |= UART_IER_MSI;
+ serial_outp(cs, UART_IER, cs->hw.elsa.IER);
+
+ debugl1(cs,"modem quot=0x%x", quot);
+ serial_outp(cs, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */
+ serial_outp(cs, UART_DLL, quot & 0xff); /* LS of divisor */
+ serial_outp(cs, UART_DLM, quot >> 8); /* MS of divisor */
+ serial_outp(cs, UART_LCR, cval); /* reset DLAB */
+ serial_inp(cs, UART_RX);
+}
+
+static int mstartup(struct IsdnCardState *cs)
+{
+ int retval=0;
+
+ /*
+ * Clear the FIFO buffers and disable them
+ * (they will be reenabled in change_speed())
+ */
+ serial_outp(cs, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT));
+
+ /*
+ * At this point there's no way the LSR could still be 0xFF;
+ * if it is, then bail out, because there's likely no UART
+ * here.
+ */
+ if (serial_inp(cs, UART_LSR) == 0xff) {
+ retval = -ENODEV;
+ goto errout;
+ }
+
+ /*
+ * Clear the interrupt registers.
+ */
+ (void) serial_inp(cs, UART_RX);
+ (void) serial_inp(cs, UART_IIR);
+ (void) serial_inp(cs, UART_MSR);
+
+ /*
+ * Now, initialize the UART
+ */
+ serial_outp(cs, UART_LCR, UART_LCR_WLEN8); /* reset DLAB */
+
+ cs->hw.elsa.MCR = 0;
+ cs->hw.elsa.MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
+ serial_outp(cs, UART_MCR, cs->hw.elsa.MCR);
+
+ /*
+ * Finally, enable interrupts
+ */
+ cs->hw.elsa.IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
+ serial_outp(cs, UART_IER, cs->hw.elsa.IER); /* enable interrupts */
+
+ /*
+ * And clear the interrupt registers again for luck.
+ */
+ (void)serial_inp(cs, UART_LSR);
+ (void)serial_inp(cs, UART_RX);
+ (void)serial_inp(cs, UART_IIR);
+ (void)serial_inp(cs, UART_MSR);
+
+ cs->hw.elsa.transcnt = cs->hw.elsa.transp = 0;
+ cs->hw.elsa.rcvcnt = cs->hw.elsa.rcvp =0;
+
+ /*
+ * and set the speed of the serial port
+ */
+ change_speed(cs, BASE_BAUD);
+ cs->hw.elsa.MFlag = 1;
+errout:
+ return retval;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void mshutdown(struct IsdnCardState *cs)
+{
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk(KERN_DEBUG"Shutting down serial ....");
+#endif
+
+ /*
+ * clear delta_msr_wait queue to avoid mem leaks: we may free the irq
+ * here so the queue might never be waken up
+ */
+
+ cs->hw.elsa.IER = 0;
+ serial_outp(cs, UART_IER, 0x00); /* disable all intrs */
+ cs->hw.elsa.MCR &= ~UART_MCR_OUT2;
+
+ /* disable break condition */
+ serial_outp(cs, UART_LCR, serial_inp(cs, UART_LCR) & ~UART_LCR_SBC);
+
+ cs->hw.elsa.MCR &= ~(UART_MCR_DTR|UART_MCR_RTS);
+ serial_outp(cs, UART_MCR, cs->hw.elsa.MCR);
+
+ /* disable FIFO's */
+ serial_outp(cs, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT));
+ serial_inp(cs, UART_RX); /* read data port to reset things */
+
+#ifdef SERIAL_DEBUG_OPEN
+ printk(" done\n");
+#endif
+}
+
+static inline int
+write_modem(struct BCState *bcs) {
+ int ret=0;
+ struct IsdnCardState *cs = bcs->cs;
+ int count, len, fp;
+
+ if (!bcs->tx_skb)
+ return 0;
+ if (bcs->tx_skb->len <= 0)
+ return 0;
+ len = bcs->tx_skb->len;
+ if (len > MAX_MODEM_BUF - cs->hw.elsa.transcnt)
+ len = MAX_MODEM_BUF - cs->hw.elsa.transcnt;
+ fp = cs->hw.elsa.transcnt + cs->hw.elsa.transp;
+ fp &= (MAX_MODEM_BUF -1);
+ count = len;
+ if (count > MAX_MODEM_BUF - fp) {
+ count = MAX_MODEM_BUF - fp;
+ skb_copy_from_linear_data(bcs->tx_skb,
+ cs->hw.elsa.transbuf + fp, count);
+ skb_pull(bcs->tx_skb, count);
+ cs->hw.elsa.transcnt += count;
+ ret = count;
+ count = len - count;
+ fp = 0;
+ }
+ skb_copy_from_linear_data(bcs->tx_skb,
+ cs->hw.elsa.transbuf + fp, count);
+ skb_pull(bcs->tx_skb, count);
+ cs->hw.elsa.transcnt += count;
+ ret += count;
+
+ if (cs->hw.elsa.transcnt &&
+ !(cs->hw.elsa.IER & UART_IER_THRI)) {
+ cs->hw.elsa.IER |= UART_IER_THRI;
+ serial_outp(cs, UART_IER, cs->hw.elsa.IER);
+ }
+ return(ret);
+}
+
+static inline void
+modem_fill(struct BCState *bcs) {
+
+ if (bcs->tx_skb) {
+ if (bcs->tx_skb->len) {
+ write_modem(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.hscx.count;
+ spin_unlock_irqrestore(&bcs->aclock, flags);
+ schedule_event(bcs, B_ACKPENDING);
+ }
+ dev_kfree_skb_any(bcs->tx_skb);
+ bcs->tx_skb = NULL;
+ }
+ }
+ if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+ bcs->hw.hscx.count = 0;
+ test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+ write_modem(bcs);
+ } else {
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ schedule_event(bcs, B_XMTBUFREADY);
+ }
+}
+
+static inline void receive_chars(struct IsdnCardState *cs,
+ int *status)
+{
+ unsigned char ch;
+ struct sk_buff *skb;
+
+ do {
+ ch = serial_in(cs, UART_RX);
+ if (cs->hw.elsa.rcvcnt >= MAX_MODEM_BUF)
+ break;
+ cs->hw.elsa.rcvbuf[cs->hw.elsa.rcvcnt++] = ch;
+#ifdef SERIAL_DEBUG_INTR
+ printk("DR%02x:%02x...", ch, *status);
+#endif
+ if (*status & (UART_LSR_BI | UART_LSR_PE |
+ UART_LSR_FE | UART_LSR_OE)) {
+
+#ifdef SERIAL_DEBUG_INTR
+ printk("handling exept....");
+#endif
+ }
+ *status = serial_inp(cs, UART_LSR);
+ } while (*status & UART_LSR_DR);
+ if (cs->hw.elsa.MFlag == 2) {
+ if (!(skb = dev_alloc_skb(cs->hw.elsa.rcvcnt)))
+ printk(KERN_WARNING "ElsaSER: receive out of memory\n");
+ else {
+ memcpy(skb_put(skb, cs->hw.elsa.rcvcnt), cs->hw.elsa.rcvbuf,
+ cs->hw.elsa.rcvcnt);
+ skb_queue_tail(& cs->hw.elsa.bcs->rqueue, skb);
+ }
+ schedule_event(cs->hw.elsa.bcs, B_RCVBUFREADY);
+ } else {
+ char tmp[128];
+ char *t = tmp;
+
+ t += sprintf(t, "modem read cnt %d", cs->hw.elsa.rcvcnt);
+ QuickHex(t, cs->hw.elsa.rcvbuf, cs->hw.elsa.rcvcnt);
+ debugl1(cs, tmp);
+ }
+ cs->hw.elsa.rcvcnt = 0;
+}
+
+static inline void transmit_chars(struct IsdnCardState *cs, int *intr_done)
+{
+ int count;
+
+ debugl1(cs, "transmit_chars: p(%x) cnt(%x)", cs->hw.elsa.transp,
+ cs->hw.elsa.transcnt);
+
+ if (cs->hw.elsa.transcnt <= 0) {
+ cs->hw.elsa.IER &= ~UART_IER_THRI;
+ serial_out(cs, UART_IER, cs->hw.elsa.IER);
+ return;
+ }
+ count = 16;
+ do {
+ serial_outp(cs, UART_TX, cs->hw.elsa.transbuf[cs->hw.elsa.transp++]);
+ if (cs->hw.elsa.transp >= MAX_MODEM_BUF)
+ cs->hw.elsa.transp=0;
+ if (--cs->hw.elsa.transcnt <= 0)
+ break;
+ } while (--count > 0);
+ if ((cs->hw.elsa.transcnt < WAKEUP_CHARS) && (cs->hw.elsa.MFlag==2))
+ modem_fill(cs->hw.elsa.bcs);
+
+#ifdef SERIAL_DEBUG_INTR
+ printk("THRE...");
+#endif
+ if (intr_done)
+ *intr_done = 0;
+ if (cs->hw.elsa.transcnt <= 0) {
+ cs->hw.elsa.IER &= ~UART_IER_THRI;
+ serial_outp(cs, UART_IER, cs->hw.elsa.IER);
+ }
+}
+
+
+static void rs_interrupt_elsa(struct IsdnCardState *cs)
+{
+ int status, iir, msr;
+ int pass_counter = 0;
+
+#ifdef SERIAL_DEBUG_INTR
+ printk(KERN_DEBUG "rs_interrupt_single(%d)...", cs->irq);
+#endif
+
+ do {
+ status = serial_inp(cs, UART_LSR);
+ debugl1(cs,"rs LSR %02x", status);
+#ifdef SERIAL_DEBUG_INTR
+ printk("status = %x...", status);
+#endif
+ if (status & UART_LSR_DR)
+ receive_chars(cs, &status);
+ if (status & UART_LSR_THRE)
+ transmit_chars(cs, NULL);
+ if (pass_counter++ > RS_ISR_PASS_LIMIT) {
+ printk("rs_single loop break.\n");
+ break;
+ }
+ iir = serial_inp(cs, UART_IIR);
+ debugl1(cs,"rs IIR %02x", iir);
+ if ((iir & 0xf) == 0) {
+ msr = serial_inp(cs, UART_MSR);
+ debugl1(cs,"rs MSR %02x", msr);
+ }
+ } while (!(iir & UART_IIR_NO_INT));
+#ifdef SERIAL_DEBUG_INTR
+ printk("end.\n");
+#endif
+}
+
+extern int open_hscxstate(struct IsdnCardState *cs, struct BCState *bcs);
+extern void modehscx(struct BCState *bcs, int mode, int bc);
+extern void hscx_l2l1(struct PStack *st, int pr, void *arg);
+
+static void
+close_elsastate(struct BCState *bcs)
+{
+ modehscx(bcs, 0, bcs->channel);
+ if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+ if (bcs->hw.hscx.rcvbuf) {
+ if (bcs->mode != L1_MODE_MODEM)
+ kfree(bcs->hw.hscx.rcvbuf);
+ bcs->hw.hscx.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);
+ }
+ }
+}
+
+static void
+modem_write_cmd(struct IsdnCardState *cs, u_char *buf, int len) {
+ int count, fp;
+ u_char *msg = buf;
+
+ if (!len)
+ return;
+ if (len > (MAX_MODEM_BUF - cs->hw.elsa.transcnt)) {
+ return;
+ }
+ fp = cs->hw.elsa.transcnt + cs->hw.elsa.transp;
+ fp &= (MAX_MODEM_BUF -1);
+ count = len;
+ if (count > MAX_MODEM_BUF - fp) {
+ count = MAX_MODEM_BUF - fp;
+ memcpy(cs->hw.elsa.transbuf + fp, msg, count);
+ cs->hw.elsa.transcnt += count;
+ msg += count;
+ count = len - count;
+ fp = 0;
+ }
+ memcpy(cs->hw.elsa.transbuf + fp, msg, count);
+ cs->hw.elsa.transcnt += count;
+ if (cs->hw.elsa.transcnt &&
+ !(cs->hw.elsa.IER & UART_IER_THRI)) {
+ cs->hw.elsa.IER |= UART_IER_THRI;
+ serial_outp(cs, UART_IER, cs->hw.elsa.IER);
+ }
+}
+
+static void
+modem_set_init(struct IsdnCardState *cs) {
+ int timeout;
+
+#define RCV_DELAY 20000
+ modem_write_cmd(cs, MInit_1, strlen(MInit_1));
+ timeout = 1000;
+ while(timeout-- && cs->hw.elsa.transcnt)
+ udelay(1000);
+ debugl1(cs, "msi tout=%d", timeout);
+ udelay(RCV_DELAY);
+ modem_write_cmd(cs, MInit_2, strlen(MInit_2));
+ timeout = 1000;
+ while(timeout-- && cs->hw.elsa.transcnt)
+ udelay(1000);
+ debugl1(cs, "msi tout=%d", timeout);
+ udelay(RCV_DELAY);
+ modem_write_cmd(cs, MInit_3, strlen(MInit_3));
+ timeout = 1000;
+ while(timeout-- && cs->hw.elsa.transcnt)
+ udelay(1000);
+ debugl1(cs, "msi tout=%d", timeout);
+ udelay(RCV_DELAY);
+ modem_write_cmd(cs, MInit_4, strlen(MInit_4));
+ timeout = 1000;
+ while(timeout-- && cs->hw.elsa.transcnt)
+ udelay(1000);
+ debugl1(cs, "msi tout=%d", timeout);
+ udelay(RCV_DELAY );
+ modem_write_cmd(cs, MInit_5, strlen(MInit_5));
+ timeout = 1000;
+ while(timeout-- && cs->hw.elsa.transcnt)
+ udelay(1000);
+ debugl1(cs, "msi tout=%d", timeout);
+ udelay(RCV_DELAY);
+ modem_write_cmd(cs, MInit_6, strlen(MInit_6));
+ timeout = 1000;
+ while(timeout-- && cs->hw.elsa.transcnt)
+ udelay(1000);
+ debugl1(cs, "msi tout=%d", timeout);
+ udelay(RCV_DELAY);
+ modem_write_cmd(cs, MInit_7, strlen(MInit_7));
+ timeout = 1000;
+ while(timeout-- && cs->hw.elsa.transcnt)
+ udelay(1000);
+ debugl1(cs, "msi tout=%d", timeout);
+ udelay(RCV_DELAY);
+}
+
+static void
+modem_set_dial(struct IsdnCardState *cs, int outgoing) {
+ int timeout;
+#define RCV_DELAY 20000
+
+ modem_write_cmd(cs, MInit_speed28800, strlen(MInit_speed28800));
+ timeout = 1000;
+ while(timeout-- && cs->hw.elsa.transcnt)
+ udelay(1000);
+ debugl1(cs, "msi tout=%d", timeout);
+ udelay(RCV_DELAY);
+ if (outgoing)
+ modem_write_cmd(cs, MInit_dialout, strlen(MInit_dialout));
+ else
+ modem_write_cmd(cs, MInit_dialin, strlen(MInit_dialin));
+ timeout = 1000;
+ while(timeout-- && cs->hw.elsa.transcnt)
+ udelay(1000);
+ debugl1(cs, "msi tout=%d", timeout);
+ udelay(RCV_DELAY);
+}
+
+static void
+modem_l2l1(struct PStack *st, int pr, void *arg)
+{
+ struct BCState *bcs = st->l1.bcs;
+ struct sk_buff *skb = arg;
+ u_long flags;
+
+ if (pr == (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);
+ bcs->hw.hscx.count = 0;
+ write_modem(bcs);
+ }
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
+ } else if (pr == (PH_ACTIVATE | REQUEST)) {
+ test_and_set_bit(BC_FLG_ACTIV, &bcs->Flag);
+ st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+ set_arcofi(bcs->cs, st->l1.bc);
+ mstartup(bcs->cs);
+ modem_set_dial(bcs->cs, test_bit(FLG_ORIG, &st->l2.flag));
+ bcs->cs->hw.elsa.MFlag=2;
+ } else if (pr == (PH_DEACTIVATE | REQUEST)) {
+ test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+ bcs->cs->dc.isac.arcofi_bc = st->l1.bc;
+ arcofi_fsm(bcs->cs, ARCOFI_START, &ARCOFI_XOP_0);
+ interruptible_sleep_on(&bcs->cs->dc.isac.arcofi_wait);
+ bcs->cs->hw.elsa.MFlag=1;
+ } else {
+ printk(KERN_WARNING"ElsaSer: unknown pr %x\n", pr);
+ }
+}
+
+static int
+setstack_elsa(struct PStack *st, struct BCState *bcs)
+{
+
+ bcs->channel = st->l1.bc;
+ switch (st->l1.mode) {
+ case L1_MODE_HDLC:
+ case L1_MODE_TRANS:
+ if (open_hscxstate(st->l1.hardware, bcs))
+ return (-1);
+ st->l2.l2l1 = hscx_l2l1;
+ break;
+ case L1_MODE_MODEM:
+ bcs->mode = L1_MODE_MODEM;
+ if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+ bcs->hw.hscx.rcvbuf = bcs->cs->hw.elsa.rcvbuf;
+ 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);
+ bcs->event = 0;
+ bcs->hw.hscx.rcvidx = 0;
+ bcs->tx_cnt = 0;
+ bcs->cs->hw.elsa.bcs = bcs;
+ st->l2.l2l1 = modem_l2l1;
+ break;
+ }
+ st->l1.bcs = bcs;
+ setstack_manager(st);
+ bcs->st = st;
+ setstack_l1_B(st);
+ return (0);
+}
+
+static void
+init_modem(struct IsdnCardState *cs) {
+
+ cs->bcs[0].BC_SetStack = setstack_elsa;
+ cs->bcs[1].BC_SetStack = setstack_elsa;
+ cs->bcs[0].BC_Close = close_elsastate;
+ cs->bcs[1].BC_Close = close_elsastate;
+ if (!(cs->hw.elsa.rcvbuf = kmalloc(MAX_MODEM_BUF,
+ GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "Elsa: No modem mem hw.elsa.rcvbuf\n");
+ return;
+ }
+ if (!(cs->hw.elsa.transbuf = kmalloc(MAX_MODEM_BUF,
+ GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "Elsa: No modem mem hw.elsa.transbuf\n");
+ kfree(cs->hw.elsa.rcvbuf);
+ cs->hw.elsa.rcvbuf = NULL;
+ return;
+ }
+ if (mstartup(cs)) {
+ printk(KERN_WARNING "Elsa: problem startup modem\n");
+ }
+ modem_set_init(cs);
+}
+
+static void
+release_modem(struct IsdnCardState *cs) {
+
+ cs->hw.elsa.MFlag = 0;
+ if (cs->hw.elsa.transbuf) {
+ if (cs->hw.elsa.rcvbuf) {
+ mshutdown(cs);
+ kfree(cs->hw.elsa.rcvbuf);
+ cs->hw.elsa.rcvbuf = NULL;
+ }
+ kfree(cs->hw.elsa.transbuf);
+ cs->hw.elsa.transbuf = NULL;
+ }
+}
diff --git a/drivers/isdn/hisax/enternow_pci.c b/drivers/isdn/hisax/enternow_pci.c
new file mode 100644
index 0000000..39f421e
--- /dev/null
+++ b/drivers/isdn/hisax/enternow_pci.c
@@ -0,0 +1,424 @@
+/* enternow_pci.c,v 0.99 2001/10/02
+ *
+ * enternow_pci.c Card-specific routines for
+ * Formula-n enter:now ISDN PCI ab
+ * Gerdes AG Power ISDN PCI
+ * Woerltronic SA 16 PCI
+ * (based on HiSax driver by Karsten Keil)
+ *
+ * Author Christoph Ersfeld <info@formula-n.de>
+ * Formula-n Europe AG (www.formula-n.com)
+ * previously Gerdes AG
+ *
+ *
+ * This file is (c) under GNU PUBLIC LICENSE
+ *
+ * Notes:
+ * This driver interfaces to netjet.c which performs B-channel
+ * processing.
+ *
+ * Version 0.99 is the first release of this driver and there are
+ * certainly a few bugs.
+ * It isn't testet on linux 2.4 yet, so consider this code to be
+ * beta.
+ *
+ * Please don't report me any malfunction without sending
+ * (compressed) debug-logs.
+ * It would be nearly impossible to retrace it.
+ *
+ * Log D-channel-processing as follows:
+ *
+ * 1. Load hisax with card-specific parameters, this example ist for
+ * Formula-n enter:now ISDN PCI and compatible
+ * (f.e. Gerdes Power ISDN PCI)
+ *
+ * modprobe hisax type=41 protocol=2 id=gerdes
+ *
+ * if you chose an other value for id, you need to modify the
+ * code below, too.
+ *
+ * 2. set debug-level
+ *
+ * hisaxctrl gerdes 1 0x3ff
+ * hisaxctrl gerdes 11 0x4f
+ * cat /dev/isdnctrl >> ~/log &
+ *
+ * Please take also a look into /var/log/messages if there is
+ * anything importand concerning HISAX.
+ *
+ *
+ * Credits:
+ * Programming the driver for Formula-n enter:now ISDN PCI and
+ * necessary the driver for the used Amd 7930 D-channel-controller
+ * was spnsored by Formula-n Europe AG.
+ * Thanks to Karsten Keil and Petr Novak, who gave me support in
+ * Hisax-specific questions.
+ * I want so say special thanks to Carl-Friedrich Braun, who had to
+ * answer a lot of questions about generally ISDN and about handling
+ * of the Amd-Chip.
+ *
+ */
+
+
+#include "hisax.h"
+#include "isac.h"
+#include "isdnl1.h"
+#include "amd7930_fn.h"
+#include <linux/interrupt.h>
+#include <linux/ppp_defs.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include "netjet.h"
+
+
+
+static const char *enternow_pci_rev = "$Revision: 1.1.4.5 $";
+
+
+/* for PowerISDN PCI */
+#define TJ_AMD_IRQ 0x20
+#define TJ_LED1 0x40
+#define TJ_LED2 0x80
+
+
+/* The window to [the] AMD [chip]...
+ * From address hw.njet.base + TJ_AMD_PORT onwards, the AMD
+ * maps [consecutive/multiple] 8 bits into the TigerJet I/O space
+ * -> 0x01 of the AMD at hw.njet.base + 0C4 */
+#define TJ_AMD_PORT 0xC0
+
+
+
+/* *************************** I/O-Interface functions ************************************* */
+
+
+/* cs->readisac, macro rByteAMD */
+static unsigned char
+ReadByteAmd7930(struct IsdnCardState *cs, unsigned char offset)
+{
+ /* direct register */
+ if(offset < 8)
+ return (inb(cs->hw.njet.isac + 4*offset));
+
+ /* indirect register */
+ else {
+ outb(offset, cs->hw.njet.isac + 4*AMD_CR);
+ return(inb(cs->hw.njet.isac + 4*AMD_DR));
+ }
+}
+
+/* cs->writeisac, macro wByteAMD */
+static void
+WriteByteAmd7930(struct IsdnCardState *cs, unsigned char offset, unsigned char value)
+{
+ /* direct register */
+ if(offset < 8)
+ outb(value, cs->hw.njet.isac + 4*offset);
+
+ /* indirect register */
+ else {
+ outb(offset, cs->hw.njet.isac + 4*AMD_CR);
+ outb(value, cs->hw.njet.isac + 4*AMD_DR);
+ }
+}
+
+
+static void
+enpci_setIrqMask(struct IsdnCardState *cs, unsigned char val) {
+ if (!val)
+ outb(0x00, cs->hw.njet.base+NETJET_IRQMASK1);
+ else
+ outb(TJ_AMD_IRQ, cs->hw.njet.base+NETJET_IRQMASK1);
+}
+
+
+static unsigned char dummyrr(struct IsdnCardState *cs, int chan, unsigned char off)
+{
+ return(5);
+}
+
+static void dummywr(struct IsdnCardState *cs, int chan, unsigned char off, unsigned char value)
+{
+
+}
+
+
+/* ******************************************************************************** */
+
+
+static void
+reset_enpci(struct IsdnCardState *cs)
+{
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "enter:now PCI: reset");
+
+ /* Reset on, (also for AMD) */
+ cs->hw.njet.ctrl_reg = 0x07;
+ outb(cs->hw.njet.ctrl_reg, cs->hw.njet.base + NETJET_CTRL);
+ mdelay(20);
+ /* Reset off */
+ cs->hw.njet.ctrl_reg = 0x30;
+ outb(cs->hw.njet.ctrl_reg, cs->hw.njet.base + NETJET_CTRL);
+ /* 20ms delay */
+ mdelay(20);
+ cs->hw.njet.auxd = 0; // LED-status
+ cs->hw.njet.dmactrl = 0;
+ outb(~TJ_AMD_IRQ, cs->hw.njet.base + NETJET_AUXCTRL);
+ outb(TJ_AMD_IRQ, cs->hw.njet.base + NETJET_IRQMASK1);
+ outb(cs->hw.njet.auxd, cs->hw.njet.auxa); // LED off
+}
+
+
+static int
+enpci_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ u_long flags;
+ unsigned char *chan;
+
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "enter:now PCI: card_msg: 0x%04X", mt);
+
+ switch (mt) {
+ case CARD_RESET:
+ spin_lock_irqsave(&cs->lock, flags);
+ reset_enpci(cs);
+ Amd7930_init(cs);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case CARD_RELEASE:
+ release_io_netjet(cs);
+ break;
+ case CARD_INIT:
+ reset_enpci(cs);
+ inittiger(cs);
+ /* irq must be on here */
+ Amd7930_init(cs);
+ break;
+ case CARD_TEST:
+ break;
+ case MDL_ASSIGN:
+ /* TEI assigned, LED1 on */
+ cs->hw.njet.auxd = TJ_AMD_IRQ << 1;
+ outb(cs->hw.njet.auxd, cs->hw.njet.base + NETJET_AUXDATA);
+ break;
+ case MDL_REMOVE:
+ /* TEI removed, LEDs off */
+ cs->hw.njet.auxd = 0;
+ outb(0x00, cs->hw.njet.base + NETJET_AUXDATA);
+ break;
+ case MDL_BC_ASSIGN:
+ /* activate B-channel */
+ chan = (unsigned char *)arg;
+
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "enter:now PCI: assign phys. BC %d in AMD LMR1", *chan);
+
+ cs->dc.amd7930.ph_command(cs, (cs->dc.amd7930.lmr1 | (*chan + 1)), "MDL_BC_ASSIGN");
+ /* at least one b-channel in use, LED 2 on */
+ cs->hw.njet.auxd |= TJ_AMD_IRQ << 2;
+ outb(cs->hw.njet.auxd, cs->hw.njet.base + NETJET_AUXDATA);
+ break;
+ case MDL_BC_RELEASE:
+ /* deactivate B-channel */
+ chan = (unsigned char *)arg;
+
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "enter:now PCI: release phys. BC %d in Amd LMR1", *chan);
+
+ cs->dc.amd7930.ph_command(cs, (cs->dc.amd7930.lmr1 & ~(*chan + 1)), "MDL_BC_RELEASE");
+ /* no b-channel active -> LED2 off */
+ if (!(cs->dc.amd7930.lmr1 & 3)) {
+ cs->hw.njet.auxd &= ~(TJ_AMD_IRQ << 2);
+ outb(cs->hw.njet.auxd, cs->hw.njet.base + NETJET_AUXDATA);
+ }
+ break;
+ default:
+ break;
+
+ }
+ return(0);
+}
+
+static irqreturn_t
+enpci_interrupt(int intno, void *dev_id)
+{
+ struct IsdnCardState *cs = dev_id;
+ unsigned char s0val, s1val, ir;
+ u_long flags;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ s1val = inb(cs->hw.njet.base + NETJET_IRQSTAT1);
+
+ /* AMD threw an interrupt */
+ if (!(s1val & TJ_AMD_IRQ)) {
+ /* read and clear interrupt-register */
+ ir = ReadByteAmd7930(cs, 0x00);
+ Amd7930_interrupt(cs, ir);
+ s1val = 1;
+ } else
+ s1val = 0;
+ s0val = inb(cs->hw.njet.base + NETJET_IRQSTAT0);
+ if ((s0val | s1val)==0) { // shared IRQ
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_NONE;
+ }
+ if (s0val)
+ outb(s0val, cs->hw.njet.base + NETJET_IRQSTAT0);
+
+ /* DMA-Interrupt: B-channel-stuff */
+ /* set bits in sval to indicate which page is free */
+ if (inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR) <
+ inl(cs->hw.njet.base + NETJET_DMA_WRITE_IRQ))
+ /* the 2nd write page is free */
+ s0val = 0x08;
+ else /* the 1st write page is free */
+ s0val = 0x04;
+ if (inl(cs->hw.njet.base + NETJET_DMA_READ_ADR) <
+ inl(cs->hw.njet.base + NETJET_DMA_READ_IRQ))
+ /* the 2nd read page is free */
+ s0val = s0val | 0x02;
+ else /* the 1st read page is free */
+ s0val = s0val | 0x01;
+ if (s0val != cs->hw.njet.last_is0) /* we have a DMA interrupt */
+ {
+ if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+ }
+ cs->hw.njet.irqstat0 = s0val;
+ if ((cs->hw.njet.irqstat0 & NETJET_IRQM0_READ) !=
+ (cs->hw.njet.last_is0 & NETJET_IRQM0_READ))
+ /* we have a read dma int */
+ read_tiger(cs);
+ if ((cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE) !=
+ (cs->hw.njet.last_is0 & NETJET_IRQM0_WRITE))
+ /* we have a write dma int */
+ write_tiger(cs);
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static int __devinit en_pci_probe(struct pci_dev *dev_netjet,
+ struct IsdnCardState *cs)
+{
+ if (pci_enable_device(dev_netjet))
+ return(0);
+ cs->irq = dev_netjet->irq;
+ if (!cs->irq) {
+ printk(KERN_WARNING "enter:now PCI: No IRQ for PCI card found\n");
+ return(0);
+ }
+ cs->hw.njet.base = pci_resource_start(dev_netjet, 0);
+ if (!cs->hw.njet.base) {
+ printk(KERN_WARNING "enter:now PCI: No IO-Adr for PCI card found\n");
+ return(0);
+ }
+ /* checks Sub-Vendor ID because system crashes with Traverse-Card */
+ if ((dev_netjet->subsystem_vendor != 0x55) ||
+ (dev_netjet->subsystem_device != 0x02)) {
+ printk(KERN_WARNING "enter:now: You tried to load this driver with an incompatible TigerJet-card\n");
+ printk(KERN_WARNING "Use type=20 for Traverse NetJet PCI Card.\n");
+ return(0);
+ }
+
+ return(1);
+}
+
+static void __devinit en_cs_init(struct IsdnCard *card,
+ struct IsdnCardState *cs)
+{
+ cs->hw.njet.auxa = cs->hw.njet.base + NETJET_AUXDATA;
+ cs->hw.njet.isac = cs->hw.njet.base + 0xC0; // Fenster zum AMD
+
+ /* Reset an */
+ cs->hw.njet.ctrl_reg = 0x07; // geändert von 0xff
+ outb(cs->hw.njet.ctrl_reg, cs->hw.njet.base + NETJET_CTRL);
+ /* 20 ms Pause */
+ mdelay(20);
+
+ cs->hw.njet.ctrl_reg = 0x30; /* Reset Off and status read clear */
+ outb(cs->hw.njet.ctrl_reg, cs->hw.njet.base + NETJET_CTRL);
+ mdelay(10);
+
+ cs->hw.njet.auxd = 0x00; // war 0xc0
+ cs->hw.njet.dmactrl = 0;
+
+ outb(~TJ_AMD_IRQ, cs->hw.njet.base + NETJET_AUXCTRL);
+ outb(TJ_AMD_IRQ, cs->hw.njet.base + NETJET_IRQMASK1);
+ outb(cs->hw.njet.auxd, cs->hw.njet.auxa);
+}
+
+static int __devinit en_cs_init_rest(struct IsdnCard *card,
+ struct IsdnCardState *cs)
+{
+ const int bytecnt = 256;
+
+ printk(KERN_INFO
+ "enter:now PCI: PCI card configured at 0x%lx IRQ %d\n",
+ cs->hw.njet.base, cs->irq);
+ if (!request_region(cs->hw.njet.base, bytecnt, "Fn_ISDN")) {
+ printk(KERN_WARNING
+ "HiSax: enter:now config port %lx-%lx already in use\n",
+ cs->hw.njet.base,
+ cs->hw.njet.base + bytecnt);
+ return (0);
+ }
+
+ setup_Amd7930(cs);
+ cs->hw.njet.last_is0 = 0;
+ /* macro rByteAMD */
+ cs->readisac = &ReadByteAmd7930;
+ /* macro wByteAMD */
+ cs->writeisac = &WriteByteAmd7930;
+ cs->dc.amd7930.setIrqMask = &enpci_setIrqMask;
+
+ cs->BC_Read_Reg = &dummyrr;
+ cs->BC_Write_Reg = &dummywr;
+ cs->BC_Send_Data = &netjet_fill_dma;
+ cs->cardmsg = &enpci_card_msg;
+ cs->irq_func = &enpci_interrupt;
+ cs->irq_flags |= IRQF_SHARED;
+
+ return (1);
+}
+
+static struct pci_dev *dev_netjet __devinitdata = NULL;
+
+/* called by config.c */
+int __devinit
+setup_enternow_pci(struct IsdnCard *card)
+{
+ int ret;
+ struct IsdnCardState *cs = card->cs;
+ char tmp[64];
+
+#ifdef __BIG_ENDIAN
+#error "not running on big endian machines now"
+#endif
+
+ strcpy(tmp, enternow_pci_rev);
+ printk(KERN_INFO "HiSax: Formula-n Europe AG enter:now ISDN PCI driver Rev. %s\n", HiSax_getrev(tmp));
+ if (cs->typ != ISDN_CTYPE_ENTERNOW)
+ return(0);
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+
+ for ( ;; )
+ {
+ if ((dev_netjet = pci_find_device(PCI_VENDOR_ID_TIGERJET,
+ PCI_DEVICE_ID_TIGERJET_300, dev_netjet))) {
+ ret = en_pci_probe(dev_netjet, cs);
+ if (!ret)
+ return(0);
+ } else {
+ printk(KERN_WARNING "enter:now PCI: No PCI card found\n");
+ return(0);
+ }
+
+ en_cs_init(card, cs);
+ break;
+ }
+
+ return en_cs_init_rest(card, cs);
+}
diff --git a/drivers/isdn/hisax/fsm.c b/drivers/isdn/hisax/fsm.c
new file mode 100644
index 0000000..34fade9
--- /dev/null
+++ b/drivers/isdn/hisax/fsm.c
@@ -0,0 +1,161 @@
+/* $Id: fsm.c,v 1.14.6.4 2001/09/23 22:24:47 kai Exp $
+ *
+ * Finite state machine
+ *
+ * Author Karsten Keil
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ * by Kai Germaschewski <kai.germaschewski@gmx.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to Jan den Ouden
+ * Fritz Elfert
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include "hisax.h"
+
+#define FSM_TIMER_DEBUG 0
+
+int
+FsmNew(struct Fsm *fsm, struct FsmNode *fnlist, int fncount)
+{
+ int i;
+
+ fsm->jumpmatrix = (FSMFNPTR *)
+ kzalloc(sizeof (FSMFNPTR) * fsm->state_count * fsm->event_count, GFP_KERNEL);
+ if (!fsm->jumpmatrix)
+ return -ENOMEM;
+
+ for (i = 0; i < fncount; i++)
+ if ((fnlist[i].state>=fsm->state_count) || (fnlist[i].event>=fsm->event_count)) {
+ printk(KERN_ERR "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;
+ return 0;
+}
+
+void
+FsmFree(struct Fsm *fsm)
+{
+ kfree((void *) fsm->jumpmatrix);
+}
+
+int
+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 "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 routine",
+ fi->fsm->strState[fi->state],
+ fi->fsm->strEvent[event]);
+ return (!0);
+ }
+}
+
+void
+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
+ FsmEvent(ft->fi, ft->event, ft->arg);
+}
+
+void
+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, "FsmInitTimer %lx", (long) ft);
+#endif
+ init_timer(&ft->tl);
+}
+
+void
+FsmDelTimer(struct FsmTimer *ft, int where)
+{
+#if FSM_TIMER_DEBUG
+ if (ft->fi->debug)
+ ft->fi->printdebug(ft->fi, "FsmDelTimer %lx %d", (long) ft, where);
+#endif
+ del_timer(&ft->tl);
+}
+
+int
+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, "FsmAddTimer %lx %d %d",
+ (long) ft, millisec, where);
+#endif
+
+ if (timer_pending(&ft->tl)) {
+ printk(KERN_WARNING "FsmAddTimer: timer already active!\n");
+ ft->fi->printdebug(ft->fi, "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
+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, "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);
+}
diff --git a/drivers/isdn/hisax/fsm.h b/drivers/isdn/hisax/fsm.h
new file mode 100644
index 0000000..f02f7da
--- /dev/null
+++ b/drivers/isdn/hisax/fsm.h
@@ -0,0 +1,61 @@
+/* $Id: fsm.h,v 1.3.2.2 2001/09/23 22:24:47 kai Exp $
+ *
+ * Finite state machine
+ *
+ * Author Karsten Keil
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ * by Kai Germaschewski <kai.germaschewski@gmx.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef __FSM_H__
+#define __FSM_H__
+
+#include <linux/timer.h>
+
+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;
+};
+
+int FsmNew(struct Fsm *fsm, struct FsmNode *fnlist, int fncount);
+void FsmFree(struct Fsm *fsm);
+int FsmEvent(struct FsmInst *fi, int event, void *arg);
+void FsmChangeState(struct FsmInst *fi, int newstate);
+void FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft);
+int FsmAddTimer(struct FsmTimer *ft, int millisec, int event,
+ void *arg, int where);
+void FsmRestartTimer(struct FsmTimer *ft, int millisec, int event,
+ void *arg, int where);
+void FsmDelTimer(struct FsmTimer *ft, int where);
+
+#endif
diff --git a/drivers/isdn/hisax/gazel.c b/drivers/isdn/hisax/gazel.c
new file mode 100644
index 0000000..0ea3b46
--- /dev/null
+++ b/drivers/isdn/hisax/gazel.c
@@ -0,0 +1,690 @@
+/* $Id: gazel.c,v 2.19.2.4 2004/01/14 16:04:48 keil Exp $
+ *
+ * low level stuff for Gazel isdn cards
+ *
+ * Author BeWan Systems
+ * based on source code from Karsten Keil
+ * Copyright by BeWan Systems
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+#include "ipac.h"
+#include <linux/pci.h>
+
+static const char *gazel_revision = "$Revision: 2.19.2.4 $";
+
+#define R647 1
+#define R685 2
+#define R753 3
+#define R742 4
+
+#define PLX_CNTRL 0x50 /* registre de controle PLX */
+#define RESET_GAZEL 0x4
+#define RESET_9050 0x40000000
+#define PLX_INCSR 0x4C /* registre d'IT du 9050 */
+#define INT_ISAC_EN 0x8 /* 1 = enable IT isac */
+#define INT_ISAC 0x20 /* 1 = IT isac en cours */
+#define INT_HSCX_EN 0x1 /* 1 = enable IT hscx */
+#define INT_HSCX 0x4 /* 1 = IT hscx en cours */
+#define INT_PCI_EN 0x40 /* 1 = enable IT PCI */
+#define INT_IPAC_EN 0x3 /* enable IT ipac */
+
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+static inline u_char
+readreg(unsigned int adr, u_short off)
+{
+ return bytein(adr + off);
+}
+
+static inline void
+writereg(unsigned int adr, u_short off, u_char data)
+{
+ byteout(adr + off, data);
+}
+
+
+static inline void
+read_fifo(unsigned int adr, u_char * data, int size)
+{
+ insb(adr, data, size);
+}
+
+static void
+write_fifo(unsigned int adr, u_char * data, int size)
+{
+ outsb(adr, data, size);
+}
+
+static inline u_char
+readreg_ipac(unsigned int adr, u_short off)
+{
+ register u_char ret;
+
+ byteout(adr, off);
+ ret = bytein(adr + 4);
+ return ret;
+}
+
+static inline void
+writereg_ipac(unsigned int adr, u_short off, u_char data)
+{
+ byteout(adr, off);
+ byteout(adr + 4, data);
+}
+
+
+static inline void
+read_fifo_ipac(unsigned int adr, u_short off, u_char * data, int size)
+{
+ byteout(adr, off);
+ insb(adr + 4, data, size);
+}
+
+static void
+write_fifo_ipac(unsigned int adr, u_short off, u_char * data, int size)
+{
+ byteout(adr, off);
+ outsb(adr + 4, data, size);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+ u_short off2 = offset;
+
+ switch (cs->subtyp) {
+ case R647:
+ off2 = ((off2 << 8 & 0xf000) | (off2 & 0xf));
+ case R685:
+ return (readreg(cs->hw.gazel.isac, off2));
+ case R753:
+ case R742:
+ return (readreg_ipac(cs->hw.gazel.ipac, 0x80 + off2));
+ }
+ return 0;
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ u_short off2 = offset;
+
+ switch (cs->subtyp) {
+ case R647:
+ off2 = ((off2 << 8 & 0xf000) | (off2 & 0xf));
+ case R685:
+ writereg(cs->hw.gazel.isac, off2, value);
+ break;
+ case R753:
+ case R742:
+ writereg_ipac(cs->hw.gazel.ipac, 0x80 + off2, value);
+ break;
+ }
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ switch (cs->subtyp) {
+ case R647:
+ case R685:
+ read_fifo(cs->hw.gazel.isacfifo, data, size);
+ break;
+ case R753:
+ case R742:
+ read_fifo_ipac(cs->hw.gazel.ipac, 0x80, data, size);
+ break;
+ }
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ switch (cs->subtyp) {
+ case R647:
+ case R685:
+ write_fifo(cs->hw.gazel.isacfifo, data, size);
+ break;
+ case R753:
+ case R742:
+ write_fifo_ipac(cs->hw.gazel.ipac, 0x80, data, size);
+ break;
+ }
+}
+
+static void
+ReadHSCXfifo(struct IsdnCardState *cs, int hscx, u_char * data, int size)
+{
+ switch (cs->subtyp) {
+ case R647:
+ case R685:
+ read_fifo(cs->hw.gazel.hscxfifo[hscx], data, size);
+ break;
+ case R753:
+ case R742:
+ read_fifo_ipac(cs->hw.gazel.ipac, hscx * 0x40, data, size);
+ break;
+ }
+}
+
+static void
+WriteHSCXfifo(struct IsdnCardState *cs, int hscx, u_char * data, int size)
+{
+ switch (cs->subtyp) {
+ case R647:
+ case R685:
+ write_fifo(cs->hw.gazel.hscxfifo[hscx], data, size);
+ break;
+ case R753:
+ case R742:
+ write_fifo_ipac(cs->hw.gazel.ipac, hscx * 0x40, data, size);
+ break;
+ }
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+ u_short off2 = offset;
+
+ switch (cs->subtyp) {
+ case R647:
+ off2 = ((off2 << 8 & 0xf000) | (off2 & 0xf));
+ case R685:
+ return (readreg(cs->hw.gazel.hscx[hscx], off2));
+ case R753:
+ case R742:
+ return (readreg_ipac(cs->hw.gazel.ipac, hscx * 0x40 + off2));
+ }
+ return 0;
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+ u_short off2 = offset;
+
+ switch (cs->subtyp) {
+ case R647:
+ off2 = ((off2 << 8 & 0xf000) | (off2 & 0xf));
+ case R685:
+ writereg(cs->hw.gazel.hscx[hscx], off2, value);
+ break;
+ case R753:
+ case R742:
+ writereg_ipac(cs->hw.gazel.ipac, hscx * 0x40 + off2, value);
+ break;
+ }
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) ReadHSCX(cs, nr, reg)
+#define WRITEHSCX(cs, nr, reg, data) WriteHSCX(cs, nr, reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) ReadHSCXfifo(cs, nr, ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) WriteHSCXfifo(cs, nr, ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+gazel_interrupt(int intno, void *dev_id)
+{
+#define MAXCOUNT 5
+ struct IsdnCardState *cs = dev_id;
+ u_char valisac, valhscx;
+ int count = 0;
+ u_long flags;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ do {
+ valhscx = ReadHSCX(cs, 1, HSCX_ISTA);
+ if (valhscx)
+ hscx_int_main(cs, valhscx);
+ valisac = ReadISAC(cs, ISAC_ISTA);
+ if (valisac)
+ isac_interrupt(cs, valisac);
+ count++;
+ } while ((valhscx || valisac) && (count < MAXCOUNT));
+
+ WriteHSCX(cs, 0, HSCX_MASK, 0xFF);
+ WriteHSCX(cs, 1, HSCX_MASK, 0xFF);
+ WriteISAC(cs, ISAC_MASK, 0xFF);
+ WriteISAC(cs, ISAC_MASK, 0x0);
+ WriteHSCX(cs, 0, HSCX_MASK, 0x0);
+ WriteHSCX(cs, 1, HSCX_MASK, 0x0);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+
+static irqreturn_t
+gazel_interrupt_ipac(int intno, void *dev_id)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char ista, val;
+ int count = 0;
+ u_long flags;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ ista = ReadISAC(cs, IPAC_ISTA - 0x80);
+ do {
+ if (ista & 0x0f) {
+ val = ReadHSCX(cs, 1, HSCX_ISTA);
+ if (ista & 0x01)
+ val |= 0x01;
+ if (ista & 0x04)
+ val |= 0x02;
+ if (ista & 0x08)
+ val |= 0x04;
+ if (val) {
+ hscx_int_main(cs, val);
+ }
+ }
+ if (ista & 0x20) {
+ val = 0xfe & ReadISAC(cs, ISAC_ISTA);
+ if (val) {
+ isac_interrupt(cs, val);
+ }
+ }
+ if (ista & 0x10) {
+ val = 0x01;
+ isac_interrupt(cs, val);
+ }
+ ista = ReadISAC(cs, IPAC_ISTA - 0x80);
+ count++;
+ }
+ while ((ista & 0x3f) && (count < MAXCOUNT));
+
+ WriteISAC(cs, IPAC_MASK - 0x80, 0xFF);
+ WriteISAC(cs, IPAC_MASK - 0x80, 0xC0);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static void
+release_io_gazel(struct IsdnCardState *cs)
+{
+ unsigned int i;
+
+ switch (cs->subtyp) {
+ case R647:
+ for (i = 0x0000; i < 0xC000; i += 0x1000)
+ release_region(i + cs->hw.gazel.hscx[0], 16);
+ release_region(0xC000 + cs->hw.gazel.hscx[0], 1);
+ break;
+
+ case R685:
+ release_region(cs->hw.gazel.hscx[0], 0x100);
+ release_region(cs->hw.gazel.cfg_reg, 0x80);
+ break;
+
+ case R753:
+ release_region(cs->hw.gazel.ipac, 0x8);
+ release_region(cs->hw.gazel.cfg_reg, 0x80);
+ break;
+
+ case R742:
+ release_region(cs->hw.gazel.ipac, 8);
+ break;
+ }
+}
+
+static int
+reset_gazel(struct IsdnCardState *cs)
+{
+ unsigned long plxcntrl, addr = cs->hw.gazel.cfg_reg;
+
+ switch (cs->subtyp) {
+ case R647:
+ writereg(addr, 0, 0);
+ HZDELAY(10);
+ writereg(addr, 0, 1);
+ HZDELAY(2);
+ break;
+ case R685:
+ plxcntrl = inl(addr + PLX_CNTRL);
+ plxcntrl |= (RESET_9050 + RESET_GAZEL);
+ outl(plxcntrl, addr + PLX_CNTRL);
+ plxcntrl &= ~(RESET_9050 + RESET_GAZEL);
+ HZDELAY(4);
+ outl(plxcntrl, addr + PLX_CNTRL);
+ HZDELAY(10);
+ outb(INT_ISAC_EN + INT_HSCX_EN + INT_PCI_EN, addr + PLX_INCSR);
+ break;
+ case R753:
+ plxcntrl = inl(addr + PLX_CNTRL);
+ plxcntrl |= (RESET_9050 + RESET_GAZEL);
+ outl(plxcntrl, addr + PLX_CNTRL);
+ plxcntrl &= ~(RESET_9050 + RESET_GAZEL);
+ WriteISAC(cs, IPAC_POTA2 - 0x80, 0x20);
+ HZDELAY(4);
+ outl(plxcntrl, addr + PLX_CNTRL);
+ HZDELAY(10);
+ WriteISAC(cs, IPAC_POTA2 - 0x80, 0x00);
+ WriteISAC(cs, IPAC_ACFG - 0x80, 0xff);
+ WriteISAC(cs, IPAC_AOE - 0x80, 0x0);
+ WriteISAC(cs, IPAC_MASK - 0x80, 0xff);
+ WriteISAC(cs, IPAC_CONF - 0x80, 0x1);
+ outb(INT_IPAC_EN + INT_PCI_EN, addr + PLX_INCSR);
+ WriteISAC(cs, IPAC_MASK - 0x80, 0xc0);
+ break;
+ case R742:
+ WriteISAC(cs, IPAC_POTA2 - 0x80, 0x20);
+ HZDELAY(4);
+ WriteISAC(cs, IPAC_POTA2 - 0x80, 0x00);
+ WriteISAC(cs, IPAC_ACFG - 0x80, 0xff);
+ WriteISAC(cs, IPAC_AOE - 0x80, 0x0);
+ WriteISAC(cs, IPAC_MASK - 0x80, 0xff);
+ WriteISAC(cs, IPAC_CONF - 0x80, 0x1);
+ WriteISAC(cs, IPAC_MASK - 0x80, 0xc0);
+ break;
+ }
+ return (0);
+}
+
+static int
+Gazel_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ u_long flags;
+
+ switch (mt) {
+ case CARD_RESET:
+ spin_lock_irqsave(&cs->lock, flags);
+ reset_gazel(cs);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return (0);
+ case CARD_RELEASE:
+ release_io_gazel(cs);
+ return (0);
+ case CARD_INIT:
+ spin_lock_irqsave(&cs->lock, flags);
+ inithscxisac(cs, 1);
+ if ((cs->subtyp==R647)||(cs->subtyp==R685)) {
+ int i;
+ for (i=0;i<(2+MAX_WAITING_CALLS);i++) {
+ cs->bcs[i].hw.hscx.tsaxr0 = 0x1f;
+ cs->bcs[i].hw.hscx.tsaxr1 = 0x23;
+ }
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return (0);
+ case CARD_TEST:
+ return (0);
+ }
+ return (0);
+}
+
+static int
+reserve_regions(struct IsdnCard *card, struct IsdnCardState *cs)
+{
+ unsigned int i, j, base = 0, adr = 0, len = 0;
+
+ switch (cs->subtyp) {
+ case R647:
+ base = cs->hw.gazel.hscx[0];
+ if (!request_region(adr = (0xC000 + base), len = 1, "gazel"))
+ goto error;
+ for (i = 0x0000; i < 0xC000; i += 0x1000) {
+ if (!request_region(adr = (i + base), len = 16, "gazel"))
+ goto error;
+ }
+ if (i != 0xC000) {
+ for (j = 0; j < i; j+= 0x1000)
+ release_region(j + base, 16);
+ release_region(0xC000 + base, 1);
+ goto error;
+ }
+ break;
+
+ case R685:
+ if (!request_region(adr = cs->hw.gazel.hscx[0], len = 0x100, "gazel"))
+ goto error;
+ if (!request_region(adr = cs->hw.gazel.cfg_reg, len = 0x80, "gazel")) {
+ release_region(cs->hw.gazel.hscx[0],0x100);
+ goto error;
+ }
+ break;
+
+ case R753:
+ if (!request_region(adr = cs->hw.gazel.ipac, len = 0x8, "gazel"))
+ goto error;
+ if (!request_region(adr = cs->hw.gazel.cfg_reg, len = 0x80, "gazel")) {
+ release_region(cs->hw.gazel.ipac, 8);
+ goto error;
+ }
+ break;
+
+ case R742:
+ if (!request_region(adr = cs->hw.gazel.ipac, len = 0x8, "gazel"))
+ goto error;
+ break;
+ }
+
+ return 0;
+
+ error:
+ printk(KERN_WARNING "Gazel: io ports 0x%x-0x%x already in use\n",
+ adr, adr + len);
+ return 1;
+}
+
+static int __devinit
+setup_gazelisa(struct IsdnCard *card, struct IsdnCardState *cs)
+{
+ printk(KERN_INFO "Gazel: ISA PnP card automatic recognition\n");
+ // we got an irq parameter, assume it is an ISA card
+ // R742 decodes address even in not started...
+ // R647 returns FF if not present or not started
+ // eventually needs improvment
+ if (readreg_ipac(card->para[1], IPAC_ID) == 1)
+ cs->subtyp = R742;
+ else
+ cs->subtyp = R647;
+
+ setup_isac(cs);
+ cs->hw.gazel.cfg_reg = card->para[1] + 0xC000;
+ cs->hw.gazel.ipac = card->para[1];
+ cs->hw.gazel.isac = card->para[1] + 0x8000;
+ cs->hw.gazel.hscx[0] = card->para[1];
+ cs->hw.gazel.hscx[1] = card->para[1] + 0x4000;
+ cs->irq = card->para[0];
+ cs->hw.gazel.isacfifo = cs->hw.gazel.isac;
+ cs->hw.gazel.hscxfifo[0] = cs->hw.gazel.hscx[0];
+ cs->hw.gazel.hscxfifo[1] = cs->hw.gazel.hscx[1];
+
+ switch (cs->subtyp) {
+ case R647:
+ printk(KERN_INFO "Gazel: Card ISA R647/R648 found\n");
+ cs->dc.isac.adf2 = 0x87;
+ printk(KERN_INFO
+ "Gazel: config irq:%d isac:0x%X cfg:0x%X\n",
+ cs->irq, cs->hw.gazel.isac, cs->hw.gazel.cfg_reg);
+ printk(KERN_INFO
+ "Gazel: hscx A:0x%X hscx B:0x%X\n",
+ cs->hw.gazel.hscx[0], cs->hw.gazel.hscx[1]);
+
+ break;
+ case R742:
+ printk(KERN_INFO "Gazel: Card ISA R742 found\n");
+ test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+ printk(KERN_INFO
+ "Gazel: config irq:%d ipac:0x%X\n",
+ cs->irq, cs->hw.gazel.ipac);
+ break;
+ }
+
+ return (0);
+}
+
+#ifdef CONFIG_PCI_LEGACY
+static struct pci_dev *dev_tel __devinitdata = NULL;
+
+static int __devinit
+setup_gazelpci(struct IsdnCardState *cs)
+{
+ u_int pci_ioaddr0 = 0, pci_ioaddr1 = 0;
+ u_char pci_irq = 0, found;
+ u_int nbseek, seekcard;
+
+ printk(KERN_WARNING "Gazel: PCI card automatic recognition\n");
+
+ found = 0;
+ seekcard = PCI_DEVICE_ID_PLX_R685;
+ for (nbseek = 0; nbseek < 4; nbseek++) {
+ if ((dev_tel = pci_find_device(PCI_VENDOR_ID_PLX,
+ seekcard, dev_tel))) {
+ if (pci_enable_device(dev_tel))
+ return 1;
+ pci_irq = dev_tel->irq;
+ pci_ioaddr0 = pci_resource_start(dev_tel, 1);
+ pci_ioaddr1 = pci_resource_start(dev_tel, 2);
+ found = 1;
+ }
+ if (found)
+ break;
+ else {
+ switch (seekcard) {
+ case PCI_DEVICE_ID_PLX_R685:
+ seekcard = PCI_DEVICE_ID_PLX_R753;
+ break;
+ case PCI_DEVICE_ID_PLX_R753:
+ seekcard = PCI_DEVICE_ID_PLX_DJINN_ITOO;
+ break;
+ case PCI_DEVICE_ID_PLX_DJINN_ITOO:
+ seekcard = PCI_DEVICE_ID_PLX_OLITEC;
+ break;
+ }
+ }
+ }
+ if (!found) {
+ printk(KERN_WARNING "Gazel: No PCI card found\n");
+ return (1);
+ }
+ if (!pci_irq) {
+ printk(KERN_WARNING "Gazel: No IRQ for PCI card found\n");
+ return 1;
+ }
+ cs->hw.gazel.pciaddr[0] = pci_ioaddr0;
+ cs->hw.gazel.pciaddr[1] = pci_ioaddr1;
+ setup_isac(cs);
+ pci_ioaddr1 &= 0xfffe;
+ cs->hw.gazel.cfg_reg = pci_ioaddr0 & 0xfffe;
+ cs->hw.gazel.ipac = pci_ioaddr1;
+ cs->hw.gazel.isac = pci_ioaddr1 + 0x80;
+ cs->hw.gazel.hscx[0] = pci_ioaddr1;
+ cs->hw.gazel.hscx[1] = pci_ioaddr1 + 0x40;
+ cs->hw.gazel.isacfifo = cs->hw.gazel.isac;
+ cs->hw.gazel.hscxfifo[0] = cs->hw.gazel.hscx[0];
+ cs->hw.gazel.hscxfifo[1] = cs->hw.gazel.hscx[1];
+ cs->irq = pci_irq;
+ cs->irq_flags |= IRQF_SHARED;
+
+ switch (seekcard) {
+ case PCI_DEVICE_ID_PLX_R685:
+ printk(KERN_INFO "Gazel: Card PCI R685 found\n");
+ cs->subtyp = R685;
+ cs->dc.isac.adf2 = 0x87;
+ printk(KERN_INFO
+ "Gazel: config irq:%d isac:0x%X cfg:0x%X\n",
+ cs->irq, cs->hw.gazel.isac, cs->hw.gazel.cfg_reg);
+ printk(KERN_INFO
+ "Gazel: hscx A:0x%X hscx B:0x%X\n",
+ cs->hw.gazel.hscx[0], cs->hw.gazel.hscx[1]);
+ break;
+ case PCI_DEVICE_ID_PLX_R753:
+ case PCI_DEVICE_ID_PLX_DJINN_ITOO:
+ case PCI_DEVICE_ID_PLX_OLITEC:
+ printk(KERN_INFO "Gazel: Card PCI R753 found\n");
+ cs->subtyp = R753;
+ test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+ printk(KERN_INFO
+ "Gazel: config irq:%d ipac:0x%X cfg:0x%X\n",
+ cs->irq, cs->hw.gazel.ipac, cs->hw.gazel.cfg_reg);
+ break;
+ }
+
+ return (0);
+}
+#endif /* CONFIG_PCI_LEGACY */
+
+int __devinit
+setup_gazel(struct IsdnCard *card)
+{
+ struct IsdnCardState *cs = card->cs;
+ char tmp[64];
+ u_char val;
+
+ strcpy(tmp, gazel_revision);
+ printk(KERN_INFO "Gazel: Driver Revision %s\n", HiSax_getrev(tmp));
+
+ if (cs->typ != ISDN_CTYPE_GAZEL)
+ return (0);
+
+ if (card->para[0]) {
+ if (setup_gazelisa(card, cs))
+ return (0);
+ } else {
+
+#ifdef CONFIG_PCI_LEGACY
+ if (setup_gazelpci(cs))
+ return (0);
+#else
+ printk(KERN_WARNING "Gazel: Card PCI requested and NO_PCI_BIOS, unable to config\n");
+ return (0);
+#endif /* CONFIG_PCI */
+ }
+
+ if (reserve_regions(card, cs)) {
+ return (0);
+ }
+ if (reset_gazel(cs)) {
+ printk(KERN_WARNING "Gazel: wrong IRQ\n");
+ release_io_gazel(cs);
+ return (0);
+ }
+ cs->readisac = &ReadISAC;
+ cs->writeisac = &WriteISAC;
+ cs->readisacfifo = &ReadISACfifo;
+ cs->writeisacfifo = &WriteISACfifo;
+ cs->BC_Read_Reg = &ReadHSCX;
+ cs->BC_Write_Reg = &WriteHSCX;
+ cs->BC_Send_Data = &hscx_fill_fifo;
+ cs->cardmsg = &Gazel_card_msg;
+
+ switch (cs->subtyp) {
+ case R647:
+ case R685:
+ cs->irq_func = &gazel_interrupt;
+ ISACVersion(cs, "Gazel:");
+ if (HscxVersion(cs, "Gazel:")) {
+ printk(KERN_WARNING
+ "Gazel: wrong HSCX versions check IO address\n");
+ release_io_gazel(cs);
+ return (0);
+ }
+ break;
+ case R742:
+ case R753:
+ cs->irq_func = &gazel_interrupt_ipac;
+ val = ReadISAC(cs, IPAC_ID - 0x80);
+ printk(KERN_INFO "Gazel: IPAC version %x\n", val);
+ break;
+ }
+
+ return (1);
+}
diff --git a/drivers/isdn/hisax/hfc4s8s_l1.c b/drivers/isdn/hisax/hfc4s8s_l1.c
new file mode 100644
index 0000000..ab98e13
--- /dev/null
+++ b/drivers/isdn/hisax/hfc4s8s_l1.c
@@ -0,0 +1,1715 @@
+/*************************************************************************/
+/* $Id: hfc4s8s_l1.c,v 1.10 2005/02/09 16:31:09 martinb1 Exp $ */
+/* HFC-4S/8S low layer interface for Cologne Chip HFC-4S/8S isdn chips */
+/* The low layer (L1) is implemented as a loadable module for usage with */
+/* the HiSax isdn driver for passive cards. */
+/* */
+/* Author: Werner Cornelius */
+/* (C) 2003 Cornelius Consult (werner@cornelius-consult.de) */
+/* */
+/* Driver maintained by Cologne Chip */
+/* - Martin Bachem, support@colognechip.com */
+/* */
+/* This driver only works with chip revisions >= 1, older revision 0 */
+/* engineering samples (only first manufacturer sample cards) will not */
+/* work and are rejected by the driver. */
+/* */
+/* This file distributed under the GNU GPL. */
+/* */
+/* See Version History at the end of this file */
+/* */
+/*************************************************************************/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/skbuff.h>
+#include <linux/wait.h>
+#include <asm/io.h>
+#include "hisax_if.h"
+#include "hfc4s8s_l1.h"
+
+static const char hfc4s8s_rev[] = "Revision: 1.10";
+
+/***************************************************************/
+/* adjustable transparent mode fifo threshold */
+/* The value defines the used fifo threshold with the equation */
+/* */
+/* notify number of bytes = 2 * 2 ^ TRANS_FIFO_THRES */
+/* */
+/* The default value is 5 which results in a buffer size of 64 */
+/* and an interrupt rate of 8ms. */
+/* The maximum value is 7 due to fifo size restrictions. */
+/* Values below 3-4 are not recommended due to high interrupt */
+/* load of the processor. For non critical applications the */
+/* value should be raised to 7 to reduce any interrupt overhead*/
+/***************************************************************/
+#define TRANS_FIFO_THRES 5
+
+/*************/
+/* constants */
+/*************/
+#define CLOCKMODE_0 0 /* ext. 24.576 MhZ clk freq, int. single clock mode */
+#define CLOCKMODE_1 1 /* ext. 49.576 MhZ clk freq, int. single clock mode */
+#define CHIP_ID_SHIFT 4
+#define HFC_MAX_ST 8
+#define MAX_D_FRAME_SIZE 270
+#define MAX_B_FRAME_SIZE 1536
+#define TRANS_TIMER_MODE (TRANS_FIFO_THRES & 0xf)
+#define TRANS_FIFO_BYTES (2 << TRANS_FIFO_THRES)
+#define MAX_F_CNT 0x0f
+
+#define CLKDEL_NT 0x6c
+#define CLKDEL_TE 0xf
+#define CTRL0_NT 4
+#define CTRL0_TE 0
+
+#define L1_TIMER_T4 2 /* minimum in jiffies */
+#define L1_TIMER_T3 (7 * HZ) /* activation timeout */
+#define L1_TIMER_T1 ((120 * HZ) / 1000) /* NT mode deactivation timeout */
+
+
+/******************/
+/* types and vars */
+/******************/
+static int card_cnt;
+
+/* private driver_data */
+typedef struct {
+ int chip_id;
+ int clock_mode;
+ int max_st_ports;
+ char *device_name;
+} hfc4s8s_param;
+
+static struct pci_device_id hfc4s8s_ids[] = {
+ {.vendor = PCI_VENDOR_ID_CCD,
+ .device = PCI_DEVICE_ID_4S,
+ .subvendor = 0x1397,
+ .subdevice = 0x08b4,
+ .driver_data =
+ (unsigned long) &((hfc4s8s_param) {CHIP_ID_4S, CLOCKMODE_0, 4,
+ "HFC-4S Evaluation Board"}),
+ },
+ {.vendor = PCI_VENDOR_ID_CCD,
+ .device = PCI_DEVICE_ID_8S,
+ .subvendor = 0x1397,
+ .subdevice = 0x16b8,
+ .driver_data =
+ (unsigned long) &((hfc4s8s_param) {CHIP_ID_8S, CLOCKMODE_0, 8,
+ "HFC-8S Evaluation Board"}),
+ },
+ {.vendor = PCI_VENDOR_ID_CCD,
+ .device = PCI_DEVICE_ID_4S,
+ .subvendor = 0x1397,
+ .subdevice = 0xb520,
+ .driver_data =
+ (unsigned long) &((hfc4s8s_param) {CHIP_ID_4S, CLOCKMODE_1, 4,
+ "IOB4ST"}),
+ },
+ {.vendor = PCI_VENDOR_ID_CCD,
+ .device = PCI_DEVICE_ID_8S,
+ .subvendor = 0x1397,
+ .subdevice = 0xb522,
+ .driver_data =
+ (unsigned long) &((hfc4s8s_param) {CHIP_ID_8S, CLOCKMODE_1, 8,
+ "IOB8ST"}),
+ },
+ {}
+};
+
+MODULE_DEVICE_TABLE(pci, hfc4s8s_ids);
+
+MODULE_AUTHOR("Werner Cornelius, werner@cornelius-consult.de");
+MODULE_DESCRIPTION("ISDN layer 1 for Cologne Chip HFC-4S/8S chips");
+MODULE_LICENSE("GPL");
+
+/***********/
+/* layer 1 */
+/***********/
+struct hfc4s8s_btype {
+ spinlock_t lock;
+ struct hisax_b_if b_if;
+ struct hfc4s8s_l1 *l1p;
+ struct sk_buff_head tx_queue;
+ struct sk_buff *tx_skb;
+ struct sk_buff *rx_skb;
+ __u8 *rx_ptr;
+ int tx_cnt;
+ int bchan;
+ int mode;
+};
+
+struct _hfc4s8s_hw;
+
+struct hfc4s8s_l1 {
+ spinlock_t lock;
+ struct _hfc4s8s_hw *hw; /* pointer to hardware area */
+ int l1_state; /* actual l1 state */
+ struct timer_list l1_timer; /* layer 1 timer structure */
+ int nt_mode; /* set to nt mode */
+ int st_num; /* own index */
+ int enabled; /* interface is enabled */
+ struct sk_buff_head d_tx_queue; /* send queue */
+ int tx_cnt; /* bytes to send */
+ struct hisax_d_if d_if; /* D-channel interface */
+ struct hfc4s8s_btype b_ch[2]; /* B-channel data */
+ struct hisax_b_if *b_table[2];
+};
+
+/**********************/
+/* hardware structure */
+/**********************/
+typedef struct _hfc4s8s_hw {
+ spinlock_t lock;
+
+ int cardnum;
+ int ifnum;
+ int iobase;
+ int nt_mode;
+ u_char *membase;
+ u_char *hw_membase;
+ void *pdev;
+ int max_fifo;
+ hfc4s8s_param driver_data;
+ int irq;
+ int fifo_sched_cnt;
+ struct work_struct tqueue;
+ struct hfc4s8s_l1 l1[HFC_MAX_ST];
+ char card_name[60];
+ struct {
+ u_char r_irq_ctrl;
+ u_char r_ctrl0;
+ volatile u_char r_irq_statech; /* active isdn l1 status */
+ u_char r_irqmsk_statchg; /* enabled isdn status ints */
+ u_char r_irq_fifo_blx[8]; /* fifo status registers */
+ u_char fifo_rx_trans_enables[8]; /* mask for enabled transparent rx fifos */
+ u_char fifo_slow_timer_service[8]; /* mask for fifos needing slower timer service */
+ volatile u_char r_irq_oview; /* contents of overview register */
+ volatile u_char timer_irq;
+ int timer_usg_cnt; /* number of channels using timer */
+ } mr;
+} hfc4s8s_hw;
+
+
+
+/***************************/
+/* inline function defines */
+/***************************/
+#ifdef HISAX_HFC4S8S_PCIMEM /* inline functions memory mapped */
+
+/* memory write and dummy IO read to avoid PCI byte merge problems */
+#define Write_hfc8(a,b,c) {(*((volatile u_char *)(a->membase+b)) = c); inb(a->iobase+4);}
+/* memory write without dummy IO access for fifo data access */
+#define fWrite_hfc8(a,b,c) (*((volatile u_char *)(a->membase+b)) = c)
+#define Read_hfc8(a,b) (*((volatile u_char *)(a->membase+b)))
+#define Write_hfc16(a,b,c) (*((volatile unsigned short *)(a->membase+b)) = c)
+#define Read_hfc16(a,b) (*((volatile unsigned short *)(a->membase+b)))
+#define Write_hfc32(a,b,c) (*((volatile unsigned long *)(a->membase+b)) = c)
+#define Read_hfc32(a,b) (*((volatile unsigned long *)(a->membase+b)))
+#define wait_busy(a) {while ((Read_hfc8(a, R_STATUS) & M_BUSY));}
+#define PCI_ENA_MEMIO 0x03
+
+#else
+
+/* inline functions io mapped */
+static inline void
+SetRegAddr(hfc4s8s_hw * a, u_char b)
+{
+ outb(b, (a->iobase) + 4);
+}
+
+static inline u_char
+GetRegAddr(hfc4s8s_hw * a)
+{
+ return (inb((volatile u_int) (a->iobase + 4)));
+}
+
+
+static inline void
+Write_hfc8(hfc4s8s_hw * a, u_char b, u_char c)
+{
+ SetRegAddr(a, b);
+ outb(c, a->iobase);
+}
+
+static inline void
+fWrite_hfc8(hfc4s8s_hw * a, u_char c)
+{
+ outb(c, a->iobase);
+}
+
+static inline void
+Write_hfc16(hfc4s8s_hw * a, u_char b, u_short c)
+{
+ SetRegAddr(a, b);
+ outw(c, a->iobase);
+}
+
+static inline void
+Write_hfc32(hfc4s8s_hw * a, u_char b, u_long c)
+{
+ SetRegAddr(a, b);
+ outl(c, a->iobase);
+}
+
+static inline void
+fWrite_hfc32(hfc4s8s_hw * a, u_long c)
+{
+ outl(c, a->iobase);
+}
+
+static inline u_char
+Read_hfc8(hfc4s8s_hw * a, u_char b)
+{
+ SetRegAddr(a, b);
+ return (inb((volatile u_int) a->iobase));
+}
+
+static inline u_char
+fRead_hfc8(hfc4s8s_hw * a)
+{
+ return (inb((volatile u_int) a->iobase));
+}
+
+
+static inline u_short
+Read_hfc16(hfc4s8s_hw * a, u_char b)
+{
+ SetRegAddr(a, b);
+ return (inw((volatile u_int) a->iobase));
+}
+
+static inline u_long
+Read_hfc32(hfc4s8s_hw * a, u_char b)
+{
+ SetRegAddr(a, b);
+ return (inl((volatile u_int) a->iobase));
+}
+
+static inline u_long
+fRead_hfc32(hfc4s8s_hw * a)
+{
+ return (inl((volatile u_int) a->iobase));
+}
+
+static inline void
+wait_busy(hfc4s8s_hw * a)
+{
+ SetRegAddr(a, R_STATUS);
+ while (inb((volatile u_int) a->iobase) & M_BUSY);
+}
+
+#define PCI_ENA_REGIO 0x01
+
+#endif /* HISAX_HFC4S8S_PCIMEM */
+
+/******************************************************/
+/* function to read critical counter registers that */
+/* may be udpated by the chip during read */
+/******************************************************/
+static u_char
+Read_hfc8_stable(hfc4s8s_hw * hw, int reg)
+{
+ u_char ref8;
+ u_char in8;
+ ref8 = Read_hfc8(hw, reg);
+ while (((in8 = Read_hfc8(hw, reg)) != ref8)) {
+ ref8 = in8;
+ }
+ return in8;
+}
+
+static int
+Read_hfc16_stable(hfc4s8s_hw * hw, int reg)
+{
+ int ref16;
+ int in16;
+
+ ref16 = Read_hfc16(hw, reg);
+ while (((in16 = Read_hfc16(hw, reg)) != ref16)) {
+ ref16 = in16;
+ }
+ return in16;
+}
+
+/*****************************/
+/* D-channel call from HiSax */
+/*****************************/
+static void
+dch_l2l1(struct hisax_d_if *iface, int pr, void *arg)
+{
+ struct hfc4s8s_l1 *l1 = iface->ifc.priv;
+ struct sk_buff *skb = (struct sk_buff *) arg;
+ u_long flags;
+
+ switch (pr) {
+
+ case (PH_DATA | REQUEST):
+ if (!l1->enabled) {
+ dev_kfree_skb(skb);
+ break;
+ }
+ spin_lock_irqsave(&l1->lock, flags);
+ skb_queue_tail(&l1->d_tx_queue, skb);
+ if ((skb_queue_len(&l1->d_tx_queue) == 1) &&
+ (l1->tx_cnt <= 0)) {
+ l1->hw->mr.r_irq_fifo_blx[l1->st_num] |=
+ 0x10;
+ spin_unlock_irqrestore(&l1->lock, flags);
+ schedule_work(&l1->hw->tqueue);
+ } else
+ spin_unlock_irqrestore(&l1->lock, flags);
+ break;
+
+ case (PH_ACTIVATE | REQUEST):
+ if (!l1->enabled)
+ break;
+ if (!l1->nt_mode) {
+ if (l1->l1_state < 6) {
+ spin_lock_irqsave(&l1->lock,
+ flags);
+
+ Write_hfc8(l1->hw, R_ST_SEL,
+ l1->st_num);
+ Write_hfc8(l1->hw, A_ST_WR_STA,
+ 0x60);
+ mod_timer(&l1->l1_timer,
+ jiffies + L1_TIMER_T3);
+ spin_unlock_irqrestore(&l1->lock,
+ flags);
+ } else if (l1->l1_state == 7)
+ l1->d_if.ifc.l1l2(&l1->d_if.ifc,
+ PH_ACTIVATE |
+ INDICATION,
+ NULL);
+ } else {
+ if (l1->l1_state != 3) {
+ spin_lock_irqsave(&l1->lock,
+ flags);
+ Write_hfc8(l1->hw, R_ST_SEL,
+ l1->st_num);
+ Write_hfc8(l1->hw, A_ST_WR_STA,
+ 0x60);
+ spin_unlock_irqrestore(&l1->lock,
+ flags);
+ } else if (l1->l1_state == 3)
+ l1->d_if.ifc.l1l2(&l1->d_if.ifc,
+ PH_ACTIVATE |
+ INDICATION,
+ NULL);
+ }
+ break;
+
+ default:
+ printk(KERN_INFO
+ "HFC-4S/8S: Unknown D-chan cmd 0x%x received, ignored\n",
+ pr);
+ break;
+ }
+ if (!l1->enabled)
+ l1->d_if.ifc.l1l2(&l1->d_if.ifc,
+ PH_DEACTIVATE | INDICATION, NULL);
+} /* dch_l2l1 */
+
+/*****************************/
+/* B-channel call from HiSax */
+/*****************************/
+static void
+bch_l2l1(struct hisax_if *ifc, int pr, void *arg)
+{
+ struct hfc4s8s_btype *bch = ifc->priv;
+ struct hfc4s8s_l1 *l1 = bch->l1p;
+ struct sk_buff *skb = (struct sk_buff *) arg;
+ long mode = (long) arg;
+ u_long flags;
+
+ switch (pr) {
+
+ case (PH_DATA | REQUEST):
+ if (!l1->enabled || (bch->mode == L1_MODE_NULL)) {
+ dev_kfree_skb(skb);
+ break;
+ }
+ spin_lock_irqsave(&l1->lock, flags);
+ skb_queue_tail(&bch->tx_queue, skb);
+ if (!bch->tx_skb && (bch->tx_cnt <= 0)) {
+ l1->hw->mr.r_irq_fifo_blx[l1->st_num] |=
+ ((bch->bchan == 1) ? 1 : 4);
+ spin_unlock_irqrestore(&l1->lock, flags);
+ schedule_work(&l1->hw->tqueue);
+ } else
+ spin_unlock_irqrestore(&l1->lock, flags);
+ break;
+
+ case (PH_ACTIVATE | REQUEST):
+ case (PH_DEACTIVATE | REQUEST):
+ if (!l1->enabled)
+ break;
+ if (pr == (PH_DEACTIVATE | REQUEST))
+ mode = L1_MODE_NULL;
+
+ switch (mode) {
+ case L1_MODE_HDLC:
+ spin_lock_irqsave(&l1->lock,
+ flags);
+ l1->hw->mr.timer_usg_cnt++;
+ l1->hw->mr.
+ fifo_slow_timer_service[l1->
+ st_num]
+ |=
+ ((bch->bchan ==
+ 1) ? 0x2 : 0x8);
+ Write_hfc8(l1->hw, R_FIFO,
+ (l1->st_num * 8 +
+ ((bch->bchan ==
+ 1) ? 0 : 2)));
+ wait_busy(l1->hw);
+ Write_hfc8(l1->hw, A_CON_HDLC, 0xc); /* HDLC mode, flag fill, connect ST */
+ Write_hfc8(l1->hw, A_SUBCH_CFG, 0); /* 8 bits */
+ Write_hfc8(l1->hw, A_IRQ_MSK, 1); /* enable TX interrupts for hdlc */
+ Write_hfc8(l1->hw, A_INC_RES_FIFO, 2); /* reset fifo */
+ wait_busy(l1->hw);
+
+ Write_hfc8(l1->hw, R_FIFO,
+ (l1->st_num * 8 +
+ ((bch->bchan ==
+ 1) ? 1 : 3)));
+ wait_busy(l1->hw);
+ Write_hfc8(l1->hw, A_CON_HDLC, 0xc); /* HDLC mode, flag fill, connect ST */
+ Write_hfc8(l1->hw, A_SUBCH_CFG, 0); /* 8 bits */
+ Write_hfc8(l1->hw, A_IRQ_MSK, 1); /* enable RX interrupts for hdlc */
+ Write_hfc8(l1->hw, A_INC_RES_FIFO, 2); /* reset fifo */
+
+ Write_hfc8(l1->hw, R_ST_SEL,
+ l1->st_num);
+ l1->hw->mr.r_ctrl0 |=
+ (bch->bchan & 3);
+ Write_hfc8(l1->hw, A_ST_CTRL0,
+ l1->hw->mr.r_ctrl0);
+ bch->mode = L1_MODE_HDLC;
+ spin_unlock_irqrestore(&l1->lock,
+ flags);
+
+ bch->b_if.ifc.l1l2(&bch->b_if.ifc,
+ PH_ACTIVATE |
+ INDICATION,
+ NULL);
+ break;
+
+ case L1_MODE_TRANS:
+ spin_lock_irqsave(&l1->lock,
+ flags);
+ l1->hw->mr.
+ fifo_rx_trans_enables[l1->
+ st_num]
+ |=
+ ((bch->bchan ==
+ 1) ? 0x2 : 0x8);
+ l1->hw->mr.timer_usg_cnt++;
+ Write_hfc8(l1->hw, R_FIFO,
+ (l1->st_num * 8 +
+ ((bch->bchan ==
+ 1) ? 0 : 2)));
+ wait_busy(l1->hw);
+ Write_hfc8(l1->hw, A_CON_HDLC, 0xf); /* Transparent mode, 1 fill, connect ST */
+ Write_hfc8(l1->hw, A_SUBCH_CFG, 0); /* 8 bits */
+ Write_hfc8(l1->hw, A_IRQ_MSK, 0); /* disable TX interrupts */
+ Write_hfc8(l1->hw, A_INC_RES_FIFO, 2); /* reset fifo */
+ wait_busy(l1->hw);
+
+ Write_hfc8(l1->hw, R_FIFO,
+ (l1->st_num * 8 +
+ ((bch->bchan ==
+ 1) ? 1 : 3)));
+ wait_busy(l1->hw);
+ Write_hfc8(l1->hw, A_CON_HDLC, 0xf); /* Transparent mode, 1 fill, connect ST */
+ Write_hfc8(l1->hw, A_SUBCH_CFG, 0); /* 8 bits */
+ Write_hfc8(l1->hw, A_IRQ_MSK, 0); /* disable RX interrupts */
+ Write_hfc8(l1->hw, A_INC_RES_FIFO, 2); /* reset fifo */
+
+ Write_hfc8(l1->hw, R_ST_SEL,
+ l1->st_num);
+ l1->hw->mr.r_ctrl0 |=
+ (bch->bchan & 3);
+ Write_hfc8(l1->hw, A_ST_CTRL0,
+ l1->hw->mr.r_ctrl0);
+ bch->mode = L1_MODE_TRANS;
+ spin_unlock_irqrestore(&l1->lock,
+ flags);
+
+ bch->b_if.ifc.l1l2(&bch->b_if.ifc,
+ PH_ACTIVATE |
+ INDICATION,
+ NULL);
+ break;
+
+ default:
+ if (bch->mode == L1_MODE_NULL)
+ break;
+ spin_lock_irqsave(&l1->lock,
+ flags);
+ l1->hw->mr.
+ fifo_slow_timer_service[l1->
+ st_num]
+ &=
+ ~((bch->bchan ==
+ 1) ? 0x3 : 0xc);
+ l1->hw->mr.
+ fifo_rx_trans_enables[l1->
+ st_num]
+ &=
+ ~((bch->bchan ==
+ 1) ? 0x3 : 0xc);
+ l1->hw->mr.timer_usg_cnt--;
+ Write_hfc8(l1->hw, R_FIFO,
+ (l1->st_num * 8 +
+ ((bch->bchan ==
+ 1) ? 0 : 2)));
+ wait_busy(l1->hw);
+ Write_hfc8(l1->hw, A_IRQ_MSK, 0); /* disable TX interrupts */
+ wait_busy(l1->hw);
+ Write_hfc8(l1->hw, R_FIFO,
+ (l1->st_num * 8 +
+ ((bch->bchan ==
+ 1) ? 1 : 3)));
+ wait_busy(l1->hw);
+ Write_hfc8(l1->hw, A_IRQ_MSK, 0); /* disable RX interrupts */
+ Write_hfc8(l1->hw, R_ST_SEL,
+ l1->st_num);
+ l1->hw->mr.r_ctrl0 &=
+ ~(bch->bchan & 3);
+ Write_hfc8(l1->hw, A_ST_CTRL0,
+ l1->hw->mr.r_ctrl0);
+ spin_unlock_irqrestore(&l1->lock,
+ flags);
+
+ bch->mode = L1_MODE_NULL;
+ bch->b_if.ifc.l1l2(&bch->b_if.ifc,
+ PH_DEACTIVATE |
+ INDICATION,
+ NULL);
+ if (bch->tx_skb) {
+ dev_kfree_skb(bch->tx_skb);
+ bch->tx_skb = NULL;
+ }
+ if (bch->rx_skb) {
+ dev_kfree_skb(bch->rx_skb);
+ bch->rx_skb = NULL;
+ }
+ skb_queue_purge(&bch->tx_queue);
+ bch->tx_cnt = 0;
+ bch->rx_ptr = NULL;
+ break;
+ }
+
+ /* timer is only used when at least one b channel */
+ /* is set up to transparent mode */
+ if (l1->hw->mr.timer_usg_cnt) {
+ Write_hfc8(l1->hw, R_IRQMSK_MISC,
+ M_TI_IRQMSK);
+ } else {
+ Write_hfc8(l1->hw, R_IRQMSK_MISC, 0);
+ }
+
+ break;
+
+ default:
+ printk(KERN_INFO
+ "HFC-4S/8S: Unknown B-chan cmd 0x%x received, ignored\n",
+ pr);
+ break;
+ }
+ if (!l1->enabled)
+ bch->b_if.ifc.l1l2(&bch->b_if.ifc,
+ PH_DEACTIVATE | INDICATION, NULL);
+} /* bch_l2l1 */
+
+/**************************/
+/* layer 1 timer function */
+/**************************/
+static void
+hfc_l1_timer(struct hfc4s8s_l1 *l1)
+{
+ u_long flags;
+
+ if (!l1->enabled)
+ return;
+
+ spin_lock_irqsave(&l1->lock, flags);
+ if (l1->nt_mode) {
+ l1->l1_state = 1;
+ Write_hfc8(l1->hw, R_ST_SEL, l1->st_num);
+ Write_hfc8(l1->hw, A_ST_WR_STA, 0x11);
+ spin_unlock_irqrestore(&l1->lock, flags);
+ l1->d_if.ifc.l1l2(&l1->d_if.ifc,
+ PH_DEACTIVATE | INDICATION, NULL);
+ spin_lock_irqsave(&l1->lock, flags);
+ l1->l1_state = 1;
+ Write_hfc8(l1->hw, A_ST_WR_STA, 0x1);
+ spin_unlock_irqrestore(&l1->lock, flags);
+ } else {
+ /* activation timed out */
+ Write_hfc8(l1->hw, R_ST_SEL, l1->st_num);
+ Write_hfc8(l1->hw, A_ST_WR_STA, 0x13);
+ spin_unlock_irqrestore(&l1->lock, flags);
+ l1->d_if.ifc.l1l2(&l1->d_if.ifc,
+ PH_DEACTIVATE | INDICATION, NULL);
+ spin_lock_irqsave(&l1->lock, flags);
+ Write_hfc8(l1->hw, R_ST_SEL, l1->st_num);
+ Write_hfc8(l1->hw, A_ST_WR_STA, 0x3);
+ spin_unlock_irqrestore(&l1->lock, flags);
+ }
+} /* hfc_l1_timer */
+
+/****************************************/
+/* a complete D-frame has been received */
+/****************************************/
+static void
+rx_d_frame(struct hfc4s8s_l1 *l1p, int ech)
+{
+ int z1, z2;
+ u_char f1, f2, df;
+ struct sk_buff *skb;
+ u_char *cp;
+
+
+ if (!l1p->enabled)
+ return;
+ do {
+ /* E/D RX fifo */
+ Write_hfc8(l1p->hw, R_FIFO,
+ (l1p->st_num * 8 + ((ech) ? 7 : 5)));
+ wait_busy(l1p->hw);
+
+ f1 = Read_hfc8_stable(l1p->hw, A_F1);
+ f2 = Read_hfc8(l1p->hw, A_F2);
+ df = f1 - f2;
+ if ((f1 - f2) < 0)
+ df = f1 - f2 + MAX_F_CNT + 1;
+
+
+ if (!df) {
+ return; /* no complete frame in fifo */
+ }
+
+ z1 = Read_hfc16_stable(l1p->hw, A_Z1);
+ z2 = Read_hfc16(l1p->hw, A_Z2);
+
+ z1 = z1 - z2 + 1;
+ if (z1 < 0)
+ z1 += 384;
+
+ if (!(skb = dev_alloc_skb(MAX_D_FRAME_SIZE))) {
+ printk(KERN_INFO
+ "HFC-4S/8S: Could not allocate D/E "
+ "channel receive buffer");
+ Write_hfc8(l1p->hw, A_INC_RES_FIFO, 2);
+ wait_busy(l1p->hw);
+ return;
+ }
+
+ if (((z1 < 4) || (z1 > MAX_D_FRAME_SIZE))) {
+ if (skb)
+ dev_kfree_skb(skb);
+ /* remove errornous D frame */
+ if (df == 1) {
+ /* reset fifo */
+ Write_hfc8(l1p->hw, A_INC_RES_FIFO, 2);
+ wait_busy(l1p->hw);
+ return;
+ } else {
+ /* read errornous D frame */
+
+#ifndef HISAX_HFC4S8S_PCIMEM
+ SetRegAddr(l1p->hw, A_FIFO_DATA0);
+#endif
+
+ while (z1 >= 4) {
+#ifdef HISAX_HFC4S8S_PCIMEM
+ Read_hfc32(l1p->hw, A_FIFO_DATA0);
+#else
+ fRead_hfc32(l1p->hw);
+#endif
+ z1 -= 4;
+ }
+
+ while (z1--)
+#ifdef HISAX_HFC4S8S_PCIMEM
+ Read_hfc8(l1p->hw, A_FIFO_DATA0);
+#else
+ fRead_hfc8(l1p->hw);
+#endif
+
+ Write_hfc8(l1p->hw, A_INC_RES_FIFO, 1);
+ wait_busy(l1p->hw);
+ return;
+ }
+ }
+
+ cp = skb->data;
+
+#ifndef HISAX_HFC4S8S_PCIMEM
+ SetRegAddr(l1p->hw, A_FIFO_DATA0);
+#endif
+
+ while (z1 >= 4) {
+#ifdef HISAX_HFC4S8S_PCIMEM
+ *((unsigned long *) cp) =
+ Read_hfc32(l1p->hw, A_FIFO_DATA0);
+#else
+ *((unsigned long *) cp) = fRead_hfc32(l1p->hw);
+#endif
+ cp += 4;
+ z1 -= 4;
+ }
+
+ while (z1--)
+#ifdef HISAX_HFC4S8S_PCIMEM
+ *cp++ = Read_hfc8(l1p->hw, A_FIFO_DATA0);
+#else
+ *cp++ = fRead_hfc8(l1p->hw);
+#endif
+
+ Write_hfc8(l1p->hw, A_INC_RES_FIFO, 1); /* increment f counter */
+ wait_busy(l1p->hw);
+
+ if (*(--cp)) {
+ dev_kfree_skb(skb);
+ } else {
+ skb->len = (cp - skb->data) - 2;
+ if (ech)
+ l1p->d_if.ifc.l1l2(&l1p->d_if.ifc,
+ PH_DATA_E | INDICATION,
+ skb);
+ else
+ l1p->d_if.ifc.l1l2(&l1p->d_if.ifc,
+ PH_DATA | INDICATION,
+ skb);
+ }
+ } while (1);
+} /* rx_d_frame */
+
+/*************************************************************/
+/* a B-frame has been received (perhaps not fully completed) */
+/*************************************************************/
+static void
+rx_b_frame(struct hfc4s8s_btype *bch)
+{
+ int z1, z2, hdlc_complete;
+ u_char f1, f2;
+ struct hfc4s8s_l1 *l1 = bch->l1p;
+ struct sk_buff *skb;
+
+ if (!l1->enabled || (bch->mode == L1_MODE_NULL))
+ return;
+
+ do {
+ /* RX Fifo */
+ Write_hfc8(l1->hw, R_FIFO,
+ (l1->st_num * 8 + ((bch->bchan == 1) ? 1 : 3)));
+ wait_busy(l1->hw);
+
+ if (bch->mode == L1_MODE_HDLC) {
+ f1 = Read_hfc8_stable(l1->hw, A_F1);
+ f2 = Read_hfc8(l1->hw, A_F2);
+ hdlc_complete = ((f1 ^ f2) & MAX_F_CNT);
+ } else
+ hdlc_complete = 0;
+ z1 = Read_hfc16_stable(l1->hw, A_Z1);
+ z2 = Read_hfc16(l1->hw, A_Z2);
+ z1 = (z1 - z2);
+ if (hdlc_complete)
+ z1++;
+ if (z1 < 0)
+ z1 += 384;
+
+ if (!z1)
+ break;
+
+ if (!(skb = bch->rx_skb)) {
+ if (!
+ (skb =
+ dev_alloc_skb((bch->mode ==
+ L1_MODE_TRANS) ? z1
+ : (MAX_B_FRAME_SIZE + 3)))) {
+ printk(KERN_ERR
+ "HFC-4S/8S: Could not allocate B "
+ "channel receive buffer");
+ return;
+ }
+ bch->rx_ptr = skb->data;
+ bch->rx_skb = skb;
+ }
+
+ skb->len = (bch->rx_ptr - skb->data) + z1;
+
+ /* HDLC length check */
+ if ((bch->mode == L1_MODE_HDLC) &&
+ ((hdlc_complete && (skb->len < 4)) ||
+ (skb->len > (MAX_B_FRAME_SIZE + 3)))) {
+
+ skb->len = 0;
+ bch->rx_ptr = skb->data;
+ Write_hfc8(l1->hw, A_INC_RES_FIFO, 2); /* reset fifo */
+ wait_busy(l1->hw);
+ return;
+ }
+#ifndef HISAX_HFC4S8S_PCIMEM
+ SetRegAddr(l1->hw, A_FIFO_DATA0);
+#endif
+
+ while (z1 >= 4) {
+#ifdef HISAX_HFC4S8S_PCIMEM
+ *((unsigned long *) bch->rx_ptr) =
+ Read_hfc32(l1->hw, A_FIFO_DATA0);
+#else
+ *((unsigned long *) bch->rx_ptr) =
+ fRead_hfc32(l1->hw);
+#endif
+ bch->rx_ptr += 4;
+ z1 -= 4;
+ }
+
+ while (z1--)
+#ifdef HISAX_HFC4S8S_PCIMEM
+ *(bch->rx_ptr++) = Read_hfc8(l1->hw, A_FIFO_DATA0);
+#else
+ *(bch->rx_ptr++) = fRead_hfc8(l1->hw);
+#endif
+
+ if (hdlc_complete) {
+ /* increment f counter */
+ Write_hfc8(l1->hw, A_INC_RES_FIFO, 1);
+ wait_busy(l1->hw);
+
+ /* hdlc crc check */
+ bch->rx_ptr--;
+ if (*bch->rx_ptr) {
+ skb->len = 0;
+ bch->rx_ptr = skb->data;
+ continue;
+ }
+ skb->len -= 3;
+ }
+ if (hdlc_complete || (bch->mode == L1_MODE_TRANS)) {
+ bch->rx_skb = NULL;
+ bch->rx_ptr = NULL;
+ bch->b_if.ifc.l1l2(&bch->b_if.ifc,
+ PH_DATA | INDICATION, skb);
+ }
+
+ } while (1);
+} /* rx_b_frame */
+
+/********************************************/
+/* a D-frame has been/should be transmitted */
+/********************************************/
+static void
+tx_d_frame(struct hfc4s8s_l1 *l1p)
+{
+ struct sk_buff *skb;
+ u_char f1, f2;
+ u_char *cp;
+ long cnt;
+
+ if (l1p->l1_state != 7)
+ return;
+
+ /* TX fifo */
+ Write_hfc8(l1p->hw, R_FIFO, (l1p->st_num * 8 + 4));
+ wait_busy(l1p->hw);
+
+ f1 = Read_hfc8(l1p->hw, A_F1);
+ f2 = Read_hfc8_stable(l1p->hw, A_F2);
+
+ if ((f1 ^ f2) & MAX_F_CNT)
+ return; /* fifo is still filled */
+
+ if (l1p->tx_cnt > 0) {
+ cnt = l1p->tx_cnt;
+ l1p->tx_cnt = 0;
+ l1p->d_if.ifc.l1l2(&l1p->d_if.ifc, PH_DATA | CONFIRM,
+ (void *) cnt);
+ }
+
+ if ((skb = skb_dequeue(&l1p->d_tx_queue))) {
+ cp = skb->data;
+ cnt = skb->len;
+#ifndef HISAX_HFC4S8S_PCIMEM
+ SetRegAddr(l1p->hw, A_FIFO_DATA0);
+#endif
+
+ while (cnt >= 4) {
+#ifdef HISAX_HFC4S8S_PCIMEM
+ fWrite_hfc32(l1p->hw, A_FIFO_DATA0,
+ *(unsigned long *) cp);
+#else
+ SetRegAddr(l1p->hw, A_FIFO_DATA0);
+ fWrite_hfc32(l1p->hw, *(unsigned long *) cp);
+#endif
+ cp += 4;
+ cnt -= 4;
+ }
+
+#ifdef HISAX_HFC4S8S_PCIMEM
+ while (cnt--)
+ fWrite_hfc8(l1p->hw, A_FIFO_DATA0, *cp++);
+#else
+ while (cnt--)
+ fWrite_hfc8(l1p->hw, *cp++);
+#endif
+
+ l1p->tx_cnt = skb->truesize;
+ Write_hfc8(l1p->hw, A_INC_RES_FIFO, 1); /* increment f counter */
+ wait_busy(l1p->hw);
+
+ dev_kfree_skb(skb);
+ }
+} /* tx_d_frame */
+
+/******************************************************/
+/* a B-frame may be transmitted (or is not completed) */
+/******************************************************/
+static void
+tx_b_frame(struct hfc4s8s_btype *bch)
+{
+ struct sk_buff *skb;
+ struct hfc4s8s_l1 *l1 = bch->l1p;
+ u_char *cp;
+ int cnt, max, hdlc_num;
+ long ack_len = 0;
+
+ if (!l1->enabled || (bch->mode == L1_MODE_NULL))
+ return;
+
+ /* TX fifo */
+ Write_hfc8(l1->hw, R_FIFO,
+ (l1->st_num * 8 + ((bch->bchan == 1) ? 0 : 2)));
+ wait_busy(l1->hw);
+ do {
+
+ if (bch->mode == L1_MODE_HDLC) {
+ hdlc_num = Read_hfc8(l1->hw, A_F1) & MAX_F_CNT;
+ hdlc_num -=
+ (Read_hfc8_stable(l1->hw, A_F2) & MAX_F_CNT);
+ if (hdlc_num < 0)
+ hdlc_num += 16;
+ if (hdlc_num >= 15)
+ break; /* fifo still filled up with hdlc frames */
+ } else
+ hdlc_num = 0;
+
+ if (!(skb = bch->tx_skb)) {
+ if (!(skb = skb_dequeue(&bch->tx_queue))) {
+ l1->hw->mr.fifo_slow_timer_service[l1->
+ st_num]
+ &= ~((bch->bchan == 1) ? 1 : 4);
+ break; /* list empty */
+ }
+ bch->tx_skb = skb;
+ bch->tx_cnt = 0;
+ }
+
+ if (!hdlc_num)
+ l1->hw->mr.fifo_slow_timer_service[l1->st_num] |=
+ ((bch->bchan == 1) ? 1 : 4);
+ else
+ l1->hw->mr.fifo_slow_timer_service[l1->st_num] &=
+ ~((bch->bchan == 1) ? 1 : 4);
+
+ max = Read_hfc16_stable(l1->hw, A_Z2);
+ max -= Read_hfc16(l1->hw, A_Z1);
+ if (max <= 0)
+ max += 384;
+ max--;
+
+ if (max < 16)
+ break; /* don't write to small amounts of bytes */
+
+ cnt = skb->len - bch->tx_cnt;
+ if (cnt > max)
+ cnt = max;
+ cp = skb->data + bch->tx_cnt;
+ bch->tx_cnt += cnt;
+
+#ifndef HISAX_HFC4S8S_PCIMEM
+ SetRegAddr(l1->hw, A_FIFO_DATA0);
+#endif
+ while (cnt >= 4) {
+#ifdef HISAX_HFC4S8S_PCIMEM
+ fWrite_hfc32(l1->hw, A_FIFO_DATA0,
+ *(unsigned long *) cp);
+#else
+ fWrite_hfc32(l1->hw, *(unsigned long *) cp);
+#endif
+ cp += 4;
+ cnt -= 4;
+ }
+
+ while (cnt--)
+#ifdef HISAX_HFC4S8S_PCIMEM
+ fWrite_hfc8(l1->hw, A_FIFO_DATA0, *cp++);
+#else
+ fWrite_hfc8(l1->hw, *cp++);
+#endif
+
+ if (bch->tx_cnt >= skb->len) {
+ if (bch->mode == L1_MODE_HDLC) {
+ /* increment f counter */
+ Write_hfc8(l1->hw, A_INC_RES_FIFO, 1);
+ }
+ ack_len += skb->truesize;
+ bch->tx_skb = NULL;
+ bch->tx_cnt = 0;
+ dev_kfree_skb(skb);
+ } else
+ /* Re-Select */
+ Write_hfc8(l1->hw, R_FIFO,
+ (l1->st_num * 8 +
+ ((bch->bchan == 1) ? 0 : 2)));
+ wait_busy(l1->hw);
+ } while (1);
+
+ if (ack_len)
+ bch->b_if.ifc.l1l2((struct hisax_if *) &bch->b_if,
+ PH_DATA | CONFIRM, (void *) ack_len);
+} /* tx_b_frame */
+
+/*************************************/
+/* bottom half handler for interrupt */
+/*************************************/
+static void
+hfc4s8s_bh(struct work_struct *work)
+{
+ hfc4s8s_hw *hw = container_of(work, hfc4s8s_hw, tqueue);
+ u_char b;
+ struct hfc4s8s_l1 *l1p;
+ volatile u_char *fifo_stat;
+ int idx;
+
+ /* handle layer 1 state changes */
+ b = 1;
+ l1p = hw->l1;
+ while (b) {
+ if ((b & hw->mr.r_irq_statech)) {
+ /* reset l1 event */
+ hw->mr.r_irq_statech &= ~b;
+ if (l1p->enabled) {
+ if (l1p->nt_mode) {
+ u_char oldstate = l1p->l1_state;
+
+ Write_hfc8(l1p->hw, R_ST_SEL,
+ l1p->st_num);
+ l1p->l1_state =
+ Read_hfc8(l1p->hw,
+ A_ST_RD_STA) & 0xf;
+
+ if ((oldstate == 3)
+ && (l1p->l1_state != 3))
+ l1p->d_if.ifc.l1l2(&l1p->
+ d_if.
+ ifc,
+ PH_DEACTIVATE
+ |
+ INDICATION,
+ NULL);
+
+ if (l1p->l1_state != 2) {
+ del_timer(&l1p->l1_timer);
+ if (l1p->l1_state == 3) {
+ l1p->d_if.ifc.
+ l1l2(&l1p->
+ d_if.ifc,
+ PH_ACTIVATE
+ |
+ INDICATION,
+ NULL);
+ }
+ } else {
+ /* allow transition */
+ Write_hfc8(hw, A_ST_WR_STA,
+ M_SET_G2_G3);
+ mod_timer(&l1p->l1_timer,
+ jiffies +
+ L1_TIMER_T1);
+ }
+ printk(KERN_INFO
+ "HFC-4S/8S: NT ch %d l1 state %d -> %d\n",
+ l1p->st_num, oldstate,
+ l1p->l1_state);
+ } else {
+ u_char oldstate = l1p->l1_state;
+
+ Write_hfc8(l1p->hw, R_ST_SEL,
+ l1p->st_num);
+ l1p->l1_state =
+ Read_hfc8(l1p->hw,
+ A_ST_RD_STA) & 0xf;
+
+ if (((l1p->l1_state == 3) &&
+ ((oldstate == 7) ||
+ (oldstate == 8))) ||
+ ((timer_pending
+ (&l1p->l1_timer))
+ && (l1p->l1_state == 8))) {
+ mod_timer(&l1p->l1_timer,
+ L1_TIMER_T4 +
+ jiffies);
+ } else {
+ if (l1p->l1_state == 7) {
+ del_timer(&l1p->
+ l1_timer);
+ l1p->d_if.ifc.
+ l1l2(&l1p->
+ d_if.ifc,
+ PH_ACTIVATE
+ |
+ INDICATION,
+ NULL);
+ tx_d_frame(l1p);
+ }
+ if (l1p->l1_state == 3) {
+ if (oldstate != 3)
+ l1p->d_if.
+ ifc.
+ l1l2
+ (&l1p->
+ d_if.
+ ifc,
+ PH_DEACTIVATE
+ |
+ INDICATION,
+ NULL);
+ }
+ }
+ printk(KERN_INFO
+ "HFC-4S/8S: TE %d ch %d l1 state %d -> %d\n",
+ l1p->hw->cardnum,
+ l1p->st_num, oldstate,
+ l1p->l1_state);
+ }
+ }
+ }
+ b <<= 1;
+ l1p++;
+ }
+
+ /* now handle the fifos */
+ idx = 0;
+ fifo_stat = hw->mr.r_irq_fifo_blx;
+ l1p = hw->l1;
+ while (idx < hw->driver_data.max_st_ports) {
+
+ if (hw->mr.timer_irq) {
+ *fifo_stat |= hw->mr.fifo_rx_trans_enables[idx];
+ if (hw->fifo_sched_cnt <= 0) {
+ *fifo_stat |=
+ hw->mr.fifo_slow_timer_service[l1p->
+ st_num];
+ }
+ }
+ /* ignore fifo 6 (TX E fifo) */
+ *fifo_stat &= 0xff - 0x40;
+
+ while (*fifo_stat) {
+
+ if (!l1p->nt_mode) {
+ /* RX Fifo has data to read */
+ if ((*fifo_stat & 0x20)) {
+ *fifo_stat &= ~0x20;
+ rx_d_frame(l1p, 0);
+ }
+ /* E Fifo has data to read */
+ if ((*fifo_stat & 0x80)) {
+ *fifo_stat &= ~0x80;
+ rx_d_frame(l1p, 1);
+ }
+ /* TX Fifo completed send */
+ if ((*fifo_stat & 0x10)) {
+ *fifo_stat &= ~0x10;
+ tx_d_frame(l1p);
+ }
+ }
+ /* B1 RX Fifo has data to read */
+ if ((*fifo_stat & 0x2)) {
+ *fifo_stat &= ~0x2;
+ rx_b_frame(l1p->b_ch);
+ }
+ /* B1 TX Fifo has send completed */
+ if ((*fifo_stat & 0x1)) {
+ *fifo_stat &= ~0x1;
+ tx_b_frame(l1p->b_ch);
+ }
+ /* B2 RX Fifo has data to read */
+ if ((*fifo_stat & 0x8)) {
+ *fifo_stat &= ~0x8;
+ rx_b_frame(l1p->b_ch + 1);
+ }
+ /* B2 TX Fifo has send completed */
+ if ((*fifo_stat & 0x4)) {
+ *fifo_stat &= ~0x4;
+ tx_b_frame(l1p->b_ch + 1);
+ }
+ }
+ fifo_stat++;
+ l1p++;
+ idx++;
+ }
+
+ if (hw->fifo_sched_cnt <= 0)
+ hw->fifo_sched_cnt += (1 << (7 - TRANS_TIMER_MODE));
+ hw->mr.timer_irq = 0; /* clear requested timer irq */
+} /* hfc4s8s_bh */
+
+/*********************/
+/* interrupt handler */
+/*********************/
+static irqreturn_t
+hfc4s8s_interrupt(int intno, void *dev_id)
+{
+ hfc4s8s_hw *hw = dev_id;
+ u_char b, ovr;
+ volatile u_char *ovp;
+ int idx;
+ u_char old_ioreg;
+
+ if (!hw || !(hw->mr.r_irq_ctrl & M_GLOB_IRQ_EN))
+ return IRQ_NONE;
+
+#ifndef HISAX_HFC4S8S_PCIMEM
+ /* read current selected regsister */
+ old_ioreg = GetRegAddr(hw);
+#endif
+
+ /* Layer 1 State change */
+ hw->mr.r_irq_statech |=
+ (Read_hfc8(hw, R_SCI) & hw->mr.r_irqmsk_statchg);
+ if (!
+ (b = (Read_hfc8(hw, R_STATUS) & (M_MISC_IRQSTA | M_FR_IRQSTA)))
+&& !hw->mr.r_irq_statech) {
+#ifndef HISAX_HFC4S8S_PCIMEM
+ SetRegAddr(hw, old_ioreg);
+#endif
+ return IRQ_NONE;
+ }
+
+ /* timer event */
+ if (Read_hfc8(hw, R_IRQ_MISC) & M_TI_IRQ) {
+ hw->mr.timer_irq = 1;
+ hw->fifo_sched_cnt--;
+ }
+
+ /* FIFO event */
+ if ((ovr = Read_hfc8(hw, R_IRQ_OVIEW))) {
+ hw->mr.r_irq_oview |= ovr;
+ idx = R_IRQ_FIFO_BL0;
+ ovp = hw->mr.r_irq_fifo_blx;
+ while (ovr) {
+ if ((ovr & 1)) {
+ *ovp |= Read_hfc8(hw, idx);
+ }
+ ovp++;
+ idx++;
+ ovr >>= 1;
+ }
+ }
+
+ /* queue the request to allow other cards to interrupt */
+ schedule_work(&hw->tqueue);
+
+#ifndef HISAX_HFC4S8S_PCIMEM
+ SetRegAddr(hw, old_ioreg);
+#endif
+ return IRQ_HANDLED;
+} /* hfc4s8s_interrupt */
+
+/***********************************************************************/
+/* reset the complete chip, don't release the chips irq but disable it */
+/***********************************************************************/
+static void
+chipreset(hfc4s8s_hw * hw)
+{
+ u_long flags;
+
+ spin_lock_irqsave(&hw->lock, flags);
+ Write_hfc8(hw, R_CTRL, 0); /* use internal RAM */
+ Write_hfc8(hw, R_RAM_MISC, 0); /* 32k*8 RAM */
+ Write_hfc8(hw, R_FIFO_MD, 0); /* fifo mode 386 byte/fifo simple mode */
+ Write_hfc8(hw, R_CIRM, M_SRES); /* reset chip */
+ hw->mr.r_irq_ctrl = 0; /* interrupt is inactive */
+ spin_unlock_irqrestore(&hw->lock, flags);
+
+ udelay(3);
+ Write_hfc8(hw, R_CIRM, 0); /* disable reset */
+ wait_busy(hw);
+
+ Write_hfc8(hw, R_PCM_MD0, M_PCM_MD); /* master mode */
+ Write_hfc8(hw, R_RAM_MISC, M_FZ_MD); /* transmit fifo option */
+ if (hw->driver_data.clock_mode == 1)
+ Write_hfc8(hw, R_BRG_PCM_CFG, M_PCM_CLK); /* PCM clk / 2 */
+ Write_hfc8(hw, R_TI_WD, TRANS_TIMER_MODE); /* timer interval */
+
+ memset(&hw->mr, 0, sizeof(hw->mr));
+} /* chipreset */
+
+/********************************************/
+/* disable/enable hardware in nt or te mode */
+/********************************************/
+static void
+hfc_hardware_enable(hfc4s8s_hw * hw, int enable, int nt_mode)
+{
+ u_long flags;
+ char if_name[40];
+ int i;
+
+ if (enable) {
+ /* save system vars */
+ hw->nt_mode = nt_mode;
+
+ /* enable fifo and state irqs, but not global irq enable */
+ hw->mr.r_irq_ctrl = M_FIFO_IRQ;
+ Write_hfc8(hw, R_IRQ_CTRL, hw->mr.r_irq_ctrl);
+ hw->mr.r_irqmsk_statchg = 0;
+ Write_hfc8(hw, R_SCI_MSK, hw->mr.r_irqmsk_statchg);
+ Write_hfc8(hw, R_PWM_MD, 0x80);
+ Write_hfc8(hw, R_PWM1, 26);
+ if (!nt_mode)
+ Write_hfc8(hw, R_ST_SYNC, M_AUTO_SYNC);
+
+ /* enable the line interfaces and fifos */
+ for (i = 0; i < hw->driver_data.max_st_ports; i++) {
+ hw->mr.r_irqmsk_statchg |= (1 << i);
+ Write_hfc8(hw, R_SCI_MSK, hw->mr.r_irqmsk_statchg);
+ Write_hfc8(hw, R_ST_SEL, i);
+ Write_hfc8(hw, A_ST_CLK_DLY,
+ ((nt_mode) ? CLKDEL_NT : CLKDEL_TE));
+ hw->mr.r_ctrl0 = ((nt_mode) ? CTRL0_NT : CTRL0_TE);
+ Write_hfc8(hw, A_ST_CTRL0, hw->mr.r_ctrl0);
+ Write_hfc8(hw, A_ST_CTRL2, 3);
+ Write_hfc8(hw, A_ST_WR_STA, 0); /* enable state machine */
+
+ hw->l1[i].enabled = 1;
+ hw->l1[i].nt_mode = nt_mode;
+
+ if (!nt_mode) {
+ /* setup E-fifo */
+ Write_hfc8(hw, R_FIFO, i * 8 + 7); /* E fifo */
+ wait_busy(hw);
+ Write_hfc8(hw, A_CON_HDLC, 0x11); /* HDLC mode, 1 fill, connect ST */
+ Write_hfc8(hw, A_SUBCH_CFG, 2); /* only 2 bits */
+ Write_hfc8(hw, A_IRQ_MSK, 1); /* enable interrupt */
+ Write_hfc8(hw, A_INC_RES_FIFO, 2); /* reset fifo */
+ wait_busy(hw);
+
+ /* setup D RX-fifo */
+ Write_hfc8(hw, R_FIFO, i * 8 + 5); /* RX fifo */
+ wait_busy(hw);
+ Write_hfc8(hw, A_CON_HDLC, 0x11); /* HDLC mode, 1 fill, connect ST */
+ Write_hfc8(hw, A_SUBCH_CFG, 2); /* only 2 bits */
+ Write_hfc8(hw, A_IRQ_MSK, 1); /* enable interrupt */
+ Write_hfc8(hw, A_INC_RES_FIFO, 2); /* reset fifo */
+ wait_busy(hw);
+
+ /* setup D TX-fifo */
+ Write_hfc8(hw, R_FIFO, i * 8 + 4); /* TX fifo */
+ wait_busy(hw);
+ Write_hfc8(hw, A_CON_HDLC, 0x11); /* HDLC mode, 1 fill, connect ST */
+ Write_hfc8(hw, A_SUBCH_CFG, 2); /* only 2 bits */
+ Write_hfc8(hw, A_IRQ_MSK, 1); /* enable interrupt */
+ Write_hfc8(hw, A_INC_RES_FIFO, 2); /* reset fifo */
+ wait_busy(hw);
+ }
+
+ sprintf(if_name, "hfc4s8s_%d%d_", hw->cardnum, i);
+
+ if (hisax_register
+ (&hw->l1[i].d_if, hw->l1[i].b_table, if_name,
+ ((nt_mode) ? 3 : 2))) {
+
+ hw->l1[i].enabled = 0;
+ hw->mr.r_irqmsk_statchg &= ~(1 << i);
+ Write_hfc8(hw, R_SCI_MSK,
+ hw->mr.r_irqmsk_statchg);
+ printk(KERN_INFO
+ "HFC-4S/8S: Unable to register S/T device %s, break\n",
+ if_name);
+ break;
+ }
+ }
+ spin_lock_irqsave(&hw->lock, flags);
+ hw->mr.r_irq_ctrl |= M_GLOB_IRQ_EN;
+ Write_hfc8(hw, R_IRQ_CTRL, hw->mr.r_irq_ctrl);
+ spin_unlock_irqrestore(&hw->lock, flags);
+ } else {
+ /* disable hardware */
+ spin_lock_irqsave(&hw->lock, flags);
+ hw->mr.r_irq_ctrl &= ~M_GLOB_IRQ_EN;
+ Write_hfc8(hw, R_IRQ_CTRL, hw->mr.r_irq_ctrl);
+ spin_unlock_irqrestore(&hw->lock, flags);
+
+ for (i = hw->driver_data.max_st_ports - 1; i >= 0; i--) {
+ hw->l1[i].enabled = 0;
+ hisax_unregister(&hw->l1[i].d_if);
+ del_timer(&hw->l1[i].l1_timer);
+ skb_queue_purge(&hw->l1[i].d_tx_queue);
+ skb_queue_purge(&hw->l1[i].b_ch[0].tx_queue);
+ skb_queue_purge(&hw->l1[i].b_ch[1].tx_queue);
+ }
+ chipreset(hw);
+ }
+} /* hfc_hardware_enable */
+
+/******************************************/
+/* disable memory mapped ports / io ports */
+/******************************************/
+static void
+release_pci_ports(hfc4s8s_hw * hw)
+{
+ pci_write_config_word(hw->pdev, PCI_COMMAND, 0);
+#ifdef HISAX_HFC4S8S_PCIMEM
+ if (hw->membase)
+ iounmap((void *) hw->membase);
+#else
+ if (hw->iobase)
+ release_region(hw->iobase, 8);
+#endif
+}
+
+/*****************************************/
+/* enable memory mapped ports / io ports */
+/*****************************************/
+static void
+enable_pci_ports(hfc4s8s_hw * hw)
+{
+#ifdef HISAX_HFC4S8S_PCIMEM
+ pci_write_config_word(hw->pdev, PCI_COMMAND, PCI_ENA_MEMIO);
+#else
+ pci_write_config_word(hw->pdev, PCI_COMMAND, PCI_ENA_REGIO);
+#endif
+}
+
+/*************************************/
+/* initialise the HFC-4s/8s hardware */
+/* return 0 on success. */
+/*************************************/
+static int __devinit
+setup_instance(hfc4s8s_hw * hw)
+{
+ int err = -EIO;
+ int i;
+
+ for (i = 0; i < HFC_MAX_ST; i++) {
+ struct hfc4s8s_l1 *l1p;
+
+ l1p = hw->l1 + i;
+ spin_lock_init(&l1p->lock);
+ l1p->hw = hw;
+ l1p->l1_timer.function = (void *) hfc_l1_timer;
+ l1p->l1_timer.data = (long) (l1p);
+ init_timer(&l1p->l1_timer);
+ l1p->st_num = i;
+ skb_queue_head_init(&l1p->d_tx_queue);
+ l1p->d_if.ifc.priv = hw->l1 + i;
+ l1p->d_if.ifc.l2l1 = (void *) dch_l2l1;
+
+ spin_lock_init(&l1p->b_ch[0].lock);
+ l1p->b_ch[0].b_if.ifc.l2l1 = (void *) bch_l2l1;
+ l1p->b_ch[0].b_if.ifc.priv = (void *) &l1p->b_ch[0];
+ l1p->b_ch[0].l1p = hw->l1 + i;
+ l1p->b_ch[0].bchan = 1;
+ l1p->b_table[0] = &l1p->b_ch[0].b_if;
+ skb_queue_head_init(&l1p->b_ch[0].tx_queue);
+
+ spin_lock_init(&l1p->b_ch[1].lock);
+ l1p->b_ch[1].b_if.ifc.l2l1 = (void *) bch_l2l1;
+ l1p->b_ch[1].b_if.ifc.priv = (void *) &l1p->b_ch[1];
+ l1p->b_ch[1].l1p = hw->l1 + i;
+ l1p->b_ch[1].bchan = 2;
+ l1p->b_table[1] = &l1p->b_ch[1].b_if;
+ skb_queue_head_init(&l1p->b_ch[1].tx_queue);
+ }
+
+ enable_pci_ports(hw);
+ chipreset(hw);
+
+ i = Read_hfc8(hw, R_CHIP_ID) >> CHIP_ID_SHIFT;
+ if (i != hw->driver_data.chip_id) {
+ printk(KERN_INFO
+ "HFC-4S/8S: invalid chip id 0x%x instead of 0x%x, card ignored\n",
+ i, hw->driver_data.chip_id);
+ goto out;
+ }
+
+ i = Read_hfc8(hw, R_CHIP_RV) & 0xf;
+ if (!i) {
+ printk(KERN_INFO
+ "HFC-4S/8S: chip revision 0 not supported, card ignored\n");
+ goto out;
+ }
+
+ INIT_WORK(&hw->tqueue, hfc4s8s_bh);
+
+ if (request_irq
+ (hw->irq, hfc4s8s_interrupt, IRQF_SHARED, hw->card_name, hw)) {
+ printk(KERN_INFO
+ "HFC-4S/8S: unable to alloc irq %d, card ignored\n",
+ hw->irq);
+ goto out;
+ }
+#ifdef HISAX_HFC4S8S_PCIMEM
+ printk(KERN_INFO
+ "HFC-4S/8S: found PCI card at membase 0x%p, irq %d\n",
+ hw->hw_membase, hw->irq);
+#else
+ printk(KERN_INFO
+ "HFC-4S/8S: found PCI card at iobase 0x%x, irq %d\n",
+ hw->iobase, hw->irq);
+#endif
+
+ hfc_hardware_enable(hw, 1, 0);
+
+ return (0);
+
+ out:
+ hw->irq = 0;
+ release_pci_ports(hw);
+ kfree(hw);
+ return (err);
+}
+
+/*****************************************/
+/* PCI hotplug interface: probe new card */
+/*****************************************/
+static int __devinit
+hfc4s8s_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ int err = -ENOMEM;
+ hfc4s8s_param *driver_data = (hfc4s8s_param *) ent->driver_data;
+ hfc4s8s_hw *hw;
+
+ if (!(hw = kzalloc(sizeof(hfc4s8s_hw), GFP_ATOMIC))) {
+ printk(KERN_ERR "No kmem for HFC-4S/8S card\n");
+ return (err);
+ }
+
+ hw->pdev = pdev;
+ err = pci_enable_device(pdev);
+
+ if (err)
+ goto out;
+
+ hw->cardnum = card_cnt;
+ sprintf(hw->card_name, "hfc4s8s_%d", hw->cardnum);
+ printk(KERN_INFO "HFC-4S/8S: found adapter %s (%s) at %s\n",
+ driver_data->device_name, hw->card_name, pci_name(pdev));
+
+ spin_lock_init(&hw->lock);
+
+ hw->driver_data = *driver_data;
+ hw->irq = pdev->irq;
+ hw->iobase = pci_resource_start(pdev, 0);
+
+#ifdef HISAX_HFC4S8S_PCIMEM
+ hw->hw_membase = (u_char *) pci_resource_start(pdev, 1);
+ hw->membase = ioremap((ulong) hw->hw_membase, 256);
+#else
+ if (!request_region(hw->iobase, 8, hw->card_name)) {
+ printk(KERN_INFO
+ "HFC-4S/8S: failed to rquest address space at 0x%04x\n",
+ hw->iobase);
+ goto out;
+ }
+#endif
+
+ pci_set_drvdata(pdev, hw);
+ err = setup_instance(hw);
+ if (!err)
+ card_cnt++;
+ return (err);
+
+ out:
+ kfree(hw);
+ return (err);
+}
+
+/**************************************/
+/* PCI hotplug interface: remove card */
+/**************************************/
+static void __devexit
+hfc4s8s_remove(struct pci_dev *pdev)
+{
+ hfc4s8s_hw *hw = pci_get_drvdata(pdev);
+
+ printk(KERN_INFO "HFC-4S/8S: removing card %d\n", hw->cardnum);
+ hfc_hardware_enable(hw, 0, 0);
+
+ if (hw->irq)
+ free_irq(hw->irq, hw);
+ hw->irq = 0;
+ release_pci_ports(hw);
+
+ card_cnt--;
+ pci_disable_device(pdev);
+ kfree(hw);
+ return;
+}
+
+static struct pci_driver hfc4s8s_driver = {
+ .name = "hfc4s8s_l1",
+ .probe = hfc4s8s_probe,
+ .remove = __devexit_p(hfc4s8s_remove),
+ .id_table = hfc4s8s_ids,
+};
+
+/**********************/
+/* driver Module init */
+/**********************/
+static int __init
+hfc4s8s_module_init(void)
+{
+ int err;
+
+ printk(KERN_INFO
+ "HFC-4S/8S: Layer 1 driver module for HFC-4S/8S isdn chips, %s\n",
+ hfc4s8s_rev);
+ printk(KERN_INFO
+ "HFC-4S/8S: (C) 2003 Cornelius Consult, www.cornelius-consult.de\n");
+
+ card_cnt = 0;
+
+ err = pci_register_driver(&hfc4s8s_driver);
+ if (err < 0) {
+ goto out;
+ }
+ printk(KERN_INFO "HFC-4S/8S: found %d cards\n", card_cnt);
+
+#if !defined(CONFIG_HOTPLUG)
+ if (err == 0) {
+ err = -ENODEV;
+ pci_unregister_driver(&hfc4s8s_driver);
+ goto out;
+ }
+#endif
+
+ return 0;
+ out:
+ return (err);
+} /* hfc4s8s_init_hw */
+
+/*************************************/
+/* driver module exit : */
+/* release the HFC-4s/8s hardware */
+/*************************************/
+static void __exit
+hfc4s8s_module_exit(void)
+{
+ pci_unregister_driver(&hfc4s8s_driver);
+ printk(KERN_INFO "HFC-4S/8S: module removed\n");
+} /* hfc4s8s_release_hw */
+
+module_init(hfc4s8s_module_init);
+module_exit(hfc4s8s_module_exit);
diff --git a/drivers/isdn/hisax/hfc4s8s_l1.h b/drivers/isdn/hisax/hfc4s8s_l1.h
new file mode 100644
index 0000000..9d5d2a5
--- /dev/null
+++ b/drivers/isdn/hisax/hfc4s8s_l1.h
@@ -0,0 +1,88 @@
+/***************************************************************/
+/* $Id: hfc4s8s_l1.h,v 1.1 2005/02/02 17:28:55 martinb1 Exp $ */
+/* */
+/* This file is a minimal required extraction of hfc48scu.h */
+/* (Genero 3.2, HFC XML 1.7a for HFC-E1, HFC-4S and HFC-8S) */
+/* */
+/* To get this complete register description contact */
+/* Cologne Chip AG : */
+/* Internet: http://www.colognechip.com/ */
+/* E-Mail: info@colognechip.com */
+/***************************************************************/
+
+#ifndef _HFC4S8S_L1_H_
+#define _HFC4S8S_L1_H_
+
+
+/*
+* include Genero generated HFC-4S/8S header file hfc48scu.h
+* for complete register description. This will define _HFC48SCU_H_
+* to prevent redefinitions
+*/
+
+// #include "hfc48scu.h"
+
+#ifndef _HFC48SCU_H_
+#define _HFC48SCU_H_
+
+#ifndef PCI_VENDOR_ID_CCD
+#define PCI_VENDOR_ID_CCD 0x1397
+#endif
+
+#define CHIP_ID_4S 0x0C
+#define CHIP_ID_8S 0x08
+#define PCI_DEVICE_ID_4S 0x08B4
+#define PCI_DEVICE_ID_8S 0x16B8
+
+#define R_IRQ_MISC 0x11
+#define M_TI_IRQ 0x02
+#define A_ST_RD_STA 0x30
+#define A_ST_WR_STA 0x30
+#define M_SET_G2_G3 0x80
+#define A_ST_CTRL0 0x31
+#define A_ST_CTRL2 0x33
+#define A_ST_CLK_DLY 0x37
+#define A_Z1 0x04
+#define A_Z2 0x06
+#define R_CIRM 0x00
+#define M_SRES 0x08
+#define R_CTRL 0x01
+#define R_BRG_PCM_CFG 0x02
+#define M_PCM_CLK 0x20
+#define R_RAM_MISC 0x0C
+#define M_FZ_MD 0x80
+#define R_FIFO_MD 0x0D
+#define A_INC_RES_FIFO 0x0E
+#define R_FIFO 0x0F
+#define A_F1 0x0C
+#define A_F2 0x0D
+#define R_IRQ_OVIEW 0x10
+#define R_CHIP_ID 0x16
+#define R_STATUS 0x1C
+#define M_BUSY 0x01
+#define M_MISC_IRQSTA 0x40
+#define M_FR_IRQSTA 0x80
+#define R_CHIP_RV 0x1F
+#define R_IRQ_CTRL 0x13
+#define M_FIFO_IRQ 0x01
+#define M_GLOB_IRQ_EN 0x08
+#define R_PCM_MD0 0x14
+#define M_PCM_MD 0x01
+#define A_FIFO_DATA0 0x80
+#define R_TI_WD 0x1A
+#define R_PWM1 0x39
+#define R_PWM_MD 0x46
+#define R_IRQ_FIFO_BL0 0xC8
+#define A_CON_HDLC 0xFA
+#define A_SUBCH_CFG 0xFB
+#define A_IRQ_MSK 0xFF
+#define R_SCI_MSK 0x12
+#define R_ST_SEL 0x16
+#define R_ST_SYNC 0x17
+#define M_AUTO_SYNC 0x08
+#define R_SCI 0x12
+#define R_IRQMSK_MISC 0x11
+#define M_TI_IRQMSK 0x02
+
+#endif /* _HFC4S8S_L1_H_ */
+#endif /* _HFC48SCU_H_ */
diff --git a/drivers/isdn/hisax/hfc_2bds0.c b/drivers/isdn/hisax/hfc_2bds0.c
new file mode 100644
index 0000000..5c46a71
--- /dev/null
+++ b/drivers/isdn/hisax/hfc_2bds0.c
@@ -0,0 +1,1078 @@
+/* $Id: hfc_2bds0.c,v 1.18.2.6 2004/02/11 13:21:33 keil Exp $
+ *
+ * specific routines for CCD's HFC 2BDS0
+ *
+ * Author Karsten Keil
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "hfc_2bds0.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+/*
+#define KDEBUG_DEF
+#include "kdebug.h"
+*/
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+static void
+dummyf(struct IsdnCardState *cs, u_char * data, int size)
+{
+ printk(KERN_WARNING "HiSax: hfcd dummy fifo called\n");
+}
+
+static inline u_char
+ReadReg(struct IsdnCardState *cs, int data, u_char reg)
+{
+ register u_char ret;
+
+ if (data) {
+ if (cs->hw.hfcD.cip != reg) {
+ cs->hw.hfcD.cip = reg;
+ byteout(cs->hw.hfcD.addr | 1, reg);
+ }
+ ret = bytein(cs->hw.hfcD.addr);
+#ifdef HFC_REG_DEBUG
+ if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2))
+ debugl1(cs, "t3c RD %02x %02x", reg, ret);
+#endif
+ } else
+ ret = bytein(cs->hw.hfcD.addr | 1);
+ return (ret);
+}
+
+static inline void
+WriteReg(struct IsdnCardState *cs, int data, u_char reg, u_char value)
+{
+ if (cs->hw.hfcD.cip != reg) {
+ cs->hw.hfcD.cip = reg;
+ byteout(cs->hw.hfcD.addr | 1, reg);
+ }
+ if (data)
+ byteout(cs->hw.hfcD.addr, value);
+#ifdef HFC_REG_DEBUG
+ if (cs->debug & L1_DEB_HSCX_FIFO && (data != HFCD_DATA_NODEB))
+ debugl1(cs, "t3c W%c %02x %02x", data ? 'D' : 'C', reg, value);
+#endif
+}
+
+/* Interface functions */
+
+static u_char
+readreghfcd(struct IsdnCardState *cs, u_char offset)
+{
+ return(ReadReg(cs, HFCD_DATA, offset));
+}
+
+static void
+writereghfcd(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ WriteReg(cs, HFCD_DATA, offset, value);
+}
+
+static inline int
+WaitForBusy(struct IsdnCardState *cs)
+{
+ int to = 130;
+
+ while (!(ReadReg(cs, HFCD_DATA, HFCD_STAT) & HFCD_BUSY) && to) {
+ udelay(1);
+ to--;
+ }
+ if (!to)
+ printk(KERN_WARNING "HiSax: WaitForBusy timeout\n");
+ return (to);
+}
+
+static inline int
+WaitNoBusy(struct IsdnCardState *cs)
+{
+ int to = 130;
+
+ while ((ReadReg(cs, HFCD_STATUS, HFCD_STATUS) & HFCD_BUSY) && to) {
+ udelay(1);
+ to--;
+ }
+ if (!to)
+ printk(KERN_WARNING "HiSax: WaitNoBusy timeout\n");
+ return (to);
+}
+
+static int
+SelFiFo(struct IsdnCardState *cs, u_char FiFo)
+{
+ u_char cip;
+
+ if (cs->hw.hfcD.fifo == FiFo)
+ return(1);
+ switch(FiFo) {
+ case 0: cip = HFCB_FIFO | HFCB_Z1 | HFCB_SEND | HFCB_B1;
+ break;
+ case 1: cip = HFCB_FIFO | HFCB_Z1 | HFCB_REC | HFCB_B1;
+ break;
+ case 2: cip = HFCB_FIFO | HFCB_Z1 | HFCB_SEND | HFCB_B2;
+ break;
+ case 3: cip = HFCB_FIFO | HFCB_Z1 | HFCB_REC | HFCB_B2;
+ break;
+ case 4: cip = HFCD_FIFO | HFCD_Z1 | HFCD_SEND;
+ break;
+ case 5: cip = HFCD_FIFO | HFCD_Z1 | HFCD_REC;
+ break;
+ default:
+ debugl1(cs, "SelFiFo Error");
+ return(0);
+ }
+ cs->hw.hfcD.fifo = FiFo;
+ WaitNoBusy(cs);
+ cs->BC_Write_Reg(cs, HFCD_DATA, cip, 0);
+ WaitForBusy(cs);
+ return(2);
+}
+
+static int
+GetFreeFifoBytes_B(struct BCState *bcs)
+{
+ int s;
+
+ if (bcs->hw.hfc.f1 == bcs->hw.hfc.f2)
+ return (bcs->cs->hw.hfcD.bfifosize);
+ s = bcs->hw.hfc.send[bcs->hw.hfc.f1] - bcs->hw.hfc.send[bcs->hw.hfc.f2];
+ if (s <= 0)
+ s += bcs->cs->hw.hfcD.bfifosize;
+ s = bcs->cs->hw.hfcD.bfifosize - s;
+ return (s);
+}
+
+static int
+GetFreeFifoBytes_D(struct IsdnCardState *cs)
+{
+ int s;
+
+ if (cs->hw.hfcD.f1 == cs->hw.hfcD.f2)
+ return (cs->hw.hfcD.dfifosize);
+ s = cs->hw.hfcD.send[cs->hw.hfcD.f1] - cs->hw.hfcD.send[cs->hw.hfcD.f2];
+ if (s <= 0)
+ s += cs->hw.hfcD.dfifosize;
+ s = cs->hw.hfcD.dfifosize - s;
+ return (s);
+}
+
+static int
+ReadZReg(struct IsdnCardState *cs, u_char reg)
+{
+ int val;
+
+ WaitNoBusy(cs);
+ val = 256 * ReadReg(cs, HFCD_DATA, reg | HFCB_Z_HIGH);
+ WaitNoBusy(cs);
+ val += ReadReg(cs, HFCD_DATA, reg | HFCB_Z_LOW);
+ return (val);
+}
+
+static struct sk_buff
+*hfc_empty_fifo(struct BCState *bcs, int count)
+{
+ u_char *ptr;
+ struct sk_buff *skb;
+ struct IsdnCardState *cs = bcs->cs;
+ int idx;
+ int chksum;
+ u_char stat, cip;
+
+ if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+ debugl1(cs, "hfc_empty_fifo");
+ idx = 0;
+ if (count > HSCX_BUFMAX + 3) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "hfc_empty_fifo: incoming packet too large");
+ cip = HFCB_FIFO | HFCB_FIFO_OUT | HFCB_REC | HFCB_CHANNEL(bcs->channel);
+ while (idx++ < count) {
+ WaitNoBusy(cs);
+ ReadReg(cs, HFCD_DATA_NODEB, cip);
+ }
+ skb = NULL;
+ } else if (count < 4) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "hfc_empty_fifo: incoming packet too small");
+ cip = HFCB_FIFO | HFCB_FIFO_OUT | HFCB_REC | HFCB_CHANNEL(bcs->channel);
+#ifdef ERROR_STATISTIC
+ bcs->err_inv++;
+#endif
+ while ((idx++ < count) && WaitNoBusy(cs))
+ ReadReg(cs, HFCD_DATA_NODEB, cip);
+ skb = NULL;
+ } else if (!(skb = dev_alloc_skb(count - 3)))
+ printk(KERN_WARNING "HFC: receive out of memory\n");
+ else {
+ ptr = skb_put(skb, count - 3);
+ idx = 0;
+ cip = HFCB_FIFO | HFCB_FIFO_OUT | HFCB_REC | HFCB_CHANNEL(bcs->channel);
+ while (idx < (count - 3)) {
+ if (!WaitNoBusy(cs))
+ break;
+ *ptr = ReadReg(cs, HFCD_DATA_NODEB, cip);
+ ptr++;
+ idx++;
+ }
+ if (idx != count - 3) {
+ debugl1(cs, "RFIFO BUSY error");
+ printk(KERN_WARNING "HFC FIFO channel %d BUSY Error\n", bcs->channel);
+ dev_kfree_skb_irq(skb);
+ skb = NULL;
+ } else {
+ WaitNoBusy(cs);
+ chksum = (ReadReg(cs, HFCD_DATA, cip) << 8);
+ WaitNoBusy(cs);
+ chksum += ReadReg(cs, HFCD_DATA, cip);
+ WaitNoBusy(cs);
+ stat = ReadReg(cs, HFCD_DATA, cip);
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfc_empty_fifo %d chksum %x stat %x",
+ bcs->channel, chksum, stat);
+ if (stat) {
+ debugl1(cs, "FIFO CRC error");
+ dev_kfree_skb_irq(skb);
+ skb = NULL;
+#ifdef ERROR_STATISTIC
+ bcs->err_crc++;
+#endif
+ }
+ }
+ }
+ WaitForBusy(cs);
+ WaitNoBusy(cs);
+ stat = ReadReg(cs, HFCD_DATA, HFCB_FIFO | HFCB_F2_INC |
+ HFCB_REC | HFCB_CHANNEL(bcs->channel));
+ WaitForBusy(cs);
+ return (skb);
+}
+
+static void
+hfc_fill_fifo(struct BCState *bcs)
+{
+ struct IsdnCardState *cs = bcs->cs;
+ int idx, fcnt;
+ int count;
+ u_char cip;
+
+ if (!bcs->tx_skb)
+ return;
+ if (bcs->tx_skb->len <= 0)
+ return;
+ SelFiFo(cs, HFCB_SEND | HFCB_CHANNEL(bcs->channel));
+ cip = HFCB_FIFO | HFCB_F1 | HFCB_SEND | HFCB_CHANNEL(bcs->channel);
+ WaitNoBusy(cs);
+ bcs->hw.hfc.f1 = ReadReg(cs, HFCD_DATA, cip);
+ WaitNoBusy(cs);
+ cip = HFCB_FIFO | HFCB_F2 | HFCB_SEND | HFCB_CHANNEL(bcs->channel);
+ WaitNoBusy(cs);
+ bcs->hw.hfc.f2 = ReadReg(cs, HFCD_DATA, cip);
+ bcs->hw.hfc.send[bcs->hw.hfc.f1] = ReadZReg(cs, HFCB_FIFO | HFCB_Z1 | HFCB_SEND | HFCB_CHANNEL(bcs->channel));
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfc_fill_fifo %d f1(%d) f2(%d) z1(%x)",
+ bcs->channel, bcs->hw.hfc.f1, bcs->hw.hfc.f2,
+ bcs->hw.hfc.send[bcs->hw.hfc.f1]);
+ fcnt = bcs->hw.hfc.f1 - bcs->hw.hfc.f2;
+ if (fcnt < 0)
+ fcnt += 32;
+ if (fcnt > 30) {
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfc_fill_fifo more as 30 frames");
+ return;
+ }
+ count = GetFreeFifoBytes_B(bcs);
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfc_fill_fifo %d count(%ld/%d),%lx",
+ bcs->channel, bcs->tx_skb->len,
+ count, current->state);
+ if (count < bcs->tx_skb->len) {
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfc_fill_fifo no fifo mem");
+ return;
+ }
+ cip = HFCB_FIFO | HFCB_FIFO_IN | HFCB_SEND | HFCB_CHANNEL(bcs->channel);
+ idx = 0;
+ WaitForBusy(cs);
+ WaitNoBusy(cs);
+ WriteReg(cs, HFCD_DATA_NODEB, cip, bcs->tx_skb->data[idx++]);
+ while (idx < bcs->tx_skb->len) {
+ if (!WaitNoBusy(cs))
+ break;
+ WriteReg(cs, HFCD_DATA_NODEB, cip, bcs->tx_skb->data[idx]);
+ idx++;
+ }
+ if (idx != bcs->tx_skb->len) {
+ debugl1(cs, "FIFO Send BUSY error");
+ printk(KERN_WARNING "HFC S FIFO channel %d BUSY Error\n", bcs->channel);
+ } else {
+ bcs->tx_cnt -= bcs->tx_skb->len;
+ 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->tx_skb->len;
+ spin_unlock_irqrestore(&bcs->aclock, flags);
+ schedule_event(bcs, B_ACKPENDING);
+ }
+ dev_kfree_skb_any(bcs->tx_skb);
+ bcs->tx_skb = NULL;
+ }
+ WaitForBusy(cs);
+ WaitNoBusy(cs);
+ ReadReg(cs, HFCD_DATA, HFCB_FIFO | HFCB_F1_INC | HFCB_SEND | HFCB_CHANNEL(bcs->channel));
+ WaitForBusy(cs);
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ return;
+}
+
+static void
+hfc_send_data(struct BCState *bcs)
+{
+ struct IsdnCardState *cs = bcs->cs;
+
+ if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ hfc_fill_fifo(bcs);
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ } else
+ debugl1(cs,"send_data %d blocked", bcs->channel);
+}
+
+static void
+main_rec_2bds0(struct BCState *bcs)
+{
+ struct IsdnCardState *cs = bcs->cs;
+ int z1, z2, rcnt;
+ u_char f1, f2, cip;
+ int receive, count = 5;
+ struct sk_buff *skb;
+
+ Begin:
+ count--;
+ if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ debugl1(cs,"rec_data %d blocked", bcs->channel);
+ return;
+ }
+ SelFiFo(cs, HFCB_REC | HFCB_CHANNEL(bcs->channel));
+ cip = HFCB_FIFO | HFCB_F1 | HFCB_REC | HFCB_CHANNEL(bcs->channel);
+ WaitNoBusy(cs);
+ f1 = ReadReg(cs, HFCD_DATA, cip);
+ cip = HFCB_FIFO | HFCB_F2 | HFCB_REC | HFCB_CHANNEL(bcs->channel);
+ WaitNoBusy(cs);
+ f2 = ReadReg(cs, HFCD_DATA, cip);
+ if (f1 != f2) {
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfc rec %d f1(%d) f2(%d)",
+ bcs->channel, f1, f2);
+ z1 = ReadZReg(cs, HFCB_FIFO | HFCB_Z1 | HFCB_REC | HFCB_CHANNEL(bcs->channel));
+ z2 = ReadZReg(cs, HFCB_FIFO | HFCB_Z2 | HFCB_REC | HFCB_CHANNEL(bcs->channel));
+ rcnt = z1 - z2;
+ if (rcnt < 0)
+ rcnt += cs->hw.hfcD.bfifosize;
+ rcnt++;
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfc rec %d z1(%x) z2(%x) cnt(%d)",
+ bcs->channel, z1, z2, rcnt);
+ if ((skb = hfc_empty_fifo(bcs, rcnt))) {
+ skb_queue_tail(&bcs->rqueue, skb);
+ schedule_event(bcs, B_RCVBUFREADY);
+ }
+ rcnt = f1 -f2;
+ if (rcnt<0)
+ rcnt += 32;
+ if (rcnt>1)
+ receive = 1;
+ else
+ receive = 0;
+ } else
+ receive = 0;
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ if (count && receive)
+ goto Begin;
+ return;
+}
+
+static void
+mode_2bs0(struct BCState *bcs, int mode, int bc)
+{
+ struct IsdnCardState *cs = bcs->cs;
+
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "HFCD bchannel mode %d bchan %d/%d",
+ mode, bc, bcs->channel);
+ bcs->mode = mode;
+ bcs->channel = bc;
+ switch (mode) {
+ case (L1_MODE_NULL):
+ if (bc) {
+ cs->hw.hfcD.conn |= 0x18;
+ cs->hw.hfcD.sctrl &= ~SCTRL_B2_ENA;
+ } else {
+ cs->hw.hfcD.conn |= 0x3;
+ cs->hw.hfcD.sctrl &= ~SCTRL_B1_ENA;
+ }
+ break;
+ case (L1_MODE_TRANS):
+ if (bc) {
+ cs->hw.hfcD.ctmt |= 2;
+ cs->hw.hfcD.conn &= ~0x18;
+ cs->hw.hfcD.sctrl |= SCTRL_B2_ENA;
+ } else {
+ cs->hw.hfcD.ctmt |= 1;
+ cs->hw.hfcD.conn &= ~0x3;
+ cs->hw.hfcD.sctrl |= SCTRL_B1_ENA;
+ }
+ break;
+ case (L1_MODE_HDLC):
+ if (bc) {
+ cs->hw.hfcD.ctmt &= ~2;
+ cs->hw.hfcD.conn &= ~0x18;
+ cs->hw.hfcD.sctrl |= SCTRL_B2_ENA;
+ } else {
+ cs->hw.hfcD.ctmt &= ~1;
+ cs->hw.hfcD.conn &= ~0x3;
+ cs->hw.hfcD.sctrl |= SCTRL_B1_ENA;
+ }
+ break;
+ }
+ WriteReg(cs, HFCD_DATA, HFCD_SCTRL, cs->hw.hfcD.sctrl);
+ WriteReg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt);
+ WriteReg(cs, HFCD_DATA, HFCD_CONN, cs->hw.hfcD.conn);
+}
+
+static void
+hfc_l2l1(struct PStack *st, int pr, void *arg)
+{
+ struct BCState *bcs = st->l1.bcs;
+ struct sk_buff *skb = arg;
+ 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);
+ 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 "hfc_l2l1: this shouldn't happen\n");
+ } else {
+// test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+ bcs->tx_skb = skb;
+ 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);
+ mode_2bs0(bcs, st->l1.mode, st->l1.bc);
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | REQUEST):
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | CONFIRM):
+ spin_lock_irqsave(&bcs->cs->lock, flags);
+ test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ mode_2bs0(bcs, 0, st->l1.bc);
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
+ st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+ break;
+ }
+}
+
+static void
+close_2bs0(struct BCState *bcs)
+{
+ mode_2bs0(bcs, 0, bcs->channel);
+ if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+ 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);
+ }
+ }
+}
+
+static int
+open_hfcstate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+ if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+ 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);
+ bcs->event = 0;
+ bcs->tx_cnt = 0;
+ return (0);
+}
+
+static int
+setstack_2b(struct PStack *st, struct BCState *bcs)
+{
+ bcs->channel = st->l1.bc;
+ if (open_hfcstate(st->l1.hardware, bcs))
+ return (-1);
+ st->l1.bcs = bcs;
+ st->l2.l2l1 = hfc_l2l1;
+ setstack_manager(st);
+ bcs->st = st;
+ setstack_l1_B(st);
+ return (0);
+}
+
+static void
+hfcd_bh(struct work_struct *work)
+{
+ struct IsdnCardState *cs =
+ container_of(work, struct IsdnCardState, tqueue);
+
+ if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) {
+ switch (cs->dc.hfcd.ph_state) {
+ case (0):
+ l1_msg(cs, HW_RESET | INDICATION, NULL);
+ break;
+ case (3):
+ l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
+ break;
+ case (8):
+ l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+ break;
+ case (6):
+ l1_msg(cs, HW_INFO2 | INDICATION, NULL);
+ break;
+ case (7):
+ l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
+ break;
+ default:
+ break;
+ }
+ }
+ if (test_and_clear_bit(D_RCVBUFREADY, &cs->event))
+ DChannel_proc_rcv(cs);
+ if (test_and_clear_bit(D_XMTBUFREADY, &cs->event))
+ DChannel_proc_xmt(cs);
+}
+
+static
+int receive_dmsg(struct IsdnCardState *cs)
+{
+ struct sk_buff *skb;
+ int idx;
+ int rcnt, z1, z2;
+ u_char stat, cip, f1, f2;
+ int chksum;
+ int count=5;
+ u_char *ptr;
+
+ if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ debugl1(cs, "rec_dmsg blocked");
+ return(1);
+ }
+ SelFiFo(cs, 4 | HFCD_REC);
+ cip = HFCD_FIFO | HFCD_F1 | HFCD_REC;
+ WaitNoBusy(cs);
+ f1 = cs->readisac(cs, cip) & 0xf;
+ cip = HFCD_FIFO | HFCD_F2 | HFCD_REC;
+ WaitNoBusy(cs);
+ f2 = cs->readisac(cs, cip) & 0xf;
+ while ((f1 != f2) && count--) {
+ z1 = ReadZReg(cs, HFCD_FIFO | HFCD_Z1 | HFCD_REC);
+ z2 = ReadZReg(cs, HFCD_FIFO | HFCD_Z2 | HFCD_REC);
+ rcnt = z1 - z2;
+ if (rcnt < 0)
+ rcnt += cs->hw.hfcD.dfifosize;
+ rcnt++;
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "hfcd recd f1(%d) f2(%d) z1(%x) z2(%x) cnt(%d)",
+ f1, f2, z1, z2, rcnt);
+ idx = 0;
+ cip = HFCD_FIFO | HFCD_FIFO_OUT | HFCD_REC;
+ if (rcnt > MAX_DFRAME_LEN + 3) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "empty_fifo d: incoming packet too large");
+ while (idx < rcnt) {
+ if (!(WaitNoBusy(cs)))
+ break;
+ ReadReg(cs, HFCD_DATA_NODEB, cip);
+ idx++;
+ }
+ } else if (rcnt < 4) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "empty_fifo d: incoming packet too small");
+ while ((idx++ < rcnt) && WaitNoBusy(cs))
+ ReadReg(cs, HFCD_DATA_NODEB, cip);
+ } else if ((skb = dev_alloc_skb(rcnt - 3))) {
+ ptr = skb_put(skb, rcnt - 3);
+ while (idx < (rcnt - 3)) {
+ if (!(WaitNoBusy(cs)))
+ break;
+ *ptr = ReadReg(cs, HFCD_DATA_NODEB, cip);
+ idx++;
+ ptr++;
+ }
+ if (idx != (rcnt - 3)) {
+ debugl1(cs, "RFIFO D BUSY error");
+ printk(KERN_WARNING "HFC DFIFO channel BUSY Error\n");
+ dev_kfree_skb_irq(skb);
+ skb = NULL;
+#ifdef ERROR_STATISTIC
+ cs->err_rx++;
+#endif
+ } else {
+ WaitNoBusy(cs);
+ chksum = (ReadReg(cs, HFCD_DATA, cip) << 8);
+ WaitNoBusy(cs);
+ chksum += ReadReg(cs, HFCD_DATA, cip);
+ WaitNoBusy(cs);
+ stat = ReadReg(cs, HFCD_DATA, cip);
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "empty_dfifo chksum %x stat %x",
+ chksum, stat);
+ if (stat) {
+ debugl1(cs, "FIFO CRC error");
+ dev_kfree_skb_irq(skb);
+ skb = NULL;
+#ifdef ERROR_STATISTIC
+ cs->err_crc++;
+#endif
+ } else {
+ skb_queue_tail(&cs->rq, skb);
+ schedule_event(cs, D_RCVBUFREADY);
+ }
+ }
+ } else
+ printk(KERN_WARNING "HFC: D receive out of memory\n");
+ WaitForBusy(cs);
+ cip = HFCD_FIFO | HFCD_F2_INC | HFCD_REC;
+ WaitNoBusy(cs);
+ stat = ReadReg(cs, HFCD_DATA, cip);
+ WaitForBusy(cs);
+ cip = HFCD_FIFO | HFCD_F2 | HFCD_REC;
+ WaitNoBusy(cs);
+ f2 = cs->readisac(cs, cip) & 0xf;
+ }
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ return(1);
+}
+
+static void
+hfc_fill_dfifo(struct IsdnCardState *cs)
+{
+ int idx, fcnt;
+ int count;
+ u_char cip;
+
+ if (!cs->tx_skb)
+ return;
+ if (cs->tx_skb->len <= 0)
+ return;
+
+ SelFiFo(cs, 4 | HFCD_SEND);
+ cip = HFCD_FIFO | HFCD_F1 | HFCD_SEND;
+ WaitNoBusy(cs);
+ cs->hw.hfcD.f1 = ReadReg(cs, HFCD_DATA, cip) & 0xf;
+ WaitNoBusy(cs);
+ cip = HFCD_FIFO | HFCD_F2 | HFCD_SEND;
+ cs->hw.hfcD.f2 = ReadReg(cs, HFCD_DATA, cip) & 0xf;
+ cs->hw.hfcD.send[cs->hw.hfcD.f1] = ReadZReg(cs, HFCD_FIFO | HFCD_Z1 | HFCD_SEND);
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "hfc_fill_Dfifo f1(%d) f2(%d) z1(%x)",
+ cs->hw.hfcD.f1, cs->hw.hfcD.f2,
+ cs->hw.hfcD.send[cs->hw.hfcD.f1]);
+ fcnt = cs->hw.hfcD.f1 - cs->hw.hfcD.f2;
+ if (fcnt < 0)
+ fcnt += 16;
+ if (fcnt > 14) {
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfc_fill_Dfifo more as 14 frames");
+ return;
+ }
+ count = GetFreeFifoBytes_D(cs);
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "hfc_fill_Dfifo count(%ld/%d)",
+ cs->tx_skb->len, count);
+ if (count < cs->tx_skb->len) {
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "hfc_fill_Dfifo no fifo mem");
+ return;
+ }
+ cip = HFCD_FIFO | HFCD_FIFO_IN | HFCD_SEND;
+ idx = 0;
+ WaitForBusy(cs);
+ WaitNoBusy(cs);
+ WriteReg(cs, HFCD_DATA_NODEB, cip, cs->tx_skb->data[idx++]);
+ while (idx < cs->tx_skb->len) {
+ if (!(WaitNoBusy(cs)))
+ break;
+ WriteReg(cs, HFCD_DATA_NODEB, cip, cs->tx_skb->data[idx]);
+ idx++;
+ }
+ if (idx != cs->tx_skb->len) {
+ debugl1(cs, "DFIFO Send BUSY error");
+ printk(KERN_WARNING "HFC S DFIFO channel BUSY Error\n");
+ }
+ WaitForBusy(cs);
+ WaitNoBusy(cs);
+ ReadReg(cs, HFCD_DATA, HFCD_FIFO | HFCD_F1_INC | HFCD_SEND);
+ dev_kfree_skb_any(cs->tx_skb);
+ cs->tx_skb = NULL;
+ WaitForBusy(cs);
+ return;
+}
+
+static
+struct BCState *Sel_BCS(struct IsdnCardState *cs, int channel)
+{
+ if (cs->bcs[0].mode && (cs->bcs[0].channel == channel))
+ return(&cs->bcs[0]);
+ else if (cs->bcs[1].mode && (cs->bcs[1].channel == channel))
+ return(&cs->bcs[1]);
+ else
+ return(NULL);
+}
+
+void
+hfc2bds0_interrupt(struct IsdnCardState *cs, u_char val)
+{
+ u_char exval;
+ struct BCState *bcs;
+ int count=15;
+
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "HFCD irq %x %s", val,
+ test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags) ?
+ "locked" : "unlocked");
+ val &= cs->hw.hfcD.int_m1;
+ if (val & 0x40) { /* TE state machine irq */
+ exval = cs->readisac(cs, HFCD_STATES) & 0xf;
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ph_state chg %d->%d", cs->dc.hfcd.ph_state,
+ exval);
+ cs->dc.hfcd.ph_state = exval;
+ schedule_event(cs, D_L1STATECHANGE);
+ val &= ~0x40;
+ }
+ while (val) {
+ if (test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ cs->hw.hfcD.int_s1 |= val;
+ return;
+ }
+ if (cs->hw.hfcD.int_s1 & 0x18) {
+ exval = val;
+ val = cs->hw.hfcD.int_s1;
+ cs->hw.hfcD.int_s1 = exval;
+ }
+ if (val & 0x08) {
+ if (!(bcs=Sel_BCS(cs, 0))) {
+ if (cs->debug)
+ debugl1(cs, "hfcd spurious 0x08 IRQ");
+ } else
+ main_rec_2bds0(bcs);
+ }
+ if (val & 0x10) {
+ if (!(bcs=Sel_BCS(cs, 1))) {
+ if (cs->debug)
+ debugl1(cs, "hfcd spurious 0x10 IRQ");
+ } else
+ main_rec_2bds0(bcs);
+ }
+ if (val & 0x01) {
+ if (!(bcs=Sel_BCS(cs, 0))) {
+ if (cs->debug)
+ debugl1(cs, "hfcd spurious 0x01 IRQ");
+ } else {
+ if (bcs->tx_skb) {
+ if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ hfc_fill_fifo(bcs);
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ } else
+ debugl1(cs,"fill_data %d blocked", bcs->channel);
+ } else {
+ if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+ if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ hfc_fill_fifo(bcs);
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ } else
+ debugl1(cs,"fill_data %d blocked", bcs->channel);
+ } else {
+ schedule_event(bcs, B_XMTBUFREADY);
+ }
+ }
+ }
+ }
+ if (val & 0x02) {
+ if (!(bcs=Sel_BCS(cs, 1))) {
+ if (cs->debug)
+ debugl1(cs, "hfcd spurious 0x02 IRQ");
+ } else {
+ if (bcs->tx_skb) {
+ if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ hfc_fill_fifo(bcs);
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ } else
+ debugl1(cs,"fill_data %d blocked", bcs->channel);
+ } else {
+ if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+ if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ hfc_fill_fifo(bcs);
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ } else
+ debugl1(cs,"fill_data %d blocked", bcs->channel);
+ } else {
+ schedule_event(bcs, B_XMTBUFREADY);
+ }
+ }
+ }
+ }
+ if (val & 0x20) { /* receive dframe */
+ receive_dmsg(cs);
+ }
+ if (val & 0x04) { /* dframe transmitted */
+ if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+ del_timer(&cs->dbusytimer);
+ if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+ schedule_event(cs, D_CLEARBUSY);
+ if (cs->tx_skb) {
+ if (cs->tx_skb->len) {
+ if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ hfc_fill_dfifo(cs);
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ } else {
+ debugl1(cs, "hfc_fill_dfifo irq blocked");
+ }
+ goto afterXPR;
+ } else {
+ dev_kfree_skb_irq(cs->tx_skb);
+ cs->tx_cnt = 0;
+ cs->tx_skb = NULL;
+ }
+ }
+ if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
+ cs->tx_cnt = 0;
+ if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ hfc_fill_dfifo(cs);
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ } else {
+ debugl1(cs, "hfc_fill_dfifo irq blocked");
+ }
+ } else
+ schedule_event(cs, D_XMTBUFREADY);
+ }
+ afterXPR:
+ if (cs->hw.hfcD.int_s1 && count--) {
+ val = cs->hw.hfcD.int_s1;
+ cs->hw.hfcD.int_s1 = 0;
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "HFCD irq %x loop %d", val, 15-count);
+ } else
+ val = 0;
+ }
+}
+
+static void
+HFCD_l1hw(struct PStack *st, int pr, void *arg)
+{
+ struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+ struct sk_buff *skb = arg;
+ u_long flags;
+
+ switch (pr) {
+ case (PH_DATA | REQUEST):
+ if (cs->debug & DEB_DLOG_HEX)
+ LogFrame(cs, skb->data, skb->len);
+ if (cs->debug & DEB_DLOG_VERBOSE)
+ dlogframe(cs, skb, 0);
+ spin_lock_irqsave(&cs->lock, flags);
+ if (cs->tx_skb) {
+ skb_queue_tail(&cs->sq, skb);
+#ifdef L2FRAME_DEBUG /* psa */
+ if (cs->debug & L1_DEB_LAPD)
+ Logl2Frame(cs, skb, "PH_DATA Queued", 0);
+#endif
+ } else {
+ cs->tx_skb = skb;
+ cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG /* psa */
+ if (cs->debug & L1_DEB_LAPD)
+ Logl2Frame(cs, skb, "PH_DATA", 0);
+#endif
+ if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ hfc_fill_dfifo(cs);
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ } else
+ debugl1(cs, "hfc_fill_dfifo blocked");
+
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (PH_PULL | INDICATION):
+ spin_lock_irqsave(&cs->lock, flags);
+ if (cs->tx_skb) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
+ skb_queue_tail(&cs->sq, skb);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ }
+ if (cs->debug & DEB_DLOG_HEX)
+ LogFrame(cs, skb->data, skb->len);
+ if (cs->debug & DEB_DLOG_VERBOSE)
+ dlogframe(cs, skb, 0);
+ cs->tx_skb = skb;
+ cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG /* psa */
+ if (cs->debug & L1_DEB_LAPD)
+ Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
+#endif
+ if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ hfc_fill_dfifo(cs);
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ } else
+ debugl1(cs, "hfc_fill_dfifo blocked");
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (PH_PULL | REQUEST):
+#ifdef L2FRAME_DEBUG /* psa */
+ if (cs->debug & L1_DEB_LAPD)
+ debugl1(cs, "-> PH_REQUEST_PULL");
+#endif
+ if (!cs->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 (HW_RESET | REQUEST):
+ spin_lock_irqsave(&cs->lock, flags);
+ cs->writeisac(cs, HFCD_STATES, HFCD_LOAD_STATE | 3); /* HFC ST 3 */
+ udelay(6);
+ cs->writeisac(cs, HFCD_STATES, 3); /* HFC ST 2 */
+ cs->hw.hfcD.mst_m |= HFCD_MASTER;
+ cs->writeisac(cs, HFCD_MST_MODE, cs->hw.hfcD.mst_m);
+ cs->writeisac(cs, HFCD_STATES, HFCD_ACTIVATE | HFCD_DO_ACTION);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
+ break;
+ case (HW_ENABLE | REQUEST):
+ spin_lock_irqsave(&cs->lock, flags);
+ cs->writeisac(cs, HFCD_STATES, HFCD_ACTIVATE | HFCD_DO_ACTION);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (HW_DEACTIVATE | REQUEST):
+ spin_lock_irqsave(&cs->lock, flags);
+ cs->hw.hfcD.mst_m &= ~HFCD_MASTER;
+ cs->writeisac(cs, HFCD_MST_MODE, cs->hw.hfcD.mst_m);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (HW_INFO3 | REQUEST):
+ spin_lock_irqsave(&cs->lock, flags);
+ cs->hw.hfcD.mst_m |= HFCD_MASTER;
+ cs->writeisac(cs, HFCD_MST_MODE, cs->hw.hfcD.mst_m);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ default:
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "hfcd_l1hw unknown pr %4x", pr);
+ break;
+ }
+}
+
+static void
+setstack_hfcd(struct PStack *st, struct IsdnCardState *cs)
+{
+ st->l1.l1hw = HFCD_l1hw;
+}
+
+static void
+hfc_dbusy_timer(struct IsdnCardState *cs)
+{
+}
+
+static unsigned int
+*init_send_hfcd(int cnt)
+{
+ int i;
+ unsigned *send;
+
+ if (!(send = kmalloc(cnt * sizeof(unsigned int), GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "HiSax: No memory for hfcd.send\n");
+ return(NULL);
+ }
+ for (i = 0; i < cnt; i++)
+ send[i] = 0x1fff;
+ return(send);
+}
+
+void
+init2bds0(struct IsdnCardState *cs)
+{
+ cs->setstack_d = setstack_hfcd;
+ if (!cs->hw.hfcD.send)
+ cs->hw.hfcD.send = init_send_hfcd(16);
+ if (!cs->bcs[0].hw.hfc.send)
+ cs->bcs[0].hw.hfc.send = init_send_hfcd(32);
+ if (!cs->bcs[1].hw.hfc.send)
+ cs->bcs[1].hw.hfc.send = init_send_hfcd(32);
+ cs->BC_Send_Data = &hfc_send_data;
+ cs->bcs[0].BC_SetStack = setstack_2b;
+ cs->bcs[1].BC_SetStack = setstack_2b;
+ cs->bcs[0].BC_Close = close_2bs0;
+ cs->bcs[1].BC_Close = close_2bs0;
+ mode_2bs0(cs->bcs, 0, 0);
+ mode_2bs0(cs->bcs + 1, 0, 1);
+}
+
+void
+release2bds0(struct IsdnCardState *cs)
+{
+ kfree(cs->bcs[0].hw.hfc.send);
+ cs->bcs[0].hw.hfc.send = NULL;
+ kfree(cs->bcs[1].hw.hfc.send);
+ cs->bcs[1].hw.hfc.send = NULL;
+ kfree(cs->hw.hfcD.send);
+ cs->hw.hfcD.send = NULL;
+}
+
+void
+set_cs_func(struct IsdnCardState *cs)
+{
+ cs->readisac = &readreghfcd;
+ cs->writeisac = &writereghfcd;
+ cs->readisacfifo = &dummyf;
+ cs->writeisacfifo = &dummyf;
+ cs->BC_Read_Reg = &ReadReg;
+ cs->BC_Write_Reg = &WriteReg;
+ cs->dbusytimer.function = (void *) hfc_dbusy_timer;
+ cs->dbusytimer.data = (long) cs;
+ init_timer(&cs->dbusytimer);
+ INIT_WORK(&cs->tqueue, hfcd_bh);
+}
diff --git a/drivers/isdn/hisax/hfc_2bds0.h b/drivers/isdn/hisax/hfc_2bds0.h
new file mode 100644
index 0000000..30f1924
--- /dev/null
+++ b/drivers/isdn/hisax/hfc_2bds0.h
@@ -0,0 +1,128 @@
+/* $Id: hfc_2bds0.h,v 1.6.2.2 2004/01/12 22:52:26 keil Exp $
+ *
+ * specific defines for CCD's HFC 2BDS0
+ *
+ * Author Karsten Keil
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#define HFCD_CIRM 0x18
+#define HFCD_CTMT 0x19
+#define HFCD_INT_M1 0x1A
+#define HFCD_INT_M2 0x1B
+#define HFCD_INT_S1 0x1E
+#define HFCD_STAT 0x1C
+#define HFCD_STAT_DISB 0x1D
+#define HFCD_STATES 0x30
+#define HFCD_SCTRL 0x31
+#define HFCD_TEST 0x32
+#define HFCD_SQ 0x34
+#define HFCD_CLKDEL 0x37
+#define HFCD_MST_MODE 0x2E
+#define HFCD_CONN 0x2F
+
+#define HFCD_FIFO 0x80
+#define HFCD_Z1 0x10
+#define HFCD_Z2 0x18
+#define HFCD_Z_LOW 0x00
+#define HFCD_Z_HIGH 0x04
+#define HFCD_F1_INC 0x12
+#define HFCD_FIFO_IN 0x16
+#define HFCD_F1 0x1a
+#define HFCD_F2 0x1e
+#define HFCD_F2_INC 0x22
+#define HFCD_FIFO_OUT 0x26
+#define HFCD_REC 0x01
+#define HFCD_SEND 0x00
+
+#define HFCB_FIFO 0x80
+#define HFCB_Z1 0x00
+#define HFCB_Z2 0x08
+#define HFCB_Z_LOW 0x00
+#define HFCB_Z_HIGH 0x04
+#define HFCB_F1_INC 0x28
+#define HFCB_FIFO_IN 0x2c
+#define HFCB_F1 0x30
+#define HFCB_F2 0x34
+#define HFCB_F2_INC 0x38
+#define HFCB_FIFO_OUT 0x3c
+#define HFCB_REC 0x01
+#define HFCB_SEND 0x00
+#define HFCB_B1 0x00
+#define HFCB_B2 0x02
+#define HFCB_CHANNEL(ch) (ch ? HFCB_B2 : HFCB_B1)
+
+#define HFCD_STATUS 0
+#define HFCD_DATA 1
+#define HFCD_DATA_NODEB 2
+
+/* Status (READ) */
+#define HFCD_BUSY 0x01
+#define HFCD_BUSY_NBUSY 0x04
+#define HFCD_TIMER_ELAP 0x10
+#define HFCD_STATINT 0x20
+#define HFCD_FRAMEINT 0x40
+#define HFCD_ANYINT 0x80
+
+/* CTMT (Write) */
+#define HFCD_CLTIMER 0x80
+#define HFCD_TIM25 0x00
+#define HFCD_TIM50 0x08
+#define HFCD_TIM400 0x10
+#define HFCD_TIM800 0x18
+#define HFCD_AUTO_TIMER 0x20
+#define HFCD_TRANSB2 0x02
+#define HFCD_TRANSB1 0x01
+
+/* CIRM (Write) */
+#define HFCD_RESET 0x08
+#define HFCD_MEM8K 0x10
+#define HFCD_INTA 0x01
+#define HFCD_INTB 0x02
+#define HFCD_INTC 0x03
+#define HFCD_INTD 0x04
+#define HFCD_INTE 0x05
+#define HFCD_INTF 0x06
+
+/* INT_M1;INT_S1 */
+#define HFCD_INTS_B1TRANS 0x01
+#define HFCD_INTS_B2TRANS 0x02
+#define HFCD_INTS_DTRANS 0x04
+#define HFCD_INTS_B1REC 0x08
+#define HFCD_INTS_B2REC 0x10
+#define HFCD_INTS_DREC 0x20
+#define HFCD_INTS_L1STATE 0x40
+#define HFCD_INTS_TIMER 0x80
+
+/* INT_M2 */
+#define HFCD_IRQ_ENABLE 0x08
+
+/* STATES */
+#define HFCD_LOAD_STATE 0x10
+#define HFCD_ACTIVATE 0x20
+#define HFCD_DO_ACTION 0x40
+
+/* HFCD_MST_MODE */
+#define HFCD_MASTER 0x01
+
+/* HFCD_SCTRL */
+#define SCTRL_B1_ENA 0x01
+#define SCTRL_B2_ENA 0x02
+#define SCTRL_LOW_PRIO 0x08
+#define SCTRL_SQ_ENA 0x10
+#define SCTRL_TEST 0x20
+#define SCTRL_NONE_CAP 0x40
+#define SCTRL_PWR_DOWN 0x80
+
+/* HFCD_TEST */
+#define HFCD_AUTO_AWAKE 0x01
+
+extern void main_irq_2bds0(struct BCState *bcs);
+extern void init2bds0(struct IsdnCardState *cs);
+extern void release2bds0(struct IsdnCardState *cs);
+extern void hfc2bds0_interrupt(struct IsdnCardState *cs, u_char val);
+extern void set_cs_func(struct IsdnCardState *cs);
diff --git a/drivers/isdn/hisax/hfc_2bs0.c b/drivers/isdn/hisax/hfc_2bs0.c
new file mode 100644
index 0000000..d0520ad
--- /dev/null
+++ b/drivers/isdn/hisax/hfc_2bs0.c
@@ -0,0 +1,589 @@
+/* $Id: hfc_2bs0.c,v 1.20.2.6 2004/02/11 13:21:33 keil Exp $
+ *
+ * specific routines for CCD's HFC 2BS0
+ *
+ * Author Karsten Keil
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "hfc_2bs0.h"
+#include "isac.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+
+static inline int
+WaitForBusy(struct IsdnCardState *cs)
+{
+ int to = 130;
+ u_char val;
+
+ while (!(cs->BC_Read_Reg(cs, HFC_STATUS, 0) & HFC_BUSY) && to) {
+ val = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2 |
+ (cs->hw.hfc.cip & 3));
+ udelay(1);
+ to--;
+ }
+ if (!to) {
+ printk(KERN_WARNING "HiSax: waitforBusy timeout\n");
+ return (0);
+ } else
+ return (to);
+}
+
+static inline int
+WaitNoBusy(struct IsdnCardState *cs)
+{
+ int to = 125;
+
+ while ((cs->BC_Read_Reg(cs, HFC_STATUS, 0) & HFC_BUSY) && to) {
+ udelay(1);
+ to--;
+ }
+ if (!to) {
+ printk(KERN_WARNING "HiSax: waitforBusy timeout\n");
+ return (0);
+ } else
+ return (to);
+}
+
+static int
+GetFreeFifoBytes(struct BCState *bcs)
+{
+ int s;
+
+ if (bcs->hw.hfc.f1 == bcs->hw.hfc.f2)
+ return (bcs->cs->hw.hfc.fifosize);
+ s = bcs->hw.hfc.send[bcs->hw.hfc.f1] - bcs->hw.hfc.send[bcs->hw.hfc.f2];
+ if (s <= 0)
+ s += bcs->cs->hw.hfc.fifosize;
+ s = bcs->cs->hw.hfc.fifosize - s;
+ return (s);
+}
+
+static int
+ReadZReg(struct BCState *bcs, u_char reg)
+{
+ int val;
+
+ WaitNoBusy(bcs->cs);
+ val = 256 * bcs->cs->BC_Read_Reg(bcs->cs, HFC_DATA, reg | HFC_CIP | HFC_Z_HIGH);
+ WaitNoBusy(bcs->cs);
+ val += bcs->cs->BC_Read_Reg(bcs->cs, HFC_DATA, reg | HFC_CIP | HFC_Z_LOW);
+ return (val);
+}
+
+static void
+hfc_clear_fifo(struct BCState *bcs)
+{
+ struct IsdnCardState *cs = bcs->cs;
+ int idx, cnt;
+ int rcnt, z1, z2;
+ u_char cip, f1, f2;
+
+ if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+ debugl1(cs, "hfc_clear_fifo");
+ cip = HFC_CIP | HFC_F1 | HFC_REC | HFC_CHANNEL(bcs->channel);
+ if ((cip & 0xc3) != (cs->hw.hfc.cip & 0xc3)) {
+ cs->BC_Write_Reg(cs, HFC_STATUS, cip, cip);
+ WaitForBusy(cs);
+ }
+ WaitNoBusy(cs);
+ f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+ cip = HFC_CIP | HFC_F2 | HFC_REC | HFC_CHANNEL(bcs->channel);
+ WaitNoBusy(cs);
+ f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+ z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel));
+ z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel));
+ cnt = 32;
+ while (((f1 != f2) || (z1 != z2)) && cnt--) {
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfc clear %d f1(%d) f2(%d)",
+ bcs->channel, f1, f2);
+ rcnt = z1 - z2;
+ if (rcnt < 0)
+ rcnt += cs->hw.hfc.fifosize;
+ if (rcnt)
+ rcnt++;
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfc clear %d z1(%x) z2(%x) cnt(%d)",
+ bcs->channel, z1, z2, rcnt);
+ cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel);
+ idx = 0;
+ while ((idx < rcnt) && WaitNoBusy(cs)) {
+ cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip);
+ idx++;
+ }
+ if (f1 != f2) {
+ WaitNoBusy(cs);
+ cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
+ HFC_CHANNEL(bcs->channel));
+ WaitForBusy(cs);
+ }
+ cip = HFC_CIP | HFC_F1 | HFC_REC | HFC_CHANNEL(bcs->channel);
+ WaitNoBusy(cs);
+ f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+ cip = HFC_CIP | HFC_F2 | HFC_REC | HFC_CHANNEL(bcs->channel);
+ WaitNoBusy(cs);
+ f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+ z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel));
+ z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel));
+ }
+ return;
+}
+
+
+static struct sk_buff
+*
+hfc_empty_fifo(struct BCState *bcs, int count)
+{
+ u_char *ptr;
+ struct sk_buff *skb;
+ struct IsdnCardState *cs = bcs->cs;
+ int idx;
+ int chksum;
+ u_char stat, cip;
+
+ if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+ debugl1(cs, "hfc_empty_fifo");
+ idx = 0;
+ if (count > HSCX_BUFMAX + 3) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "hfc_empty_fifo: incoming packet too large");
+ cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel);
+ while ((idx++ < count) && WaitNoBusy(cs))
+ cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip);
+ WaitNoBusy(cs);
+ stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
+ HFC_CHANNEL(bcs->channel));
+ WaitForBusy(cs);
+ return (NULL);
+ }
+ if ((count < 4) && (bcs->mode != L1_MODE_TRANS)) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "hfc_empty_fifo: incoming packet too small");
+ cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel);
+ while ((idx++ < count) && WaitNoBusy(cs))
+ cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip);
+ WaitNoBusy(cs);
+ stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
+ HFC_CHANNEL(bcs->channel));
+ WaitForBusy(cs);
+#ifdef ERROR_STATISTIC
+ bcs->err_inv++;
+#endif
+ return (NULL);
+ }
+ if (bcs->mode == L1_MODE_TRANS)
+ count -= 1;
+ else
+ count -= 3;
+ if (!(skb = dev_alloc_skb(count)))
+ printk(KERN_WARNING "HFC: receive out of memory\n");
+ else {
+ ptr = skb_put(skb, count);
+ idx = 0;
+ cip = HFC_CIP | HFC_FIFO_OUT | HFC_REC | HFC_CHANNEL(bcs->channel);
+ while ((idx < count) && WaitNoBusy(cs)) {
+ *ptr++ = cs->BC_Read_Reg(cs, HFC_DATA_NODEB, cip);
+ idx++;
+ }
+ if (idx != count) {
+ debugl1(cs, "RFIFO BUSY error");
+ printk(KERN_WARNING "HFC FIFO channel %d BUSY Error\n", bcs->channel);
+ dev_kfree_skb_any(skb);
+ if (bcs->mode != L1_MODE_TRANS) {
+ WaitNoBusy(cs);
+ stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
+ HFC_CHANNEL(bcs->channel));
+ WaitForBusy(cs);
+ }
+ return (NULL);
+ }
+ if (bcs->mode != L1_MODE_TRANS) {
+ WaitNoBusy(cs);
+ chksum = (cs->BC_Read_Reg(cs, HFC_DATA, cip) << 8);
+ WaitNoBusy(cs);
+ chksum += cs->BC_Read_Reg(cs, HFC_DATA, cip);
+ WaitNoBusy(cs);
+ stat = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfc_empty_fifo %d chksum %x stat %x",
+ bcs->channel, chksum, stat);
+ if (stat) {
+ debugl1(cs, "FIFO CRC error");
+ dev_kfree_skb_any(skb);
+ skb = NULL;
+#ifdef ERROR_STATISTIC
+ bcs->err_crc++;
+#endif
+ }
+ WaitNoBusy(cs);
+ stat = cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F2_INC | HFC_REC |
+ HFC_CHANNEL(bcs->channel));
+ WaitForBusy(cs);
+ }
+ }
+ return (skb);
+}
+
+static void
+hfc_fill_fifo(struct BCState *bcs)
+{
+ struct IsdnCardState *cs = bcs->cs;
+ int idx, fcnt;
+ int count;
+ int z1, z2;
+ u_char cip;
+
+ if (!bcs->tx_skb)
+ return;
+ if (bcs->tx_skb->len <= 0)
+ return;
+
+ cip = HFC_CIP | HFC_F1 | HFC_SEND | HFC_CHANNEL(bcs->channel);
+ if ((cip & 0xc3) != (cs->hw.hfc.cip & 0xc3)) {
+ cs->BC_Write_Reg(cs, HFC_STATUS, cip, cip);
+ WaitForBusy(cs);
+ }
+ WaitNoBusy(cs);
+ if (bcs->mode != L1_MODE_TRANS) {
+ bcs->hw.hfc.f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+ cip = HFC_CIP | HFC_F2 | HFC_SEND | HFC_CHANNEL(bcs->channel);
+ WaitNoBusy(cs);
+ bcs->hw.hfc.f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+ bcs->hw.hfc.send[bcs->hw.hfc.f1] = ReadZReg(bcs, HFC_Z1 | HFC_SEND | HFC_CHANNEL(bcs->channel));
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfc_fill_fifo %d f1(%d) f2(%d) z1(%x)",
+ bcs->channel, bcs->hw.hfc.f1, bcs->hw.hfc.f2,
+ bcs->hw.hfc.send[bcs->hw.hfc.f1]);
+ fcnt = bcs->hw.hfc.f1 - bcs->hw.hfc.f2;
+ if (fcnt < 0)
+ fcnt += 32;
+ if (fcnt > 30) {
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfc_fill_fifo more as 30 frames");
+ return;
+ }
+ count = GetFreeFifoBytes(bcs);
+ }
+ else {
+ WaitForBusy(cs);
+ z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel));
+ z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel));
+ count = z1 - z2;
+ if (count < 0)
+ count += cs->hw.hfc.fifosize;
+ } /* L1_MODE_TRANS */
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfc_fill_fifo %d count(%ld/%d)",
+ bcs->channel, bcs->tx_skb->len,
+ count);
+ if (count < bcs->tx_skb->len) {
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfc_fill_fifo no fifo mem");
+ return;
+ }
+ cip = HFC_CIP | HFC_FIFO_IN | HFC_SEND | HFC_CHANNEL(bcs->channel);
+ idx = 0;
+ while ((idx < bcs->tx_skb->len) && WaitNoBusy(cs))
+ cs->BC_Write_Reg(cs, HFC_DATA_NODEB, cip, bcs->tx_skb->data[idx++]);
+ if (idx != bcs->tx_skb->len) {
+ debugl1(cs, "FIFO Send BUSY error");
+ printk(KERN_WARNING "HFC S FIFO channel %d BUSY Error\n", bcs->channel);
+ } else {
+ count = bcs->tx_skb->len;
+ bcs->tx_cnt -= count;
+ if (PACKET_NOACK == bcs->tx_skb->pkt_type)
+ count = -1;
+ dev_kfree_skb_any(bcs->tx_skb);
+ bcs->tx_skb = NULL;
+ if (bcs->mode != L1_MODE_TRANS) {
+ WaitForBusy(cs);
+ WaitNoBusy(cs);
+ cs->BC_Read_Reg(cs, HFC_DATA, HFC_CIP | HFC_F1_INC | HFC_SEND | HFC_CHANNEL(bcs->channel));
+ }
+ if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag) &&
+ (count >= 0)) {
+ u_long flags;
+ spin_lock_irqsave(&bcs->aclock, flags);
+ bcs->ackcnt += count;
+ spin_unlock_irqrestore(&bcs->aclock, flags);
+ schedule_event(bcs, B_ACKPENDING);
+ }
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ }
+ return;
+}
+
+void
+main_irq_hfc(struct BCState *bcs)
+{
+ struct IsdnCardState *cs = bcs->cs;
+ int z1, z2, rcnt;
+ u_char f1, f2, cip;
+ int receive, transmit, count = 5;
+ struct sk_buff *skb;
+
+ Begin:
+ count--;
+ cip = HFC_CIP | HFC_F1 | HFC_REC | HFC_CHANNEL(bcs->channel);
+ if ((cip & 0xc3) != (cs->hw.hfc.cip & 0xc3)) {
+ cs->BC_Write_Reg(cs, HFC_STATUS, cip, cip);
+ WaitForBusy(cs);
+ }
+ WaitNoBusy(cs);
+ receive = 0;
+ if (bcs->mode == L1_MODE_HDLC) {
+ f1 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+ cip = HFC_CIP | HFC_F2 | HFC_REC | HFC_CHANNEL(bcs->channel);
+ WaitNoBusy(cs);
+ f2 = cs->BC_Read_Reg(cs, HFC_DATA, cip);
+ if (f1 != f2) {
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfc rec %d f1(%d) f2(%d)",
+ bcs->channel, f1, f2);
+ receive = 1;
+ }
+ }
+ if (receive || (bcs->mode == L1_MODE_TRANS)) {
+ WaitForBusy(cs);
+ z1 = ReadZReg(bcs, HFC_Z1 | HFC_REC | HFC_CHANNEL(bcs->channel));
+ z2 = ReadZReg(bcs, HFC_Z2 | HFC_REC | HFC_CHANNEL(bcs->channel));
+ rcnt = z1 - z2;
+ if (rcnt < 0)
+ rcnt += cs->hw.hfc.fifosize;
+ if ((bcs->mode == L1_MODE_HDLC) || (rcnt)) {
+ rcnt++;
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfc rec %d z1(%x) z2(%x) cnt(%d)",
+ bcs->channel, z1, z2, rcnt);
+ /* sti(); */
+ if ((skb = hfc_empty_fifo(bcs, rcnt))) {
+ skb_queue_tail(&bcs->rqueue, skb);
+ schedule_event(bcs, B_RCVBUFREADY);
+ }
+ }
+ receive = 1;
+ }
+ if (bcs->tx_skb) {
+ transmit = 1;
+ test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+ hfc_fill_fifo(bcs);
+ if (test_bit(BC_FLG_BUSY, &bcs->Flag))
+ transmit = 0;
+ } else {
+ if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+ transmit = 1;
+ test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+ hfc_fill_fifo(bcs);
+ if (test_bit(BC_FLG_BUSY, &bcs->Flag))
+ transmit = 0;
+ } else {
+ transmit = 0;
+ schedule_event(bcs, B_XMTBUFREADY);
+ }
+ }
+ if ((receive || transmit) && count)
+ goto Begin;
+ return;
+}
+
+static void
+mode_hfc(struct BCState *bcs, int mode, int bc)
+{
+ struct IsdnCardState *cs = bcs->cs;
+
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "HFC 2BS0 mode %d bchan %d/%d",
+ mode, bc, bcs->channel);
+ bcs->mode = mode;
+ bcs->channel = bc;
+
+ switch (mode) {
+ case (L1_MODE_NULL):
+ if (bc) {
+ cs->hw.hfc.ctmt &= ~1;
+ cs->hw.hfc.isac_spcr &= ~0x03;
+ }
+ else {
+ cs->hw.hfc.ctmt &= ~2;
+ cs->hw.hfc.isac_spcr &= ~0x0c;
+ }
+ break;
+ case (L1_MODE_TRANS):
+ cs->hw.hfc.ctmt &= ~(1 << bc); /* set HDLC mode */
+ cs->BC_Write_Reg(cs, HFC_STATUS, cs->hw.hfc.ctmt, cs->hw.hfc.ctmt);
+ hfc_clear_fifo(bcs); /* complete fifo clear */
+ if (bc) {
+ cs->hw.hfc.ctmt |= 1;
+ cs->hw.hfc.isac_spcr &= ~0x03;
+ cs->hw.hfc.isac_spcr |= 0x02;
+ } else {
+ cs->hw.hfc.ctmt |= 2;
+ cs->hw.hfc.isac_spcr &= ~0x0c;
+ cs->hw.hfc.isac_spcr |= 0x08;
+ }
+ break;
+ case (L1_MODE_HDLC):
+ if (bc) {
+ cs->hw.hfc.ctmt &= ~1;
+ cs->hw.hfc.isac_spcr &= ~0x03;
+ cs->hw.hfc.isac_spcr |= 0x02;
+ } else {
+ cs->hw.hfc.ctmt &= ~2;
+ cs->hw.hfc.isac_spcr &= ~0x0c;
+ cs->hw.hfc.isac_spcr |= 0x08;
+ }
+ break;
+ }
+ cs->BC_Write_Reg(cs, HFC_STATUS, cs->hw.hfc.ctmt, cs->hw.hfc.ctmt);
+ cs->writeisac(cs, ISAC_SPCR, cs->hw.hfc.isac_spcr);
+ if (mode == L1_MODE_HDLC)
+ hfc_clear_fifo(bcs);
+}
+
+static void
+hfc_l2l1(struct PStack *st, int pr, void *arg)
+{
+ struct BCState *bcs = st->l1.bcs;
+ struct sk_buff *skb = arg;
+ 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);
+ 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 "hfc_l2l1: this shouldn't happen\n");
+ } else {
+ test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+ bcs->tx_skb = skb;
+ 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);
+ mode_hfc(bcs, st->l1.mode, st->l1.bc);
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | REQUEST):
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | CONFIRM):
+ spin_lock_irqsave(&bcs->cs->lock, flags);
+ test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ mode_hfc(bcs, 0, st->l1.bc);
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
+ st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+ break;
+ }
+}
+
+
+static void
+close_hfcstate(struct BCState *bcs)
+{
+ mode_hfc(bcs, 0, bcs->channel);
+ if (test_bit(BC_FLG_INIT, &bcs->Flag)) {
+ 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);
+ }
+ }
+ test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+}
+
+static int
+open_hfcstate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+ if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+ 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);
+ bcs->event = 0;
+ bcs->tx_cnt = 0;
+ return (0);
+}
+
+static int
+setstack_hfc(struct PStack *st, struct BCState *bcs)
+{
+ bcs->channel = st->l1.bc;
+ if (open_hfcstate(st->l1.hardware, bcs))
+ return (-1);
+ st->l1.bcs = bcs;
+ st->l2.l2l1 = hfc_l2l1;
+ setstack_manager(st);
+ bcs->st = st;
+ setstack_l1_B(st);
+ return (0);
+}
+
+static void
+init_send(struct BCState *bcs)
+{
+ int i;
+
+ if (!(bcs->hw.hfc.send = kmalloc(32 * sizeof(unsigned int), GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "HiSax: No memory for hfc.send\n");
+ return;
+ }
+ for (i = 0; i < 32; i++)
+ bcs->hw.hfc.send[i] = 0x1fff;
+}
+
+void
+inithfc(struct IsdnCardState *cs)
+{
+ init_send(&cs->bcs[0]);
+ init_send(&cs->bcs[1]);
+ cs->BC_Send_Data = &hfc_fill_fifo;
+ cs->bcs[0].BC_SetStack = setstack_hfc;
+ cs->bcs[1].BC_SetStack = setstack_hfc;
+ cs->bcs[0].BC_Close = close_hfcstate;
+ cs->bcs[1].BC_Close = close_hfcstate;
+ mode_hfc(cs->bcs, 0, 0);
+ mode_hfc(cs->bcs + 1, 0, 0);
+}
+
+void
+releasehfc(struct IsdnCardState *cs)
+{
+ kfree(cs->bcs[0].hw.hfc.send);
+ cs->bcs[0].hw.hfc.send = NULL;
+ kfree(cs->bcs[1].hw.hfc.send);
+ cs->bcs[1].hw.hfc.send = NULL;
+}
diff --git a/drivers/isdn/hisax/hfc_2bs0.h b/drivers/isdn/hisax/hfc_2bs0.h
new file mode 100644
index 0000000..1a50d4a
--- /dev/null
+++ b/drivers/isdn/hisax/hfc_2bs0.h
@@ -0,0 +1,60 @@
+/* $Id: hfc_2bs0.h,v 1.5.2.2 2004/01/12 22:52:26 keil Exp $
+ *
+ * specific defines for CCD's HFC 2BS0
+ *
+ * Author Karsten Keil
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#define HFC_CTMT 0xe0
+#define HFC_CIRM 0xc0
+#define HFC_CIP 0x80
+#define HFC_Z1 0x00
+#define HFC_Z2 0x08
+#define HFC_Z_LOW 0x00
+#define HFC_Z_HIGH 0x04
+#define HFC_F1_INC 0x28
+#define HFC_FIFO_IN 0x2c
+#define HFC_F1 0x30
+#define HFC_F2 0x34
+#define HFC_F2_INC 0x38
+#define HFC_FIFO_OUT 0x3c
+#define HFC_B1 0x00
+#define HFC_B2 0x02
+#define HFC_REC 0x01
+#define HFC_SEND 0x00
+#define HFC_CHANNEL(ch) (ch ? HFC_B2 : HFC_B1)
+
+#define HFC_STATUS 0
+#define HFC_DATA 1
+#define HFC_DATA_NODEB 2
+
+/* Status (READ) */
+#define HFC_BUSY 0x01
+#define HFC_TIMINT 0x02
+#define HFC_EXTINT 0x04
+
+/* CTMT (Write) */
+#define HFC_CLTIMER 0x10
+#define HFC_TIM50MS 0x08
+#define HFC_TIMIRQE 0x04
+#define HFC_TRANSB2 0x02
+#define HFC_TRANSB1 0x01
+
+/* CIRM (Write) */
+#define HFC_RESET 0x08
+#define HFC_MEM8K 0x10
+#define HFC_INTA 0x01
+#define HFC_INTB 0x02
+#define HFC_INTC 0x03
+#define HFC_INTD 0x04
+#define HFC_INTE 0x05
+#define HFC_INTF 0x06
+
+extern void main_irq_hfc(struct BCState *bcs);
+extern void inithfc(struct IsdnCardState *cs);
+extern void releasehfc(struct IsdnCardState *cs);
diff --git a/drivers/isdn/hisax/hfc_pci.c b/drivers/isdn/hisax/hfc_pci.c
new file mode 100644
index 0000000..f126566
--- /dev/null
+++ b/drivers/isdn/hisax/hfc_pci.c
@@ -0,0 +1,1744 @@
+/* $Id: hfc_pci.c,v 1.48.2.4 2004/02/11 13:21:33 keil Exp $
+ *
+ * low level driver for CCD's hfc-pci based cards
+ *
+ * Author Werner Cornelius
+ * based on existing driver for CCD hfc ISA cards
+ * Copyright by Werner Cornelius <werner@isdn4linux.de>
+ * by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "hfc_pci.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+
+static const char *hfcpci_revision = "$Revision: 1.48.2.4 $";
+
+/* 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 */
+
+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_CCD, PCI_DEVICE_ID_CCD_B700, "Primux II S0", "B700"},
+ {PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_B701, "Primux II S0 NT", "B701"},
+ {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)"},
+ {PCI_VENDOR_ID_SITECOM, PCI_DEVICE_ID_SITECOM_DC105V2, "Sitecom Europe", "DC-105 ISDN PCI"},
+ {0, 0, NULL, NULL},
+};
+
+
+/******************************************/
+/* free hardware resources used by driver */
+/******************************************/
+static void
+release_io_hfcpci(struct IsdnCardState *cs)
+{
+ printk(KERN_INFO "HiSax: release hfcpci at %p\n",
+ cs->hw.hfcpci.pci_io);
+ cs->hw.hfcpci.int_m2 = 0; /* interrupt output off ! */
+ Write_hfc(cs, HFCPCI_INT_M2, cs->hw.hfcpci.int_m2);
+ Write_hfc(cs, HFCPCI_CIRM, HFCPCI_RESET); /* Reset On */
+ mdelay(10);
+ Write_hfc(cs, HFCPCI_CIRM, 0); /* Reset Off */
+ mdelay(10);
+ Write_hfc(cs, HFCPCI_INT_M2, cs->hw.hfcpci.int_m2);
+ pci_write_config_word(cs->hw.hfcpci.dev, PCI_COMMAND, 0); /* disable memory mapped ports + busmaster */
+ del_timer(&cs->hw.hfcpci.timer);
+ kfree(cs->hw.hfcpci.share_start);
+ cs->hw.hfcpci.share_start = NULL;
+ iounmap((void *)cs->hw.hfcpci.pci_io);
+}
+
+/********************************************************************************/
+/* function called to reset the HFC PCI chip. A complete software reset of chip */
+/* and fifos is done. */
+/********************************************************************************/
+static void
+reset_hfcpci(struct IsdnCardState *cs)
+{
+ pci_write_config_word(cs->hw.hfcpci.dev, PCI_COMMAND, PCI_ENA_MEMIO); /* enable memory mapped ports, disable busmaster */
+ cs->hw.hfcpci.int_m2 = 0; /* interrupt output off ! */
+ Write_hfc(cs, HFCPCI_INT_M2, cs->hw.hfcpci.int_m2);
+
+ printk(KERN_INFO "HFC_PCI: resetting card\n");
+ pci_write_config_word(cs->hw.hfcpci.dev, PCI_COMMAND, PCI_ENA_MEMIO + PCI_ENA_MASTER); /* enable memory ports + busmaster */
+ Write_hfc(cs, HFCPCI_CIRM, HFCPCI_RESET); /* Reset On */
+ mdelay(10);
+ Write_hfc(cs, HFCPCI_CIRM, 0); /* Reset Off */
+ mdelay(10);
+ if (Read_hfc(cs, HFCPCI_STATUS) & 2)
+ printk(KERN_WARNING "HFC-PCI init bit busy\n");
+
+ cs->hw.hfcpci.fifo_en = 0x30; /* only D fifos enabled */
+ Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en);
+
+ cs->hw.hfcpci.trm = 0 + HFCPCI_BTRANS_THRESMASK; /* no echo connect , threshold */
+ Write_hfc(cs, HFCPCI_TRM, cs->hw.hfcpci.trm);
+
+ Write_hfc(cs, HFCPCI_CLKDEL, CLKDEL_TE); /* ST-Bit delay for TE-Mode */
+ cs->hw.hfcpci.sctrl_e = HFCPCI_AUTO_AWAKE;
+ Write_hfc(cs, HFCPCI_SCTRL_E, cs->hw.hfcpci.sctrl_e); /* S/T Auto awake */
+ cs->hw.hfcpci.bswapped = 0; /* no exchange */
+ cs->hw.hfcpci.nt_mode = 0; /* we are in TE mode */
+ cs->hw.hfcpci.ctmt = HFCPCI_TIM3_125 | HFCPCI_AUTO_TIMER;
+ Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt);
+
+ cs->hw.hfcpci.int_m1 = HFCPCI_INTS_DTRANS | HFCPCI_INTS_DREC |
+ HFCPCI_INTS_L1STATE | HFCPCI_INTS_TIMER;
+ Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1);
+
+ /* Clear already pending ints */
+ if (Read_hfc(cs, HFCPCI_INT_S1));
+
+ Write_hfc(cs, HFCPCI_STATES, HFCPCI_LOAD_STATE | 2); /* HFC ST 2 */
+ udelay(10);
+ Write_hfc(cs, HFCPCI_STATES, 2); /* HFC ST 2 */
+ cs->hw.hfcpci.mst_m = HFCPCI_MASTER; /* HFC Master Mode */
+
+ Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m);
+ cs->hw.hfcpci.sctrl = 0x40; /* set tx_lo mode, error in datasheet ! */
+ Write_hfc(cs, HFCPCI_SCTRL, cs->hw.hfcpci.sctrl);
+ cs->hw.hfcpci.sctrl_r = 0;
+ Write_hfc(cs, HFCPCI_SCTRL_R, cs->hw.hfcpci.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 */
+ cs->hw.hfcpci.conn = 0x36; /* set data flow directions */
+ Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn);
+ Write_hfc(cs, HFCPCI_B1_SSL, 0x80); /* B1-Slot 0 STIO1 out enabled */
+ Write_hfc(cs, HFCPCI_B2_SSL, 0x81); /* B2-Slot 1 STIO1 out enabled */
+ Write_hfc(cs, HFCPCI_B1_RSL, 0x80); /* B1-Slot 0 STIO2 in enabled */
+ Write_hfc(cs, HFCPCI_B2_RSL, 0x81); /* B2-Slot 1 STIO2 in enabled */
+
+ /* Finally enable IRQ output */
+ cs->hw.hfcpci.int_m2 = HFCPCI_IRQ_ENABLE;
+ Write_hfc(cs, HFCPCI_INT_M2, cs->hw.hfcpci.int_m2);
+ if (Read_hfc(cs, HFCPCI_INT_S1));
+}
+
+/***************************************************/
+/* Timer function called when kernel timer expires */
+/***************************************************/
+static void
+hfcpci_Timer(struct IsdnCardState *cs)
+{
+ cs->hw.hfcpci.timer.expires = jiffies + 75;
+ /* WD RESET */
+/* WriteReg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcpci.ctmt | 0x80);
+ add_timer(&cs->hw.hfcpci.timer);
+ */
+}
+
+
+/*********************************/
+/* schedule a new D-channel task */
+/*********************************/
+static void
+sched_event_D_pci(struct IsdnCardState *cs, int event)
+{
+ test_and_set_bit(event, &cs->event);
+ schedule_work(&cs->tqueue);
+}
+
+/*********************************/
+/* schedule a new b_channel task */
+/*********************************/
+static void
+hfcpci_sched_event(struct BCState *bcs, int event)
+{
+ test_and_set_bit(event, &bcs->event);
+ schedule_work(&bcs->tqueue);
+}
+
+/************************************************/
+/* select a b-channel entry matching and active */
+/************************************************/
+static
+struct BCState *
+Sel_BCS(struct IsdnCardState *cs, int channel)
+{
+ if (cs->bcs[0].mode && (cs->bcs[0].channel == channel))
+ return (&cs->bcs[0]);
+ else if (cs->bcs[1].mode && (cs->bcs[1].channel == channel))
+ return (&cs->bcs[1]);
+ else
+ return (NULL);
+}
+
+/***************************************/
+/* clear the desired B-channel rx fifo */
+/***************************************/
+static void hfcpci_clear_fifo_rx(struct IsdnCardState *cs, int fifo)
+{ u_char fifo_state;
+ bzfifo_type *bzr;
+
+ if (fifo) {
+ bzr = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxbz_b2;
+ fifo_state = cs->hw.hfcpci.fifo_en & HFCPCI_FIFOEN_B2RX;
+ } else {
+ bzr = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxbz_b1;
+ fifo_state = cs->hw.hfcpci.fifo_en & HFCPCI_FIFOEN_B1RX;
+ }
+ if (fifo_state)
+ cs->hw.hfcpci.fifo_en ^= fifo_state;
+ Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en);
+ cs->hw.hfcpci.last_bfifo_cnt[fifo] = 0;
+ 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;
+ bzr->f1 = MAX_B_FRAMES;
+ bzr->f2 = bzr->f1; /* init F pointers to remain constant */
+ if (fifo_state)
+ cs->hw.hfcpci.fifo_en |= fifo_state;
+ Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en);
+}
+
+/***************************************/
+/* clear the desired B-channel tx fifo */
+/***************************************/
+static void hfcpci_clear_fifo_tx(struct IsdnCardState *cs, int fifo)
+{ u_char fifo_state;
+ bzfifo_type *bzt;
+
+ if (fifo) {
+ bzt = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txbz_b2;
+ fifo_state = cs->hw.hfcpci.fifo_en & HFCPCI_FIFOEN_B2TX;
+ } else {
+ bzt = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txbz_b1;
+ fifo_state = cs->hw.hfcpci.fifo_en & HFCPCI_FIFOEN_B1TX;
+ }
+ if (fifo_state)
+ cs->hw.hfcpci.fifo_en ^= fifo_state;
+ Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en);
+ 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;
+ bzt->f1 = MAX_B_FRAMES;
+ bzt->f2 = bzt->f1; /* init F pointers to remain constant */
+ if (fifo_state)
+ cs->hw.hfcpci.fifo_en |= fifo_state;
+ Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en);
+}
+
+/*********************************************/
+/* read a complete B-frame out of the buffer */
+/*********************************************/
+static struct sk_buff
+*
+hfcpci_empty_fifo(struct BCState *bcs, bzfifo_type * bz, u_char * bdata, int count)
+{
+ u_char *ptr, *ptr1, new_f2;
+ struct sk_buff *skb;
+ struct IsdnCardState *cs = bcs->cs;
+ int total, maxlen, new_z2;
+ z_type *zp;
+
+ if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+ debugl1(cs, "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 > HSCX_BUFMAX + 3) || (count < 4) ||
+ (*(bdata + (zp->z1 - B_SUB_VAL)))) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "hfcpci_empty_fifo: incoming packet invalid length %d or crc", count);
+#ifdef ERROR_STATISTIC
+ bcs->err_inv++;
+#endif
+ bz->za[new_f2].z2 = new_z2;
+ bz->f2 = new_f2; /* next buffer */
+ skb = NULL;
+ } else if (!(skb = dev_alloc_skb(count - 3)))
+ 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(struct IsdnCardState *cs)
+{
+ struct sk_buff *skb;
+ int maxlen;
+ int rcnt, total;
+ int count = 5;
+ u_char *ptr, *ptr1;
+ dfifo_type *df;
+ z_type *zp;
+
+ df = &((fifo_area *) (cs->hw.hfcpci.fifos))->d_chan.d_rx;
+ if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ debugl1(cs, "rec_dmsg blocked");
+ return (1);
+ }
+ 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 (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "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 (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "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 = dev_alloc_skb(rcnt - 3))) {
+ 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);
+
+ skb_queue_tail(&cs->rq, skb);
+ sched_event_D_pci(cs, D_RCVBUFREADY);
+ } else
+ printk(KERN_WARNING "HFC-PCI: D receive out of memory\n");
+ }
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ return (1);
+}
+
+/*******************************************************************************/
+/* check for transparent receive data and read max one threshold size if avail */
+/*******************************************************************************/
+static int
+hfcpci_empty_fifo_trans(struct BCState *bcs, 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 = dev_alloc_skb(fcnt)))
+ 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(&bcs->rqueue, skb);
+ hfcpci_sched_event(bcs, B_RCVBUFREADY);
+ }
+
+ *z2r = new_z2; /* new position */
+ return (1);
+} /* hfcpci_empty_fifo_trans */
+
+/**********************************/
+/* B-channel main receive routine */
+/**********************************/
+static void
+main_rec_hfcpci(struct BCState *bcs)
+{
+ struct IsdnCardState *cs = bcs->cs;
+ int rcnt, real_fifo;
+ int receive, count = 5;
+ struct sk_buff *skb;
+ bzfifo_type *bz;
+ u_char *bdata;
+ z_type *zp;
+
+
+ if ((bcs->channel) && (!cs->hw.hfcpci.bswapped)) {
+ bz = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxbz_b2;
+ bdata = ((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxdat_b2;
+ real_fifo = 1;
+ } else {
+ bz = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxbz_b1;
+ bdata = ((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxdat_b1;
+ real_fifo = 0;
+ }
+ Begin:
+ count--;
+ if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ debugl1(cs, "rec_data %d blocked", bcs->channel);
+ return;
+ }
+ if (bz->f1 != bz->f2) {
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfcpci rec %d f1(%d) f2(%d)",
+ bcs->channel, bz->f1, bz->f2);
+ zp = &bz->za[bz->f2];
+
+ rcnt = zp->z1 - zp->z2;
+ if (rcnt < 0)
+ rcnt += B_FIFO_SIZE;
+ rcnt++;
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfcpci rec %d z1(%x) z2(%x) cnt(%d)",
+ bcs->channel, zp->z1, zp->z2, rcnt);
+ if ((skb = hfcpci_empty_fifo(bcs, bz, bdata, rcnt))) {
+ skb_queue_tail(&bcs->rqueue, skb);
+ hfcpci_sched_event(bcs, B_RCVBUFREADY);
+ }
+ rcnt = bz->f1 - bz->f2;
+ if (rcnt < 0)
+ rcnt += MAX_B_FRAMES + 1;
+ if (cs->hw.hfcpci.last_bfifo_cnt[real_fifo] > rcnt + 1) {
+ rcnt = 0;
+ hfcpci_clear_fifo_rx(cs, real_fifo);
+ }
+ cs->hw.hfcpci.last_bfifo_cnt[real_fifo] = rcnt;
+ if (rcnt > 1)
+ receive = 1;
+ else
+ receive = 0;
+ } else if (bcs->mode == L1_MODE_TRANS)
+ receive = hfcpci_empty_fifo_trans(bcs, bz, bdata);
+ else
+ receive = 0;
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ if (count && receive)
+ goto Begin;
+}
+
+/**************************/
+/* D-channel send routine */
+/**************************/
+static void
+hfcpci_fill_dfifo(struct IsdnCardState *cs)
+{
+ int fcnt;
+ int count, new_z1, maxlen;
+ dfifo_type *df;
+ u_char *src, *dst, new_f1;
+
+ if (!cs->tx_skb)
+ return;
+ if (cs->tx_skb->len <= 0)
+ return;
+
+ df = &((fifo_area *) (cs->hw.hfcpci.fifos))->d_chan.d_tx;
+
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "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 (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "hfcpci_fill_Dfifo more as 14 frames");
+#ifdef ERROR_STATISTIC
+ cs->err_tx++;
+#endif
+ return;
+ }
+ /* now determine free bytes in FIFO buffer */
+ count = df->za[df->f2 & D_FREG_MASK].z2 - df->za[df->f1 & D_FREG_MASK].z1 - 1;
+ if (count <= 0)
+ count += D_FIFO_SIZE; /* count now contains available bytes */
+
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "hfcpci_fill_Dfifo count(%ld/%d)",
+ cs->tx_skb->len, count);
+ if (count < cs->tx_skb->len) {
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "hfcpci_fill_Dfifo no fifo mem");
+ return;
+ }
+ count = cs->tx_skb->len; /* get frame len */
+ 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 = cs->tx_skb->data; /* 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 */
+
+ dev_kfree_skb_any(cs->tx_skb);
+ cs->tx_skb = NULL;
+}
+
+/**************************/
+/* B-channel send routine */
+/**************************/
+static void
+hfcpci_fill_fifo(struct BCState *bcs)
+{
+ struct IsdnCardState *cs = bcs->cs;
+ int maxlen, fcnt;
+ int count, new_z1;
+ bzfifo_type *bz;
+ u_char *bdata;
+ u_char new_f1, *src, *dst;
+ unsigned short *z1t, *z2t;
+
+ if (!bcs->tx_skb)
+ return;
+ if (bcs->tx_skb->len <= 0)
+ return;
+
+ if ((bcs->channel) && (!cs->hw.hfcpci.bswapped)) {
+ bz = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txbz_b2;
+ bdata = ((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txdat_b2;
+ } else {
+ bz = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txbz_b1;
+ bdata = ((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txdat_b1;
+ }
+
+ if (bcs->mode == L1_MODE_TRANS) {
+ z1t = &bz->za[MAX_B_FRAMES].z1;
+ z2t = z1t + 1;
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfcpci_fill_fifo_trans %d z1(%x) z2(%x)",
+ bcs->channel, *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 */
+
+ while ((fcnt < 2 * HFCPCI_BTRANS_THRESHOLD) && (bcs->tx_skb)) {
+ if (bcs->tx_skb->len < B_FIFO_SIZE - fcnt) {
+ /* data is suitable for fifo */
+ count = bcs->tx_skb->len;
+
+ new_z1 = *z1t + count; /* new buffer Position */
+ if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL))
+ new_z1 -= B_FIFO_SIZE; /* buffer wrap */
+ src = bcs->tx_skb->data; /* source pointer */
+ dst = bdata + (*z1t - B_SUB_VAL);
+ maxlen = (B_FIFO_SIZE + B_SUB_VAL) - *z1t; /* end of 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);
+ }
+ bcs->tx_cnt -= bcs->tx_skb->len;
+ fcnt += bcs->tx_skb->len;
+ *z1t = new_z1; /* now send data */
+ } else if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfcpci_fill_fifo_trans %d frame length %d discarded",
+ bcs->channel, bcs->tx_skb->len);
+
+ 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->tx_skb->len;
+ spin_unlock_irqrestore(&bcs->aclock, flags);
+ schedule_event(bcs, B_ACKPENDING);
+ }
+
+ dev_kfree_skb_any(bcs->tx_skb);
+ bcs->tx_skb = skb_dequeue(&bcs->squeue); /* fetch next data */
+ }
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ return;
+ }
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfcpci_fill_fifo_hdlc %d f1(%d) f2(%d) z1(f1)(%x)",
+ bcs->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 (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfcpci_fill_Bfifo more as 14 frames");
+ return;
+ }
+ /* now determine free bytes in FIFO buffer */
+ count = bz->za[bz->f2].z2 - bz->za[bz->f1].z1 - 1;
+ if (count <= 0)
+ count += B_FIFO_SIZE; /* count now contains available bytes */
+
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfcpci_fill_fifo %d count(%ld/%d),%lx",
+ bcs->channel, bcs->tx_skb->len,
+ count, current->state);
+
+ if (count < bcs->tx_skb->len) {
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hfcpci_fill_fifo no fifo mem");
+ return;
+ }
+ count = bcs->tx_skb->len; /* get frame len */
+ 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 = bcs->tx_skb->data; /* 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);
+ }
+ bcs->tx_cnt -= bcs->tx_skb->len;
+ 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->tx_skb->len;
+ spin_unlock_irqrestore(&bcs->aclock, flags);
+ schedule_event(bcs, B_ACKPENDING);
+ }
+
+ bz->za[new_f1].z1 = new_z1; /* for next buffer */
+ bz->f1 = new_f1; /* next frame */
+
+ dev_kfree_skb_any(bcs->tx_skb);
+ bcs->tx_skb = NULL;
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+}
+
+/**********************************************/
+/* D-channel l1 state call for leased NT-mode */
+/**********************************************/
+static void
+dch_nt_l2l1(struct PStack *st, int pr, void *arg)
+{
+ struct IsdnCardState *cs = (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(cs, "PH_TEST_LOOP B1");
+ if (2 & (long) arg)
+ debugl1(cs, "PH_TEST_LOOP B2");
+ if (!(3 & (long) arg))
+ debugl1(cs, "PH_TEST_LOOP DISABLED");
+ st->l1.l1hw(st, HW_TESTLOOP | REQUEST, arg);
+ break;
+ default:
+ if (cs->debug)
+ debugl1(cs, "dch_nt_l2l1 msg %04X unhandled", pr);
+ break;
+ }
+}
+
+
+
+/***********************/
+/* set/reset echo mode */
+/***********************/
+static int
+hfcpci_auxcmd(struct IsdnCardState *cs, isdn_ctrl * ic)
+{
+ u_long flags;
+ int i = *(unsigned int *) ic->parm.num;
+
+ if ((ic->arg == 98) &&
+ (!(cs->hw.hfcpci.int_m1 & (HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC + HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC)))) {
+ spin_lock_irqsave(&cs->lock, flags);
+ Write_hfc(cs, HFCPCI_CLKDEL, CLKDEL_NT); /* ST-Bit delay for NT-Mode */
+ Write_hfc(cs, HFCPCI_STATES, HFCPCI_LOAD_STATE | 0); /* HFC ST G0 */
+ udelay(10);
+ cs->hw.hfcpci.sctrl |= SCTRL_MODE_NT;
+ Write_hfc(cs, HFCPCI_SCTRL, cs->hw.hfcpci.sctrl); /* set NT-mode */
+ udelay(10);
+ Write_hfc(cs, HFCPCI_STATES, HFCPCI_LOAD_STATE | 1); /* HFC ST G1 */
+ udelay(10);
+ Write_hfc(cs, HFCPCI_STATES, 1 | HFCPCI_ACTIVATE | HFCPCI_DO_ACTION);
+ cs->dc.hfcpci.ph_state = 1;
+ cs->hw.hfcpci.nt_mode = 1;
+ cs->hw.hfcpci.nt_timer = 0;
+ cs->stlist->l2.l2l1 = dch_nt_l2l1;
+ spin_unlock_irqrestore(&cs->lock, flags);
+ debugl1(cs, "NT mode activated");
+ return (0);
+ }
+ if ((cs->chanlimit > 1) || (cs->hw.hfcpci.bswapped) ||
+ (cs->hw.hfcpci.nt_mode) || (ic->arg != 12))
+ return (-EINVAL);
+
+ spin_lock_irqsave(&cs->lock, flags);
+ if (i) {
+ cs->logecho = 1;
+ cs->hw.hfcpci.trm |= 0x20; /* enable echo chan */
+ cs->hw.hfcpci.int_m1 |= HFCPCI_INTS_B2REC;
+ cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B2RX;
+ } else {
+ cs->logecho = 0;
+ cs->hw.hfcpci.trm &= ~0x20; /* disable echo chan */
+ cs->hw.hfcpci.int_m1 &= ~HFCPCI_INTS_B2REC;
+ cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B2RX;
+ }
+ cs->hw.hfcpci.sctrl_r &= ~SCTRL_B2_ENA;
+ cs->hw.hfcpci.sctrl &= ~SCTRL_B2_ENA;
+ cs->hw.hfcpci.conn |= 0x10; /* B2-IOM -> B2-ST */
+ cs->hw.hfcpci.ctmt &= ~2;
+ Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt);
+ Write_hfc(cs, HFCPCI_SCTRL_R, cs->hw.hfcpci.sctrl_r);
+ Write_hfc(cs, HFCPCI_SCTRL, cs->hw.hfcpci.sctrl);
+ Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn);
+ Write_hfc(cs, HFCPCI_TRM, cs->hw.hfcpci.trm);
+ Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en);
+ Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return (0);
+} /* hfcpci_auxcmd */
+
+/*****************************/
+/* E-channel receive routine */
+/*****************************/
+static void
+receive_emsg(struct IsdnCardState *cs)
+{
+ 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 *) (cs->hw.hfcpci.fifos))->b_chans.rxbz_b2;
+ bdata = ((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxdat_b2;
+ Begin:
+ count--;
+ if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ debugl1(cs, "echo_rec_data blocked");
+ return;
+ }
+ if (bz->f1 != bz->f2) {
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "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 (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "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 (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "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 (cs->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 += QuickHex(ptr, e_buffer, total - 3);
+ ptr--;
+ *ptr++ = '\n';
+ *ptr = 0;
+ HiSax_putstatus(cs, NULL, cs->dlog);
+ } else
+ HiSax_putstatus(cs, "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;
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ if (count && receive)
+ goto Begin;
+} /* receive_emsg */
+
+/*********************/
+/* Interrupt handler */
+/*********************/
+static irqreturn_t
+hfcpci_interrupt(int intno, void *dev_id)
+{
+ u_long flags;
+ struct IsdnCardState *cs = dev_id;
+ u_char exval;
+ struct BCState *bcs;
+ int count = 15;
+ u_char val, stat;
+
+ if (!(cs->hw.hfcpci.int_m2 & 0x08)) {
+ debugl1(cs, "HFC-PCI: int_m2 %x not initialised", cs->hw.hfcpci.int_m2);
+ return IRQ_NONE; /* not initialised */
+ }
+ spin_lock_irqsave(&cs->lock, flags);
+ if (HFCPCI_ANYINT & (stat = Read_hfc(cs, HFCPCI_STATUS))) {
+ val = Read_hfc(cs, HFCPCI_INT_S1);
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "HFC-PCI: stat(%02x) s1(%02x)", stat, val);
+ } else {
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_NONE;
+ }
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "HFC-PCI irq %x %s", val,
+ test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags) ?
+ "locked" : "unlocked");
+ val &= cs->hw.hfcpci.int_m1;
+ if (val & 0x40) { /* state machine irq */
+ exval = Read_hfc(cs, HFCPCI_STATES) & 0xf;
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ph_state chg %d->%d", cs->dc.hfcpci.ph_state,
+ exval);
+ cs->dc.hfcpci.ph_state = exval;
+ sched_event_D_pci(cs, D_L1STATECHANGE);
+ val &= ~0x40;
+ }
+ if (val & 0x80) { /* timer irq */
+ if (cs->hw.hfcpci.nt_mode) {
+ if ((--cs->hw.hfcpci.nt_timer) < 0)
+ sched_event_D_pci(cs, D_L1STATECHANGE);
+ }
+ val &= ~0x80;
+ Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt | HFCPCI_CLTIMER);
+ }
+ while (val) {
+ if (test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ cs->hw.hfcpci.int_s1 |= val;
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+ }
+ if (cs->hw.hfcpci.int_s1 & 0x18) {
+ exval = val;
+ val = cs->hw.hfcpci.int_s1;
+ cs->hw.hfcpci.int_s1 = exval;
+ }
+ if (val & 0x08) {
+ if (!(bcs = Sel_BCS(cs, cs->hw.hfcpci.bswapped ? 1 : 0))) {
+ if (cs->debug)
+ debugl1(cs, "hfcpci spurious 0x08 IRQ");
+ } else
+ main_rec_hfcpci(bcs);
+ }
+ if (val & 0x10) {
+ if (cs->logecho)
+ receive_emsg(cs);
+ else if (!(bcs = Sel_BCS(cs, 1))) {
+ if (cs->debug)
+ debugl1(cs, "hfcpci spurious 0x10 IRQ");
+ } else
+ main_rec_hfcpci(bcs);
+ }
+ if (val & 0x01) {
+ if (!(bcs = Sel_BCS(cs, cs->hw.hfcpci.bswapped ? 1 : 0))) {
+ if (cs->debug)
+ debugl1(cs, "hfcpci spurious 0x01 IRQ");
+ } else {
+ if (bcs->tx_skb) {
+ if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ hfcpci_fill_fifo(bcs);
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ } else
+ debugl1(cs, "fill_data %d blocked", bcs->channel);
+ } else {
+ if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+ if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ hfcpci_fill_fifo(bcs);
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ } else
+ debugl1(cs, "fill_data %d blocked", bcs->channel);
+ } else {
+ hfcpci_sched_event(bcs, B_XMTBUFREADY);
+ }
+ }
+ }
+ }
+ if (val & 0x02) {
+ if (!(bcs = Sel_BCS(cs, 1))) {
+ if (cs->debug)
+ debugl1(cs, "hfcpci spurious 0x02 IRQ");
+ } else {
+ if (bcs->tx_skb) {
+ if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ hfcpci_fill_fifo(bcs);
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ } else
+ debugl1(cs, "fill_data %d blocked", bcs->channel);
+ } else {
+ if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+ if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ hfcpci_fill_fifo(bcs);
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ } else
+ debugl1(cs, "fill_data %d blocked", bcs->channel);
+ } else {
+ hfcpci_sched_event(bcs, B_XMTBUFREADY);
+ }
+ }
+ }
+ }
+ if (val & 0x20) { /* receive dframe */
+ receive_dmsg(cs);
+ }
+ if (val & 0x04) { /* dframe transmitted */
+ if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+ del_timer(&cs->dbusytimer);
+ if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+ sched_event_D_pci(cs, D_CLEARBUSY);
+ if (cs->tx_skb) {
+ if (cs->tx_skb->len) {
+ if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ hfcpci_fill_dfifo(cs);
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ } else {
+ debugl1(cs, "hfcpci_fill_dfifo irq blocked");
+ }
+ goto afterXPR;
+ } else {
+ dev_kfree_skb_irq(cs->tx_skb);
+ cs->tx_cnt = 0;
+ cs->tx_skb = NULL;
+ }
+ }
+ if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
+ cs->tx_cnt = 0;
+ if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ hfcpci_fill_dfifo(cs);
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ } else {
+ debugl1(cs, "hfcpci_fill_dfifo irq blocked");
+ }
+ } else
+ sched_event_D_pci(cs, D_XMTBUFREADY);
+ }
+ afterXPR:
+ if (cs->hw.hfcpci.int_s1 && count--) {
+ val = cs->hw.hfcpci.int_s1;
+ cs->hw.hfcpci.int_s1 = 0;
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "HFC-PCI irq %x loop %d", val, 15 - count);
+ } else
+ val = 0;
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+/********************************************************************/
+/* timer callback for D-chan busy resolution. Currently no function */
+/********************************************************************/
+static void
+hfcpci_dbusy_timer(struct IsdnCardState *cs)
+{
+}
+
+/*************************************/
+/* Layer 1 D-channel hardware access */
+/*************************************/
+static void
+HFCPCI_l1hw(struct PStack *st, int pr, void *arg)
+{
+ u_long flags;
+ struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+ struct sk_buff *skb = arg;
+
+ switch (pr) {
+ case (PH_DATA | REQUEST):
+ if (cs->debug & DEB_DLOG_HEX)
+ LogFrame(cs, skb->data, skb->len);
+ if (cs->debug & DEB_DLOG_VERBOSE)
+ dlogframe(cs, skb, 0);
+ spin_lock_irqsave(&cs->lock, flags);
+ if (cs->tx_skb) {
+ skb_queue_tail(&cs->sq, skb);
+#ifdef L2FRAME_DEBUG /* psa */
+ if (cs->debug & L1_DEB_LAPD)
+ Logl2Frame(cs, skb, "PH_DATA Queued", 0);
+#endif
+ } else {
+ cs->tx_skb = skb;
+ cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG /* psa */
+ if (cs->debug & L1_DEB_LAPD)
+ Logl2Frame(cs, skb, "PH_DATA", 0);
+#endif
+ if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ hfcpci_fill_dfifo(cs);
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ } else
+ debugl1(cs, "hfcpci_fill_dfifo blocked");
+
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (PH_PULL | INDICATION):
+ spin_lock_irqsave(&cs->lock, flags);
+ if (cs->tx_skb) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
+ skb_queue_tail(&cs->sq, skb);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ }
+ if (cs->debug & DEB_DLOG_HEX)
+ LogFrame(cs, skb->data, skb->len);
+ if (cs->debug & DEB_DLOG_VERBOSE)
+ dlogframe(cs, skb, 0);
+ cs->tx_skb = skb;
+ cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG /* psa */
+ if (cs->debug & L1_DEB_LAPD)
+ Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
+#endif
+ if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ hfcpci_fill_dfifo(cs);
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ } else
+ debugl1(cs, "hfcpci_fill_dfifo blocked");
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (PH_PULL | REQUEST):
+#ifdef L2FRAME_DEBUG /* psa */
+ if (cs->debug & L1_DEB_LAPD)
+ debugl1(cs, "-> PH_REQUEST_PULL");
+#endif
+ if (!cs->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 (HW_RESET | REQUEST):
+ spin_lock_irqsave(&cs->lock, flags);
+ Write_hfc(cs, HFCPCI_STATES, HFCPCI_LOAD_STATE | 3); /* HFC ST 3 */
+ udelay(6);
+ Write_hfc(cs, HFCPCI_STATES, 3); /* HFC ST 2 */
+ cs->hw.hfcpci.mst_m |= HFCPCI_MASTER;
+ Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m);
+ Write_hfc(cs, HFCPCI_STATES, HFCPCI_ACTIVATE | HFCPCI_DO_ACTION);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
+ break;
+ case (HW_ENABLE | REQUEST):
+ spin_lock_irqsave(&cs->lock, flags);
+ Write_hfc(cs, HFCPCI_STATES, HFCPCI_DO_ACTION);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (HW_DEACTIVATE | REQUEST):
+ spin_lock_irqsave(&cs->lock, flags);
+ cs->hw.hfcpci.mst_m &= ~HFCPCI_MASTER;
+ Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (HW_INFO3 | REQUEST):
+ spin_lock_irqsave(&cs->lock, flags);
+ cs->hw.hfcpci.mst_m |= HFCPCI_MASTER;
+ Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (HW_TESTLOOP | REQUEST):
+ spin_lock_irqsave(&cs->lock, flags);
+ switch ((long) arg) {
+ case (1):
+ Write_hfc(cs, HFCPCI_B1_SSL, 0x80); /* tx slot */
+ Write_hfc(cs, HFCPCI_B1_RSL, 0x80); /* rx slot */
+ cs->hw.hfcpci.conn = (cs->hw.hfcpci.conn & ~7) | 1;
+ Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn);
+ break;
+
+ case (2):
+ Write_hfc(cs, HFCPCI_B2_SSL, 0x81); /* tx slot */
+ Write_hfc(cs, HFCPCI_B2_RSL, 0x81); /* rx slot */
+ cs->hw.hfcpci.conn = (cs->hw.hfcpci.conn & ~0x38) | 0x08;
+ Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn);
+ break;
+
+ default:
+ spin_unlock_irqrestore(&cs->lock, flags);
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "hfcpci_l1hw loop invalid %4lx", (long) arg);
+ return;
+ }
+ cs->hw.hfcpci.trm |= 0x80; /* enable IOM-loop */
+ Write_hfc(cs, HFCPCI_TRM, cs->hw.hfcpci.trm);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ default:
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "hfcpci_l1hw unknown pr %4x", pr);
+ break;
+ }
+}
+
+/***********************************************/
+/* called during init setting l1 stack pointer */
+/***********************************************/
+static void
+setstack_hfcpci(struct PStack *st, struct IsdnCardState *cs)
+{
+ st->l1.l1hw = HFCPCI_l1hw;
+}
+
+/**************************************/
+/* send B-channel data if not blocked */
+/**************************************/
+static void
+hfcpci_send_data(struct BCState *bcs)
+{
+ struct IsdnCardState *cs = bcs->cs;
+
+ if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ hfcpci_fill_fifo(bcs);
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ } else
+ debugl1(cs, "send_data %d blocked", bcs->channel);
+}
+
+/***************************************************************/
+/* activate/deactivate hardware for selected channels and mode */
+/***************************************************************/
+static void
+mode_hfcpci(struct BCState *bcs, int mode, int bc)
+{
+ struct IsdnCardState *cs = bcs->cs;
+ int fifo2;
+
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "HFCPCI bchannel mode %d bchan %d/%d",
+ mode, bc, bcs->channel);
+ bcs->mode = mode;
+ bcs->channel = bc;
+ fifo2 = bc;
+ if (cs->chanlimit > 1) {
+ cs->hw.hfcpci.bswapped = 0; /* B1 and B2 normal mode */
+ cs->hw.hfcpci.sctrl_e &= ~0x80;
+ } else {
+ if (bc) {
+ if (mode != L1_MODE_NULL) {
+ cs->hw.hfcpci.bswapped = 1; /* B1 and B2 exchanged */
+ cs->hw.hfcpci.sctrl_e |= 0x80;
+ } else {
+ cs->hw.hfcpci.bswapped = 0; /* B1 and B2 normal mode */
+ cs->hw.hfcpci.sctrl_e &= ~0x80;
+ }
+ fifo2 = 0;
+ } else {
+ cs->hw.hfcpci.bswapped = 0; /* B1 and B2 normal mode */
+ cs->hw.hfcpci.sctrl_e &= ~0x80;
+ }
+ }
+ switch (mode) {
+ case (L1_MODE_NULL):
+ if (bc) {
+ cs->hw.hfcpci.sctrl &= ~SCTRL_B2_ENA;
+ cs->hw.hfcpci.sctrl_r &= ~SCTRL_B2_ENA;
+ } else {
+ cs->hw.hfcpci.sctrl &= ~SCTRL_B1_ENA;
+ cs->hw.hfcpci.sctrl_r &= ~SCTRL_B1_ENA;
+ }
+ if (fifo2) {
+ cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B2;
+ cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC);
+ } else {
+ cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B1;
+ cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC);
+ }
+ break;
+ case (L1_MODE_TRANS):
+ hfcpci_clear_fifo_rx(cs, fifo2);
+ hfcpci_clear_fifo_tx(cs, fifo2);
+ if (bc) {
+ cs->hw.hfcpci.sctrl |= SCTRL_B2_ENA;
+ cs->hw.hfcpci.sctrl_r |= SCTRL_B2_ENA;
+ } else {
+ cs->hw.hfcpci.sctrl |= SCTRL_B1_ENA;
+ cs->hw.hfcpci.sctrl_r |= SCTRL_B1_ENA;
+ }
+ if (fifo2) {
+ cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B2;
+ cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC);
+ cs->hw.hfcpci.ctmt |= 2;
+ cs->hw.hfcpci.conn &= ~0x18;
+ } else {
+ cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B1;
+ cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC);
+ cs->hw.hfcpci.ctmt |= 1;
+ cs->hw.hfcpci.conn &= ~0x03;
+ }
+ break;
+ case (L1_MODE_HDLC):
+ hfcpci_clear_fifo_rx(cs, fifo2);
+ hfcpci_clear_fifo_tx(cs, fifo2);
+ if (bc) {
+ cs->hw.hfcpci.sctrl |= SCTRL_B2_ENA;
+ cs->hw.hfcpci.sctrl_r |= SCTRL_B2_ENA;
+ } else {
+ cs->hw.hfcpci.sctrl |= SCTRL_B1_ENA;
+ cs->hw.hfcpci.sctrl_r |= SCTRL_B1_ENA;
+ }
+ if (fifo2) {
+ cs->hw.hfcpci.last_bfifo_cnt[1] = 0;
+ cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B2;
+ cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC);
+ cs->hw.hfcpci.ctmt &= ~2;
+ cs->hw.hfcpci.conn &= ~0x18;
+ } else {
+ cs->hw.hfcpci.last_bfifo_cnt[0] = 0;
+ cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B1;
+ cs->hw.hfcpci.int_m1 |= (HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC);
+ cs->hw.hfcpci.ctmt &= ~1;
+ cs->hw.hfcpci.conn &= ~0x03;
+ }
+ break;
+ case (L1_MODE_EXTRN):
+ if (bc) {
+ cs->hw.hfcpci.conn |= 0x10;
+ cs->hw.hfcpci.sctrl |= SCTRL_B2_ENA;
+ cs->hw.hfcpci.sctrl_r |= SCTRL_B2_ENA;
+ cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B2;
+ cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B2TRANS + HFCPCI_INTS_B2REC);
+ } else {
+ cs->hw.hfcpci.conn |= 0x02;
+ cs->hw.hfcpci.sctrl |= SCTRL_B1_ENA;
+ cs->hw.hfcpci.sctrl_r |= SCTRL_B1_ENA;
+ cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B1;
+ cs->hw.hfcpci.int_m1 &= ~(HFCPCI_INTS_B1TRANS + HFCPCI_INTS_B1REC);
+ }
+ break;
+ }
+ Write_hfc(cs, HFCPCI_SCTRL_E, cs->hw.hfcpci.sctrl_e);
+ Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1);
+ Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en);
+ Write_hfc(cs, HFCPCI_SCTRL, cs->hw.hfcpci.sctrl);
+ Write_hfc(cs, HFCPCI_SCTRL_R, cs->hw.hfcpci.sctrl_r);
+ Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt);
+ Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn);
+}
+
+/******************************/
+/* Layer2 -> Layer 1 Transfer */
+/******************************/
+static void
+hfcpci_l2l1(struct PStack *st, int pr, void *arg)
+{
+ struct BCState *bcs = st->l1.bcs;
+ u_long flags;
+ struct sk_buff *skb = arg;
+
+ 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);
+ 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) {
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
+ printk(KERN_WARNING "hfc_l2l1: this shouldn't happen\n");
+ break;
+ }
+// test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+ bcs->tx_skb = skb;
+ 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);
+ mode_hfcpci(bcs, st->l1.mode, st->l1.bc);
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | REQUEST):
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | CONFIRM):
+ spin_lock_irqsave(&bcs->cs->lock, flags);
+ test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ mode_hfcpci(bcs, 0, st->l1.bc);
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
+ st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+ break;
+ }
+}
+
+/******************************************/
+/* deactivate B-channel access and queues */
+/******************************************/
+static void
+close_hfcpci(struct BCState *bcs)
+{
+ mode_hfcpci(bcs, 0, bcs->channel);
+ if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+ 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);
+ }
+ }
+}
+
+/*************************************/
+/* init B-channel queues and control */
+/*************************************/
+static int
+open_hfcpcistate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+ if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+ 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);
+ bcs->event = 0;
+ bcs->tx_cnt = 0;
+ return (0);
+}
+
+/*********************************/
+/* inits the stack for B-channel */
+/*********************************/
+static int
+setstack_2b(struct PStack *st, struct BCState *bcs)
+{
+ bcs->channel = st->l1.bc;
+ if (open_hfcpcistate(st->l1.hardware, bcs))
+ return (-1);
+ st->l1.bcs = bcs;
+ st->l2.l2l1 = hfcpci_l2l1;
+ setstack_manager(st);
+ bcs->st = st;
+ setstack_l1_B(st);
+ return (0);
+}
+
+/***************************/
+/* handle L1 state changes */
+/***************************/
+static void
+hfcpci_bh(struct work_struct *work)
+{
+ struct IsdnCardState *cs =
+ container_of(work, struct IsdnCardState, tqueue);
+ u_long flags;
+// struct PStack *stptr;
+
+ if (!cs)
+ return;
+ if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) {
+ if (!cs->hw.hfcpci.nt_mode)
+ switch (cs->dc.hfcpci.ph_state) {
+ case (0):
+ l1_msg(cs, HW_RESET | INDICATION, NULL);
+ break;
+ case (3):
+ l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
+ break;
+ case (8):
+ l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+ break;
+ case (6):
+ l1_msg(cs, HW_INFO2 | INDICATION, NULL);
+ break;
+ case (7):
+ l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
+ break;
+ default:
+ break;
+ } else {
+ spin_lock_irqsave(&cs->lock, flags);
+ switch (cs->dc.hfcpci.ph_state) {
+ case (2):
+ if (cs->hw.hfcpci.nt_timer < 0) {
+ cs->hw.hfcpci.nt_timer = 0;
+ cs->hw.hfcpci.int_m1 &= ~HFCPCI_INTS_TIMER;
+ Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1);
+ /* Clear already pending ints */
+ if (Read_hfc(cs, HFCPCI_INT_S1));
+ Write_hfc(cs, HFCPCI_STATES, 4 | HFCPCI_LOAD_STATE);
+ udelay(10);
+ Write_hfc(cs, HFCPCI_STATES, 4);
+ cs->dc.hfcpci.ph_state = 4;
+ } else {
+ cs->hw.hfcpci.int_m1 |= HFCPCI_INTS_TIMER;
+ Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1);
+ cs->hw.hfcpci.ctmt &= ~HFCPCI_AUTO_TIMER;
+ cs->hw.hfcpci.ctmt |= HFCPCI_TIM3_125;
+ Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt | HFCPCI_CLTIMER);
+ Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt | HFCPCI_CLTIMER);
+ cs->hw.hfcpci.nt_timer = NT_T1_COUNT;
+ Write_hfc(cs, HFCPCI_STATES, 2 | HFCPCI_NT_G2_G3); /* allow G2 -> G3 transition */
+ }
+ break;
+ case (1):
+ case (3):
+ case (4):
+ cs->hw.hfcpci.nt_timer = 0;
+ cs->hw.hfcpci.int_m1 &= ~HFCPCI_INTS_TIMER;
+ Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1);
+ break;
+ default:
+ break;
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+ }
+ }
+ if (test_and_clear_bit(D_RCVBUFREADY, &cs->event))
+ DChannel_proc_rcv(cs);
+ if (test_and_clear_bit(D_XMTBUFREADY, &cs->event))
+ DChannel_proc_xmt(cs);
+}
+
+
+/********************************/
+/* called for card init message */
+/********************************/
+static void
+inithfcpci(struct IsdnCardState *cs)
+{
+ cs->bcs[0].BC_SetStack = setstack_2b;
+ cs->bcs[1].BC_SetStack = setstack_2b;
+ cs->bcs[0].BC_Close = close_hfcpci;
+ cs->bcs[1].BC_Close = close_hfcpci;
+ cs->dbusytimer.function = (void *) hfcpci_dbusy_timer;
+ cs->dbusytimer.data = (long) cs;
+ init_timer(&cs->dbusytimer);
+ mode_hfcpci(cs->bcs, 0, 0);
+ mode_hfcpci(cs->bcs + 1, 0, 1);
+}
+
+
+
+/*******************************************/
+/* handle card messages from control layer */
+/*******************************************/
+static int
+hfcpci_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ u_long flags;
+
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "HFCPCI: card_msg %x", mt);
+ switch (mt) {
+ case CARD_RESET:
+ spin_lock_irqsave(&cs->lock, flags);
+ reset_hfcpci(cs);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return (0);
+ case CARD_RELEASE:
+ release_io_hfcpci(cs);
+ return (0);
+ case CARD_INIT:
+ spin_lock_irqsave(&cs->lock, flags);
+ inithfcpci(cs);
+ reset_hfcpci(cs);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ msleep(80); /* Timeout 80ms */
+ /* now switch timer interrupt off */
+ spin_lock_irqsave(&cs->lock, flags);
+ cs->hw.hfcpci.int_m1 &= ~HFCPCI_INTS_TIMER;
+ Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1);
+ /* reinit mode reg */
+ Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return (0);
+ case CARD_TEST:
+ return (0);
+ }
+ return (0);
+}
+
+
+/* this variable is used as card index when more than one cards are present */
+static struct pci_dev *dev_hfcpci __devinitdata = NULL;
+
+int __devinit
+setup_hfcpci(struct IsdnCard *card)
+{
+ u_long flags;
+ struct IsdnCardState *cs = card->cs;
+ char tmp[64];
+ int i;
+ 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 "HiSax: HFC-PCI driver Rev. %s\n", HiSax_getrev(tmp));
+
+ cs->hw.hfcpci.int_s1 = 0;
+ cs->dc.hfcpci.ph_state = 0;
+ cs->hw.hfcpci.fifo = 255;
+ if (cs->typ != ISDN_CTYPE_HFC_PCI)
+ return(0);
+
+ i = 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);
+ if ((card->para[0]) && (card->para[0] != (tmp_hfcpci->resource[ 0].start & PCI_BASE_ADDRESS_IO_MASK)))
+ continue;
+ else
+ break;
+ }
+ }
+
+ if (!tmp_hfcpci) {
+ printk(KERN_WARNING "HFC-PCI: No PCI card found\n");
+ return (0);
+ }
+
+ i--;
+ dev_hfcpci = tmp_hfcpci; /* old device */
+ cs->hw.hfcpci.dev = dev_hfcpci;
+ cs->irq = dev_hfcpci->irq;
+ if (!cs->irq) {
+ printk(KERN_WARNING "HFC-PCI: No IRQ for PCI card found\n");
+ return (0);
+ }
+ cs->hw.hfcpci.pci_io = (char *)(unsigned long)dev_hfcpci->resource[1].start;
+ printk(KERN_INFO "HiSax: HFC-PCI card manufacturer: %s card name: %s\n", id_list[i].vendor_name, id_list[i].card_name);
+
+ if (!cs->hw.hfcpci.pci_io) {
+ printk(KERN_WARNING "HFC-PCI: No IO-Mem for PCI card found\n");
+ return (0);
+ }
+ /* 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 (!(cs->hw.hfcpci.share_start = kmalloc(65536, GFP_KERNEL))) {
+ printk(KERN_WARNING "HFC-PCI: Error allocating memory for FIFO!\n");
+ return 0;
+ }
+ cs->hw.hfcpci.fifos = (void *)
+ (((ulong) cs->hw.hfcpci.share_start) & ~0x7FFF) + 0x8000;
+ pci_write_config_dword(cs->hw.hfcpci.dev, 0x80, (u_int) virt_to_bus(cs->hw.hfcpci.fifos));
+ cs->hw.hfcpci.pci_io = ioremap((ulong) cs->hw.hfcpci.pci_io, 256);
+ printk(KERN_INFO
+ "HFC-PCI: defined at mem %p fifo %p(%#x) IRQ %d HZ %d\n",
+ cs->hw.hfcpci.pci_io,
+ cs->hw.hfcpci.fifos,
+ (u_int) virt_to_bus(cs->hw.hfcpci.fifos),
+ cs->irq, HZ);
+
+ spin_lock_irqsave(&cs->lock, flags);
+
+ pci_write_config_word(cs->hw.hfcpci.dev, PCI_COMMAND, PCI_ENA_MEMIO); /* enable memory mapped ports, disable busmaster */
+ cs->hw.hfcpci.int_m2 = 0; /* disable alle interrupts */
+ cs->hw.hfcpci.int_m1 = 0;
+ Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1);
+ Write_hfc(cs, HFCPCI_INT_M2, cs->hw.hfcpci.int_m2);
+ /* At this point the needed PCI config is done */
+ /* fifos are still not enabled */
+
+ INIT_WORK(&cs->tqueue, hfcpci_bh);
+ cs->setstack_d = setstack_hfcpci;
+ cs->BC_Send_Data = &hfcpci_send_data;
+ cs->readisac = NULL;
+ cs->writeisac = NULL;
+ cs->readisacfifo = NULL;
+ cs->writeisacfifo = NULL;
+ cs->BC_Read_Reg = NULL;
+ cs->BC_Write_Reg = NULL;
+ cs->irq_func = &hfcpci_interrupt;
+ cs->irq_flags |= IRQF_SHARED;
+ cs->hw.hfcpci.timer.function = (void *) hfcpci_Timer;
+ cs->hw.hfcpci.timer.data = (long) cs;
+ init_timer(&cs->hw.hfcpci.timer);
+ cs->cardmsg = &hfcpci_card_msg;
+ cs->auxcmd = &hfcpci_auxcmd;
+
+ spin_unlock_irqrestore(&cs->lock, flags);
+
+ return (1);
+}
diff --git a/drivers/isdn/hisax/hfc_pci.h b/drivers/isdn/hisax/hfc_pci.h
new file mode 100644
index 0000000..9ef2981
--- /dev/null
+++ b/drivers/isdn/hisax/hfc_pci.h
@@ -0,0 +1,235 @@
+/* $Id: hfc_pci.h,v 1.10.2.2 2004/01/12 22:52:26 keil Exp $
+ *
+ * specific defines for CCD's HFC 2BDS0 PCI chips
+ *
+ * Author Werner Cornelius
+ * Copyright by Werner Cornelius <werner@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/*********************************************/
+/* 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
+/* 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.hfcpci.pci_io)+b) = c)
+#define Read_hfc(a,b) (*(((u_char *)a->hw.hfcpci.pci_io)+b))
+
+extern void main_irq_hcpci(struct BCState *bcs);
+extern void releasehfcpci(struct IsdnCardState *cs);
diff --git a/drivers/isdn/hisax/hfc_sx.c b/drivers/isdn/hisax/hfc_sx.c
new file mode 100644
index 0000000..d92e8d6
--- /dev/null
+++ b/drivers/isdn/hisax/hfc_sx.c
@@ -0,0 +1,1520 @@
+/* $Id: hfc_sx.c,v 1.12.2.5 2004/02/11 13:21:33 keil Exp $
+ *
+ * level driver for Cologne Chip Designs hfc-s+/sp based cards
+ *
+ * Author Werner Cornelius
+ * based on existing driver for CCD HFC PCI cards
+ * Copyright by Werner Cornelius <werner@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "hfc_sx.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+#include <linux/isapnp.h>
+
+static const char *hfcsx_revision = "$Revision: 1.12.2.5 $";
+
+/***************************************/
+/* IRQ-table for CCDs demo board */
+/* IRQs 6,5,10,11,12,15 are supported */
+/***************************************/
+
+/* Teles 16.3c Vendor Id TAG2620, Version 1.0, Vendor version 2.1
+ *
+ * Thanks to Uwe Wisniewski
+ *
+ * ISA-SLOT Signal PIN
+ * B25 IRQ3 92 IRQ_G
+ * B23 IRQ5 94 IRQ_A
+ * B4 IRQ2/9 95 IRQ_B
+ * D3 IRQ10 96 IRQ_C
+ * D4 IRQ11 97 IRQ_D
+ * D5 IRQ12 98 IRQ_E
+ * D6 IRQ15 99 IRQ_F
+ */
+
+#undef CCD_DEMO_BOARD
+#ifdef CCD_DEMO_BOARD
+static u_char ccd_sp_irqtab[16] = {
+ 0,0,0,0,0,2,1,0,0,0,3,4,5,0,0,6
+};
+#else /* Teles 16.3c */
+static u_char ccd_sp_irqtab[16] = {
+ 0,0,0,7,0,1,0,0,0,2,3,4,5,0,0,6
+};
+#endif
+#define NT_T1_COUNT 20 /* number of 3.125ms interrupts for G2 timeout */
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+/******************************/
+/* In/Out access to registers */
+/******************************/
+static inline void
+Write_hfc(struct IsdnCardState *cs, u_char regnum, u_char val)
+{
+ byteout(cs->hw.hfcsx.base+1, regnum);
+ byteout(cs->hw.hfcsx.base, val);
+}
+
+static inline u_char
+Read_hfc(struct IsdnCardState *cs, u_char regnum)
+{
+ u_char ret;
+
+ byteout(cs->hw.hfcsx.base+1, regnum);
+ ret = bytein(cs->hw.hfcsx.base);
+ return(ret);
+}
+
+
+/**************************************************/
+/* select a fifo and remember which one for reuse */
+/**************************************************/
+static void
+fifo_select(struct IsdnCardState *cs, u_char fifo)
+{
+ if (fifo == cs->hw.hfcsx.last_fifo)
+ return; /* still valid */
+
+ byteout(cs->hw.hfcsx.base+1, HFCSX_FIF_SEL);
+ byteout(cs->hw.hfcsx.base, fifo);
+ while (bytein(cs->hw.hfcsx.base+1) & 1); /* wait for busy */
+ udelay(4);
+ byteout(cs->hw.hfcsx.base, fifo);
+ while (bytein(cs->hw.hfcsx.base+1) & 1); /* wait for busy */
+}
+
+/******************************************/
+/* reset the specified fifo to defaults. */
+/* If its a send fifo init needed markers */
+/******************************************/
+static void
+reset_fifo(struct IsdnCardState *cs, u_char fifo)
+{
+ fifo_select(cs, fifo); /* first select the fifo */
+ byteout(cs->hw.hfcsx.base+1, HFCSX_CIRM);
+ byteout(cs->hw.hfcsx.base, cs->hw.hfcsx.cirm | 0x80); /* reset cmd */
+ udelay(1);
+ while (bytein(cs->hw.hfcsx.base+1) & 1); /* wait for busy */
+}
+
+
+/*************************************************************/
+/* write_fifo writes the skb contents to the desired fifo */
+/* if no space is available or an error occurs 0 is returned */
+/* the skb is not released in any way. */
+/*************************************************************/
+static int
+write_fifo(struct IsdnCardState *cs, struct sk_buff *skb, u_char fifo, int trans_max)
+{
+ unsigned short *msp;
+ int fifo_size, count, z1, z2;
+ u_char f_msk, f1, f2, *src;
+
+ if (skb->len <= 0) return(0);
+ if (fifo & 1) return(0); /* no write fifo */
+
+ fifo_select(cs, fifo);
+ if (fifo & 4) {
+ fifo_size = D_FIFO_SIZE; /* D-channel */
+ f_msk = MAX_D_FRAMES;
+ if (trans_max) return(0); /* only HDLC */
+ }
+ else {
+ fifo_size = cs->hw.hfcsx.b_fifo_size; /* B-channel */
+ f_msk = MAX_B_FRAMES;
+ }
+
+ z1 = Read_hfc(cs, HFCSX_FIF_Z1H);
+ z1 = ((z1 << 8) | Read_hfc(cs, HFCSX_FIF_Z1L));
+
+ /* Check for transparent mode */
+ if (trans_max) {
+ z2 = Read_hfc(cs, HFCSX_FIF_Z2H);
+ z2 = ((z2 << 8) | Read_hfc(cs, HFCSX_FIF_Z2L));
+ count = z2 - z1;
+ if (count <= 0)
+ count += fifo_size; /* free bytes */
+ if (count < skb->len+1) return(0); /* no room */
+ count = fifo_size - count; /* bytes still not send */
+ if (count > 2 * trans_max) return(0); /* delay to long */
+ count = skb->len;
+ src = skb->data;
+ while (count--)
+ Write_hfc(cs, HFCSX_FIF_DWR, *src++);
+ return(1); /* success */
+ }
+
+ msp = ((struct hfcsx_extra *)(cs->hw.hfcsx.extra))->marker;
+ msp += (((fifo >> 1) & 3) * (MAX_B_FRAMES+1));
+ f1 = Read_hfc(cs, HFCSX_FIF_F1) & f_msk;
+ f2 = Read_hfc(cs, HFCSX_FIF_F2) & f_msk;
+
+ count = f1 - f2; /* frame count actually buffered */
+ if (count < 0)
+ count += (f_msk + 1); /* if wrap around */
+ if (count > f_msk-1) {
+ if (cs->debug & L1_DEB_ISAC_FIFO)
+ debugl1(cs, "hfcsx_write_fifo %d more as %d frames",fifo,f_msk-1);
+ return(0);
+ }
+
+ *(msp + f1) = z1; /* remember marker */
+
+ if (cs->debug & L1_DEB_ISAC_FIFO)
+ debugl1(cs, "hfcsx_write_fifo %d f1(%x) f2(%x) z1(f1)(%x)",
+ fifo, f1, f2, z1);
+ /* now determine free bytes in FIFO buffer */
+ count = *(msp + f2) - z1;
+ if (count <= 0)
+ count += fifo_size; /* count now contains available bytes */
+
+ if (cs->debug & L1_DEB_ISAC_FIFO)
+ debugl1(cs, "hfcsx_write_fifo %d count(%ld/%d)",
+ fifo, skb->len, count);
+ if (count < skb->len) {
+ if (cs->debug & L1_DEB_ISAC_FIFO)
+ debugl1(cs, "hfcsx_write_fifo %d no fifo mem", fifo);
+ return(0);
+ }
+
+ count = skb->len; /* get frame len */
+ src = skb->data; /* source pointer */
+ while (count--)
+ Write_hfc(cs, HFCSX_FIF_DWR, *src++);
+
+ Read_hfc(cs, HFCSX_FIF_INCF1); /* increment F1 */
+ udelay(1);
+ while (bytein(cs->hw.hfcsx.base+1) & 1); /* wait for busy */
+ return(1);
+}
+
+/***************************************************************/
+/* read_fifo reads data to an skb from the desired fifo */
+/* if no data is available or an error occurs NULL is returned */
+/* the skb is not released in any way. */
+/***************************************************************/
+static struct sk_buff *
+read_fifo(struct IsdnCardState *cs, u_char fifo, int trans_max)
+{ int fifo_size, count, z1, z2;
+ u_char f_msk, f1, f2, *dst;
+ struct sk_buff *skb;
+
+ if (!(fifo & 1)) return(NULL); /* no read fifo */
+ fifo_select(cs, fifo);
+ if (fifo & 4) {
+ fifo_size = D_FIFO_SIZE; /* D-channel */
+ f_msk = MAX_D_FRAMES;
+ if (trans_max) return(NULL); /* only hdlc */
+ }
+ else {
+ fifo_size = cs->hw.hfcsx.b_fifo_size; /* B-channel */
+ f_msk = MAX_B_FRAMES;
+ }
+
+ /* transparent mode */
+ if (trans_max) {
+ z1 = Read_hfc(cs, HFCSX_FIF_Z1H);
+ z1 = ((z1 << 8) | Read_hfc(cs, HFCSX_FIF_Z1L));
+ z2 = Read_hfc(cs, HFCSX_FIF_Z2H);
+ z2 = ((z2 << 8) | Read_hfc(cs, HFCSX_FIF_Z2L));
+ /* now determine bytes in actual FIFO buffer */
+ count = z1 - z2;
+ if (count <= 0)
+ count += fifo_size; /* count now contains buffered bytes */
+ count++;
+ if (count > trans_max)
+ count = trans_max; /* limit length */
+ if ((skb = dev_alloc_skb(count))) {
+ dst = skb_put(skb, count);
+ while (count--)
+ *dst++ = Read_hfc(cs, HFCSX_FIF_DRD);
+ return(skb);
+ }
+ else return(NULL); /* no memory */
+ }
+
+ do {
+ f1 = Read_hfc(cs, HFCSX_FIF_F1) & f_msk;
+ f2 = Read_hfc(cs, HFCSX_FIF_F2) & f_msk;
+
+ if (f1 == f2) return(NULL); /* no frame available */
+
+ z1 = Read_hfc(cs, HFCSX_FIF_Z1H);
+ z1 = ((z1 << 8) | Read_hfc(cs, HFCSX_FIF_Z1L));
+ z2 = Read_hfc(cs, HFCSX_FIF_Z2H);
+ z2 = ((z2 << 8) | Read_hfc(cs, HFCSX_FIF_Z2L));
+
+ if (cs->debug & L1_DEB_ISAC_FIFO)
+ debugl1(cs, "hfcsx_read_fifo %d f1(%x) f2(%x) z1(f2)(%x) z2(f2)(%x)",
+ fifo, f1, f2, z1, z2);
+ /* now determine bytes in actual FIFO buffer */
+ count = z1 - z2;
+ if (count <= 0)
+ count += fifo_size; /* count now contains buffered bytes */
+ count++;
+
+ if (cs->debug & L1_DEB_ISAC_FIFO)
+ debugl1(cs, "hfcsx_read_fifo %d count %ld)",
+ fifo, count);
+
+ if ((count > fifo_size) || (count < 4)) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "hfcsx_read_fifo %d paket inv. len %d ", fifo , count);
+ while (count) {
+ count--; /* empty fifo */
+ Read_hfc(cs, HFCSX_FIF_DRD);
+ }
+ skb = NULL;
+ } else
+ if ((skb = dev_alloc_skb(count - 3))) {
+ count -= 3;
+ dst = skb_put(skb, count);
+
+ while (count--)
+ *dst++ = Read_hfc(cs, HFCSX_FIF_DRD);
+
+ Read_hfc(cs, HFCSX_FIF_DRD); /* CRC 1 */
+ Read_hfc(cs, HFCSX_FIF_DRD); /* CRC 2 */
+ if (Read_hfc(cs, HFCSX_FIF_DRD)) {
+ dev_kfree_skb_irq(skb);
+ if (cs->debug & L1_DEB_ISAC_FIFO)
+ debugl1(cs, "hfcsx_read_fifo %d crc error", fifo);
+ skb = NULL;
+ }
+ } else {
+ printk(KERN_WARNING "HFC-SX: receive out of memory\n");
+ return(NULL);
+ }
+
+ Read_hfc(cs, HFCSX_FIF_INCF2); /* increment F2 */
+ udelay(1);
+ while (bytein(cs->hw.hfcsx.base+1) & 1); /* wait for busy */
+ udelay(1);
+ } while (!skb); /* retry in case of crc error */
+ return(skb);
+}
+
+/******************************************/
+/* free hardware resources used by driver */
+/******************************************/
+static void
+release_io_hfcsx(struct IsdnCardState *cs)
+{
+ cs->hw.hfcsx.int_m2 = 0; /* interrupt output off ! */
+ Write_hfc(cs, HFCSX_INT_M2, cs->hw.hfcsx.int_m2);
+ Write_hfc(cs, HFCSX_CIRM, HFCSX_RESET); /* Reset On */
+ msleep(30); /* Timeout 30ms */
+ Write_hfc(cs, HFCSX_CIRM, 0); /* Reset Off */
+ del_timer(&cs->hw.hfcsx.timer);
+ release_region(cs->hw.hfcsx.base, 2); /* release IO-Block */
+ kfree(cs->hw.hfcsx.extra);
+ cs->hw.hfcsx.extra = NULL;
+}
+
+/**********************************************************/
+/* set_fifo_size determines the size of the RAM and FIFOs */
+/* returning 0 -> need to reset the chip again. */
+/**********************************************************/
+static int set_fifo_size(struct IsdnCardState *cs)
+{
+
+ if (cs->hw.hfcsx.b_fifo_size) return(1); /* already determined */
+
+ if ((cs->hw.hfcsx.chip >> 4) == 9) {
+ cs->hw.hfcsx.b_fifo_size = B_FIFO_SIZE_32K;
+ return(1);
+ }
+
+ cs->hw.hfcsx.b_fifo_size = B_FIFO_SIZE_8K;
+ cs->hw.hfcsx.cirm |= 0x10; /* only 8K of ram */
+ return(0);
+
+}
+
+/********************************************************************************/
+/* function called to reset the HFC SX chip. A complete software reset of chip */
+/* and fifos is done. */
+/********************************************************************************/
+static void
+reset_hfcsx(struct IsdnCardState *cs)
+{
+ cs->hw.hfcsx.int_m2 = 0; /* interrupt output off ! */
+ Write_hfc(cs, HFCSX_INT_M2, cs->hw.hfcsx.int_m2);
+
+ printk(KERN_INFO "HFC_SX: resetting card\n");
+ while (1) {
+ Write_hfc(cs, HFCSX_CIRM, HFCSX_RESET | cs->hw.hfcsx.cirm ); /* Reset */
+ mdelay(30);
+ Write_hfc(cs, HFCSX_CIRM, cs->hw.hfcsx.cirm); /* Reset Off */
+ mdelay(20);
+ if (Read_hfc(cs, HFCSX_STATUS) & 2)
+ printk(KERN_WARNING "HFC-SX init bit busy\n");
+ cs->hw.hfcsx.last_fifo = 0xff; /* invalidate */
+ if (!set_fifo_size(cs)) continue;
+ break;
+ }
+
+ cs->hw.hfcsx.trm = 0 + HFCSX_BTRANS_THRESMASK; /* no echo connect , threshold */
+ Write_hfc(cs, HFCSX_TRM, cs->hw.hfcsx.trm);
+
+ Write_hfc(cs, HFCSX_CLKDEL, 0x0e); /* ST-Bit delay for TE-Mode */
+ cs->hw.hfcsx.sctrl_e = HFCSX_AUTO_AWAKE;
+ Write_hfc(cs, HFCSX_SCTRL_E, cs->hw.hfcsx.sctrl_e); /* S/T Auto awake */
+ cs->hw.hfcsx.bswapped = 0; /* no exchange */
+ cs->hw.hfcsx.nt_mode = 0; /* we are in TE mode */
+ cs->hw.hfcsx.ctmt = HFCSX_TIM3_125 | HFCSX_AUTO_TIMER;
+ Write_hfc(cs, HFCSX_CTMT, cs->hw.hfcsx.ctmt);
+
+ cs->hw.hfcsx.int_m1 = HFCSX_INTS_DTRANS | HFCSX_INTS_DREC |
+ HFCSX_INTS_L1STATE | HFCSX_INTS_TIMER;
+ Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1);
+
+ /* Clear already pending ints */
+ if (Read_hfc(cs, HFCSX_INT_S1));
+
+ Write_hfc(cs, HFCSX_STATES, HFCSX_LOAD_STATE | 2); /* HFC ST 2 */
+ udelay(10);
+ Write_hfc(cs, HFCSX_STATES, 2); /* HFC ST 2 */
+ cs->hw.hfcsx.mst_m = HFCSX_MASTER; /* HFC Master Mode */
+
+ Write_hfc(cs, HFCSX_MST_MODE, cs->hw.hfcsx.mst_m);
+ cs->hw.hfcsx.sctrl = 0x40; /* set tx_lo mode, error in datasheet ! */
+ Write_hfc(cs, HFCSX_SCTRL, cs->hw.hfcsx.sctrl);
+ cs->hw.hfcsx.sctrl_r = 0;
+ Write_hfc(cs, HFCSX_SCTRL_R, cs->hw.hfcsx.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 */
+ cs->hw.hfcsx.conn = 0x36; /* set data flow directions */
+ Write_hfc(cs, HFCSX_CONNECT, cs->hw.hfcsx.conn);
+ Write_hfc(cs, HFCSX_B1_SSL, 0x80); /* B1-Slot 0 STIO1 out enabled */
+ Write_hfc(cs, HFCSX_B2_SSL, 0x81); /* B2-Slot 1 STIO1 out enabled */
+ Write_hfc(cs, HFCSX_B1_RSL, 0x80); /* B1-Slot 0 STIO2 in enabled */
+ Write_hfc(cs, HFCSX_B2_RSL, 0x81); /* B2-Slot 1 STIO2 in enabled */
+
+ /* Finally enable IRQ output */
+ cs->hw.hfcsx.int_m2 = HFCSX_IRQ_ENABLE;
+ Write_hfc(cs, HFCSX_INT_M2, cs->hw.hfcsx.int_m2);
+ if (Read_hfc(cs, HFCSX_INT_S2));
+}
+
+/***************************************************/
+/* Timer function called when kernel timer expires */
+/***************************************************/
+static void
+hfcsx_Timer(struct IsdnCardState *cs)
+{
+ cs->hw.hfcsx.timer.expires = jiffies + 75;
+ /* WD RESET */
+/* WriteReg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcsx.ctmt | 0x80);
+ add_timer(&cs->hw.hfcsx.timer);
+ */
+}
+
+/************************************************/
+/* select a b-channel entry matching and active */
+/************************************************/
+static
+struct BCState *
+Sel_BCS(struct IsdnCardState *cs, int channel)
+{
+ if (cs->bcs[0].mode && (cs->bcs[0].channel == channel))
+ return (&cs->bcs[0]);
+ else if (cs->bcs[1].mode && (cs->bcs[1].channel == channel))
+ return (&cs->bcs[1]);
+ else
+ return (NULL);
+}
+
+/*******************************/
+/* D-channel receive procedure */
+/*******************************/
+static
+int
+receive_dmsg(struct IsdnCardState *cs)
+{
+ struct sk_buff *skb;
+ int count = 5;
+
+ if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ debugl1(cs, "rec_dmsg blocked");
+ return (1);
+ }
+
+ do {
+ skb = read_fifo(cs, HFCSX_SEL_D_RX, 0);
+ if (skb) {
+ skb_queue_tail(&cs->rq, skb);
+ schedule_event(cs, D_RCVBUFREADY);
+ }
+ } while (--count && skb);
+
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ return (1);
+}
+
+/**********************************/
+/* B-channel main receive routine */
+/**********************************/
+static void
+main_rec_hfcsx(struct BCState *bcs)
+{
+ struct IsdnCardState *cs = bcs->cs;
+ int count = 5;
+ struct sk_buff *skb;
+
+ Begin:
+ count--;
+ if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ debugl1(cs, "rec_data %d blocked", bcs->channel);
+ return;
+ }
+ skb = read_fifo(cs, ((bcs->channel) && (!cs->hw.hfcsx.bswapped)) ?
+ HFCSX_SEL_B2_RX : HFCSX_SEL_B1_RX,
+ (bcs->mode == L1_MODE_TRANS) ?
+ HFCSX_BTRANS_THRESHOLD : 0);
+
+ if (skb) {
+ skb_queue_tail(&bcs->rqueue, skb);
+ schedule_event(bcs, B_RCVBUFREADY);
+ }
+
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ if (count && skb)
+ goto Begin;
+ return;
+}
+
+/**************************/
+/* D-channel send routine */
+/**************************/
+static void
+hfcsx_fill_dfifo(struct IsdnCardState *cs)
+{
+ if (!cs->tx_skb)
+ return;
+ if (cs->tx_skb->len <= 0)
+ return;
+
+ if (write_fifo(cs, cs->tx_skb, HFCSX_SEL_D_TX, 0)) {
+ dev_kfree_skb_any(cs->tx_skb);
+ cs->tx_skb = NULL;
+ }
+ return;
+}
+
+/**************************/
+/* B-channel send routine */
+/**************************/
+static void
+hfcsx_fill_fifo(struct BCState *bcs)
+{
+ struct IsdnCardState *cs = bcs->cs;
+
+ if (!bcs->tx_skb)
+ return;
+ if (bcs->tx_skb->len <= 0)
+ return;
+
+ if (write_fifo(cs, bcs->tx_skb,
+ ((bcs->channel) && (!cs->hw.hfcsx.bswapped)) ?
+ HFCSX_SEL_B2_TX : HFCSX_SEL_B1_TX,
+ (bcs->mode == L1_MODE_TRANS) ?
+ HFCSX_BTRANS_THRESHOLD : 0)) {
+
+ bcs->tx_cnt -= bcs->tx_skb->len;
+ 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->tx_skb->len;
+ spin_unlock_irqrestore(&bcs->aclock, flags);
+ schedule_event(bcs, B_ACKPENDING);
+ }
+ dev_kfree_skb_any(bcs->tx_skb);
+ bcs->tx_skb = NULL;
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ }
+}
+
+/**********************************************/
+/* D-channel l1 state call for leased NT-mode */
+/**********************************************/
+static void
+dch_nt_l2l1(struct PStack *st, int pr, void *arg)
+{
+ struct IsdnCardState *cs = (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(cs, "PH_TEST_LOOP B1");
+ if (2 & (long) arg)
+ debugl1(cs, "PH_TEST_LOOP B2");
+ if (!(3 & (long) arg))
+ debugl1(cs, "PH_TEST_LOOP DISABLED");
+ st->l1.l1hw(st, HW_TESTLOOP | REQUEST, arg);
+ break;
+ default:
+ if (cs->debug)
+ debugl1(cs, "dch_nt_l2l1 msg %04X unhandled", pr);
+ break;
+ }
+}
+
+
+
+/***********************/
+/* set/reset echo mode */
+/***********************/
+static int
+hfcsx_auxcmd(struct IsdnCardState *cs, isdn_ctrl * ic)
+{
+ unsigned long flags;
+ int i = *(unsigned int *) ic->parm.num;
+
+ if ((ic->arg == 98) &&
+ (!(cs->hw.hfcsx.int_m1 & (HFCSX_INTS_B2TRANS + HFCSX_INTS_B2REC + HFCSX_INTS_B1TRANS + HFCSX_INTS_B1REC)))) {
+ spin_lock_irqsave(&cs->lock, flags);
+ Write_hfc(cs, HFCSX_STATES, HFCSX_LOAD_STATE | 0); /* HFC ST G0 */
+ udelay(10);
+ cs->hw.hfcsx.sctrl |= SCTRL_MODE_NT;
+ Write_hfc(cs, HFCSX_SCTRL, cs->hw.hfcsx.sctrl); /* set NT-mode */
+ udelay(10);
+ Write_hfc(cs, HFCSX_STATES, HFCSX_LOAD_STATE | 1); /* HFC ST G1 */
+ udelay(10);
+ Write_hfc(cs, HFCSX_STATES, 1 | HFCSX_ACTIVATE | HFCSX_DO_ACTION);
+ cs->dc.hfcsx.ph_state = 1;
+ cs->hw.hfcsx.nt_mode = 1;
+ cs->hw.hfcsx.nt_timer = 0;
+ spin_unlock_irqrestore(&cs->lock, flags);
+ cs->stlist->l2.l2l1 = dch_nt_l2l1;
+ debugl1(cs, "NT mode activated");
+ return (0);
+ }
+ if ((cs->chanlimit > 1) || (cs->hw.hfcsx.bswapped) ||
+ (cs->hw.hfcsx.nt_mode) || (ic->arg != 12))
+ return (-EINVAL);
+
+ if (i) {
+ cs->logecho = 1;
+ cs->hw.hfcsx.trm |= 0x20; /* enable echo chan */
+ cs->hw.hfcsx.int_m1 |= HFCSX_INTS_B2REC;
+ /* reset Channel !!!!! */
+ } else {
+ cs->logecho = 0;
+ cs->hw.hfcsx.trm &= ~0x20; /* disable echo chan */
+ cs->hw.hfcsx.int_m1 &= ~HFCSX_INTS_B2REC;
+ }
+ cs->hw.hfcsx.sctrl_r &= ~SCTRL_B2_ENA;
+ cs->hw.hfcsx.sctrl &= ~SCTRL_B2_ENA;
+ cs->hw.hfcsx.conn |= 0x10; /* B2-IOM -> B2-ST */
+ cs->hw.hfcsx.ctmt &= ~2;
+ spin_lock_irqsave(&cs->lock, flags);
+ Write_hfc(cs, HFCSX_CTMT, cs->hw.hfcsx.ctmt);
+ Write_hfc(cs, HFCSX_SCTRL_R, cs->hw.hfcsx.sctrl_r);
+ Write_hfc(cs, HFCSX_SCTRL, cs->hw.hfcsx.sctrl);
+ Write_hfc(cs, HFCSX_CONNECT, cs->hw.hfcsx.conn);
+ Write_hfc(cs, HFCSX_TRM, cs->hw.hfcsx.trm);
+ Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return (0);
+} /* hfcsx_auxcmd */
+
+/*****************************/
+/* E-channel receive routine */
+/*****************************/
+static void
+receive_emsg(struct IsdnCardState *cs)
+{
+ int count = 5;
+ u_char *ptr;
+ struct sk_buff *skb;
+
+ if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ debugl1(cs, "echo_rec_data blocked");
+ return;
+ }
+ do {
+ skb = read_fifo(cs, HFCSX_SEL_B2_RX, 0);
+ if (skb) {
+ if (cs->debug & DEB_DLOG_HEX) {
+ ptr = cs->dlog;
+ if ((skb->len) < MAX_DLOG_SPACE / 3 - 10) {
+ *ptr++ = 'E';
+ *ptr++ = 'C';
+ *ptr++ = 'H';
+ *ptr++ = 'O';
+ *ptr++ = ':';
+ ptr += QuickHex(ptr, skb->data, skb->len);
+ ptr--;
+ *ptr++ = '\n';
+ *ptr = 0;
+ HiSax_putstatus(cs, NULL, cs->dlog);
+ } else
+ HiSax_putstatus(cs, "LogEcho: ", "warning Frame too big (%d)", skb->len);
+ }
+ dev_kfree_skb_any(skb);
+ }
+ } while (--count && skb);
+
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ return;
+} /* receive_emsg */
+
+
+/*********************/
+/* Interrupt handler */
+/*********************/
+static irqreturn_t
+hfcsx_interrupt(int intno, void *dev_id)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char exval;
+ struct BCState *bcs;
+ int count = 15;
+ u_long flags;
+ u_char val, stat;
+
+ if (!(cs->hw.hfcsx.int_m2 & 0x08))
+ return IRQ_NONE; /* not initialised */
+
+ spin_lock_irqsave(&cs->lock, flags);
+ if (HFCSX_ANYINT & (stat = Read_hfc(cs, HFCSX_STATUS))) {
+ val = Read_hfc(cs, HFCSX_INT_S1);
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "HFC-SX: stat(%02x) s1(%02x)", stat, val);
+ } else {
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_NONE;
+ }
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "HFC-SX irq %x %s", val,
+ test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags) ?
+ "locked" : "unlocked");
+ val &= cs->hw.hfcsx.int_m1;
+ if (val & 0x40) { /* state machine irq */
+ exval = Read_hfc(cs, HFCSX_STATES) & 0xf;
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ph_state chg %d->%d", cs->dc.hfcsx.ph_state,
+ exval);
+ cs->dc.hfcsx.ph_state = exval;
+ schedule_event(cs, D_L1STATECHANGE);
+ val &= ~0x40;
+ }
+ if (val & 0x80) { /* timer irq */
+ if (cs->hw.hfcsx.nt_mode) {
+ if ((--cs->hw.hfcsx.nt_timer) < 0)
+ schedule_event(cs, D_L1STATECHANGE);
+ }
+ val &= ~0x80;
+ Write_hfc(cs, HFCSX_CTMT, cs->hw.hfcsx.ctmt | HFCSX_CLTIMER);
+ }
+ while (val) {
+ if (test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ cs->hw.hfcsx.int_s1 |= val;
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+ }
+ if (cs->hw.hfcsx.int_s1 & 0x18) {
+ exval = val;
+ val = cs->hw.hfcsx.int_s1;
+ cs->hw.hfcsx.int_s1 = exval;
+ }
+ if (val & 0x08) {
+ if (!(bcs = Sel_BCS(cs, cs->hw.hfcsx.bswapped ? 1 : 0))) {
+ if (cs->debug)
+ debugl1(cs, "hfcsx spurious 0x08 IRQ");
+ } else
+ main_rec_hfcsx(bcs);
+ }
+ if (val & 0x10) {
+ if (cs->logecho)
+ receive_emsg(cs);
+ else if (!(bcs = Sel_BCS(cs, 1))) {
+ if (cs->debug)
+ debugl1(cs, "hfcsx spurious 0x10 IRQ");
+ } else
+ main_rec_hfcsx(bcs);
+ }
+ if (val & 0x01) {
+ if (!(bcs = Sel_BCS(cs, cs->hw.hfcsx.bswapped ? 1 : 0))) {
+ if (cs->debug)
+ debugl1(cs, "hfcsx spurious 0x01 IRQ");
+ } else {
+ if (bcs->tx_skb) {
+ if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ hfcsx_fill_fifo(bcs);
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ } else
+ debugl1(cs, "fill_data %d blocked", bcs->channel);
+ } else {
+ if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+ if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ hfcsx_fill_fifo(bcs);
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ } else
+ debugl1(cs, "fill_data %d blocked", bcs->channel);
+ } else {
+ schedule_event(bcs, B_XMTBUFREADY);
+ }
+ }
+ }
+ }
+ if (val & 0x02) {
+ if (!(bcs = Sel_BCS(cs, 1))) {
+ if (cs->debug)
+ debugl1(cs, "hfcsx spurious 0x02 IRQ");
+ } else {
+ if (bcs->tx_skb) {
+ if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ hfcsx_fill_fifo(bcs);
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ } else
+ debugl1(cs, "fill_data %d blocked", bcs->channel);
+ } else {
+ if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+ if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ hfcsx_fill_fifo(bcs);
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ } else
+ debugl1(cs, "fill_data %d blocked", bcs->channel);
+ } else {
+ schedule_event(bcs, B_XMTBUFREADY);
+ }
+ }
+ }
+ }
+ if (val & 0x20) { /* receive dframe */
+ receive_dmsg(cs);
+ }
+ if (val & 0x04) { /* dframe transmitted */
+ if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+ del_timer(&cs->dbusytimer);
+ if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+ schedule_event(cs, D_CLEARBUSY);
+ if (cs->tx_skb) {
+ if (cs->tx_skb->len) {
+ if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ hfcsx_fill_dfifo(cs);
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ } else {
+ debugl1(cs, "hfcsx_fill_dfifo irq blocked");
+ }
+ goto afterXPR;
+ } else {
+ dev_kfree_skb_irq(cs->tx_skb);
+ cs->tx_cnt = 0;
+ cs->tx_skb = NULL;
+ }
+ }
+ if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
+ cs->tx_cnt = 0;
+ if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ hfcsx_fill_dfifo(cs);
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ } else {
+ debugl1(cs, "hfcsx_fill_dfifo irq blocked");
+ }
+ } else
+ schedule_event(cs, D_XMTBUFREADY);
+ }
+ afterXPR:
+ if (cs->hw.hfcsx.int_s1 && count--) {
+ val = cs->hw.hfcsx.int_s1;
+ cs->hw.hfcsx.int_s1 = 0;
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "HFC-SX irq %x loop %d", val, 15 - count);
+ } else
+ val = 0;
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+/********************************************************************/
+/* timer callback for D-chan busy resolution. Currently no function */
+/********************************************************************/
+static void
+hfcsx_dbusy_timer(struct IsdnCardState *cs)
+{
+}
+
+/*************************************/
+/* Layer 1 D-channel hardware access */
+/*************************************/
+static void
+HFCSX_l1hw(struct PStack *st, int pr, void *arg)
+{
+ struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+ struct sk_buff *skb = arg;
+ u_long flags;
+
+ switch (pr) {
+ case (PH_DATA | REQUEST):
+ if (cs->debug & DEB_DLOG_HEX)
+ LogFrame(cs, skb->data, skb->len);
+ if (cs->debug & DEB_DLOG_VERBOSE)
+ dlogframe(cs, skb, 0);
+ spin_lock_irqsave(&cs->lock, flags);
+ if (cs->tx_skb) {
+ skb_queue_tail(&cs->sq, skb);
+#ifdef L2FRAME_DEBUG /* psa */
+ if (cs->debug & L1_DEB_LAPD)
+ Logl2Frame(cs, skb, "PH_DATA Queued", 0);
+#endif
+ } else {
+ cs->tx_skb = skb;
+ cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG /* psa */
+ if (cs->debug & L1_DEB_LAPD)
+ Logl2Frame(cs, skb, "PH_DATA", 0);
+#endif
+ if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ hfcsx_fill_dfifo(cs);
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ } else
+ debugl1(cs, "hfcsx_fill_dfifo blocked");
+
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (PH_PULL | INDICATION):
+ spin_lock_irqsave(&cs->lock, flags);
+ if (cs->tx_skb) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
+ skb_queue_tail(&cs->sq, skb);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ }
+ if (cs->debug & DEB_DLOG_HEX)
+ LogFrame(cs, skb->data, skb->len);
+ if (cs->debug & DEB_DLOG_VERBOSE)
+ dlogframe(cs, skb, 0);
+ cs->tx_skb = skb;
+ cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG /* psa */
+ if (cs->debug & L1_DEB_LAPD)
+ Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
+#endif
+ if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ hfcsx_fill_dfifo(cs);
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ } else
+ debugl1(cs, "hfcsx_fill_dfifo blocked");
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (PH_PULL | REQUEST):
+#ifdef L2FRAME_DEBUG /* psa */
+ if (cs->debug & L1_DEB_LAPD)
+ debugl1(cs, "-> PH_REQUEST_PULL");
+#endif
+ if (!cs->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 (HW_RESET | REQUEST):
+ spin_lock_irqsave(&cs->lock, flags);
+ Write_hfc(cs, HFCSX_STATES, HFCSX_LOAD_STATE | 3); /* HFC ST 3 */
+ udelay(6);
+ Write_hfc(cs, HFCSX_STATES, 3); /* HFC ST 2 */
+ cs->hw.hfcsx.mst_m |= HFCSX_MASTER;
+ Write_hfc(cs, HFCSX_MST_MODE, cs->hw.hfcsx.mst_m);
+ Write_hfc(cs, HFCSX_STATES, HFCSX_ACTIVATE | HFCSX_DO_ACTION);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
+ break;
+ case (HW_ENABLE | REQUEST):
+ spin_lock_irqsave(&cs->lock, flags);
+ Write_hfc(cs, HFCSX_STATES, HFCSX_ACTIVATE | HFCSX_DO_ACTION);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (HW_DEACTIVATE | REQUEST):
+ spin_lock_irqsave(&cs->lock, flags);
+ cs->hw.hfcsx.mst_m &= ~HFCSX_MASTER;
+ Write_hfc(cs, HFCSX_MST_MODE, cs->hw.hfcsx.mst_m);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (HW_INFO3 | REQUEST):
+ spin_lock_irqsave(&cs->lock, flags);
+ cs->hw.hfcsx.mst_m |= HFCSX_MASTER;
+ Write_hfc(cs, HFCSX_MST_MODE, cs->hw.hfcsx.mst_m);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (HW_TESTLOOP | REQUEST):
+ spin_lock_irqsave(&cs->lock, flags);
+ switch ((long) arg) {
+ case (1):
+ Write_hfc(cs, HFCSX_B1_SSL, 0x80); /* tx slot */
+ Write_hfc(cs, HFCSX_B1_RSL, 0x80); /* rx slot */
+ cs->hw.hfcsx.conn = (cs->hw.hfcsx.conn & ~7) | 1;
+ Write_hfc(cs, HFCSX_CONNECT, cs->hw.hfcsx.conn);
+ break;
+ case (2):
+ Write_hfc(cs, HFCSX_B2_SSL, 0x81); /* tx slot */
+ Write_hfc(cs, HFCSX_B2_RSL, 0x81); /* rx slot */
+ cs->hw.hfcsx.conn = (cs->hw.hfcsx.conn & ~0x38) | 0x08;
+ Write_hfc(cs, HFCSX_CONNECT, cs->hw.hfcsx.conn);
+ break;
+ default:
+ spin_unlock_irqrestore(&cs->lock, flags);
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "hfcsx_l1hw loop invalid %4lx", arg);
+ return;
+ }
+ cs->hw.hfcsx.trm |= 0x80; /* enable IOM-loop */
+ Write_hfc(cs, HFCSX_TRM, cs->hw.hfcsx.trm);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ default:
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "hfcsx_l1hw unknown pr %4x", pr);
+ break;
+ }
+}
+
+/***********************************************/
+/* called during init setting l1 stack pointer */
+/***********************************************/
+static void
+setstack_hfcsx(struct PStack *st, struct IsdnCardState *cs)
+{
+ st->l1.l1hw = HFCSX_l1hw;
+}
+
+/**************************************/
+/* send B-channel data if not blocked */
+/**************************************/
+static void
+hfcsx_send_data(struct BCState *bcs)
+{
+ struct IsdnCardState *cs = bcs->cs;
+
+ if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ hfcsx_fill_fifo(bcs);
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ } else
+ debugl1(cs, "send_data %d blocked", bcs->channel);
+}
+
+/***************************************************************/
+/* activate/deactivate hardware for selected channels and mode */
+/***************************************************************/
+static void
+mode_hfcsx(struct BCState *bcs, int mode, int bc)
+{
+ struct IsdnCardState *cs = bcs->cs;
+ int fifo2;
+
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "HFCSX bchannel mode %d bchan %d/%d",
+ mode, bc, bcs->channel);
+ bcs->mode = mode;
+ bcs->channel = bc;
+ fifo2 = bc;
+ if (cs->chanlimit > 1) {
+ cs->hw.hfcsx.bswapped = 0; /* B1 and B2 normal mode */
+ cs->hw.hfcsx.sctrl_e &= ~0x80;
+ } else {
+ if (bc) {
+ if (mode != L1_MODE_NULL) {
+ cs->hw.hfcsx.bswapped = 1; /* B1 and B2 exchanged */
+ cs->hw.hfcsx.sctrl_e |= 0x80;
+ } else {
+ cs->hw.hfcsx.bswapped = 0; /* B1 and B2 normal mode */
+ cs->hw.hfcsx.sctrl_e &= ~0x80;
+ }
+ fifo2 = 0;
+ } else {
+ cs->hw.hfcsx.bswapped = 0; /* B1 and B2 normal mode */
+ cs->hw.hfcsx.sctrl_e &= ~0x80;
+ }
+ }
+ switch (mode) {
+ case (L1_MODE_NULL):
+ if (bc) {
+ cs->hw.hfcsx.sctrl &= ~SCTRL_B2_ENA;
+ cs->hw.hfcsx.sctrl_r &= ~SCTRL_B2_ENA;
+ } else {
+ cs->hw.hfcsx.sctrl &= ~SCTRL_B1_ENA;
+ cs->hw.hfcsx.sctrl_r &= ~SCTRL_B1_ENA;
+ }
+ if (fifo2) {
+ cs->hw.hfcsx.int_m1 &= ~(HFCSX_INTS_B2TRANS + HFCSX_INTS_B2REC);
+ } else {
+ cs->hw.hfcsx.int_m1 &= ~(HFCSX_INTS_B1TRANS + HFCSX_INTS_B1REC);
+ }
+ break;
+ case (L1_MODE_TRANS):
+ if (bc) {
+ cs->hw.hfcsx.sctrl |= SCTRL_B2_ENA;
+ cs->hw.hfcsx.sctrl_r |= SCTRL_B2_ENA;
+ } else {
+ cs->hw.hfcsx.sctrl |= SCTRL_B1_ENA;
+ cs->hw.hfcsx.sctrl_r |= SCTRL_B1_ENA;
+ }
+ if (fifo2) {
+ cs->hw.hfcsx.int_m1 |= (HFCSX_INTS_B2TRANS + HFCSX_INTS_B2REC);
+ cs->hw.hfcsx.ctmt |= 2;
+ cs->hw.hfcsx.conn &= ~0x18;
+ } else {
+ cs->hw.hfcsx.int_m1 |= (HFCSX_INTS_B1TRANS + HFCSX_INTS_B1REC);
+ cs->hw.hfcsx.ctmt |= 1;
+ cs->hw.hfcsx.conn &= ~0x03;
+ }
+ break;
+ case (L1_MODE_HDLC):
+ if (bc) {
+ cs->hw.hfcsx.sctrl |= SCTRL_B2_ENA;
+ cs->hw.hfcsx.sctrl_r |= SCTRL_B2_ENA;
+ } else {
+ cs->hw.hfcsx.sctrl |= SCTRL_B1_ENA;
+ cs->hw.hfcsx.sctrl_r |= SCTRL_B1_ENA;
+ }
+ if (fifo2) {
+ cs->hw.hfcsx.int_m1 |= (HFCSX_INTS_B2TRANS + HFCSX_INTS_B2REC);
+ cs->hw.hfcsx.ctmt &= ~2;
+ cs->hw.hfcsx.conn &= ~0x18;
+ } else {
+ cs->hw.hfcsx.int_m1 |= (HFCSX_INTS_B1TRANS + HFCSX_INTS_B1REC);
+ cs->hw.hfcsx.ctmt &= ~1;
+ cs->hw.hfcsx.conn &= ~0x03;
+ }
+ break;
+ case (L1_MODE_EXTRN):
+ if (bc) {
+ cs->hw.hfcsx.conn |= 0x10;
+ cs->hw.hfcsx.sctrl |= SCTRL_B2_ENA;
+ cs->hw.hfcsx.sctrl_r |= SCTRL_B2_ENA;
+ cs->hw.hfcsx.int_m1 &= ~(HFCSX_INTS_B2TRANS + HFCSX_INTS_B2REC);
+ } else {
+ cs->hw.hfcsx.conn |= 0x02;
+ cs->hw.hfcsx.sctrl |= SCTRL_B1_ENA;
+ cs->hw.hfcsx.sctrl_r |= SCTRL_B1_ENA;
+ cs->hw.hfcsx.int_m1 &= ~(HFCSX_INTS_B1TRANS + HFCSX_INTS_B1REC);
+ }
+ break;
+ }
+ Write_hfc(cs, HFCSX_SCTRL_E, cs->hw.hfcsx.sctrl_e);
+ Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1);
+ Write_hfc(cs, HFCSX_SCTRL, cs->hw.hfcsx.sctrl);
+ Write_hfc(cs, HFCSX_SCTRL_R, cs->hw.hfcsx.sctrl_r);
+ Write_hfc(cs, HFCSX_CTMT, cs->hw.hfcsx.ctmt);
+ Write_hfc(cs, HFCSX_CONNECT, cs->hw.hfcsx.conn);
+ if (mode != L1_MODE_EXTRN) {
+ reset_fifo(cs, fifo2 ? HFCSX_SEL_B2_RX : HFCSX_SEL_B1_RX);
+ reset_fifo(cs, fifo2 ? HFCSX_SEL_B2_TX : HFCSX_SEL_B1_TX);
+ }
+}
+
+/******************************/
+/* Layer2 -> Layer 1 Transfer */
+/******************************/
+static void
+hfcsx_l2l1(struct PStack *st, int pr, void *arg)
+{
+ struct BCState *bcs = st->l1.bcs;
+ struct sk_buff *skb = arg;
+ 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);
+ 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 "hfc_l2l1: this shouldn't happen\n");
+ } else {
+// test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+ bcs->tx_skb = skb;
+ 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);
+ mode_hfcsx(bcs, st->l1.mode, st->l1.bc);
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | REQUEST):
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | CONFIRM):
+ spin_lock_irqsave(&bcs->cs->lock, flags);
+ test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ mode_hfcsx(bcs, 0, st->l1.bc);
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
+ st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+ break;
+ }
+}
+
+/******************************************/
+/* deactivate B-channel access and queues */
+/******************************************/
+static void
+close_hfcsx(struct BCState *bcs)
+{
+ mode_hfcsx(bcs, 0, bcs->channel);
+ if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+ 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);
+ }
+ }
+}
+
+/*************************************/
+/* init B-channel queues and control */
+/*************************************/
+static int
+open_hfcsxstate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+ if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+ 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);
+ bcs->event = 0;
+ bcs->tx_cnt = 0;
+ return (0);
+}
+
+/*********************************/
+/* inits the stack for B-channel */
+/*********************************/
+static int
+setstack_2b(struct PStack *st, struct BCState *bcs)
+{
+ bcs->channel = st->l1.bc;
+ if (open_hfcsxstate(st->l1.hardware, bcs))
+ return (-1);
+ st->l1.bcs = bcs;
+ st->l2.l2l1 = hfcsx_l2l1;
+ setstack_manager(st);
+ bcs->st = st;
+ setstack_l1_B(st);
+ return (0);
+}
+
+/***************************/
+/* handle L1 state changes */
+/***************************/
+static void
+hfcsx_bh(struct work_struct *work)
+{
+ struct IsdnCardState *cs =
+ container_of(work, struct IsdnCardState, tqueue);
+ u_long flags;
+
+ if (!cs)
+ return;
+ if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) {
+ if (!cs->hw.hfcsx.nt_mode)
+ switch (cs->dc.hfcsx.ph_state) {
+ case (0):
+ l1_msg(cs, HW_RESET | INDICATION, NULL);
+ break;
+ case (3):
+ l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
+ break;
+ case (8):
+ l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+ break;
+ case (6):
+ l1_msg(cs, HW_INFO2 | INDICATION, NULL);
+ break;
+ case (7):
+ l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
+ break;
+ default:
+ break;
+ } else {
+ switch (cs->dc.hfcsx.ph_state) {
+ case (2):
+ spin_lock_irqsave(&cs->lock, flags);
+ if (cs->hw.hfcsx.nt_timer < 0) {
+ cs->hw.hfcsx.nt_timer = 0;
+ cs->hw.hfcsx.int_m1 &= ~HFCSX_INTS_TIMER;
+ Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1);
+ /* Clear already pending ints */
+ if (Read_hfc(cs, HFCSX_INT_S1));
+
+ Write_hfc(cs, HFCSX_STATES, 4 | HFCSX_LOAD_STATE);
+ udelay(10);
+ Write_hfc(cs, HFCSX_STATES, 4);
+ cs->dc.hfcsx.ph_state = 4;
+ } else {
+ cs->hw.hfcsx.int_m1 |= HFCSX_INTS_TIMER;
+ Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1);
+ cs->hw.hfcsx.ctmt &= ~HFCSX_AUTO_TIMER;
+ cs->hw.hfcsx.ctmt |= HFCSX_TIM3_125;
+ Write_hfc(cs, HFCSX_CTMT, cs->hw.hfcsx.ctmt | HFCSX_CLTIMER);
+ Write_hfc(cs, HFCSX_CTMT, cs->hw.hfcsx.ctmt | HFCSX_CLTIMER);
+ cs->hw.hfcsx.nt_timer = NT_T1_COUNT;
+ Write_hfc(cs, HFCSX_STATES, 2 | HFCSX_NT_G2_G3); /* allow G2 -> G3 transition */
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (1):
+ case (3):
+ case (4):
+ spin_lock_irqsave(&cs->lock, flags);
+ cs->hw.hfcsx.nt_timer = 0;
+ cs->hw.hfcsx.int_m1 &= ~HFCSX_INTS_TIMER;
+ Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ if (test_and_clear_bit(D_RCVBUFREADY, &cs->event))
+ DChannel_proc_rcv(cs);
+ if (test_and_clear_bit(D_XMTBUFREADY, &cs->event))
+ DChannel_proc_xmt(cs);
+}
+
+
+/********************************/
+/* called for card init message */
+/********************************/
+static void inithfcsx(struct IsdnCardState *cs)
+{
+ cs->setstack_d = setstack_hfcsx;
+ cs->BC_Send_Data = &hfcsx_send_data;
+ cs->bcs[0].BC_SetStack = setstack_2b;
+ cs->bcs[1].BC_SetStack = setstack_2b;
+ cs->bcs[0].BC_Close = close_hfcsx;
+ cs->bcs[1].BC_Close = close_hfcsx;
+ mode_hfcsx(cs->bcs, 0, 0);
+ mode_hfcsx(cs->bcs + 1, 0, 1);
+}
+
+
+
+/*******************************************/
+/* handle card messages from control layer */
+/*******************************************/
+static int
+hfcsx_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ u_long flags;
+
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "HFCSX: card_msg %x", mt);
+ switch (mt) {
+ case CARD_RESET:
+ spin_lock_irqsave(&cs->lock, flags);
+ reset_hfcsx(cs);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return (0);
+ case CARD_RELEASE:
+ release_io_hfcsx(cs);
+ return (0);
+ case CARD_INIT:
+ spin_lock_irqsave(&cs->lock, flags);
+ inithfcsx(cs);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ msleep(80); /* Timeout 80ms */
+ /* now switch timer interrupt off */
+ spin_lock_irqsave(&cs->lock, flags);
+ cs->hw.hfcsx.int_m1 &= ~HFCSX_INTS_TIMER;
+ Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1);
+ /* reinit mode reg */
+ Write_hfc(cs, HFCSX_MST_MODE, cs->hw.hfcsx.mst_m);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return (0);
+ case CARD_TEST:
+ return (0);
+ }
+ return (0);
+}
+
+#ifdef __ISAPNP__
+static struct isapnp_device_id hfc_ids[] __devinitdata = {
+ { ISAPNP_VENDOR('T', 'A', 'G'), ISAPNP_FUNCTION(0x2620),
+ ISAPNP_VENDOR('T', 'A', 'G'), ISAPNP_FUNCTION(0x2620),
+ (unsigned long) "Teles 16.3c2" },
+ { 0, }
+};
+
+static struct isapnp_device_id *ipid __devinitdata = &hfc_ids[0];
+static struct pnp_card *pnp_c __devinitdata = NULL;
+#endif
+
+int __devinit
+setup_hfcsx(struct IsdnCard *card)
+{
+ struct IsdnCardState *cs = card->cs;
+ char tmp[64];
+
+ strcpy(tmp, hfcsx_revision);
+ printk(KERN_INFO "HiSax: HFC-SX driver Rev. %s\n", HiSax_getrev(tmp));
+#ifdef __ISAPNP__
+ if (!card->para[1] && isapnp_present()) {
+ struct pnp_dev *pnp_d;
+ while(ipid->card_vendor) {
+ if ((pnp_c = pnp_find_card(ipid->card_vendor,
+ ipid->card_device, pnp_c))) {
+ pnp_d = NULL;
+ if ((pnp_d = pnp_find_dev(pnp_c,
+ ipid->vendor, ipid->function, pnp_d))) {
+ int err;
+
+ printk(KERN_INFO "HiSax: %s detected\n",
+ (char *)ipid->driver_data);
+ pnp_disable_dev(pnp_d);
+ err = pnp_activate_dev(pnp_d);
+ if (err<0) {
+ printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
+ __func__, err);
+ return(0);
+ }
+ card->para[1] = pnp_port_start(pnp_d, 0);
+ card->para[0] = pnp_irq(pnp_d, 0);
+ if (!card->para[0] || !card->para[1]) {
+ printk(KERN_ERR "HFC PnP:some resources are missing %ld/%lx\n",
+ card->para[0], card->para[1]);
+ pnp_disable_dev(pnp_d);
+ return(0);
+ }
+ break;
+ } else {
+ printk(KERN_ERR "HFC PnP: PnP error card found, no device\n");
+ }
+ }
+ ipid++;
+ pnp_c = NULL;
+ }
+ if (!ipid->card_vendor) {
+ printk(KERN_INFO "HFC PnP: no ISAPnP card found\n");
+ return(0);
+ }
+ }
+#endif
+ cs->hw.hfcsx.base = card->para[1] & 0xfffe;
+ cs->irq = card->para[0];
+ cs->hw.hfcsx.int_s1 = 0;
+ cs->dc.hfcsx.ph_state = 0;
+ cs->hw.hfcsx.fifo = 255;
+ if ((cs->typ == ISDN_CTYPE_HFC_SX) ||
+ (cs->typ == ISDN_CTYPE_HFC_SP_PCMCIA)) {
+ if ((!cs->hw.hfcsx.base) || !request_region(cs->hw.hfcsx.base, 2, "HFCSX isdn")) {
+ printk(KERN_WARNING
+ "HiSax: HFC-SX io-base %#lx already in use\n",
+ cs->hw.hfcsx.base);
+ return(0);
+ }
+ byteout(cs->hw.hfcsx.base, cs->hw.hfcsx.base & 0xFF);
+ byteout(cs->hw.hfcsx.base + 1,
+ ((cs->hw.hfcsx.base >> 8) & 3) | 0x54);
+ udelay(10);
+ cs->hw.hfcsx.chip = Read_hfc(cs,HFCSX_CHIP_ID);
+ switch (cs->hw.hfcsx.chip >> 4) {
+ case 1:
+ tmp[0] ='+';
+ break;
+ case 9:
+ tmp[0] ='P';
+ break;
+ default:
+ printk(KERN_WARNING
+ "HFC-SX: invalid chip id 0x%x\n",
+ cs->hw.hfcsx.chip >> 4);
+ release_region(cs->hw.hfcsx.base, 2);
+ return(0);
+ }
+ if (!ccd_sp_irqtab[cs->irq & 0xF]) {
+ printk(KERN_WARNING
+ "HFC_SX: invalid irq %d specified\n",cs->irq & 0xF);
+ release_region(cs->hw.hfcsx.base, 2);
+ return(0);
+ }
+ if (!(cs->hw.hfcsx.extra = (void *)
+ kmalloc(sizeof(struct hfcsx_extra), GFP_ATOMIC))) {
+ release_region(cs->hw.hfcsx.base, 2);
+ printk(KERN_WARNING "HFC-SX: unable to allocate memory\n");
+ return(0);
+ }
+ printk(KERN_INFO "HFC-S%c chip detected at base 0x%x IRQ %d HZ %d\n",
+ tmp[0], (u_int) cs->hw.hfcsx.base, cs->irq, HZ);
+ cs->hw.hfcsx.int_m2 = 0; /* disable alle interrupts */
+ cs->hw.hfcsx.int_m1 = 0;
+ Write_hfc(cs, HFCSX_INT_M1, cs->hw.hfcsx.int_m1);
+ Write_hfc(cs, HFCSX_INT_M2, cs->hw.hfcsx.int_m2);
+ } else
+ return (0); /* no valid card type */
+
+ cs->dbusytimer.function = (void *) hfcsx_dbusy_timer;
+ cs->dbusytimer.data = (long) cs;
+ init_timer(&cs->dbusytimer);
+ INIT_WORK(&cs->tqueue, hfcsx_bh);
+ cs->readisac = NULL;
+ cs->writeisac = NULL;
+ cs->readisacfifo = NULL;
+ cs->writeisacfifo = NULL;
+ cs->BC_Read_Reg = NULL;
+ cs->BC_Write_Reg = NULL;
+ cs->irq_func = &hfcsx_interrupt;
+
+ cs->hw.hfcsx.timer.function = (void *) hfcsx_Timer;
+ cs->hw.hfcsx.timer.data = (long) cs;
+ cs->hw.hfcsx.b_fifo_size = 0; /* fifo size still unknown */
+ cs->hw.hfcsx.cirm = ccd_sp_irqtab[cs->irq & 0xF]; /* RAM not evaluated */
+ init_timer(&cs->hw.hfcsx.timer);
+
+ reset_hfcsx(cs);
+ cs->cardmsg = &hfcsx_card_msg;
+ cs->auxcmd = &hfcsx_auxcmd;
+ return (1);
+}
diff --git a/drivers/isdn/hisax/hfc_sx.h b/drivers/isdn/hisax/hfc_sx.h
new file mode 100644
index 0000000..6792f13
--- /dev/null
+++ b/drivers/isdn/hisax/hfc_sx.h
@@ -0,0 +1,196 @@
+/* $Id: hfc_sx.h,v 1.2.6.1 2001/09/23 22:24:48 kai Exp $
+ *
+ * specific defines for CCD's HFC 2BDS0 S+,SP chips
+ *
+ * Author Werner Cornelius
+ * based on existing driver for CCD HFC PCI cards
+ * Copyright by Werner Cornelius <werner@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/*********************************************/
+/* thresholds for transparent B-channel mode */
+/* change mask and threshold simultaneously */
+/*********************************************/
+#define HFCSX_BTRANS_THRESHOLD 128
+#define HFCSX_BTRANS_THRESMASK 0x00
+
+/* GCI/IOM bus monitor registers */
+
+#define HFCSX_C_I 0x02
+#define HFCSX_TRxR 0x03
+#define HFCSX_MON1_D 0x0A
+#define HFCSX_MON2_D 0x0B
+
+
+/* GCI/IOM bus timeslot registers */
+
+#define HFCSX_B1_SSL 0x20
+#define HFCSX_B2_SSL 0x21
+#define HFCSX_AUX1_SSL 0x22
+#define HFCSX_AUX2_SSL 0x23
+#define HFCSX_B1_RSL 0x24
+#define HFCSX_B2_RSL 0x25
+#define HFCSX_AUX1_RSL 0x26
+#define HFCSX_AUX2_RSL 0x27
+
+/* GCI/IOM bus data registers */
+
+#define HFCSX_B1_D 0x28
+#define HFCSX_B2_D 0x29
+#define HFCSX_AUX1_D 0x2A
+#define HFCSX_AUX2_D 0x2B
+
+/* GCI/IOM bus configuration registers */
+
+#define HFCSX_MST_EMOD 0x2D
+#define HFCSX_MST_MODE 0x2E
+#define HFCSX_CONNECT 0x2F
+
+
+/* Interrupt and status registers */
+
+#define HFCSX_TRM 0x12
+#define HFCSX_B_MODE 0x13
+#define HFCSX_CHIP_ID 0x16
+#define HFCSX_CIRM 0x18
+#define HFCSX_CTMT 0x19
+#define HFCSX_INT_M1 0x1A
+#define HFCSX_INT_M2 0x1B
+#define HFCSX_INT_S1 0x1E
+#define HFCSX_INT_S2 0x1F
+#define HFCSX_STATUS 0x1C
+
+/* S/T section registers */
+
+#define HFCSX_STATES 0x30
+#define HFCSX_SCTRL 0x31
+#define HFCSX_SCTRL_E 0x32
+#define HFCSX_SCTRL_R 0x33
+#define HFCSX_SQ 0x34
+#define HFCSX_CLKDEL 0x37
+#define HFCSX_B1_REC 0x3C
+#define HFCSX_B1_SEND 0x3C
+#define HFCSX_B2_REC 0x3D
+#define HFCSX_B2_SEND 0x3D
+#define HFCSX_D_REC 0x3E
+#define HFCSX_D_SEND 0x3E
+#define HFCSX_E_REC 0x3F
+
+/****************/
+/* FIFO section */
+/****************/
+#define HFCSX_FIF_SEL 0x10
+#define HFCSX_FIF_Z1L 0x80
+#define HFCSX_FIF_Z1H 0x84
+#define HFCSX_FIF_Z2L 0x88
+#define HFCSX_FIF_Z2H 0x8C
+#define HFCSX_FIF_INCF1 0xA8
+#define HFCSX_FIF_DWR 0xAC
+#define HFCSX_FIF_F1 0xB0
+#define HFCSX_FIF_F2 0xB4
+#define HFCSX_FIF_INCF2 0xB8
+#define HFCSX_FIF_DRD 0xBC
+
+/* bits in status register (READ) */
+#define HFCSX_SX_PROC 0x02
+#define HFCSX_NBUSY 0x04
+#define HFCSX_TIMER_ELAP 0x10
+#define HFCSX_STATINT 0x20
+#define HFCSX_FRAMEINT 0x40
+#define HFCSX_ANYINT 0x80
+
+/* bits in CTMT (Write) */
+#define HFCSX_CLTIMER 0x80
+#define HFCSX_TIM3_125 0x04
+#define HFCSX_TIM25 0x10
+#define HFCSX_TIM50 0x14
+#define HFCSX_TIM400 0x18
+#define HFCSX_TIM800 0x1C
+#define HFCSX_AUTO_TIMER 0x20
+#define HFCSX_TRANSB2 0x02
+#define HFCSX_TRANSB1 0x01
+
+/* bits in CIRM (Write) */
+#define HFCSX_IRQ_SELMSK 0x07
+#define HFCSX_IRQ_SELDIS 0x00
+#define HFCSX_RESET 0x08
+#define HFCSX_FIFO_RESET 0x80
+
+
+/* bits in INT_M1 and INT_S1 */
+#define HFCSX_INTS_B1TRANS 0x01
+#define HFCSX_INTS_B2TRANS 0x02
+#define HFCSX_INTS_DTRANS 0x04
+#define HFCSX_INTS_B1REC 0x08
+#define HFCSX_INTS_B2REC 0x10
+#define HFCSX_INTS_DREC 0x20
+#define HFCSX_INTS_L1STATE 0x40
+#define HFCSX_INTS_TIMER 0x80
+
+/* bits in INT_M2 */
+#define HFCSX_PROC_TRANS 0x01
+#define HFCSX_GCI_I_CHG 0x02
+#define HFCSX_GCI_MON_REC 0x04
+#define HFCSX_IRQ_ENABLE 0x08
+
+/* bits in STATES */
+#define HFCSX_STATE_MSK 0x0F
+#define HFCSX_LOAD_STATE 0x10
+#define HFCSX_ACTIVATE 0x20
+#define HFCSX_DO_ACTION 0x40
+#define HFCSX_NT_G2_G3 0x80
+
+/* bits in HFCD_MST_MODE */
+#define HFCSX_MASTER 0x01
+#define HFCSX_SLAVE 0x00
+/* 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 HFCSX_AUTO_AWAKE 0x01
+#define HFCSX_DBIT_1 0x04
+#define HFCSX_IGNORE_COL 0x08
+#define HFCSX_CHG_B1_B2 0x80
+
+/**********************************/
+/* definitions for FIFO selection */
+/**********************************/
+#define HFCSX_SEL_D_RX 5
+#define HFCSX_SEL_D_TX 4
+#define HFCSX_SEL_B1_RX 1
+#define HFCSX_SEL_B1_TX 0
+#define HFCSX_SEL_B2_RX 3
+#define HFCSX_SEL_B2_TX 2
+
+#define MAX_D_FRAMES 15
+#define MAX_B_FRAMES 31
+#define B_SUB_VAL_32K 0x0200
+#define B_FIFO_SIZE_32K (0x2000 - B_SUB_VAL_32K)
+#define B_SUB_VAL_8K 0x1A00
+#define B_FIFO_SIZE_8K (0x2000 - B_SUB_VAL_8K)
+#define D_FIFO_SIZE 512
+#define D_FREG_MASK 0xF
+
+/************************************************************/
+/* structure holding additional dynamic data -> send marker */
+/************************************************************/
+struct hfcsx_extra {
+ unsigned short marker[2*(MAX_B_FRAMES+1) + (MAX_D_FRAMES+1)];
+};
+
+extern void main_irq_hfcsx(struct BCState *bcs);
+extern void releasehfcsx(struct IsdnCardState *cs);
diff --git a/drivers/isdn/hisax/hfc_usb.c b/drivers/isdn/hisax/hfc_usb.c
new file mode 100644
index 0000000..8df889b
--- /dev/null
+++ b/drivers/isdn/hisax/hfc_usb.c
@@ -0,0 +1,1608 @@
+/*
+ * hfc_usb.c
+ *
+ * $Id: hfc_usb.c,v 2.3.2.24 2007/10/14 08:40:29 mbachem Exp $
+ *
+ * modular HiSax ISDN driver for Colognechip HFC-S USB chip
+ *
+ * Authors : Peter Sprenger (sprenger@moving-bytes.de)
+ * Martin Bachem (m.bachem@gmx.de, info@colognechip.com)
+ *
+ * based on the first hfc_usb driver of
+ * Werner Cornelius (werner@isdn-development.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.
+ *
+ * See Version Histroy at the bottom of this file
+ *
+*/
+
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/timer.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel_stat.h>
+#include <linux/usb.h>
+#include <linux/kernel.h>
+#include <linux/smp_lock.h>
+#include <linux/sched.h>
+#include <linux/moduleparam.h>
+#include "hisax.h"
+#include "hisax_if.h"
+#include "hfc_usb.h"
+
+static const char *hfcusb_revision =
+ "$Revision: 2.3.2.24 $ $Date: 2007/10/14 08:40:29 $ ";
+
+/* Hisax debug support
+* debug flags defined in hfc_usb.h as HFCUSB_DBG_[*]
+*/
+#define __debug_variable hfc_debug
+#include "hisax_debug.h"
+static u_int debug;
+module_param(debug, uint, 0);
+static int hfc_debug;
+
+
+/* private vendor specific data */
+typedef struct {
+ __u8 led_scheme; // led display scheme
+ signed short led_bits[8]; // array of 8 possible LED bitmask settings
+ char *vend_name; // device name
+} hfcsusb_vdata;
+
+/* VID/PID device list */
+static struct usb_device_id hfcusb_idtab[] = {
+ {
+ USB_DEVICE(0x0959, 0x2bd0),
+ .driver_info = (unsigned long) &((hfcsusb_vdata)
+ {LED_OFF, {4, 0, 2, 1},
+ "ISDN USB TA (Cologne Chip HFC-S USB based)"}),
+ },
+ {
+ USB_DEVICE(0x0675, 0x1688),
+ .driver_info = (unsigned long) &((hfcsusb_vdata)
+ {LED_SCHEME1, {1, 2, 0, 0},
+ "DrayTek miniVigor 128 USB ISDN TA"}),
+ },
+ {
+ USB_DEVICE(0x07b0, 0x0007),
+ .driver_info = (unsigned long) &((hfcsusb_vdata)
+ {LED_SCHEME1, {0x80, -64, -32, -16},
+ "Billion tiny USB ISDN TA 128"}),
+ },
+ {
+ USB_DEVICE(0x0742, 0x2008),
+ .driver_info = (unsigned long) &((hfcsusb_vdata)
+ {LED_SCHEME1, {4, 0, 2, 1},
+ "Stollmann USB TA"}),
+ },
+ {
+ USB_DEVICE(0x0742, 0x2009),
+ .driver_info = (unsigned long) &((hfcsusb_vdata)
+ {LED_SCHEME1, {4, 0, 2, 1},
+ "Aceex USB ISDN TA"}),
+ },
+ {
+ USB_DEVICE(0x0742, 0x200A),
+ .driver_info = (unsigned long) &((hfcsusb_vdata)
+ {LED_SCHEME1, {4, 0, 2, 1},
+ "OEM USB ISDN TA"}),
+ },
+ {
+ USB_DEVICE(0x08e3, 0x0301),
+ .driver_info = (unsigned long) &((hfcsusb_vdata)
+ {LED_SCHEME1, {2, 0, 1, 4},
+ "Olitec USB RNIS"}),
+ },
+ {
+ USB_DEVICE(0x07fa, 0x0846),
+ .driver_info = (unsigned long) &((hfcsusb_vdata)
+ {LED_SCHEME1, {0x80, -64, -32, -16},
+ "Bewan Modem RNIS USB"}),
+ },
+ {
+ USB_DEVICE(0x07fa, 0x0847),
+ .driver_info = (unsigned long) &((hfcsusb_vdata)
+ {LED_SCHEME1, {0x80, -64, -32, -16},
+ "Djinn Numeris USB"}),
+ },
+ {
+ USB_DEVICE(0x07b0, 0x0006),
+ .driver_info = (unsigned long) &((hfcsusb_vdata)
+ {LED_SCHEME1, {0x80, -64, -32, -16},
+ "Twister ISDN TA"}),
+ },
+ {
+ USB_DEVICE(0x071d, 0x1005),
+ .driver_info = (unsigned long) &((hfcsusb_vdata)
+ {LED_SCHEME1, {0x02, 0, 0x01, 0x04},
+ "Eicon DIVA USB 4.0"}),
+ },
+ { }
+};
+
+/* structure defining input+output fifos (interrupt/bulk mode) */
+struct usb_fifo; /* forward definition */
+typedef struct iso_urb_struct {
+ struct urb *purb;
+ __u8 buffer[ISO_BUFFER_SIZE]; /* buffer incoming/outgoing data */
+ struct usb_fifo *owner_fifo; /* pointer to owner fifo */
+} iso_urb_struct;
+
+struct hfcusb_data; /* forward definition */
+
+typedef struct usb_fifo {
+ int fifonum; /* fifo index attached to this structure */
+ int active; /* fifo is currently active */
+ struct hfcusb_data *hfc; /* pointer to main structure */
+ int pipe; /* address of endpoint */
+ __u8 usb_packet_maxlen; /* maximum length for usb transfer */
+ unsigned int max_size; /* maximum size of receive/send packet */
+ __u8 intervall; /* interrupt interval */
+ struct sk_buff *skbuff; /* actual used buffer */
+ struct urb *urb; /* transfer structure for usb routines */
+ __u8 buffer[128]; /* buffer incoming/outgoing data */
+ int bit_line; /* how much bits are in the fifo? */
+
+ volatile __u8 usb_transfer_mode; /* switched between ISO and INT */
+ iso_urb_struct iso[2]; /* need two urbs to have one always for pending */
+ struct hisax_if *hif; /* hisax interface */
+ int delete_flg; /* only delete skbuff once */
+ int last_urblen; /* remember length of last packet */
+} usb_fifo;
+
+/* structure holding all data for one device */
+typedef struct hfcusb_data {
+ /* HiSax Interface for loadable Layer1 drivers */
+ struct hisax_d_if d_if; /* see hisax_if.h */
+ struct hisax_b_if b_if[2]; /* see hisax_if.h */
+ int protocol;
+
+ struct usb_device *dev; /* our device */
+ int if_used; /* used interface number */
+ int alt_used; /* used alternate config */
+ int ctrl_paksize; /* control pipe packet size */
+ int ctrl_in_pipe, /* handles for control pipe */
+ ctrl_out_pipe;
+ int cfg_used; /* configuration index used */
+ int vend_idx; /* vendor found */
+ int b_mode[2]; /* B-channel mode */
+ int l1_activated; /* layer 1 activated */
+ int disc_flag; /* TRUE if device was disonnected to avoid some USB actions */
+ int packet_size, iso_packet_size;
+
+ /* control pipe background handling */
+ ctrl_buft ctrl_buff[HFC_CTRL_BUFSIZE]; /* buffer holding queued data */
+ volatile int ctrl_in_idx, ctrl_out_idx, ctrl_cnt; /* input/output pointer + count */
+ struct urb *ctrl_urb; /* transfer structure for control channel */
+
+ struct usb_ctrlrequest ctrl_write; /* buffer for control write request */
+ struct usb_ctrlrequest ctrl_read; /* same for read request */
+
+ __u8 old_led_state, led_state;
+
+ volatile __u8 threshold_mask; /* threshold actually reported */
+ volatile __u8 bch_enables; /* or mask for sctrl_r and sctrl register values */
+
+ usb_fifo fifos[HFCUSB_NUM_FIFOS]; /* structure holding all fifo data */
+
+ volatile __u8 l1_state; /* actual l1 state */
+ struct timer_list t3_timer; /* timer 3 for activation/deactivation */
+ struct timer_list t4_timer; /* timer 4 for activation/deactivation */
+} hfcusb_data;
+
+
+static void collect_rx_frame(usb_fifo * fifo, __u8 * data, int len,
+ int finish);
+
+static inline const char *
+symbolic(struct hfcusb_symbolic_list list[], const int num)
+{
+ int i;
+ for (i = 0; list[i].name != NULL; i++)
+ if (list[i].num == num)
+ return (list[i].name);
+ return "<unknown ERROR>";
+}
+
+static void
+ctrl_start_transfer(hfcusb_data * hfc)
+{
+ if (hfc->ctrl_cnt) {
+ hfc->ctrl_urb->pipe = hfc->ctrl_out_pipe;
+ hfc->ctrl_urb->setup_packet = (u_char *) & hfc->ctrl_write;
+ hfc->ctrl_urb->transfer_buffer = NULL;
+ hfc->ctrl_urb->transfer_buffer_length = 0;
+ hfc->ctrl_write.wIndex =
+ cpu_to_le16(hfc->ctrl_buff[hfc->ctrl_out_idx].hfc_reg);
+ hfc->ctrl_write.wValue =
+ cpu_to_le16(hfc->ctrl_buff[hfc->ctrl_out_idx].reg_val);
+
+ usb_submit_urb(hfc->ctrl_urb, GFP_ATOMIC); /* start transfer */
+ }
+} /* ctrl_start_transfer */
+
+static int
+queue_control_request(hfcusb_data * hfc, __u8 reg, __u8 val, int action)
+{
+ ctrl_buft *buf;
+
+ if (hfc->ctrl_cnt >= HFC_CTRL_BUFSIZE)
+ return (1); /* no space left */
+ buf = &hfc->ctrl_buff[hfc->ctrl_in_idx]; /* pointer to new index */
+ buf->hfc_reg = reg;
+ buf->reg_val = val;
+ buf->action = action;
+ if (++hfc->ctrl_in_idx >= HFC_CTRL_BUFSIZE)
+ hfc->ctrl_in_idx = 0; /* pointer wrap */
+ if (++hfc->ctrl_cnt == 1)
+ ctrl_start_transfer(hfc);
+ return (0);
+}
+
+static void
+ctrl_complete(struct urb *urb)
+{
+ hfcusb_data *hfc = (hfcusb_data *) urb->context;
+ ctrl_buft *buf;
+
+ urb->dev = hfc->dev;
+ if (hfc->ctrl_cnt) {
+ buf = &hfc->ctrl_buff[hfc->ctrl_out_idx];
+ hfc->ctrl_cnt--; /* decrement actual count */
+ if (++hfc->ctrl_out_idx >= HFC_CTRL_BUFSIZE)
+ hfc->ctrl_out_idx = 0; /* pointer wrap */
+
+ ctrl_start_transfer(hfc); /* start next transfer */
+ }
+}
+
+/* write led data to auxport & invert if necessary */
+static void
+write_led(hfcusb_data * hfc, __u8 led_state)
+{
+ if (led_state != hfc->old_led_state) {
+ hfc->old_led_state = led_state;
+ queue_control_request(hfc, HFCUSB_P_DATA, led_state, 1);
+ }
+}
+
+static void
+set_led_bit(hfcusb_data * hfc, signed short led_bits, int on)
+{
+ if (on) {
+ if (led_bits < 0)
+ hfc->led_state &= ~abs(led_bits);
+ else
+ hfc->led_state |= led_bits;
+ } else {
+ if (led_bits < 0)
+ hfc->led_state |= abs(led_bits);
+ else
+ hfc->led_state &= ~led_bits;
+ }
+}
+
+/* handle LED requests */
+static void
+handle_led(hfcusb_data * hfc, int event)
+{
+ hfcsusb_vdata *driver_info =
+ (hfcsusb_vdata *) hfcusb_idtab[hfc->vend_idx].driver_info;
+
+ /* if no scheme -> no LED action */
+ if (driver_info->led_scheme == LED_OFF)
+ return;
+
+ switch (event) {
+ case LED_POWER_ON:
+ set_led_bit(hfc, driver_info->led_bits[0], 1);
+ set_led_bit(hfc, driver_info->led_bits[1], 0);
+ set_led_bit(hfc, driver_info->led_bits[2], 0);
+ set_led_bit(hfc, driver_info->led_bits[3], 0);
+ break;
+ case LED_POWER_OFF:
+ set_led_bit(hfc, driver_info->led_bits[0], 0);
+ set_led_bit(hfc, driver_info->led_bits[1], 0);
+ set_led_bit(hfc, driver_info->led_bits[2], 0);
+ set_led_bit(hfc, driver_info->led_bits[3], 0);
+ break;
+ case LED_S0_ON:
+ set_led_bit(hfc, driver_info->led_bits[1], 1);
+ break;
+ case LED_S0_OFF:
+ set_led_bit(hfc, driver_info->led_bits[1], 0);
+ break;
+ case LED_B1_ON:
+ set_led_bit(hfc, driver_info->led_bits[2], 1);
+ break;
+ case LED_B1_OFF:
+ set_led_bit(hfc, driver_info->led_bits[2], 0);
+ break;
+ case LED_B2_ON:
+ set_led_bit(hfc, driver_info->led_bits[3], 1);
+ break;
+ case LED_B2_OFF:
+ set_led_bit(hfc, driver_info->led_bits[3], 0);
+ break;
+ }
+ write_led(hfc, hfc->led_state);
+}
+
+/* ISDN l1 timer T3 expires */
+static void
+l1_timer_expire_t3(hfcusb_data * hfc)
+{
+ hfc->d_if.ifc.l1l2(&hfc->d_if.ifc, PH_DEACTIVATE | INDICATION,
+ NULL);
+
+ DBG(HFCUSB_DBG_STATES,
+ "HFC-S USB: PH_DEACTIVATE | INDICATION sent (T3 expire)");
+
+ hfc->l1_activated = 0;
+ handle_led(hfc, LED_S0_OFF);
+ /* deactivate : */
+ queue_control_request(hfc, HFCUSB_STATES, 0x10, 1);
+ queue_control_request(hfc, HFCUSB_STATES, 3, 1);
+}
+
+/* ISDN l1 timer T4 expires */
+static void
+l1_timer_expire_t4(hfcusb_data * hfc)
+{
+ hfc->d_if.ifc.l1l2(&hfc->d_if.ifc, PH_DEACTIVATE | INDICATION,
+ NULL);
+
+ DBG(HFCUSB_DBG_STATES,
+ "HFC-S USB: PH_DEACTIVATE | INDICATION sent (T4 expire)");
+
+ hfc->l1_activated = 0;
+ handle_led(hfc, LED_S0_OFF);
+}
+
+/* S0 state changed */
+static void
+s0_state_handler(hfcusb_data * hfc, __u8 state)
+{
+ __u8 old_state;
+
+ old_state = hfc->l1_state;
+ if (state == old_state || state < 1 || state > 8)
+ return;
+
+ DBG(HFCUSB_DBG_STATES, "HFC-S USB: S0 statechange(%d -> %d)",
+ old_state, state);
+
+ if (state < 4 || state == 7 || state == 8) {
+ if (timer_pending(&hfc->t3_timer))
+ del_timer(&hfc->t3_timer);
+ DBG(HFCUSB_DBG_STATES, "HFC-S USB: T3 deactivated");
+ }
+ if (state >= 7) {
+ if (timer_pending(&hfc->t4_timer))
+ del_timer(&hfc->t4_timer);
+ DBG(HFCUSB_DBG_STATES, "HFC-S USB: T4 deactivated");
+ }
+
+ if (state == 7 && !hfc->l1_activated) {
+ hfc->d_if.ifc.l1l2(&hfc->d_if.ifc,
+ PH_ACTIVATE | INDICATION, NULL);
+ DBG(HFCUSB_DBG_STATES, "HFC-S USB: PH_ACTIVATE | INDICATION sent");
+ hfc->l1_activated = 1;
+ handle_led(hfc, LED_S0_ON);
+ } else if (state <= 3 /* && activated */ ) {
+ if (old_state == 7 || old_state == 8) {
+ DBG(HFCUSB_DBG_STATES, "HFC-S USB: T4 activated");
+ if (!timer_pending(&hfc->t4_timer)) {
+ hfc->t4_timer.expires =
+ jiffies + (HFC_TIMER_T4 * HZ) / 1000;
+ add_timer(&hfc->t4_timer);
+ }
+ } else {
+ hfc->d_if.ifc.l1l2(&hfc->d_if.ifc,
+ PH_DEACTIVATE | INDICATION,
+ NULL);
+ DBG(HFCUSB_DBG_STATES,
+ "HFC-S USB: PH_DEACTIVATE | INDICATION sent");
+ hfc->l1_activated = 0;
+ handle_led(hfc, LED_S0_OFF);
+ }
+ }
+ hfc->l1_state = state;
+}
+
+static void
+fill_isoc_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe,
+ void *buf, int num_packets, int packet_size, int interval,
+ usb_complete_t complete, void *context)
+{
+ int k;
+
+ urb->dev = dev;
+ urb->pipe = pipe;
+ urb->complete = complete;
+ urb->number_of_packets = num_packets;
+ urb->transfer_buffer_length = packet_size * num_packets;
+ urb->context = context;
+ urb->transfer_buffer = buf;
+ urb->transfer_flags = URB_ISO_ASAP;
+ urb->actual_length = 0;
+ urb->interval = interval;
+ for (k = 0; k < num_packets; k++) {
+ urb->iso_frame_desc[k].offset = packet_size * k;
+ urb->iso_frame_desc[k].length = packet_size;
+ urb->iso_frame_desc[k].actual_length = 0;
+ }
+}
+
+/* allocs urbs and start isoc transfer with two pending urbs to avoid
+ * gaps in the transfer chain
+ */
+static int
+start_isoc_chain(usb_fifo * fifo, int num_packets_per_urb,
+ usb_complete_t complete, int packet_size)
+{
+ int i, k, errcode;
+
+ DBG(HFCUSB_DBG_INIT, "HFC-S USB: starting ISO-URBs for fifo:%d\n",
+ fifo->fifonum);
+
+ /* allocate Memory for Iso out Urbs */
+ for (i = 0; i < 2; i++) {
+ if (!(fifo->iso[i].purb)) {
+ fifo->iso[i].purb =
+ usb_alloc_urb(num_packets_per_urb, GFP_KERNEL);
+ if (!(fifo->iso[i].purb)) {
+ printk(KERN_INFO
+ "alloc urb for fifo %i failed!!!",
+ fifo->fifonum);
+ }
+ fifo->iso[i].owner_fifo = (struct usb_fifo *) fifo;
+
+ /* Init the first iso */
+ if (ISO_BUFFER_SIZE >=
+ (fifo->usb_packet_maxlen *
+ num_packets_per_urb)) {
+ fill_isoc_urb(fifo->iso[i].purb,
+ fifo->hfc->dev, fifo->pipe,
+ fifo->iso[i].buffer,
+ num_packets_per_urb,
+ fifo->usb_packet_maxlen,
+ fifo->intervall, complete,
+ &fifo->iso[i]);
+ memset(fifo->iso[i].buffer, 0,
+ sizeof(fifo->iso[i].buffer));
+ /* defining packet delimeters in fifo->buffer */
+ for (k = 0; k < num_packets_per_urb; k++) {
+ fifo->iso[i].purb->
+ iso_frame_desc[k].offset =
+ k * packet_size;
+ fifo->iso[i].purb->
+ iso_frame_desc[k].length =
+ packet_size;
+ }
+ } else {
+ printk(KERN_INFO
+ "HFC-S USB: ISO Buffer size to small!\n");
+ }
+ }
+ fifo->bit_line = BITLINE_INF;
+
+ errcode = usb_submit_urb(fifo->iso[i].purb, GFP_KERNEL);
+ fifo->active = (errcode >= 0) ? 1 : 0;
+ if (errcode < 0)
+ printk(KERN_INFO "HFC-S USB: usb_submit_urb URB nr:%d, error(%i): '%s'\n",
+ i, errcode, symbolic(urb_errlist, errcode));
+ }
+ return (fifo->active);
+}
+
+/* stops running iso chain and frees their pending urbs */
+static void
+stop_isoc_chain(usb_fifo * fifo)
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ if (fifo->iso[i].purb) {
+ DBG(HFCUSB_DBG_INIT,
+ "HFC-S USB: Stopping iso chain for fifo %i.%i",
+ fifo->fifonum, i);
+ usb_kill_urb(fifo->iso[i].purb);
+ usb_free_urb(fifo->iso[i].purb);
+ fifo->iso[i].purb = NULL;
+ }
+ }
+
+ usb_kill_urb(fifo->urb);
+ usb_free_urb(fifo->urb);
+ fifo->urb = NULL;
+ fifo->active = 0;
+}
+
+/* defines how much ISO packets are handled in one URB */
+static int iso_packets[8] =
+ { ISOC_PACKETS_B, ISOC_PACKETS_B, ISOC_PACKETS_B, ISOC_PACKETS_B,
+ ISOC_PACKETS_D, ISOC_PACKETS_D, ISOC_PACKETS_D, ISOC_PACKETS_D
+};
+
+static void
+tx_iso_complete(struct urb *urb)
+{
+ iso_urb_struct *context_iso_urb = (iso_urb_struct *) urb->context;
+ usb_fifo *fifo = context_iso_urb->owner_fifo;
+ hfcusb_data *hfc = fifo->hfc;
+ int k, tx_offset, num_isoc_packets, sink, len, current_len,
+ errcode;
+ int frame_complete, transp_mode, fifon, status;
+ __u8 threshbit;
+
+ fifon = fifo->fifonum;
+ status = urb->status;
+
+ tx_offset = 0;
+
+ /* ISO transfer only partially completed,
+ look at individual frame status for details */
+ if (status == -EXDEV) {
+ DBG(HFCUSB_DBG_VERBOSE_USB, "HFC-S USB: tx_iso_complete with -EXDEV"
+ ", urb->status %d, fifonum %d\n",
+ status, fifon);
+
+ for (k = 0; k < iso_packets[fifon]; ++k) {
+ errcode = urb->iso_frame_desc[k].status;
+ if (errcode)
+ DBG(HFCUSB_DBG_VERBOSE_USB, "HFC-S USB: tx_iso_complete "
+ "packet %i, status: %i\n",
+ k, errcode);
+ }
+
+ // clear status, so go on with ISO transfers
+ status = 0;
+ }
+
+ if (fifo->active && !status) {
+ transp_mode = 0;
+ if (fifon < 4 && hfc->b_mode[fifon / 2] == L1_MODE_TRANS)
+ transp_mode = 1;
+
+ /* is FifoFull-threshold set for our channel? */
+ threshbit = (hfc->threshold_mask & (1 << fifon));
+ num_isoc_packets = iso_packets[fifon];
+
+ /* predict dataflow to avoid fifo overflow */
+ if (fifon >= HFCUSB_D_TX) {
+ sink = (threshbit) ? SINK_DMIN : SINK_DMAX;
+ } else {
+ sink = (threshbit) ? SINK_MIN : SINK_MAX;
+ }
+ fill_isoc_urb(urb, fifo->hfc->dev, fifo->pipe,
+ context_iso_urb->buffer, num_isoc_packets,
+ fifo->usb_packet_maxlen, fifo->intervall,
+ tx_iso_complete, urb->context);
+ memset(context_iso_urb->buffer, 0,
+ sizeof(context_iso_urb->buffer));
+ frame_complete = 0;
+
+ /* Generate next ISO Packets */
+ for (k = 0; k < num_isoc_packets; ++k) {
+ if (fifo->skbuff) {
+ len = fifo->skbuff->len;
+ /* we lower data margin every msec */
+ fifo->bit_line -= sink;
+ current_len = (0 - fifo->bit_line) / 8;
+ /* maximum 15 byte for every ISO packet makes our life easier */
+ if (current_len > 14)
+ current_len = 14;
+ current_len =
+ (len <=
+ current_len) ? len : current_len;
+ /* how much bit do we put on the line? */
+ fifo->bit_line += current_len * 8;
+
+ context_iso_urb->buffer[tx_offset] = 0;
+ if (current_len == len) {
+ if (!transp_mode) {
+ /* here frame completion */
+ context_iso_urb->
+ buffer[tx_offset] = 1;
+ /* add 2 byte flags and 16bit CRC at end of ISDN frame */
+ fifo->bit_line += 32;
+ }
+ frame_complete = 1;
+ }
+
+ memcpy(context_iso_urb->buffer +
+ tx_offset + 1, fifo->skbuff->data,
+ current_len);
+ skb_pull(fifo->skbuff, current_len);
+
+ /* define packet delimeters within the URB buffer */
+ urb->iso_frame_desc[k].offset = tx_offset;
+ urb->iso_frame_desc[k].length =
+ current_len + 1;
+
+ tx_offset += (current_len + 1);
+ } else {
+ urb->iso_frame_desc[k].offset =
+ tx_offset++;
+
+ urb->iso_frame_desc[k].length = 1;
+ fifo->bit_line -= sink; /* we lower data margin every msec */
+
+ if (fifo->bit_line < BITLINE_INF) {
+ fifo->bit_line = BITLINE_INF;
+ }
+ }
+
+ if (frame_complete) {
+ fifo->delete_flg = 1;
+ fifo->hif->l1l2(fifo->hif,
+ PH_DATA | CONFIRM,
+ (void *) (unsigned long) fifo->skbuff->
+ truesize);
+ if (fifo->skbuff && fifo->delete_flg) {
+ dev_kfree_skb_any(fifo->skbuff);
+ fifo->skbuff = NULL;
+ fifo->delete_flg = 0;
+ }
+ frame_complete = 0;
+ }
+ }
+ errcode = usb_submit_urb(urb, GFP_ATOMIC);
+ if (errcode < 0) {
+ printk(KERN_INFO
+ "HFC-S USB: error submitting ISO URB: %d\n",
+ errcode);
+ }
+ } else {
+ if (status && !hfc->disc_flag) {
+ printk(KERN_INFO
+ "HFC-S USB: tx_iso_complete: error(%i): '%s', fifonum=%d\n",
+ status, symbolic(urb_errlist, status), fifon);
+ }
+ }
+}
+
+static void
+rx_iso_complete(struct urb *urb)
+{
+ iso_urb_struct *context_iso_urb = (iso_urb_struct *) urb->context;
+ usb_fifo *fifo = context_iso_urb->owner_fifo;
+ hfcusb_data *hfc = fifo->hfc;
+ int k, len, errcode, offset, num_isoc_packets, fifon, maxlen,
+ status;
+ unsigned int iso_status;
+ __u8 *buf;
+ static __u8 eof[8];
+
+ fifon = fifo->fifonum;
+ status = urb->status;
+
+ if (urb->status == -EOVERFLOW) {
+ DBG(HFCUSB_DBG_VERBOSE_USB,
+ "HFC-USB: ignoring USB DATAOVERRUN fifo(%i)", fifon);
+ status = 0;
+ }
+
+ /* ISO transfer only partially completed,
+ look at individual frame status for details */
+ if (status == -EXDEV) {
+ DBG(HFCUSB_DBG_VERBOSE_USB, "HFC-S USB: rx_iso_complete with -EXDEV "
+ "urb->status %d, fifonum %d\n",
+ status, fifon);
+ status = 0;
+ }
+
+ if (fifo->active && !status) {
+ num_isoc_packets = iso_packets[fifon];
+ maxlen = fifo->usb_packet_maxlen;
+ for (k = 0; k < num_isoc_packets; ++k) {
+ len = urb->iso_frame_desc[k].actual_length;
+ offset = urb->iso_frame_desc[k].offset;
+ buf = context_iso_urb->buffer + offset;
+ iso_status = urb->iso_frame_desc[k].status;
+
+ if (iso_status && !hfc->disc_flag)
+ DBG(HFCUSB_DBG_VERBOSE_USB,
+ "HFC-S USB: rx_iso_complete "
+ "ISO packet %i, status: %i\n",
+ k, iso_status);
+
+ if (fifon == HFCUSB_D_RX) {
+ DBG(HFCUSB_DBG_VERBOSE_USB,
+ "HFC-S USB: ISO-D-RX lst_urblen:%2d "
+ "act_urblen:%2d max-urblen:%2d EOF:0x%0x",
+ fifo->last_urblen, len, maxlen,
+ eof[5]);
+
+ DBG_PACKET(HFCUSB_DBG_VERBOSE_USB, buf, len);
+ }
+
+ if (fifo->last_urblen != maxlen) {
+ /* the threshold mask is in the 2nd status byte */
+ hfc->threshold_mask = buf[1];
+ /* care for L1 state only for D-Channel
+ to avoid overlapped iso completions */
+ if (fifon == HFCUSB_D_RX) {
+ /* the S0 state is in the upper half
+ of the 1st status byte */
+ s0_state_handler(hfc, buf[0] >> 4);
+ }
+ eof[fifon] = buf[0] & 1;
+ if (len > 2)
+ collect_rx_frame(fifo, buf + 2,
+ len - 2,
+ (len < maxlen) ?
+ eof[fifon] : 0);
+ } else {
+ collect_rx_frame(fifo, buf, len,
+ (len <
+ maxlen) ? eof[fifon] :
+ 0);
+ }
+ fifo->last_urblen = len;
+ }
+
+ fill_isoc_urb(urb, fifo->hfc->dev, fifo->pipe,
+ context_iso_urb->buffer, num_isoc_packets,
+ fifo->usb_packet_maxlen, fifo->intervall,
+ rx_iso_complete, urb->context);
+ errcode = usb_submit_urb(urb, GFP_ATOMIC);
+ if (errcode < 0) {
+ printk(KERN_ERR
+ "HFC-S USB: error submitting ISO URB: %d\n",
+ errcode);
+ }
+ } else {
+ if (status && !hfc->disc_flag) {
+ printk(KERN_ERR
+ "HFC-S USB: rx_iso_complete : "
+ "urb->status %d, fifonum %d\n",
+ status, fifon);
+ }
+ }
+}
+
+/* collect rx data from INT- and ISO-URBs */
+static void
+collect_rx_frame(usb_fifo * fifo, __u8 * data, int len, int finish)
+{
+ hfcusb_data *hfc = fifo->hfc;
+ int transp_mode, fifon;
+
+ fifon = fifo->fifonum;
+ transp_mode = 0;
+ if (fifon < 4 && hfc->b_mode[fifon / 2] == L1_MODE_TRANS)
+ transp_mode = 1;
+
+ if (!fifo->skbuff) {
+ fifo->skbuff = dev_alloc_skb(fifo->max_size + 3);
+ if (!fifo->skbuff) {
+ printk(KERN_ERR
+ "HFC-S USB: cannot allocate buffer for fifo(%d)\n",
+ fifon);
+ return;
+ }
+ }
+ if (len) {
+ if (fifo->skbuff->len + len < fifo->max_size) {
+ memcpy(skb_put(fifo->skbuff, len), data, len);
+ } else {
+ DBG(HFCUSB_DBG_FIFO_ERR,
+ "HCF-USB: got frame exceeded fifo->max_size(%d) fifo(%d)",
+ fifo->max_size, fifon);
+ DBG_SKB(HFCUSB_DBG_VERBOSE_USB, fifo->skbuff);
+ skb_trim(fifo->skbuff, 0);
+ }
+ }
+ if (transp_mode && fifo->skbuff->len >= 128) {
+ fifo->hif->l1l2(fifo->hif, PH_DATA | INDICATION,
+ fifo->skbuff);
+ fifo->skbuff = NULL;
+ return;
+ }
+ /* we have a complete hdlc packet */
+ if (finish) {
+ if ((!fifo->skbuff->data[fifo->skbuff->len - 1])
+ && (fifo->skbuff->len > 3)) {
+
+ if (fifon == HFCUSB_D_RX) {
+ DBG(HFCUSB_DBG_DCHANNEL,
+ "HFC-S USB: D-RX len(%d)", fifo->skbuff->len);
+ DBG_SKB(HFCUSB_DBG_DCHANNEL, fifo->skbuff);
+ }
+
+ /* remove CRC & status */
+ skb_trim(fifo->skbuff, fifo->skbuff->len - 3);
+ if (fifon == HFCUSB_PCM_RX) {
+ fifo->hif->l1l2(fifo->hif,
+ PH_DATA_E | INDICATION,
+ fifo->skbuff);
+ } else
+ fifo->hif->l1l2(fifo->hif,
+ PH_DATA | INDICATION,
+ fifo->skbuff);
+ fifo->skbuff = NULL; /* buffer was freed from upper layer */
+ } else {
+ DBG(HFCUSB_DBG_FIFO_ERR,
+ "HFC-S USB: ERROR frame len(%d) fifo(%d)",
+ fifo->skbuff->len, fifon);
+ DBG_SKB(HFCUSB_DBG_VERBOSE_USB, fifo->skbuff);
+ skb_trim(fifo->skbuff, 0);
+ }
+ }
+}
+
+static void
+rx_int_complete(struct urb *urb)
+{
+ int len;
+ int status;
+ __u8 *buf, maxlen, fifon;
+ usb_fifo *fifo = (usb_fifo *) urb->context;
+ hfcusb_data *hfc = fifo->hfc;
+ static __u8 eof[8];
+
+ urb->dev = hfc->dev; /* security init */
+
+ fifon = fifo->fifonum;
+ if ((!fifo->active) || (urb->status)) {
+ DBG(HFCUSB_DBG_INIT, "HFC-S USB: RX-Fifo %i is going down (%i)",
+ fifon, urb->status);
+
+ fifo->urb->interval = 0; /* cancel automatic rescheduling */
+ if (fifo->skbuff) {
+ dev_kfree_skb_any(fifo->skbuff);
+ fifo->skbuff = NULL;
+ }
+ return;
+ }
+ len = urb->actual_length;
+ buf = fifo->buffer;
+ maxlen = fifo->usb_packet_maxlen;
+
+ if (fifon == HFCUSB_D_RX) {
+ DBG(HFCUSB_DBG_VERBOSE_USB,
+ "HFC-S USB: INT-D-RX lst_urblen:%2d "
+ "act_urblen:%2d max-urblen:%2d EOF:0x%0x",
+ fifo->last_urblen, len, maxlen,
+ eof[5]);
+ DBG_PACKET(HFCUSB_DBG_VERBOSE_USB, buf, len);
+ }
+
+ if (fifo->last_urblen != fifo->usb_packet_maxlen) {
+ /* the threshold mask is in the 2nd status byte */
+ hfc->threshold_mask = buf[1];
+ /* the S0 state is in the upper half of the 1st status byte */
+ s0_state_handler(hfc, buf[0] >> 4);
+ eof[fifon] = buf[0] & 1;
+ /* if we have more than the 2 status bytes -> collect data */
+ if (len > 2)
+ collect_rx_frame(fifo, buf + 2,
+ urb->actual_length - 2,
+ (len < maxlen) ? eof[fifon] : 0);
+ } else {
+ collect_rx_frame(fifo, buf, urb->actual_length,
+ (len < maxlen) ? eof[fifon] : 0);
+ }
+ fifo->last_urblen = urb->actual_length;
+ status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (status) {
+ printk(KERN_INFO
+ "HFC-S USB: %s error resubmitting URB fifo(%d)\n",
+ __func__, fifon);
+ }
+}
+
+/* start initial INT-URB for certain fifo */
+static void
+start_int_fifo(usb_fifo * fifo)
+{
+ int errcode;
+
+ DBG(HFCUSB_DBG_INIT, "HFC-S USB: starting RX INT-URB for fifo:%d\n",
+ fifo->fifonum);
+
+ if (!fifo->urb) {
+ fifo->urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!fifo->urb)
+ return;
+ }
+ usb_fill_int_urb(fifo->urb, fifo->hfc->dev, fifo->pipe,
+ fifo->buffer, fifo->usb_packet_maxlen,
+ rx_int_complete, fifo, fifo->intervall);
+ fifo->active = 1; /* must be marked active */
+ errcode = usb_submit_urb(fifo->urb, GFP_KERNEL);
+ if (errcode) {
+ printk(KERN_ERR
+ "HFC-S USB: submit URB error(start_int_info): status:%i\n",
+ errcode);
+ fifo->active = 0;
+ fifo->skbuff = NULL;
+ }
+}
+
+static void
+setup_bchannel(hfcusb_data * hfc, int channel, int mode)
+{
+ __u8 val, idx_table[2] = { 0, 2 };
+
+ if (hfc->disc_flag) {
+ return;
+ }
+ DBG(HFCUSB_DBG_STATES, "HFC-S USB: setting channel %d to mode %d",
+ channel, mode);
+ hfc->b_mode[channel] = mode;
+
+ /* setup CON_HDLC */
+ val = 0;
+ if (mode != L1_MODE_NULL)
+ val = 8; /* enable fifo? */
+ if (mode == L1_MODE_TRANS)
+ val |= 2; /* set transparent bit */
+
+ /* set FIFO to transmit register */
+ queue_control_request(hfc, HFCUSB_FIFO, idx_table[channel], 1);
+ queue_control_request(hfc, HFCUSB_CON_HDLC, val, 1);
+ /* reset fifo */
+ queue_control_request(hfc, HFCUSB_INC_RES_F, 2, 1);
+ /* set FIFO to receive register */
+ queue_control_request(hfc, HFCUSB_FIFO, idx_table[channel] + 1, 1);
+ queue_control_request(hfc, HFCUSB_CON_HDLC, val, 1);
+ /* reset fifo */
+ queue_control_request(hfc, HFCUSB_INC_RES_F, 2, 1);
+
+ val = 0x40;
+ if (hfc->b_mode[0])
+ val |= 1;
+ if (hfc->b_mode[1])
+ val |= 2;
+ queue_control_request(hfc, HFCUSB_SCTRL, val, 1);
+
+ val = 0;
+ if (hfc->b_mode[0])
+ val |= 1;
+ if (hfc->b_mode[1])
+ val |= 2;
+ queue_control_request(hfc, HFCUSB_SCTRL_R, val, 1);
+
+ if (mode == L1_MODE_NULL) {
+ if (channel)
+ handle_led(hfc, LED_B2_OFF);
+ else
+ handle_led(hfc, LED_B1_OFF);
+ } else {
+ if (channel)
+ handle_led(hfc, LED_B2_ON);
+ else
+ handle_led(hfc, LED_B1_ON);
+ }
+}
+
+static void
+hfc_usb_l2l1(struct hisax_if *my_hisax_if, int pr, void *arg)
+{
+ usb_fifo *fifo = my_hisax_if->priv;
+ hfcusb_data *hfc = fifo->hfc;
+
+ switch (pr) {
+ case PH_ACTIVATE | REQUEST:
+ if (fifo->fifonum == HFCUSB_D_TX) {
+ DBG(HFCUSB_DBG_STATES,
+ "HFC_USB: hfc_usb_d_l2l1 D-chan: PH_ACTIVATE | REQUEST");
+
+ if (hfc->l1_state != 3
+ && hfc->l1_state != 7) {
+ hfc->d_if.ifc.l1l2(&hfc->d_if.ifc,
+ PH_DEACTIVATE |
+ INDICATION,
+ NULL);
+ DBG(HFCUSB_DBG_STATES,
+ "HFC-S USB: PH_DEACTIVATE | INDICATION sent (not state 3 or 7)");
+ } else {
+ if (hfc->l1_state == 7) { /* l1 already active */
+ hfc->d_if.ifc.l1l2(&hfc->
+ d_if.
+ ifc,
+ PH_ACTIVATE
+ |
+ INDICATION,
+ NULL);
+ DBG(HFCUSB_DBG_STATES,
+ "HFC-S USB: PH_ACTIVATE | INDICATION sent again ;)");
+ } else {
+ /* force sending sending INFO1 */
+ queue_control_request(hfc,
+ HFCUSB_STATES,
+ 0x14,
+ 1);
+ mdelay(1);
+ /* start l1 activation */
+ queue_control_request(hfc,
+ HFCUSB_STATES,
+ 0x04,
+ 1);
+ if (!timer_pending
+ (&hfc->t3_timer)) {
+ hfc->t3_timer.
+ expires =
+ jiffies +
+ (HFC_TIMER_T3 *
+ HZ) / 1000;
+ add_timer(&hfc->
+ t3_timer);
+ }
+ }
+ }
+ } else {
+ DBG(HFCUSB_DBG_STATES,
+ "HFC_USB: hfc_usb_d_l2l1 B-chan: PH_ACTIVATE | REQUEST");
+ setup_bchannel(hfc,
+ (fifo->fifonum ==
+ HFCUSB_B1_TX) ? 0 : 1,
+ (long) arg);
+ fifo->hif->l1l2(fifo->hif,
+ PH_ACTIVATE | INDICATION,
+ NULL);
+ }
+ break;
+ case PH_DEACTIVATE | REQUEST:
+ if (fifo->fifonum == HFCUSB_D_TX) {
+ DBG(HFCUSB_DBG_STATES,
+ "HFC_USB: hfc_usb_d_l2l1 D-chan: PH_DEACTIVATE | REQUEST");
+ } else {
+ DBG(HFCUSB_DBG_STATES,
+ "HFC_USB: hfc_usb_d_l2l1 Bx-chan: PH_DEACTIVATE | REQUEST");
+ setup_bchannel(hfc,
+ (fifo->fifonum ==
+ HFCUSB_B1_TX) ? 0 : 1,
+ (int) L1_MODE_NULL);
+ fifo->hif->l1l2(fifo->hif,
+ PH_DEACTIVATE | INDICATION,
+ NULL);
+ }
+ break;
+ case PH_DATA | REQUEST:
+ if (fifo->skbuff && fifo->delete_flg) {
+ dev_kfree_skb_any(fifo->skbuff);
+ fifo->skbuff = NULL;
+ fifo->delete_flg = 0;
+ }
+ fifo->skbuff = arg; /* we have a new buffer */
+ break;
+ default:
+ DBG(HFCUSB_DBG_STATES,
+ "HFC_USB: hfc_usb_d_l2l1: unkown state : %#x", pr);
+ break;
+ }
+}
+
+/* initial init HFC-S USB chip registers, HiSax interface, USB URBs */
+static int
+hfc_usb_init(hfcusb_data * hfc)
+{
+ usb_fifo *fifo;
+ int i, err;
+ u_char b;
+ struct hisax_b_if *p_b_if[2];
+
+ /* check the chip id */
+ if (read_usb(hfc, HFCUSB_CHIP_ID, &b) != 1) {
+ printk(KERN_INFO "HFC-USB: cannot read chip id\n");
+ return (1);
+ }
+ if (b != HFCUSB_CHIPID) {
+ printk(KERN_INFO "HFC-S USB: Invalid chip id 0x%02x\n", b);
+ return (1);
+ }
+
+ /* first set the needed config, interface and alternate */
+ err = usb_set_interface(hfc->dev, hfc->if_used, hfc->alt_used);
+
+ /* do Chip reset */
+ write_usb(hfc, HFCUSB_CIRM, 8);
+ /* aux = output, reset off */
+ write_usb(hfc, HFCUSB_CIRM, 0x10);
+
+ /* set USB_SIZE to match wMaxPacketSize for INT or BULK transfers */
+ write_usb(hfc, HFCUSB_USB_SIZE,
+ (hfc->packet_size / 8) | ((hfc->packet_size / 8) << 4));
+
+ /* set USB_SIZE_I to match wMaxPacketSize for ISO transfers */
+ write_usb(hfc, HFCUSB_USB_SIZE_I, hfc->iso_packet_size);
+
+ /* enable PCM/GCI master mode */
+ write_usb(hfc, HFCUSB_MST_MODE1, 0); /* set default values */
+ write_usb(hfc, HFCUSB_MST_MODE0, 1); /* enable master mode */
+
+ /* init the fifos */
+ write_usb(hfc, HFCUSB_F_THRES,
+ (HFCUSB_TX_THRESHOLD /
+ 8) | ((HFCUSB_RX_THRESHOLD / 8) << 4));
+
+ fifo = hfc->fifos;
+ for (i = 0; i < HFCUSB_NUM_FIFOS; i++) {
+ write_usb(hfc, HFCUSB_FIFO, i); /* select the desired fifo */
+ fifo[i].skbuff = NULL; /* init buffer pointer */
+ fifo[i].max_size =
+ (i <= HFCUSB_B2_RX) ? MAX_BCH_SIZE : MAX_DFRAME_LEN;
+ fifo[i].last_urblen = 0;
+ /* set 2 bit for D- & E-channel */
+ write_usb(hfc, HFCUSB_HDLC_PAR,
+ ((i <= HFCUSB_B2_RX) ? 0 : 2));
+ /* rx hdlc, enable IFF for D-channel */
+ write_usb(hfc, HFCUSB_CON_HDLC,
+ ((i == HFCUSB_D_TX) ? 0x09 : 0x08));
+ write_usb(hfc, HFCUSB_INC_RES_F, 2); /* reset the fifo */
+ }
+
+ write_usb(hfc, HFCUSB_CLKDEL, 0x0f); /* clock delay value */
+ write_usb(hfc, HFCUSB_STATES, 3 | 0x10); /* set deactivated mode */
+ write_usb(hfc, HFCUSB_STATES, 3); /* enable state machine */
+
+ write_usb(hfc, HFCUSB_SCTRL_R, 0); /* disable both B receivers */
+ write_usb(hfc, HFCUSB_SCTRL, 0x40); /* disable B transmitters + capacitive mode */
+
+ /* set both B-channel to not connected */
+ hfc->b_mode[0] = L1_MODE_NULL;
+ hfc->b_mode[1] = L1_MODE_NULL;
+
+ hfc->l1_activated = 0;
+ hfc->disc_flag = 0;
+ hfc->led_state = 0;
+ hfc->old_led_state = 0;
+
+ /* init the t3 timer */
+ init_timer(&hfc->t3_timer);
+ hfc->t3_timer.data = (long) hfc;
+ hfc->t3_timer.function = (void *) l1_timer_expire_t3;
+
+ /* init the t4 timer */
+ init_timer(&hfc->t4_timer);
+ hfc->t4_timer.data = (long) hfc;
+ hfc->t4_timer.function = (void *) l1_timer_expire_t4;
+
+ /* init the background machinery for control requests */
+ hfc->ctrl_read.bRequestType = 0xc0;
+ hfc->ctrl_read.bRequest = 1;
+ hfc->ctrl_read.wLength = cpu_to_le16(1);
+ hfc->ctrl_write.bRequestType = 0x40;
+ hfc->ctrl_write.bRequest = 0;
+ hfc->ctrl_write.wLength = 0;
+ usb_fill_control_urb(hfc->ctrl_urb,
+ hfc->dev,
+ hfc->ctrl_out_pipe,
+ (u_char *) & hfc->ctrl_write,
+ NULL, 0, ctrl_complete, hfc);
+ /* Init All Fifos */
+ for (i = 0; i < HFCUSB_NUM_FIFOS; i++) {
+ hfc->fifos[i].iso[0].purb = NULL;
+ hfc->fifos[i].iso[1].purb = NULL;
+ hfc->fifos[i].active = 0;
+ }
+ /* register Modul to upper Hisax Layers */
+ hfc->d_if.owner = THIS_MODULE;
+ hfc->d_if.ifc.priv = &hfc->fifos[HFCUSB_D_TX];
+ hfc->d_if.ifc.l2l1 = hfc_usb_l2l1;
+ for (i = 0; i < 2; i++) {
+ hfc->b_if[i].ifc.priv = &hfc->fifos[HFCUSB_B1_TX + i * 2];
+ hfc->b_if[i].ifc.l2l1 = hfc_usb_l2l1;
+ p_b_if[i] = &hfc->b_if[i];
+ }
+ /* default Prot: EURO ISDN, should be a module_param */
+ hfc->protocol = 2;
+ i = hisax_register(&hfc->d_if, p_b_if, "hfc_usb", hfc->protocol);
+ if (i) {
+ printk(KERN_INFO "HFC-S USB: hisax_register -> %d\n", i);
+ return i;
+ }
+
+#ifdef CONFIG_HISAX_DEBUG
+ hfc_debug = debug;
+#endif
+
+ for (i = 0; i < 4; i++)
+ hfc->fifos[i].hif = &p_b_if[i / 2]->ifc;
+ for (i = 4; i < 8; i++)
+ hfc->fifos[i].hif = &hfc->d_if.ifc;
+
+ /* 3 (+1) INT IN + 3 ISO OUT */
+ if (hfc->cfg_used == CNF_3INT3ISO || hfc->cfg_used == CNF_4INT3ISO) {
+ start_int_fifo(hfc->fifos + HFCUSB_D_RX);
+ if (hfc->fifos[HFCUSB_PCM_RX].pipe)
+ start_int_fifo(hfc->fifos + HFCUSB_PCM_RX);
+ start_int_fifo(hfc->fifos + HFCUSB_B1_RX);
+ start_int_fifo(hfc->fifos + HFCUSB_B2_RX);
+ }
+ /* 3 (+1) ISO IN + 3 ISO OUT */
+ if (hfc->cfg_used == CNF_3ISO3ISO || hfc->cfg_used == CNF_4ISO3ISO) {
+ start_isoc_chain(hfc->fifos + HFCUSB_D_RX, ISOC_PACKETS_D,
+ rx_iso_complete, 16);
+ if (hfc->fifos[HFCUSB_PCM_RX].pipe)
+ start_isoc_chain(hfc->fifos + HFCUSB_PCM_RX,
+ ISOC_PACKETS_D, rx_iso_complete,
+ 16);
+ start_isoc_chain(hfc->fifos + HFCUSB_B1_RX, ISOC_PACKETS_B,
+ rx_iso_complete, 16);
+ start_isoc_chain(hfc->fifos + HFCUSB_B2_RX, ISOC_PACKETS_B,
+ rx_iso_complete, 16);
+ }
+
+ start_isoc_chain(hfc->fifos + HFCUSB_D_TX, ISOC_PACKETS_D,
+ tx_iso_complete, 1);
+ start_isoc_chain(hfc->fifos + HFCUSB_B1_TX, ISOC_PACKETS_B,
+ tx_iso_complete, 1);
+ start_isoc_chain(hfc->fifos + HFCUSB_B2_TX, ISOC_PACKETS_B,
+ tx_iso_complete, 1);
+
+ handle_led(hfc, LED_POWER_ON);
+
+ return (0);
+}
+
+/* initial callback for each plugged USB device */
+static int
+hfc_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ hfcusb_data *context;
+ struct usb_host_interface *iface = intf->cur_altsetting;
+ struct usb_host_interface *iface_used = NULL;
+ struct usb_host_endpoint *ep;
+ int ifnum = iface->desc.bInterfaceNumber;
+ int i, idx, alt_idx, probe_alt_setting, vend_idx, cfg_used, *vcf,
+ attr, cfg_found, cidx, ep_addr;
+ int cmptbl[16], small_match, iso_packet_size, packet_size,
+ alt_used = 0;
+ hfcsusb_vdata *driver_info;
+
+ vend_idx = 0xffff;
+ for (i = 0; hfcusb_idtab[i].idVendor; i++) {
+ if ((le16_to_cpu(dev->descriptor.idVendor) == hfcusb_idtab[i].idVendor)
+ && (le16_to_cpu(dev->descriptor.idProduct) == hfcusb_idtab[i].idProduct)) {
+ vend_idx = i;
+ continue;
+ }
+ }
+
+ printk(KERN_INFO
+ "HFC-S USB: probing interface(%d) actalt(%d) minor(%d)\n",
+ ifnum, iface->desc.bAlternateSetting, intf->minor);
+
+ if (vend_idx != 0xffff) {
+ /* if vendor and product ID is OK, start probing alternate settings */
+ alt_idx = 0;
+ small_match = 0xffff;
+
+ /* default settings */
+ iso_packet_size = 16;
+ packet_size = 64;
+
+ while (alt_idx < intf->num_altsetting) {
+ iface = intf->altsetting + alt_idx;
+ probe_alt_setting = iface->desc.bAlternateSetting;
+ cfg_used = 0;
+
+ /* check for config EOL element */
+ while (validconf[cfg_used][0]) {
+ cfg_found = 1;
+ vcf = validconf[cfg_used];
+ /* first endpoint descriptor */
+ ep = iface->endpoint;
+
+ memcpy(cmptbl, vcf, 16 * sizeof(int));
+
+ /* check for all endpoints in this alternate setting */
+ for (i = 0; i < iface->desc.bNumEndpoints;
+ i++) {
+ ep_addr =
+ ep->desc.bEndpointAddress;
+ /* get endpoint base */
+ idx = ((ep_addr & 0x7f) - 1) * 2;
+ if (ep_addr & 0x80)
+ idx++;
+ attr = ep->desc.bmAttributes;
+ if (cmptbl[idx] == EP_NUL) {
+ cfg_found = 0;
+ }
+ if (attr == USB_ENDPOINT_XFER_INT
+ && cmptbl[idx] == EP_INT)
+ cmptbl[idx] = EP_NUL;
+ if (attr == USB_ENDPOINT_XFER_BULK
+ && cmptbl[idx] == EP_BLK)
+ cmptbl[idx] = EP_NUL;
+ if (attr == USB_ENDPOINT_XFER_ISOC
+ && cmptbl[idx] == EP_ISO)
+ cmptbl[idx] = EP_NUL;
+
+ /* check if all INT endpoints match minimum interval */
+ if ((attr == USB_ENDPOINT_XFER_INT)
+ && (ep->desc.bInterval < vcf[17])) {
+ cfg_found = 0;
+ }
+ ep++;
+ }
+ for (i = 0; i < 16; i++) {
+ /* all entries must be EP_NOP or EP_NUL for a valid config */
+ if (cmptbl[i] != EP_NOP
+ && cmptbl[i] != EP_NUL)
+ cfg_found = 0;
+ }
+ if (cfg_found) {
+ if (cfg_used < small_match) {
+ small_match = cfg_used;
+ alt_used =
+ probe_alt_setting;
+ iface_used = iface;
+ }
+ }
+ cfg_used++;
+ }
+ alt_idx++;
+ } /* (alt_idx < intf->num_altsetting) */
+
+ /* found a valid USB Ta Endpint config */
+ if (small_match != 0xffff) {
+ iface = iface_used;
+ if (!(context = kzalloc(sizeof(hfcusb_data), GFP_KERNEL)))
+ return (-ENOMEM); /* got no mem */
+
+ ep = iface->endpoint;
+ vcf = validconf[small_match];
+
+ for (i = 0; i < iface->desc.bNumEndpoints; i++) {
+ ep_addr = ep->desc.bEndpointAddress;
+ /* get endpoint base */
+ idx = ((ep_addr & 0x7f) - 1) * 2;
+ if (ep_addr & 0x80)
+ idx++;
+ cidx = idx & 7;
+ attr = ep->desc.bmAttributes;
+
+ /* init Endpoints */
+ if (vcf[idx] != EP_NOP
+ && vcf[idx] != EP_NUL) {
+ switch (attr) {
+ case USB_ENDPOINT_XFER_INT:
+ context->
+ fifos[cidx].
+ pipe =
+ usb_rcvintpipe
+ (dev,
+ ep->desc.
+ bEndpointAddress);
+ context->
+ fifos[cidx].
+ usb_transfer_mode
+ = USB_INT;
+ packet_size =
+ le16_to_cpu(ep->desc.wMaxPacketSize);
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ if (ep_addr & 0x80)
+ context->
+ fifos
+ [cidx].
+ pipe =
+ usb_rcvbulkpipe
+ (dev,
+ ep->
+ desc.
+ bEndpointAddress);
+ else
+ context->
+ fifos
+ [cidx].
+ pipe =
+ usb_sndbulkpipe
+ (dev,
+ ep->
+ desc.
+ bEndpointAddress);
+ context->
+ fifos[cidx].
+ usb_transfer_mode
+ = USB_BULK;
+ packet_size =
+ le16_to_cpu(ep->desc.wMaxPacketSize);
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ if (ep_addr & 0x80)
+ context->
+ fifos
+ [cidx].
+ pipe =
+ usb_rcvisocpipe
+ (dev,
+ ep->
+ desc.
+ bEndpointAddress);
+ else
+ context->
+ fifos
+ [cidx].
+ pipe =
+ usb_sndisocpipe
+ (dev,
+ ep->
+ desc.
+ bEndpointAddress);
+ context->
+ fifos[cidx].
+ usb_transfer_mode
+ = USB_ISOC;
+ iso_packet_size =
+ le16_to_cpu(ep->desc.wMaxPacketSize);
+ break;
+ default:
+ context->
+ fifos[cidx].
+ pipe = 0;
+ } /* switch attribute */
+
+ if (context->fifos[cidx].pipe) {
+ context->fifos[cidx].
+ fifonum = cidx;
+ context->fifos[cidx].hfc =
+ context;
+ context->fifos[cidx].usb_packet_maxlen =
+ le16_to_cpu(ep->desc.wMaxPacketSize);
+ context->fifos[cidx].
+ intervall =
+ ep->desc.bInterval;
+ context->fifos[cidx].
+ skbuff = NULL;
+ }
+ }
+ ep++;
+ }
+ context->dev = dev; /* save device */
+ context->if_used = ifnum; /* save used interface */
+ context->alt_used = alt_used; /* and alternate config */
+ context->ctrl_paksize = dev->descriptor.bMaxPacketSize0; /* control size */
+ context->cfg_used = vcf[16]; /* store used config */
+ context->vend_idx = vend_idx; /* store found vendor */
+ context->packet_size = packet_size;
+ context->iso_packet_size = iso_packet_size;
+
+ /* create the control pipes needed for register access */
+ context->ctrl_in_pipe =
+ usb_rcvctrlpipe(context->dev, 0);
+ context->ctrl_out_pipe =
+ usb_sndctrlpipe(context->dev, 0);
+ context->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL);
+
+ driver_info =
+ (hfcsusb_vdata *) hfcusb_idtab[vend_idx].
+ driver_info;
+ printk(KERN_INFO "HFC-S USB: detected \"%s\"\n",
+ driver_info->vend_name);
+
+ DBG(HFCUSB_DBG_INIT,
+ "HFC-S USB: Endpoint-Config: %s (if=%d alt=%d), E-Channel(%d)",
+ conf_str[small_match], context->if_used,
+ context->alt_used,
+ validconf[small_match][18]);
+
+ /* init the chip and register the driver */
+ if (hfc_usb_init(context)) {
+ usb_kill_urb(context->ctrl_urb);
+ usb_free_urb(context->ctrl_urb);
+ context->ctrl_urb = NULL;
+ kfree(context);
+ return (-EIO);
+ }
+ usb_set_intfdata(intf, context);
+ return (0);
+ }
+ } else {
+ printk(KERN_INFO
+ "HFC-S USB: no valid vendor found in USB descriptor\n");
+ }
+ return (-EIO);
+}
+
+/* callback for unplugged USB device */
+static void
+hfc_usb_disconnect(struct usb_interface *intf)
+{
+ hfcusb_data *context = usb_get_intfdata(intf);
+ int i;
+
+ handle_led(context, LED_POWER_OFF);
+ schedule_timeout(HZ / 100);
+
+ printk(KERN_INFO "HFC-S USB: device disconnect\n");
+ context->disc_flag = 1;
+ usb_set_intfdata(intf, NULL);
+
+ if (timer_pending(&context->t3_timer))
+ del_timer(&context->t3_timer);
+ if (timer_pending(&context->t4_timer))
+ del_timer(&context->t4_timer);
+
+ /* tell all fifos to terminate */
+ for (i = 0; i < HFCUSB_NUM_FIFOS; i++) {
+ if (context->fifos[i].usb_transfer_mode == USB_ISOC) {
+ if (context->fifos[i].active > 0) {
+ stop_isoc_chain(&context->fifos[i]);
+ DBG(HFCUSB_DBG_INIT,
+ "HFC-S USB: %s stopping ISOC chain Fifo(%i)",
+ __func__, i);
+ }
+ } else {
+ if (context->fifos[i].active > 0) {
+ context->fifos[i].active = 0;
+ DBG(HFCUSB_DBG_INIT,
+ "HFC-S USB: %s unlinking URB for Fifo(%i)",
+ __func__, i);
+ }
+ usb_kill_urb(context->fifos[i].urb);
+ usb_free_urb(context->fifos[i].urb);
+ context->fifos[i].urb = NULL;
+ }
+ context->fifos[i].active = 0;
+ }
+ usb_kill_urb(context->ctrl_urb);
+ usb_free_urb(context->ctrl_urb);
+ context->ctrl_urb = NULL;
+ hisax_unregister(&context->d_if);
+ kfree(context); /* free our structure again */
+}
+
+static struct usb_driver hfc_drv = {
+ .name = "hfc_usb",
+ .id_table = hfcusb_idtab,
+ .probe = hfc_usb_probe,
+ .disconnect = hfc_usb_disconnect,
+};
+
+static void __exit
+hfc_usb_mod_exit(void)
+{
+ usb_deregister(&hfc_drv); /* release our driver */
+ printk(KERN_INFO "HFC-S USB: module removed\n");
+}
+
+static int __init
+hfc_usb_mod_init(void)
+{
+ char revstr[30], datestr[30], dummy[30];
+#ifndef CONFIG_HISAX_DEBUG
+ hfc_debug = debug;
+#endif
+ sscanf(hfcusb_revision,
+ "%s %s $ %s %s %s $ ", dummy, revstr,
+ dummy, datestr, dummy);
+ printk(KERN_INFO
+ "HFC-S USB: driver module revision %s date %s loaded, (debug=%i)\n",
+ revstr, datestr, debug);
+ if (usb_register(&hfc_drv)) {
+ printk(KERN_INFO
+ "HFC-S USB: Unable to register HFC-S USB module at usb stack\n");
+ return (-1); /* unable to register */
+ }
+ return (0);
+}
+
+module_init(hfc_usb_mod_init);
+module_exit(hfc_usb_mod_exit);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(usb, hfcusb_idtab);
diff --git a/drivers/isdn/hisax/hfc_usb.h b/drivers/isdn/hisax/hfc_usb.h
new file mode 100644
index 0000000..e79f565
--- /dev/null
+++ b/drivers/isdn/hisax/hfc_usb.h
@@ -0,0 +1,207 @@
+/*
+ * hfc_usb.h
+ *
+ * $Id: hfc_usb.h,v 1.1.2.5 2007/08/20 14:36:03 mbachem Exp $
+ */
+
+#ifndef __HFC_USB_H__
+#define __HFC_USB_H__
+
+#define DRIVER_AUTHOR "Peter Sprenger (sprenger@moving-byters.de)"
+#define DRIVER_DESC "HFC-S USB based HiSAX ISDN driver"
+
+
+#define HFC_CTRL_TIMEOUT 20 /* 5ms timeout writing/reading regs */
+#define HFC_TIMER_T3 8000 /* timeout for l1 activation timer */
+#define HFC_TIMER_T4 500 /* time for state change interval */
+
+#define HFCUSB_L1_STATECHANGE 0 /* L1 state changed */
+#define HFCUSB_L1_DRX 1 /* D-frame received */
+#define HFCUSB_L1_ERX 2 /* E-frame received */
+#define HFCUSB_L1_DTX 4 /* D-frames completed */
+
+#define MAX_BCH_SIZE 2048 /* allowed B-channel packet size */
+
+#define HFCUSB_RX_THRESHOLD 64 /* threshold for fifo report bit rx */
+#define HFCUSB_TX_THRESHOLD 64 /* threshold for fifo report bit tx */
+
+#define HFCUSB_CHIP_ID 0x16 /* Chip ID register index */
+#define HFCUSB_CIRM 0x00 /* cirm register index */
+#define HFCUSB_USB_SIZE 0x07 /* int length register */
+#define HFCUSB_USB_SIZE_I 0x06 /* iso length register */
+#define HFCUSB_F_CROSS 0x0b /* bit order register */
+#define HFCUSB_CLKDEL 0x37 /* bit delay register */
+#define HFCUSB_CON_HDLC 0xfa /* channel connect register */
+#define HFCUSB_HDLC_PAR 0xfb
+#define HFCUSB_SCTRL 0x31 /* S-bus control register (tx) */
+#define HFCUSB_SCTRL_E 0x32 /* same for E and special funcs */
+#define HFCUSB_SCTRL_R 0x33 /* S-bus control register (rx) */
+#define HFCUSB_F_THRES 0x0c /* threshold register */
+#define HFCUSB_FIFO 0x0f /* fifo select register */
+#define HFCUSB_F_USAGE 0x1a /* fifo usage register */
+#define HFCUSB_MST_MODE0 0x14
+#define HFCUSB_MST_MODE1 0x15
+#define HFCUSB_P_DATA 0x1f
+#define HFCUSB_INC_RES_F 0x0e
+#define HFCUSB_STATES 0x30
+
+#define HFCUSB_CHIPID 0x40 /* ID value of HFC-S USB */
+
+
+/* fifo registers */
+#define HFCUSB_NUM_FIFOS 8 /* maximum number of fifos */
+#define HFCUSB_B1_TX 0 /* index for B1 transmit bulk/int */
+#define HFCUSB_B1_RX 1 /* index for B1 receive bulk/int */
+#define HFCUSB_B2_TX 2
+#define HFCUSB_B2_RX 3
+#define HFCUSB_D_TX 4
+#define HFCUSB_D_RX 5
+#define HFCUSB_PCM_TX 6
+#define HFCUSB_PCM_RX 7
+
+/*
+ * used to switch snd_transfer_mode for different TA modes e.g. the Billion USB TA just
+ * supports ISO out, while the Cologne Chip EVAL TA just supports BULK out
+ */
+#define USB_INT 0
+#define USB_BULK 1
+#define USB_ISOC 2
+
+#define ISOC_PACKETS_D 8
+#define ISOC_PACKETS_B 8
+#define ISO_BUFFER_SIZE 128
+
+/* Fifo flow Control for TX ISO */
+#define SINK_MAX 68
+#define SINK_MIN 48
+#define SINK_DMIN 12
+#define SINK_DMAX 18
+#define BITLINE_INF (-64*8)
+
+/* HFC-S USB register access by Control-URSs */
+#define write_usb(a,b,c)usb_control_msg((a)->dev,(a)->ctrl_out_pipe,0,0x40,(c),(b),NULL,0,HFC_CTRL_TIMEOUT)
+#define read_usb(a,b,c) usb_control_msg((a)->dev,(a)->ctrl_in_pipe,1,0xC0,0,(b),(c),1,HFC_CTRL_TIMEOUT)
+#define HFC_CTRL_BUFSIZE 32
+
+/* entry and size of output/input control buffer */
+typedef struct {
+ __u8 hfc_reg; /* register number */
+ __u8 reg_val; /* value to be written (or read) */
+ int action; /* data for action handler */
+} ctrl_buft;
+
+/* Debugging Flags */
+#define HFCUSB_DBG_INIT 0x0001
+#define HFCUSB_DBG_STATES 0x0002
+#define HFCUSB_DBG_DCHANNEL 0x0080
+#define HFCUSB_DBG_FIFO_ERR 0x4000
+#define HFCUSB_DBG_VERBOSE_USB 0x8000
+
+/*
+ * URB error codes:
+ * Used to represent a list of values and their respective symbolic names
+ */
+struct hfcusb_symbolic_list {
+ const int num;
+ const char *name;
+};
+
+static struct hfcusb_symbolic_list urb_errlist[] = {
+ {-ENOMEM, "No memory for allocation of internal structures"},
+ {-ENOSPC, "The host controller's bandwidth is already consumed"},
+ {-ENOENT, "URB was canceled by unlink_urb"},
+ {-EXDEV, "ISO transfer only partially completed"},
+ {-EAGAIN, "Too match scheduled for the future"},
+ {-ENXIO, "URB already queued"},
+ {-EFBIG, "Too much ISO frames requested"},
+ {-ENOSR, "Buffer error (overrun)"},
+ {-EPIPE, "Specified endpoint is stalled (device not responding)"},
+ {-EOVERFLOW, "Babble (bad cable?)"},
+ {-EPROTO, "Bit-stuff error (bad cable?)"},
+ {-EILSEQ, "CRC/Timeout"},
+ {-ETIMEDOUT, "NAK (device does not respond)"},
+ {-ESHUTDOWN, "Device unplugged"},
+ {-1, NULL}
+};
+
+
+/*
+ * device dependant information to support different
+ * ISDN Ta's using the HFC-S USB chip
+ */
+
+/* USB descriptor need to contain one of the following EndPoint combination: */
+#define CNF_4INT3ISO 1 // 4 INT IN, 3 ISO OUT
+#define CNF_3INT3ISO 2 // 3 INT IN, 3 ISO OUT
+#define CNF_4ISO3ISO 3 // 4 ISO IN, 3 ISO OUT
+#define CNF_3ISO3ISO 4 // 3 ISO IN, 3 ISO OUT
+
+#define EP_NUL 1 // Endpoint at this position not allowed
+#define EP_NOP 2 // all type of endpoints allowed at this position
+#define EP_ISO 3 // Isochron endpoint mandatory at this position
+#define EP_BLK 4 // Bulk endpoint mandatory at this position
+#define EP_INT 5 // Interrupt endpoint mandatory at this position
+
+/*
+ * List of all supported endpoint configuration sets, used to find the
+ * best matching endpoint configuration within a devices' USB descriptor.
+ * We need at least 3 RX endpoints, and 3 TX endpoints, either
+ * INT-in and ISO-out, or ISO-in and ISO-out)
+ * with 4 RX endpoints even E-Channel logging is possible
+ */
+static int validconf[][19] = {
+ // INT in, ISO out config
+ {EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NOP, EP_INT,
+ EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_NUL, EP_NUL,
+ CNF_4INT3ISO, 2, 1},
+ {EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_NUL,
+ EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_NUL, EP_NUL,
+ CNF_3INT3ISO, 2, 0},
+ // ISO in, ISO out config
+ {EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL,
+ EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_NOP, EP_ISO,
+ CNF_4ISO3ISO, 2, 1},
+ {EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL,
+ EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_NUL, EP_NUL,
+ CNF_3ISO3ISO, 2, 0},
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} // EOL element
+};
+
+#ifdef CONFIG_HISAX_DEBUG
+// string description of chosen config
+static char *conf_str[] = {
+ "4 Interrupt IN + 3 Isochron OUT",
+ "3 Interrupt IN + 3 Isochron OUT",
+ "4 Isochron IN + 3 Isochron OUT",
+ "3 Isochron IN + 3 Isochron OUT"
+};
+#endif
+
+typedef struct {
+ int vendor; // vendor id
+ int prod_id; // product id
+ char *vend_name; // vendor string
+ __u8 led_scheme; // led display scheme
+ signed short led_bits[8]; // array of 8 possible LED bitmask settings
+} vendor_data;
+
+#define LED_OFF 0 // no LED support
+#define LED_SCHEME1 1 // LED standard scheme
+#define LED_SCHEME2 2 // not used yet...
+
+#define LED_POWER_ON 1
+#define LED_POWER_OFF 2
+#define LED_S0_ON 3
+#define LED_S0_OFF 4
+#define LED_B1_ON 5
+#define LED_B1_OFF 6
+#define LED_B1_DATA 7
+#define LED_B2_ON 8
+#define LED_B2_OFF 9
+#define LED_B2_DATA 10
+
+#define LED_NORMAL 0 // LEDs are normal
+#define LED_INVERTED 1 // LEDs are inverted
+
+
+#endif // __HFC_USB_H__
diff --git a/drivers/isdn/hisax/hfcscard.c b/drivers/isdn/hisax/hfcscard.c
new file mode 100644
index 0000000..cf08266
--- /dev/null
+++ b/drivers/isdn/hisax/hfcscard.c
@@ -0,0 +1,265 @@
+/* $Id: hfcscard.c,v 1.10.2.4 2004/01/14 16:04:48 keil Exp $
+ *
+ * low level stuff for hfcs based cards (Teles3c, ACER P10)
+ *
+ * Author Karsten Keil
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/isapnp.h>
+#include "hisax.h"
+#include "hfc_2bds0.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+
+static const char *hfcs_revision = "$Revision: 1.10.2.4 $";
+
+static irqreturn_t
+hfcs_interrupt(int intno, void *dev_id)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char val, stat;
+ u_long flags;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ if ((HFCD_ANYINT | HFCD_BUSY_NBUSY) &
+ (stat = cs->BC_Read_Reg(cs, HFCD_DATA, HFCD_STAT))) {
+ val = cs->BC_Read_Reg(cs, HFCD_DATA, HFCD_INT_S1);
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "HFCS: stat(%02x) s1(%02x)", stat, val);
+ hfc2bds0_interrupt(cs, val);
+ } else {
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "HFCS: irq_no_irq stat(%02x)", stat);
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static void
+hfcs_Timer(struct IsdnCardState *cs)
+{
+ cs->hw.hfcD.timer.expires = jiffies + 75;
+ /* WD RESET */
+/* WriteReg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt | 0x80);
+ add_timer(&cs->hw.hfcD.timer);
+*/
+}
+
+static void
+release_io_hfcs(struct IsdnCardState *cs)
+{
+ release2bds0(cs);
+ del_timer(&cs->hw.hfcD.timer);
+ if (cs->hw.hfcD.addr)
+ release_region(cs->hw.hfcD.addr, 2);
+}
+
+static void
+reset_hfcs(struct IsdnCardState *cs)
+{
+ printk(KERN_INFO "HFCS: resetting card\n");
+ cs->hw.hfcD.cirm = HFCD_RESET;
+ if (cs->typ == ISDN_CTYPE_TELES3C)
+ cs->hw.hfcD.cirm |= HFCD_MEM8K;
+ cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm); /* Reset On */
+ mdelay(10);
+ cs->hw.hfcD.cirm = 0;
+ if (cs->typ == ISDN_CTYPE_TELES3C)
+ cs->hw.hfcD.cirm |= HFCD_MEM8K;
+ cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm); /* Reset Off */
+ mdelay(10);
+ if (cs->typ == ISDN_CTYPE_TELES3C)
+ cs->hw.hfcD.cirm |= HFCD_INTB;
+ else if (cs->typ == ISDN_CTYPE_ACERP10)
+ cs->hw.hfcD.cirm |= HFCD_INTA;
+ cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CIRM, cs->hw.hfcD.cirm);
+ cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CLKDEL, 0x0e);
+ cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_TEST, HFCD_AUTO_AWAKE); /* S/T Auto awake */
+ cs->hw.hfcD.ctmt = HFCD_TIM25 | HFCD_AUTO_TIMER;
+ cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt);
+ cs->hw.hfcD.int_m2 = HFCD_IRQ_ENABLE;
+ cs->hw.hfcD.int_m1 = HFCD_INTS_B1TRANS | HFCD_INTS_B2TRANS |
+ HFCD_INTS_DTRANS | HFCD_INTS_B1REC | HFCD_INTS_B2REC |
+ HFCD_INTS_DREC | HFCD_INTS_L1STATE;
+ cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_INT_M1, cs->hw.hfcD.int_m1);
+ cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_INT_M2, cs->hw.hfcD.int_m2);
+ cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_STATES, HFCD_LOAD_STATE | 2); /* HFC ST 2 */
+ udelay(10);
+ cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_STATES, 2); /* HFC ST 2 */
+ cs->hw.hfcD.mst_m = HFCD_MASTER;
+ cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_MST_MODE, cs->hw.hfcD.mst_m); /* HFC Master */
+ cs->hw.hfcD.sctrl = 0;
+ cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_SCTRL, cs->hw.hfcD.sctrl);
+}
+
+static int
+hfcs_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ u_long flags;
+ int delay;
+
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "HFCS: card_msg %x", mt);
+ switch (mt) {
+ case CARD_RESET:
+ spin_lock_irqsave(&cs->lock, flags);
+ reset_hfcs(cs);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return(0);
+ case CARD_RELEASE:
+ release_io_hfcs(cs);
+ return(0);
+ case CARD_INIT:
+ delay = (75*HZ)/100 +1;
+ mod_timer(&cs->hw.hfcD.timer, jiffies + delay);
+ spin_lock_irqsave(&cs->lock, flags);
+ reset_hfcs(cs);
+ init2bds0(cs);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ delay = (80*HZ)/1000 +1;
+ msleep(80);
+ spin_lock_irqsave(&cs->lock, flags);
+ cs->hw.hfcD.ctmt |= HFCD_TIM800;
+ cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcD.ctmt);
+ cs->BC_Write_Reg(cs, HFCD_DATA, HFCD_MST_MODE, cs->hw.hfcD.mst_m);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return(0);
+ case CARD_TEST:
+ return(0);
+ }
+ return(0);
+}
+
+#ifdef __ISAPNP__
+static struct isapnp_device_id hfc_ids[] __devinitdata = {
+ { ISAPNP_VENDOR('A', 'N', 'X'), ISAPNP_FUNCTION(0x1114),
+ ISAPNP_VENDOR('A', 'N', 'X'), ISAPNP_FUNCTION(0x1114),
+ (unsigned long) "Acer P10" },
+ { ISAPNP_VENDOR('B', 'I', 'L'), ISAPNP_FUNCTION(0x0002),
+ ISAPNP_VENDOR('B', 'I', 'L'), ISAPNP_FUNCTION(0x0002),
+ (unsigned long) "Billion 2" },
+ { ISAPNP_VENDOR('B', 'I', 'L'), ISAPNP_FUNCTION(0x0001),
+ ISAPNP_VENDOR('B', 'I', 'L'), ISAPNP_FUNCTION(0x0001),
+ (unsigned long) "Billion 1" },
+ { ISAPNP_VENDOR('T', 'A', 'G'), ISAPNP_FUNCTION(0x7410),
+ ISAPNP_VENDOR('T', 'A', 'G'), ISAPNP_FUNCTION(0x7410),
+ (unsigned long) "IStar PnP" },
+ { ISAPNP_VENDOR('T', 'A', 'G'), ISAPNP_FUNCTION(0x2610),
+ ISAPNP_VENDOR('T', 'A', 'G'), ISAPNP_FUNCTION(0x2610),
+ (unsigned long) "Teles 16.3c" },
+ { ISAPNP_VENDOR('S', 'F', 'M'), ISAPNP_FUNCTION(0x0001),
+ ISAPNP_VENDOR('S', 'F', 'M'), ISAPNP_FUNCTION(0x0001),
+ (unsigned long) "Tornado Tipa C" },
+ { ISAPNP_VENDOR('K', 'Y', 'E'), ISAPNP_FUNCTION(0x0001),
+ ISAPNP_VENDOR('K', 'Y', 'E'), ISAPNP_FUNCTION(0x0001),
+ (unsigned long) "Genius Speed Surfer" },
+ { 0, }
+};
+
+static struct isapnp_device_id *ipid __devinitdata = &hfc_ids[0];
+static struct pnp_card *pnp_c __devinitdata = NULL;
+#endif
+
+int __devinit
+setup_hfcs(struct IsdnCard *card)
+{
+ struct IsdnCardState *cs = card->cs;
+ char tmp[64];
+
+ strcpy(tmp, hfcs_revision);
+ printk(KERN_INFO "HiSax: HFC-S driver Rev. %s\n", HiSax_getrev(tmp));
+
+#ifdef __ISAPNP__
+ if (!card->para[1] && isapnp_present()) {
+ struct pnp_dev *pnp_d;
+ while(ipid->card_vendor) {
+ if ((pnp_c = pnp_find_card(ipid->card_vendor,
+ ipid->card_device, pnp_c))) {
+ pnp_d = NULL;
+ if ((pnp_d = pnp_find_dev(pnp_c,
+ ipid->vendor, ipid->function, pnp_d))) {
+ int err;
+
+ printk(KERN_INFO "HiSax: %s detected\n",
+ (char *)ipid->driver_data);
+ pnp_disable_dev(pnp_d);
+ err = pnp_activate_dev(pnp_d);
+ if (err<0) {
+ printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
+ __func__, err);
+ return(0);
+ }
+ card->para[1] = pnp_port_start(pnp_d, 0);
+ card->para[0] = pnp_irq(pnp_d, 0);
+ if (!card->para[0] || !card->para[1]) {
+ printk(KERN_ERR "HFC PnP:some resources are missing %ld/%lx\n",
+ card->para[0], card->para[1]);
+ pnp_disable_dev(pnp_d);
+ return(0);
+ }
+ break;
+ } else {
+ printk(KERN_ERR "HFC PnP: PnP error card found, no device\n");
+ }
+ }
+ ipid++;
+ pnp_c = NULL;
+ }
+ if (!ipid->card_vendor) {
+ printk(KERN_INFO "HFC PnP: no ISAPnP card found\n");
+ return(0);
+ }
+ }
+#endif
+ cs->hw.hfcD.addr = card->para[1] & 0xfffe;
+ cs->irq = card->para[0];
+ cs->hw.hfcD.cip = 0;
+ cs->hw.hfcD.int_s1 = 0;
+ cs->hw.hfcD.send = NULL;
+ cs->bcs[0].hw.hfc.send = NULL;
+ cs->bcs[1].hw.hfc.send = NULL;
+ cs->hw.hfcD.dfifosize = 512;
+ cs->dc.hfcd.ph_state = 0;
+ cs->hw.hfcD.fifo = 255;
+ if (cs->typ == ISDN_CTYPE_TELES3C) {
+ cs->hw.hfcD.bfifosize = 1024 + 512;
+ } else if (cs->typ == ISDN_CTYPE_ACERP10) {
+ cs->hw.hfcD.bfifosize = 7*1024 + 512;
+ } else
+ return (0);
+ if (!request_region(cs->hw.hfcD.addr, 2, "HFCS isdn")) {
+ printk(KERN_WARNING
+ "HiSax: %s config port %x-%x already in use\n",
+ CardType[card->typ],
+ cs->hw.hfcD.addr,
+ cs->hw.hfcD.addr + 2);
+ return (0);
+ }
+ printk(KERN_INFO
+ "HFCS: defined at 0x%x IRQ %d HZ %d\n",
+ cs->hw.hfcD.addr,
+ cs->irq, HZ);
+ if (cs->typ == ISDN_CTYPE_TELES3C) {
+ /* Teles 16.3c IO ADR is 0x200 | YY0U (YY Bit 15/14 address) */
+ outb(0x00, cs->hw.hfcD.addr);
+ outb(0x56, cs->hw.hfcD.addr | 1);
+ } else if (cs->typ == ISDN_CTYPE_ACERP10) {
+ /* Acer P10 IO ADR is 0x300 */
+ outb(0x00, cs->hw.hfcD.addr);
+ outb(0x57, cs->hw.hfcD.addr | 1);
+ }
+ set_cs_func(cs);
+ cs->hw.hfcD.timer.function = (void *) hfcs_Timer;
+ cs->hw.hfcD.timer.data = (long) cs;
+ init_timer(&cs->hw.hfcD.timer);
+ cs->cardmsg = &hfcs_card_msg;
+ cs->irq_func = &hfcs_interrupt;
+ return (1);
+}
diff --git a/drivers/isdn/hisax/hisax.h b/drivers/isdn/hisax/hisax.h
new file mode 100644
index 0000000..e8d429f
--- /dev/null
+++ b/drivers/isdn/hisax/hisax.h
@@ -0,0 +1,1316 @@
+/* $Id: hisax.h,v 2.64.2.4 2004/02/11 13:21:33 keil Exp $
+ *
+ * Basic declarations, defines and prototypes
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/isdnif.h>
+#include <linux/tty.h>
+#include <linux/serial_reg.h>
+#include <linux/netdevice.h>
+
+#define ERROR_STATISTIC
+
+#define REQUEST 0
+#define CONFIRM 1
+#define INDICATION 2
+#define RESPONSE 3
+
+#define HW_ENABLE 0x0000
+#define HW_RESET 0x0004
+#define HW_POWERUP 0x0008
+#define HW_ACTIVATE 0x0010
+#define HW_DEACTIVATE 0x0018
+
+#define HW_INFO1 0x0010
+#define HW_INFO2 0x0020
+#define HW_INFO3 0x0030
+#define HW_INFO4 0x0040
+#define HW_INFO4_P8 0x0040
+#define HW_INFO4_P10 0x0048
+#define HW_RSYNC 0x0060
+#define HW_TESTLOOP 0x0070
+#define CARD_RESET 0x00F0
+#define CARD_INIT 0x00F2
+#define CARD_RELEASE 0x00F3
+#define CARD_TEST 0x00F4
+#define CARD_AUX_IND 0x00F5
+
+#define PH_ACTIVATE 0x0100
+#define PH_DEACTIVATE 0x0110
+#define PH_DATA 0x0120
+#define PH_PULL 0x0130
+#define PH_TESTLOOP 0x0140
+#define PH_PAUSE 0x0150
+#define MPH_ACTIVATE 0x0180
+#define MPH_DEACTIVATE 0x0190
+#define MPH_INFORMATION 0x01A0
+
+#define DL_ESTABLISH 0x0200
+#define DL_RELEASE 0x0210
+#define DL_DATA 0x0220
+#define DL_FLUSH 0x0224
+#define DL_UNIT_DATA 0x0230
+
+#define MDL_BC_RELEASE 0x0278 // Formula-n enter:now
+#define MDL_BC_ASSIGN 0x027C // Formula-n enter:now
+#define MDL_ASSIGN 0x0280
+#define MDL_REMOVE 0x0284
+#define MDL_ERROR 0x0288
+#define MDL_INFO_SETUP 0x02E0
+#define MDL_INFO_CONN 0x02E4
+#define MDL_INFO_REL 0x02E8
+
+#define CC_SETUP 0x0300
+#define CC_RESUME 0x0304
+#define CC_MORE_INFO 0x0310
+#define CC_IGNORE 0x0320
+#define CC_REJECT 0x0324
+#define CC_SETUP_COMPL 0x0330
+#define CC_PROCEEDING 0x0340
+#define CC_ALERTING 0x0344
+#define CC_PROGRESS 0x0348
+#define CC_CONNECT 0x0350
+#define CC_CHARGE 0x0354
+#define CC_NOTIFY 0x0358
+#define CC_DISCONNECT 0x0360
+#define CC_RELEASE 0x0368
+#define CC_SUSPEND 0x0370
+#define CC_PROCEED_SEND 0x0374
+#define CC_REDIR 0x0378
+#define CC_T302 0x0382
+#define CC_T303 0x0383
+#define CC_T304 0x0384
+#define CC_T305 0x0385
+#define CC_T308_1 0x0388
+#define CC_T308_2 0x038A
+#define CC_T309 0x0309
+#define CC_T310 0x0390
+#define CC_T313 0x0393
+#define CC_T318 0x0398
+#define CC_T319 0x0399
+#define CC_TSPID 0x03A0
+#define CC_NOSETUP_RSP 0x03E0
+#define CC_SETUP_ERR 0x03E1
+#define CC_SUSPEND_ERR 0x03E2
+#define CC_RESUME_ERR 0x03E3
+#define CC_CONNECT_ERR 0x03E4
+#define CC_RELEASE_ERR 0x03E5
+#define CC_RESTART 0x03F4
+#define CC_TDSS1_IO 0x13F4 /* DSS1 IO user timer */
+#define CC_TNI1_IO 0x13F5 /* NI1 IO user timer */
+
+/* define maximum number of possible waiting incoming calls */
+#define MAX_WAITING_CALLS 2
+
+
+#ifdef __KERNEL__
+
+/* include l3dss1 & ni1 specific process structures, but no other defines */
+#ifdef CONFIG_HISAX_EURO
+ #define l3dss1_process
+ #include "l3dss1.h"
+ #undef l3dss1_process
+#endif /* CONFIG_HISAX_EURO */
+
+#ifdef CONFIG_HISAX_NI1
+ #define l3ni1_process
+ #include "l3ni1.h"
+ #undef l3ni1_process
+#endif /* CONFIG_HISAX_NI1 */
+
+#define MAX_DFRAME_LEN 260
+#define MAX_DFRAME_LEN_L1 300
+#define HSCX_BUFMAX 4096
+#define MAX_DATA_SIZE (HSCX_BUFMAX - 4)
+#define MAX_DATA_MEM (HSCX_BUFMAX + 64)
+#define RAW_BUFMAX (((HSCX_BUFMAX*6)/5) + 5)
+#define MAX_HEADER_LEN 4
+#define MAX_WINDOW 8
+#define MAX_MON_FRAME 32
+#define MAX_DLOG_SPACE 2048
+#define MAX_BLOG_SPACE 256
+
+/* #define I4L_IRQ_FLAG SA_INTERRUPT */
+#define I4L_IRQ_FLAG 0
+
+/*
+ * 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;
+};
+
+struct L3Timer {
+ struct l3_process *pc;
+ struct timer_list tl;
+ int event;
+};
+
+#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
+
+struct Layer1 {
+ void *hardware;
+ struct BCState *bcs;
+ struct PStack **stlistp;
+ unsigned long Flags;
+ struct FsmInst l1m;
+ struct FsmTimer timer;
+ void (*l1l2) (struct PStack *, int, void *);
+ void (*l1hw) (struct PStack *, int, void *);
+ void (*l1tei) (struct PStack *, int, void *);
+ int mode, bc;
+ int delay;
+};
+
+#define GROUP_TEI 127
+#define TEI_SAPI 63
+#define CTRL_SAPI 0
+#define PACKET_NOACK 7
+
+/* Layer2 Flags */
+
+#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
+
+struct Layer2 {
+ int tei;
+ int sap;
+ int maxlen;
+ u_long flag;
+ spinlock_t lock;
+ u_int vs, va, vr;
+ int rc;
+ unsigned int window;
+ unsigned int sow;
+ struct sk_buff *windowar[MAX_WINDOW];
+ struct sk_buff_head i_queue;
+ struct sk_buff_head ui_queue;
+ void (*l2l1) (struct PStack *, int, void *);
+ void (*l2l3) (struct PStack *, int, void *);
+ void (*l2tei) (struct PStack *, int, void *);
+ struct FsmInst l2m;
+ struct FsmTimer t200, t203;
+ int T200, N200, T203;
+ int debug;
+ char debug_id[16];
+};
+
+struct Layer3 {
+ void (*l3l4) (struct PStack *, int, void *);
+ void (*l3ml3) (struct PStack *, int, void *);
+ void (*l3l2) (struct PStack *, int, void *);
+ struct FsmInst l3m;
+ struct FsmTimer l3m_timer;
+ struct sk_buff_head squeue;
+ struct l3_process *proc;
+ struct l3_process *global;
+ int N303;
+ int debug;
+ char debug_id[8];
+};
+
+struct LLInterface {
+ void (*l4l3) (struct PStack *, int, void *);
+ int (*l4l3_proto) (struct PStack *, isdn_ctrl *);
+ void *userdata;
+ u_long flag;
+};
+
+#define FLG_LLI_L1WAKEUP 1
+#define FLG_LLI_L2WAKEUP 2
+
+struct Management {
+ int ri;
+ struct FsmInst tei_m;
+ struct FsmTimer t202;
+ int T202, N202, debug;
+ void (*layer) (struct PStack *, int, void *);
+};
+
+#define NO_CAUSE 254
+
+struct Param {
+ u_char cause;
+ u_char loc;
+ u_char diag[6];
+ int bchannel;
+ int chargeinfo;
+ int spv; /* SPV Flag */
+ setup_parm setup; /* from isdnif.h numbers and Serviceindicator */
+ u_char moderate; /* transfer mode and rate (bearer octet 4) */
+};
+
+
+struct PStack {
+ struct PStack *next;
+ struct Layer1 l1;
+ struct Layer2 l2;
+ struct Layer3 l3;
+ struct LLInterface lli;
+ struct Management ma;
+ int protocol; /* EDSS1, 1TR6 or NI1 */
+
+ /* protocol specific data fields */
+ union
+ { u_char uuuu; /* only as dummy */
+#ifdef CONFIG_HISAX_EURO
+ dss1_stk_priv dss1; /* private dss1 data */
+#endif /* CONFIG_HISAX_EURO */
+#ifdef CONFIG_HISAX_NI1
+ ni1_stk_priv ni1; /* private ni1 data */
+#endif /* CONFIG_HISAX_NI1 */
+ } prot;
+};
+
+struct l3_process {
+ int callref;
+ int state;
+ struct L3Timer timer;
+ int N303;
+ int debug;
+ struct Param para;
+ struct Channel *chan;
+ struct PStack *st;
+ struct l3_process *next;
+ ulong redir_result;
+
+ /* protocol specific data fields */
+ union
+ { u_char uuuu; /* only when euro not defined, avoiding empty union */
+#ifdef CONFIG_HISAX_EURO
+ dss1_proc_priv dss1; /* private dss1 data */
+#endif /* CONFIG_HISAX_EURO */
+#ifdef CONFIG_HISAX_NI1
+ ni1_proc_priv ni1; /* private ni1 data */
+#endif /* CONFIG_HISAX_NI1 */
+ } prot;
+};
+
+struct hscx_hw {
+ int hscx;
+ int rcvidx;
+ int count; /* Current skb sent count */
+ u_char *rcvbuf; /* B-Channel receive Buffer */
+ u_char tsaxr0;
+ u_char tsaxr1;
+};
+
+struct w6692B_hw {
+ int bchan;
+ int rcvidx;
+ int count; /* Current skb sent count */
+ u_char *rcvbuf; /* B-Channel receive Buffer */
+};
+
+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];
+};
+
+struct isar_hw {
+ int dpath;
+ int rcvidx;
+ int txcnt;
+ 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 *rcvbuf; /* B-Channel receive Buffer */
+ u_char conmsg[16];
+ struct isar_reg *reg;
+};
+
+struct hdlc_stat_reg {
+#ifdef __BIG_ENDIAN
+ u_char fill;
+ u_char mode;
+ u_char xml;
+ u_char cmd;
+#else
+ u_char cmd;
+ u_char xml;
+ u_char mode;
+ u_char fill;
+#endif
+} __attribute__((packed));
+
+struct hdlc_hw {
+ union {
+ u_int ctrl;
+ struct hdlc_stat_reg sr;
+ } ctrl;
+ u_int stat;
+ int rcvidx;
+ int count; /* Current skb sent count */
+ u_char *rcvbuf; /* B-Channel receive Buffer */
+};
+
+struct hfcB_hw {
+ unsigned int *send;
+ int f1;
+ int f2;
+};
+
+struct tiger_hw {
+ u_int *send;
+ u_int *s_irq;
+ u_int *s_end;
+ u_int *sendp;
+ u_int *rec;
+ int free;
+ u_char *rcvbuf;
+ u_char *sendbuf;
+ u_char *sp;
+ int sendcnt;
+ u_int s_tot;
+ u_int r_bitcnt;
+ u_int r_tot;
+ u_int r_err;
+ u_int r_fcs;
+ u_char r_state;
+ u_char r_one;
+ u_char r_val;
+ u_char s_state;
+};
+
+struct amd7930_hw {
+ u_char *tx_buff;
+ u_char *rv_buff;
+ int rv_buff_in;
+ int rv_buff_out;
+ struct sk_buff *rv_skb;
+ struct hdlc_state *hdlc_state;
+ struct work_struct tq_rcv;
+ struct work_struct tq_xmt;
+};
+
+#define BC_FLG_INIT 1
+#define BC_FLG_ACTIV 2
+#define BC_FLG_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_FTI_FTS 16
+#define BC_FLG_FRH_WAIT 17
+
+#define L1_MODE_NULL 0
+#define L1_MODE_TRANS 1
+#define L1_MODE_HDLC 2
+#define L1_MODE_EXTRN 3
+#define L1_MODE_HDLC_56K 4
+#define L1_MODE_MODEM 7
+#define L1_MODE_V32 8
+#define L1_MODE_FAX 9
+
+struct BCState {
+ int channel;
+ int mode;
+ u_long Flag;
+ struct IsdnCardState *cs;
+ int tx_cnt; /* B-Channel transmit counter */
+ struct sk_buff *tx_skb; /* B-Channel transmit Buffer */
+ struct sk_buff_head rqueue; /* B-Channel receive Queue */
+ struct sk_buff_head squeue; /* B-Channel send Queue */
+ int ackcnt;
+ spinlock_t aclock;
+ struct PStack *st;
+ u_char *blog;
+ u_char *conmsg;
+ struct timer_list transbusy;
+ struct work_struct tqueue;
+ u_long event;
+ int (*BC_SetStack) (struct PStack *, struct BCState *);
+ void (*BC_Close) (struct BCState *);
+#ifdef ERROR_STATISTIC
+ int err_crc;
+ int err_tx;
+ int err_rdo;
+ int err_inv;
+#endif
+ union {
+ struct hscx_hw hscx;
+ struct hdlc_hw hdlc;
+ struct isar_hw isar;
+ struct hfcB_hw hfc;
+ struct tiger_hw tiger;
+ struct amd7930_hw amd7930;
+ struct w6692B_hw w6692;
+ struct hisax_b_if *b_if;
+ } hw;
+};
+
+struct Channel {
+ struct PStack *b_st, *d_st;
+ struct IsdnCardState *cs;
+ struct BCState *bcs;
+ int chan;
+ int incoming;
+ struct FsmInst fi;
+ struct FsmTimer drel_timer, dial_timer;
+ int debug;
+ int l2_protocol, l2_active_protocol;
+ int l3_protocol;
+ int data_open;
+ struct l3_process *proc;
+ setup_parm setup; /* from isdnif.h numbers and Serviceindicator */
+ u_long Flags; /* for remembering action done in l4 */
+ int leased;
+};
+
+struct elsa_hw {
+ struct pci_dev *dev;
+ unsigned long base;
+ unsigned int cfg;
+ unsigned int ctrl;
+ unsigned int ale;
+ unsigned int isac;
+ unsigned int itac;
+ unsigned int hscx;
+ unsigned int trig;
+ unsigned int timer;
+ unsigned int counter;
+ unsigned int status;
+ struct timer_list tl;
+ unsigned int MFlag;
+ struct BCState *bcs;
+ u_char *transbuf;
+ u_char *rcvbuf;
+ unsigned int transp;
+ unsigned int rcvp;
+ unsigned int transcnt;
+ unsigned int rcvcnt;
+ u_char IER;
+ u_char FCR;
+ u_char LCR;
+ u_char MCR;
+ u_char ctrl_reg;
+};
+
+struct teles3_hw {
+ unsigned int cfg_reg;
+ signed int isac;
+ signed int hscx[2];
+ signed int isacfifo;
+ signed int hscxfifo[2];
+};
+
+struct teles0_hw {
+ unsigned int cfg_reg;
+ void __iomem *membase;
+ unsigned long phymem;
+};
+
+struct avm_hw {
+ unsigned int cfg_reg;
+ unsigned int isac;
+ unsigned int hscx[2];
+ unsigned int isacfifo;
+ unsigned int hscxfifo[2];
+ unsigned int counter;
+ struct pci_dev *dev;
+};
+
+struct ix1_hw {
+ unsigned int cfg_reg;
+ unsigned int isac_ale;
+ unsigned int isac;
+ unsigned int hscx_ale;
+ unsigned int hscx;
+};
+
+struct diva_hw {
+ unsigned long cfg_reg;
+ unsigned long pci_cfg;
+ unsigned int ctrl;
+ unsigned long isac_adr;
+ unsigned int isac;
+ unsigned long hscx_adr;
+ unsigned int hscx;
+ unsigned int status;
+ struct timer_list tl;
+ u_char ctrl_reg;
+ struct pci_dev *dev;
+};
+
+struct asus_hw {
+ unsigned int cfg_reg;
+ unsigned int adr;
+ unsigned int isac;
+ unsigned int hscx;
+ unsigned int u7;
+ unsigned int pots;
+};
+
+
+struct hfc_hw {
+ unsigned int addr;
+ unsigned int fifosize;
+ unsigned char cirm;
+ unsigned char ctmt;
+ unsigned char cip;
+ u_char isac_spcr;
+ struct timer_list timer;
+};
+
+struct sedl_hw {
+ unsigned int cfg_reg;
+ unsigned int adr;
+ unsigned int isac;
+ unsigned int hscx;
+ unsigned int reset_on;
+ unsigned int reset_off;
+ struct isar_reg isar;
+ unsigned int chip;
+ unsigned int bus;
+ struct pci_dev *dev;
+};
+
+struct spt_hw {
+ unsigned int cfg_reg;
+ unsigned int isac;
+ unsigned int hscx[2];
+ unsigned char res_irq;
+};
+
+struct mic_hw {
+ unsigned int cfg_reg;
+ unsigned int adr;
+ unsigned int isac;
+ unsigned int hscx;
+};
+
+struct njet_hw {
+ unsigned long base;
+ unsigned int isac;
+ unsigned int auxa;
+ unsigned char auxd;
+ unsigned char dmactrl;
+ unsigned char ctrl_reg;
+ unsigned char irqmask0;
+ unsigned char irqstat0;
+ unsigned char last_is0;
+ struct pci_dev *dev;
+};
+
+struct hfcPCI_hw {
+ unsigned char cirm;
+ unsigned char ctmt;
+ unsigned char conn;
+ unsigned char mst_m;
+ unsigned char int_m1;
+ unsigned char int_m2;
+ unsigned char int_s1;
+ unsigned char sctrl;
+ unsigned char sctrl_r;
+ unsigned char sctrl_e;
+ unsigned char trm;
+ unsigned char stat;
+ unsigned char fifo;
+ 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;
+};
+
+struct hfcSX_hw {
+ unsigned long base;
+ unsigned char cirm;
+ unsigned char ctmt;
+ unsigned char conn;
+ unsigned char mst_m;
+ unsigned char int_m1;
+ unsigned char int_m2;
+ unsigned char int_s1;
+ unsigned char sctrl;
+ unsigned char sctrl_r;
+ unsigned char sctrl_e;
+ unsigned char trm;
+ unsigned char stat;
+ unsigned char fifo;
+ unsigned char bswapped;
+ unsigned char nt_mode;
+ unsigned char chip;
+ int b_fifo_size;
+ unsigned char last_fifo;
+ void *extra;
+ int nt_timer;
+ struct timer_list timer;
+};
+
+struct hfcD_hw {
+ unsigned int addr;
+ unsigned int bfifosize;
+ unsigned int dfifosize;
+ unsigned char cirm;
+ unsigned char ctmt;
+ unsigned char cip;
+ unsigned char conn;
+ unsigned char mst_m;
+ unsigned char int_m1;
+ unsigned char int_m2;
+ unsigned char int_s1;
+ unsigned char sctrl;
+ unsigned char stat;
+ unsigned char fifo;
+ unsigned char f1;
+ unsigned char f2;
+ unsigned int *send;
+ struct timer_list timer;
+};
+
+struct isurf_hw {
+ unsigned int reset;
+ unsigned long phymem;
+ void __iomem *isac;
+ void __iomem *isar;
+ struct isar_reg isar_r;
+};
+
+struct saphir_hw {
+ struct pci_dev *dev;
+ unsigned int cfg_reg;
+ unsigned int ale;
+ unsigned int isac;
+ unsigned int hscx;
+ struct timer_list timer;
+};
+
+struct bkm_hw {
+ struct pci_dev *dev;
+ unsigned long base;
+ /* A4T stuff */
+ unsigned long isac_adr;
+ unsigned int isac_ale;
+ unsigned long jade_adr;
+ unsigned int jade_ale;
+ /* Scitel Quadro stuff */
+ unsigned long plx_adr;
+ unsigned long data_adr;
+};
+
+struct gazel_hw {
+ struct pci_dev *dev;
+ unsigned int cfg_reg;
+ unsigned int pciaddr[2];
+ signed int ipac;
+ signed int isac;
+ signed int hscx[2];
+ signed int isacfifo;
+ signed int hscxfifo[2];
+ unsigned char timeslot;
+ unsigned char iom2;
+};
+
+struct w6692_hw {
+ struct pci_dev *dev;
+ unsigned int iobase;
+ struct timer_list timer;
+};
+
+struct arcofi_msg {
+ struct arcofi_msg *next;
+ u_char receive;
+ u_char len;
+ u_char msg[10];
+};
+
+struct isac_chip {
+ int ph_state;
+ 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;
+};
+
+struct hfcd_chip {
+ int ph_state;
+};
+
+struct hfcpci_chip {
+ int ph_state;
+};
+
+struct hfcsx_chip {
+ int ph_state;
+};
+
+struct w6692_chip {
+ int ph_state;
+};
+
+struct amd7930_chip {
+ u_char lmr1;
+ u_char ph_state;
+ u_char old_state;
+ u_char flg_t3;
+ unsigned int tx_xmtlen;
+ struct timer_list timer3;
+ void (*ph_command) (struct IsdnCardState *, u_char, char *);
+ void (*setIrqMask) (struct IsdnCardState *, u_char);
+};
+
+struct icc_chip {
+ int ph_state;
+ 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;
+};
+
+#define HW_IOM1 0
+#define HW_IPAC 1
+#define HW_ISAR 2
+#define HW_ARCOFI 3
+#define FLG_TWO_DCHAN 4
+#define FLG_L1_DBUSY 5
+#define FLG_DBUSY_TIMER 6
+#define FLG_LOCK_ATOMIC 7
+#define FLG_ARCOFI_TIMER 8
+#define FLG_ARCOFI_ERROR 9
+#define FLG_HW_L1_UINT 10
+
+struct IsdnCardState {
+ spinlock_t lock;
+ u_char typ;
+ u_char subtyp;
+ int protocol;
+ u_int irq;
+ u_long irq_flags;
+ u_long HW_Flags;
+ int *busy_flag;
+ int chanlimit; /* limited number of B-chans to use */
+ int logecho; /* log echo if supported by card */
+ union {
+ struct elsa_hw elsa;
+ struct teles0_hw teles0;
+ struct teles3_hw teles3;
+ struct avm_hw avm;
+ struct ix1_hw ix1;
+ struct diva_hw diva;
+ struct asus_hw asus;
+ struct hfc_hw hfc;
+ struct sedl_hw sedl;
+ struct spt_hw spt;
+ struct mic_hw mic;
+ struct njet_hw njet;
+ struct hfcD_hw hfcD;
+ struct hfcPCI_hw hfcpci;
+ struct hfcSX_hw hfcsx;
+ struct ix1_hw niccy;
+ struct isurf_hw isurf;
+ struct saphir_hw saphir;
+ struct bkm_hw ax;
+ struct gazel_hw gazel;
+ struct w6692_hw w6692;
+ struct hisax_d_if *hisax_d_if;
+ } hw;
+ int myid;
+ isdn_if iif;
+ spinlock_t statlock;
+ u_char *status_buf;
+ u_char *status_read;
+ u_char *status_write;
+ u_char *status_end;
+ u_char (*readisac) (struct IsdnCardState *, u_char);
+ void (*writeisac) (struct IsdnCardState *, u_char, u_char);
+ void (*readisacfifo) (struct IsdnCardState *, u_char *, int);
+ void (*writeisacfifo) (struct IsdnCardState *, u_char *, int);
+ u_char (*BC_Read_Reg) (struct IsdnCardState *, int, u_char);
+ void (*BC_Write_Reg) (struct IsdnCardState *, int, u_char, u_char);
+ void (*BC_Send_Data) (struct BCState *);
+ int (*cardmsg) (struct IsdnCardState *, int, void *);
+ void (*setstack_d) (struct PStack *, struct IsdnCardState *);
+ void (*DC_Close) (struct IsdnCardState *);
+ irq_handler_t irq_func;
+ int (*auxcmd) (struct IsdnCardState *, isdn_ctrl *);
+ struct Channel channel[2+MAX_WAITING_CALLS];
+ struct BCState bcs[2+MAX_WAITING_CALLS];
+ struct PStack *stlist;
+ struct sk_buff_head rq, sq; /* D-channel queues */
+ int cardnr;
+ char *dlog;
+ int debug;
+ union {
+ struct isac_chip isac;
+ struct hfcd_chip hfcd;
+ struct hfcpci_chip hfcpci;
+ struct hfcsx_chip hfcsx;
+ struct w6692_chip w6692;
+ struct amd7930_chip amd7930;
+ struct icc_chip icc;
+ } dc;
+ u_char *rcvbuf;
+ int rcvidx;
+ struct sk_buff *tx_skb;
+ int tx_cnt;
+ u_long event;
+ struct work_struct tqueue;
+ struct timer_list dbusytimer;
+#ifdef ERROR_STATISTIC
+ int err_crc;
+ int err_tx;
+ int err_rx;
+#endif
+};
+
+
+#define schedule_event(s, ev) do {test_and_set_bit(ev, &s->event);schedule_work(&s->tqueue); } while(0)
+
+#define MON0_RX 1
+#define MON1_RX 2
+#define MON0_TX 4
+#define MON1_TX 8
+
+
+#ifdef ISDN_CHIP_ISAC
+#undef ISDN_CHIP_ISAC
+#endif
+
+#ifdef CONFIG_HISAX_16_0
+#define CARD_TELES0 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_TELES0 0
+#endif
+
+#ifdef CONFIG_HISAX_16_3
+#define CARD_TELES3 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_TELES3 0
+#endif
+
+#ifdef CONFIG_HISAX_TELESPCI
+#define CARD_TELESPCI 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_TELESPCI 0
+#endif
+
+#ifdef CONFIG_HISAX_AVM_A1
+#define CARD_AVM_A1 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_AVM_A1 0
+#endif
+
+#ifdef CONFIG_HISAX_AVM_A1_PCMCIA
+#define CARD_AVM_A1_PCMCIA 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_AVM_A1_PCMCIA 0
+#endif
+
+#ifdef CONFIG_HISAX_FRITZPCI
+#define CARD_FRITZPCI 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_FRITZPCI 0
+#endif
+
+#ifdef CONFIG_HISAX_ELSA
+#define CARD_ELSA 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_ELSA 0
+#endif
+
+#ifdef CONFIG_HISAX_IX1MICROR2
+#define CARD_IX1MICROR2 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_IX1MICROR2 0
+#endif
+
+#ifdef CONFIG_HISAX_DIEHLDIVA
+#define CARD_DIEHLDIVA 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_DIEHLDIVA 0
+#endif
+
+#ifdef CONFIG_HISAX_ASUSCOM
+#define CARD_ASUSCOM 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_ASUSCOM 0
+#endif
+
+#ifdef CONFIG_HISAX_TELEINT
+#define CARD_TELEINT 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_TELEINT 0
+#endif
+
+#ifdef CONFIG_HISAX_SEDLBAUER
+#define CARD_SEDLBAUER 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_SEDLBAUER 0
+#endif
+
+#ifdef CONFIG_HISAX_SPORTSTER
+#define CARD_SPORTSTER 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_SPORTSTER 0
+#endif
+
+#ifdef CONFIG_HISAX_MIC
+#define CARD_MIC 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_MIC 0
+#endif
+
+#ifdef CONFIG_HISAX_NETJET
+#define CARD_NETJET_S 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_NETJET_S 0
+#endif
+
+#ifdef CONFIG_HISAX_HFCS
+#define CARD_HFCS 1
+#else
+#define CARD_HFCS 0
+#endif
+
+#ifdef CONFIG_HISAX_HFC_PCI
+#define CARD_HFC_PCI 1
+#else
+#define CARD_HFC_PCI 0
+#endif
+
+#ifdef CONFIG_HISAX_HFC_SX
+#define CARD_HFC_SX 1
+#else
+#define CARD_HFC_SX 0
+#endif
+
+#ifdef CONFIG_HISAX_NICCY
+#define CARD_NICCY 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_NICCY 0
+#endif
+
+#ifdef CONFIG_HISAX_ISURF
+#define CARD_ISURF 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_ISURF 0
+#endif
+
+#ifdef CONFIG_HISAX_S0BOX
+#define CARD_S0BOX 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_S0BOX 0
+#endif
+
+#ifdef CONFIG_HISAX_HSTSAPHIR
+#define CARD_HSTSAPHIR 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_HSTSAPHIR 0
+#endif
+
+#ifdef CONFIG_HISAX_BKM_A4T
+#define CARD_BKM_A4T 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_BKM_A4T 0
+#endif
+
+#ifdef CONFIG_HISAX_SCT_QUADRO
+#define CARD_SCT_QUADRO 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_SCT_QUADRO 0
+#endif
+
+#ifdef CONFIG_HISAX_GAZEL
+#define CARD_GAZEL 1
+#ifndef ISDN_CHIP_ISAC
+#define ISDN_CHIP_ISAC 1
+#endif
+#else
+#define CARD_GAZEL 0
+#endif
+
+#ifdef CONFIG_HISAX_W6692
+#define CARD_W6692 1
+#ifndef ISDN_CHIP_W6692
+#define ISDN_CHIP_W6692 1
+#endif
+#else
+#define CARD_W6692 0
+#endif
+
+#ifdef CONFIG_HISAX_NETJET_U
+#define CARD_NETJET_U 1
+#ifndef ISDN_CHIP_ICC
+#define ISDN_CHIP_ICC 1
+#endif
+#ifndef HISAX_UINTERFACE
+#define HISAX_UINTERFACE 1
+#endif
+#else
+#define CARD_NETJET_U 0
+#endif
+
+#ifdef CONFIG_HISAX_ENTERNOW_PCI
+#define CARD_FN_ENTERNOW_PCI 1
+#else
+#define CARD_FN_ENTERNOW_PCI 0
+#endif
+
+#define TEI_PER_CARD 1
+
+/* 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
+
+#define L2FRAME_DEBUG
+
+#ifdef L2FRAME_DEBUG
+extern void Logl2Frame(struct IsdnCardState *cs, struct sk_buff *skb, char *buf, int dir);
+#endif
+
+#include "hisax_cfg.h"
+
+void init_bcstate(struct IsdnCardState *cs, int bc);
+
+void setstack_HiSax(struct PStack *st, struct IsdnCardState *cs);
+void HiSax_addlist(struct IsdnCardState *sp, struct PStack *st);
+void HiSax_rmlist(struct IsdnCardState *sp, struct PStack *st);
+
+void setstack_l1_B(struct PStack *st);
+
+void setstack_tei(struct PStack *st);
+void setstack_manager(struct PStack *st);
+
+void setstack_isdnl2(struct PStack *st, char *debug_id);
+void releasestack_isdnl2(struct PStack *st);
+void setstack_transl2(struct PStack *st);
+void releasestack_transl2(struct PStack *st);
+void lli_writewakeup(struct PStack *st, int len);
+
+void setstack_l3dc(struct PStack *st, struct Channel *chanp);
+void setstack_l3bc(struct PStack *st, struct Channel *chanp);
+void releasestack_isdnl3(struct PStack *st);
+
+u_char *findie(u_char * p, int size, u_char ie, int wanted_set);
+int getcallref(u_char * p);
+int newcallref(void);
+
+int FsmNew(struct Fsm *fsm, struct FsmNode *fnlist, int fncount);
+void FsmFree(struct Fsm *fsm);
+int FsmEvent(struct FsmInst *fi, int event, void *arg);
+void FsmChangeState(struct FsmInst *fi, int newstate);
+void FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft);
+int FsmAddTimer(struct FsmTimer *ft, int millisec, int event,
+ void *arg, int where);
+void FsmRestartTimer(struct FsmTimer *ft, int millisec, int event,
+ void *arg, int where);
+void FsmDelTimer(struct FsmTimer *ft, int where);
+int jiftime(char *s, long mark);
+
+int HiSax_command(isdn_ctrl * ic);
+int HiSax_writebuf_skb(int id, int chan, int ack, struct sk_buff *skb);
+void HiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, ...);
+void VHiSax_putstatus(struct IsdnCardState *cs, char *head, char *fmt, va_list args);
+void HiSax_reportcard(int cardnr, int sel);
+int QuickHex(char *txt, u_char * p, int cnt);
+void LogFrame(struct IsdnCardState *cs, u_char * p, int size);
+void dlogframe(struct IsdnCardState *cs, struct sk_buff *skb, int dir);
+void iecpy(u_char * dest, u_char * iestart, int ieoffset);
+#endif /* __KERNEL__ */
+
+/*
+ * Busywait delay for `jiffs' jiffies
+ */
+#define HZDELAY(jiffs) do { \
+ int tout = jiffs; \
+ \
+ while (tout--) { \
+ int loops = USEC_PER_SEC / HZ; \
+ while (loops--) \
+ udelay(1); \
+ } \
+ } while (0)
+
+int ll_run(struct IsdnCardState *cs, int addfeatures);
+int CallcNew(void);
+void CallcFree(void);
+int CallcNewChan(struct IsdnCardState *cs);
+void CallcFreeChan(struct IsdnCardState *cs);
+int Isdnl1New(void);
+void Isdnl1Free(void);
+int Isdnl2New(void);
+void Isdnl2Free(void);
+int Isdnl3New(void);
+void Isdnl3Free(void);
+void init_tei(struct IsdnCardState *cs, int protocol);
+void release_tei(struct IsdnCardState *cs);
+char *HiSax_getrev(const char *revision);
+int TeiNew(void);
+void TeiFree(void);
diff --git a/drivers/isdn/hisax/hisax_cfg.h b/drivers/isdn/hisax/hisax_cfg.h
new file mode 100644
index 0000000..17a2fea
--- /dev/null
+++ b/drivers/isdn/hisax/hisax_cfg.h
@@ -0,0 +1,66 @@
+/* $Id: hisax_cfg.h,v 1.1.2.1 2004/01/24 20:47:23 keil Exp $
+ * define of the basic HiSax configuration structures
+ * and pcmcia interface
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#define ISDN_CTYPE_16_0 1
+#define ISDN_CTYPE_8_0 2
+#define ISDN_CTYPE_16_3 3
+#define ISDN_CTYPE_PNP 4
+#define ISDN_CTYPE_A1 5
+#define ISDN_CTYPE_ELSA 6
+#define ISDN_CTYPE_ELSA_PNP 7
+#define ISDN_CTYPE_TELESPCMCIA 8
+#define ISDN_CTYPE_IX1MICROR2 9
+#define ISDN_CTYPE_ELSA_PCMCIA 10
+#define ISDN_CTYPE_DIEHLDIVA 11
+#define ISDN_CTYPE_ASUSCOM 12
+#define ISDN_CTYPE_TELEINT 13
+#define ISDN_CTYPE_TELES3C 14
+#define ISDN_CTYPE_SEDLBAUER 15
+#define ISDN_CTYPE_SPORTSTER 16
+#define ISDN_CTYPE_MIC 17
+#define ISDN_CTYPE_ELSA_PCI 18
+#define ISDN_CTYPE_COMPAQ_ISA 19
+#define ISDN_CTYPE_NETJET_S 20
+#define ISDN_CTYPE_TELESPCI 21
+#define ISDN_CTYPE_SEDLBAUER_PCMCIA 22
+#define ISDN_CTYPE_AMD7930 23
+#define ISDN_CTYPE_NICCY 24
+#define ISDN_CTYPE_S0BOX 25
+#define ISDN_CTYPE_A1_PCMCIA 26
+#define ISDN_CTYPE_FRITZPCI 27
+#define ISDN_CTYPE_SEDLBAUER_FAX 28
+#define ISDN_CTYPE_ISURF 29
+#define ISDN_CTYPE_ACERP10 30
+#define ISDN_CTYPE_HSTSAPHIR 31
+#define ISDN_CTYPE_BKM_A4T 32
+#define ISDN_CTYPE_SCT_QUADRO 33
+#define ISDN_CTYPE_GAZEL 34
+#define ISDN_CTYPE_HFC_PCI 35
+#define ISDN_CTYPE_W6692 36
+#define ISDN_CTYPE_HFC_SX 37
+#define ISDN_CTYPE_NETJET_U 38
+#define ISDN_CTYPE_HFC_SP_PCMCIA 39
+#define ISDN_CTYPE_DYNAMIC 40
+#define ISDN_CTYPE_ENTERNOW 41
+#define ISDN_CTYPE_COUNT 41
+
+typedef struct IsdnCardState IsdnCardState_t;
+typedef struct IsdnCard IsdnCard_t;
+
+struct IsdnCard {
+ int typ;
+ int protocol; /* EDSS1, 1TR6 or NI1 */
+ unsigned long para[4];
+ IsdnCardState_t *cs;
+};
+
+typedef int (*hisax_setup_func_t)(struct IsdnCard *card);
+
+extern void HiSax_closecard(int);
+extern int hisax_init_pcmcia(void *, int *, IsdnCard_t *);
diff --git a/drivers/isdn/hisax/hisax_debug.h b/drivers/isdn/hisax/hisax_debug.h
new file mode 100644
index 0000000..5ed3b1c
--- /dev/null
+++ b/drivers/isdn/hisax/hisax_debug.h
@@ -0,0 +1,80 @@
+/*
+ * Common debugging macros for use with the hisax driver
+ *
+ * Author Frode Isaksen
+ * Copyright 2001 by Frode Isaksen <fisaksen@bewan.com>
+ * 2001 by Kai Germaschewski <kai.germaschewski@gmx.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * How to use:
+ *
+ * Before including this file, you need to
+ * #define __debug_variable my_debug
+ * where my_debug is a variable in your code which
+ * determines the debug bitmask.
+ *
+ * If CONFIG_HISAX_DEBUG is not set, all macros evaluate to nothing
+ *
+ */
+
+#ifndef __HISAX_DEBUG_H__
+#define __HISAX_DEBUG_H__
+
+
+#ifdef CONFIG_HISAX_DEBUG
+
+#define DBG(level, format, arg...) do { \
+if (level & __debug_variable) \
+printk(KERN_DEBUG "%s: " format "\n" , __func__ , ## arg); \
+} while (0)
+
+#define DBG_PACKET(level,data,count) \
+ if (level & __debug_variable) dump_packet(__func__,data,count)
+
+#define DBG_SKB(level,skb) \
+ if ((level & __debug_variable) && skb) dump_packet(__func__,skb->data,skb->len)
+
+
+static void __attribute__((unused))
+dump_packet(const char *name,const u_char *data,int pkt_len)
+{
+#define DUMP_HDR_SIZE 20
+#define DUMP_TLR_SIZE 8
+ if (pkt_len) {
+ int i,len1,len2;
+
+ printk(KERN_DEBUG "%s: length=%d,data=",name,pkt_len);
+
+ if (pkt_len > DUMP_HDR_SIZE+ DUMP_TLR_SIZE) {
+ len1 = DUMP_HDR_SIZE;
+ len2 = DUMP_TLR_SIZE;
+ } else {
+ len1 = pkt_len > DUMP_HDR_SIZE ? DUMP_HDR_SIZE : pkt_len;
+ len2 = 0;
+ }
+ for (i = 0; i < len1; ++i) {
+ printk ("%.2x", data[i]);
+ }
+ if (len2) {
+ printk ("..");
+ for (i = pkt_len-DUMP_TLR_SIZE; i < pkt_len; ++i) {
+ printk ("%.2x", data[i]);
+ }
+ }
+ printk ("\n");
+ }
+#undef DUMP_HDR_SIZE
+#undef DUMP_TLR_SIZE
+}
+
+#else
+
+#define DBG(level, format, arg...) do {} while (0)
+#define DBG_PACKET(level,data,count) do {} while (0)
+#define DBG_SKB(level,skb) do {} while (0)
+
+#endif
+
+#endif
diff --git a/drivers/isdn/hisax/hisax_fcpcipnp.c b/drivers/isdn/hisax/hisax_fcpcipnp.c
new file mode 100644
index 0000000..1925118
--- /dev/null
+++ b/drivers/isdn/hisax/hisax_fcpcipnp.c
@@ -0,0 +1,1023 @@
+/*
+ * Driver for AVM Fritz!PCI, Fritz!PCI v2, Fritz!PnP ISDN cards
+ *
+ * Author Kai Germaschewski
+ * Copyright 2001 by Kai Germaschewski <kai.germaschewski@gmx.de>
+ * 2001 by Karsten Keil <keil@isdn4linux.de>
+ *
+ * based upon Karsten Keil's original avm_pci.c driver
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to Wizard Computersysteme GmbH, Bremervoerde and
+ * SoHaNet Technology GmbH, Berlin
+ * for supporting the development of this driver
+ */
+
+
+/* TODO:
+ *
+ * o POWER PC
+ * o clean up debugging
+ * o tx_skb at PH_DEACTIVATE time
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/isapnp.h>
+#include <linux/kmod.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+
+#include <asm/io.h>
+
+#include "hisax_fcpcipnp.h"
+
+// debugging cruft
+#define __debug_variable debug
+#include "hisax_debug.h"
+
+#ifdef CONFIG_HISAX_DEBUG
+static int debug = 0;
+/* static int hdlcfifosize = 32; */
+module_param(debug, int, 0);
+/* module_param(hdlcfifosize, int, 0); */
+#endif
+
+MODULE_AUTHOR("Kai Germaschewski <kai.germaschewski@gmx.de>/Karsten Keil <kkeil@suse.de>");
+MODULE_DESCRIPTION("AVM Fritz!PCI/PnP ISDN driver");
+
+static struct pci_device_id fcpci_ids[] = {
+ { .vendor = PCI_VENDOR_ID_AVM,
+ .device = PCI_DEVICE_ID_AVM_A1,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (unsigned long) "Fritz!Card PCI",
+ },
+ { .vendor = PCI_VENDOR_ID_AVM,
+ .device = PCI_DEVICE_ID_AVM_A1_V2,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (unsigned long) "Fritz!Card PCI v2" },
+ {}
+};
+
+MODULE_DEVICE_TABLE(pci, fcpci_ids);
+
+#ifdef CONFIG_PNP
+static struct pnp_device_id fcpnp_ids[] __devinitdata = {
+ {
+ .id = "AVM0900",
+ .driver_data = (unsigned long) "Fritz!Card PnP",
+ },
+};
+
+MODULE_DEVICE_TABLE(isapnp, fcpnp_ids);
+#endif
+
+static int protocol = 2; /* EURO-ISDN Default */
+module_param(protocol, int, 0);
+MODULE_LICENSE("GPL");
+
+// ----------------------------------------------------------------------
+
+#define AVM_INDEX 0x04
+#define AVM_DATA 0x10
+
+#define AVM_IDX_HDLC_1 0x00
+#define AVM_IDX_HDLC_2 0x01
+#define AVM_IDX_ISAC_FIFO 0x02
+#define AVM_IDX_ISAC_REG_LOW 0x04
+#define AVM_IDX_ISAC_REG_HIGH 0x06
+
+#define AVM_STATUS0 0x02
+
+#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 0x03
+#define AVM_STATUS1_ENA_IOM 0x80
+
+#define HDLC_FIFO 0x0
+#define HDLC_STATUS 0x4
+#define HDLC_CTRL 0x4
+
+#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 0xff00
+
+#define HDLC_CMD_XRS 0x80
+#define HDLC_CMD_XME 0x01
+#define HDLC_CMD_RRS 0x20
+#define HDLC_CMD_XML_MASK 0xff00
+
+#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
+
+// ----------------------------------------------------------------------
+// Fritz!PCI
+
+static unsigned char fcpci_read_isac(struct isac *isac, unsigned char offset)
+{
+ struct fritz_adapter *adapter = isac->priv;
+ unsigned char idx = (offset > 0x2f) ?
+ AVM_IDX_ISAC_REG_HIGH : AVM_IDX_ISAC_REG_LOW;
+ unsigned char val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&adapter->hw_lock, flags);
+ outb(idx, adapter->io + AVM_INDEX);
+ val = inb(adapter->io + AVM_DATA + (offset & 0xf));
+ spin_unlock_irqrestore(&adapter->hw_lock, flags);
+ DBG(0x1000, " port %#x, value %#x",
+ offset, val);
+ return val;
+}
+
+static void fcpci_write_isac(struct isac *isac, unsigned char offset,
+ unsigned char value)
+{
+ struct fritz_adapter *adapter = isac->priv;
+ unsigned char idx = (offset > 0x2f) ?
+ AVM_IDX_ISAC_REG_HIGH : AVM_IDX_ISAC_REG_LOW;
+ unsigned long flags;
+
+ DBG(0x1000, " port %#x, value %#x",
+ offset, value);
+ spin_lock_irqsave(&adapter->hw_lock, flags);
+ outb(idx, adapter->io + AVM_INDEX);
+ outb(value, adapter->io + AVM_DATA + (offset & 0xf));
+ spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+static void fcpci_read_isac_fifo(struct isac *isac, unsigned char * data,
+ int size)
+{
+ struct fritz_adapter *adapter = isac->priv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&adapter->hw_lock, flags);
+ outb(AVM_IDX_ISAC_FIFO, adapter->io + AVM_INDEX);
+ insb(adapter->io + AVM_DATA, data, size);
+ spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+static void fcpci_write_isac_fifo(struct isac *isac, unsigned char * data,
+ int size)
+{
+ struct fritz_adapter *adapter = isac->priv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&adapter->hw_lock, flags);
+ outb(AVM_IDX_ISAC_FIFO, adapter->io + AVM_INDEX);
+ outsb(adapter->io + AVM_DATA, data, size);
+ spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+static u32 fcpci_read_hdlc_status(struct fritz_adapter *adapter, int nr)
+{
+ u32 val;
+ int idx = nr ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1;
+ unsigned long flags;
+
+ spin_lock_irqsave(&adapter->hw_lock, flags);
+ outl(idx, adapter->io + AVM_INDEX);
+ val = inl(adapter->io + AVM_DATA + HDLC_STATUS);
+ spin_unlock_irqrestore(&adapter->hw_lock, flags);
+ return val;
+}
+
+static void __fcpci_write_ctrl(struct fritz_bcs *bcs, int which)
+{
+ struct fritz_adapter *adapter = bcs->adapter;
+ int idx = bcs->channel ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1;
+
+ DBG(0x40, "hdlc %c wr%x ctrl %x",
+ 'A' + bcs->channel, which, bcs->ctrl.ctrl);
+
+ outl(idx, adapter->io + AVM_INDEX);
+ outl(bcs->ctrl.ctrl, adapter->io + AVM_DATA + HDLC_CTRL);
+}
+
+static void fcpci_write_ctrl(struct fritz_bcs *bcs, int which)
+{
+ struct fritz_adapter *adapter = bcs->adapter;
+ unsigned long flags;
+
+ spin_lock_irqsave(&adapter->hw_lock, flags);
+ __fcpci_write_ctrl(bcs, which);
+ spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+// ----------------------------------------------------------------------
+// Fritz!PCI v2
+
+static unsigned char fcpci2_read_isac(struct isac *isac, unsigned char offset)
+{
+ struct fritz_adapter *adapter = isac->priv;
+ unsigned char val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&adapter->hw_lock, flags);
+ outl(offset, adapter->io + AVM_ISACSX_INDEX);
+ val = inl(adapter->io + AVM_ISACSX_DATA);
+ spin_unlock_irqrestore(&adapter->hw_lock, flags);
+ DBG(0x1000, " port %#x, value %#x",
+ offset, val);
+
+ return val;
+}
+
+static void fcpci2_write_isac(struct isac *isac, unsigned char offset,
+ unsigned char value)
+{
+ struct fritz_adapter *adapter = isac->priv;
+ unsigned long flags;
+
+ DBG(0x1000, " port %#x, value %#x",
+ offset, value);
+ spin_lock_irqsave(&adapter->hw_lock, flags);
+ outl(offset, adapter->io + AVM_ISACSX_INDEX);
+ outl(value, adapter->io + AVM_ISACSX_DATA);
+ spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+static void fcpci2_read_isac_fifo(struct isac *isac, unsigned char * data,
+ int size)
+{
+ struct fritz_adapter *adapter = isac->priv;
+ int i;
+ unsigned long flags;
+
+ spin_lock_irqsave(&adapter->hw_lock, flags);
+ outl(0, adapter->io + AVM_ISACSX_INDEX);
+ for (i = 0; i < size; i++)
+ data[i] = inl(adapter->io + AVM_ISACSX_DATA);
+ spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+static void fcpci2_write_isac_fifo(struct isac *isac, unsigned char * data,
+ int size)
+{
+ struct fritz_adapter *adapter = isac->priv;
+ int i;
+ unsigned long flags;
+
+ spin_lock_irqsave(&adapter->hw_lock, flags);
+ outl(0, adapter->io + AVM_ISACSX_INDEX);
+ for (i = 0; i < size; i++)
+ outl(data[i], adapter->io + AVM_ISACSX_DATA);
+ spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+static u32 fcpci2_read_hdlc_status(struct fritz_adapter *adapter, int nr)
+{
+ int offset = nr ? AVM_HDLC_STATUS_2 : AVM_HDLC_STATUS_1;
+
+ return inl(adapter->io + offset);
+}
+
+static void fcpci2_write_ctrl(struct fritz_bcs *bcs, int which)
+{
+ struct fritz_adapter *adapter = bcs->adapter;
+ int offset = bcs->channel ? AVM_HDLC_STATUS_2 : AVM_HDLC_STATUS_1;
+
+ DBG(0x40, "hdlc %c wr%x ctrl %x",
+ 'A' + bcs->channel, which, bcs->ctrl.ctrl);
+
+ outl(bcs->ctrl.ctrl, adapter->io + offset);
+}
+
+// ----------------------------------------------------------------------
+// Fritz!PnP (ISAC access as for Fritz!PCI)
+
+static u32 fcpnp_read_hdlc_status(struct fritz_adapter *adapter, int nr)
+{
+ unsigned char idx = nr ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1;
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&adapter->hw_lock, flags);
+ outb(idx, adapter->io + AVM_INDEX);
+ val = inb(adapter->io + AVM_DATA + HDLC_STATUS);
+ if (val & HDLC_INT_RPR)
+ val |= inb(adapter->io + AVM_DATA + HDLC_STATUS + 1) << 8;
+ spin_unlock_irqrestore(&adapter->hw_lock, flags);
+ return val;
+}
+
+static void __fcpnp_write_ctrl(struct fritz_bcs *bcs, int which)
+{
+ struct fritz_adapter *adapter = bcs->adapter;
+ unsigned char idx = bcs->channel ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1;
+
+ DBG(0x40, "hdlc %c wr%x ctrl %x",
+ 'A' + bcs->channel, which, bcs->ctrl.ctrl);
+
+ outb(idx, adapter->io + AVM_INDEX);
+ if (which & 4)
+ outb(bcs->ctrl.sr.mode,
+ adapter->io + AVM_DATA + HDLC_STATUS + 2);
+ if (which & 2)
+ outb(bcs->ctrl.sr.xml,
+ adapter->io + AVM_DATA + HDLC_STATUS + 1);
+ if (which & 1)
+ outb(bcs->ctrl.sr.cmd,
+ adapter->io + AVM_DATA + HDLC_STATUS + 0);
+}
+
+static void fcpnp_write_ctrl(struct fritz_bcs *bcs, int which)
+{
+ struct fritz_adapter *adapter = bcs->adapter;
+ unsigned long flags;
+
+ spin_lock_irqsave(&adapter->hw_lock, flags);
+ __fcpnp_write_ctrl(bcs, which);
+ spin_unlock_irqrestore(&adapter->hw_lock, flags);
+}
+
+// ----------------------------------------------------------------------
+
+static inline void B_L1L2(struct fritz_bcs *bcs, int pr, void *arg)
+{
+ struct hisax_if *ifc = (struct hisax_if *) &bcs->b_if;
+
+ DBG(2, "pr %#x", pr);
+ ifc->l1l2(ifc, pr, arg);
+}
+
+static void hdlc_fill_fifo(struct fritz_bcs *bcs)
+{
+ struct fritz_adapter *adapter = bcs->adapter;
+ struct sk_buff *skb = bcs->tx_skb;
+ int count;
+ unsigned long flags;
+ unsigned char *p;
+
+ DBG(0x40, "hdlc_fill_fifo");
+
+ BUG_ON(skb->len == 0);
+
+ bcs->ctrl.sr.cmd &= ~HDLC_CMD_XME;
+ if (bcs->tx_skb->len > bcs->fifo_size) {
+ count = bcs->fifo_size;
+ } else {
+ count = bcs->tx_skb->len;
+ if (bcs->mode != L1_MODE_TRANS)
+ bcs->ctrl.sr.cmd |= HDLC_CMD_XME;
+ }
+ DBG(0x40, "hdlc_fill_fifo %d/%d", count, bcs->tx_skb->len);
+ p = bcs->tx_skb->data;
+ skb_pull(bcs->tx_skb, count);
+ bcs->tx_cnt += count;
+ bcs->ctrl.sr.xml = ((count == bcs->fifo_size) ? 0 : count);
+
+ switch (adapter->type) {
+ case AVM_FRITZ_PCI:
+ spin_lock_irqsave(&adapter->hw_lock, flags);
+ // sets the correct AVM_INDEX, too
+ __fcpci_write_ctrl(bcs, 3);
+ outsl(adapter->io + AVM_DATA + HDLC_FIFO,
+ p, (count + 3) / 4);
+ spin_unlock_irqrestore(&adapter->hw_lock, flags);
+ break;
+ case AVM_FRITZ_PCIV2:
+ fcpci2_write_ctrl(bcs, 3);
+ outsl(adapter->io +
+ (bcs->channel ? AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1),
+ p, (count + 3) / 4);
+ break;
+ case AVM_FRITZ_PNP:
+ spin_lock_irqsave(&adapter->hw_lock, flags);
+ // sets the correct AVM_INDEX, too
+ __fcpnp_write_ctrl(bcs, 3);
+ outsb(adapter->io + AVM_DATA, p, count);
+ spin_unlock_irqrestore(&adapter->hw_lock, flags);
+ break;
+ }
+}
+
+static inline void hdlc_empty_fifo(struct fritz_bcs *bcs, int count)
+{
+ struct fritz_adapter *adapter = bcs->adapter;
+ unsigned char *p;
+ unsigned char idx = bcs->channel ? AVM_IDX_HDLC_2 : AVM_IDX_HDLC_1;
+
+ DBG(0x10, "hdlc_empty_fifo %d", count);
+ if (bcs->rcvidx + count > HSCX_BUFMAX) {
+ DBG(0x10, "hdlc_empty_fifo: incoming packet too large");
+ return;
+ }
+ p = bcs->rcvbuf + bcs->rcvidx;
+ bcs->rcvidx += count;
+ switch (adapter->type) {
+ case AVM_FRITZ_PCI:
+ spin_lock(&adapter->hw_lock);
+ outl(idx, adapter->io + AVM_INDEX);
+ insl(adapter->io + AVM_DATA + HDLC_FIFO,
+ p, (count + 3) / 4);
+ spin_unlock(&adapter->hw_lock);
+ break;
+ case AVM_FRITZ_PCIV2:
+ insl(adapter->io +
+ (bcs->channel ? AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1),
+ p, (count + 3) / 4);
+ break;
+ case AVM_FRITZ_PNP:
+ spin_lock(&adapter->hw_lock);
+ outb(idx, adapter->io + AVM_INDEX);
+ insb(adapter->io + AVM_DATA, p, count);
+ spin_unlock(&adapter->hw_lock);
+ break;
+ }
+}
+
+static inline void hdlc_rpr_irq(struct fritz_bcs *bcs, u32 stat)
+{
+ struct fritz_adapter *adapter = bcs->adapter;
+ struct sk_buff *skb;
+ int len;
+
+ if (stat & HDLC_STAT_RDO) {
+ DBG(0x10, "RDO");
+ bcs->ctrl.sr.xml = 0;
+ bcs->ctrl.sr.cmd |= HDLC_CMD_RRS;
+ adapter->write_ctrl(bcs, 1);
+ bcs->ctrl.sr.cmd &= ~HDLC_CMD_RRS;
+ adapter->write_ctrl(bcs, 1);
+ bcs->rcvidx = 0;
+ return;
+ }
+
+ len = (stat & HDLC_STAT_RML_MASK) >> 8;
+ if (len == 0)
+ len = bcs->fifo_size;
+
+ hdlc_empty_fifo(bcs, len);
+
+ if ((stat & HDLC_STAT_RME) || (bcs->mode == L1_MODE_TRANS)) {
+ if (((stat & HDLC_STAT_CRCVFRRAB)== HDLC_STAT_CRCVFR) ||
+ (bcs->mode == L1_MODE_TRANS)) {
+ skb = dev_alloc_skb(bcs->rcvidx);
+ if (!skb) {
+ printk(KERN_WARNING "HDLC: receive out of memory\n");
+ } else {
+ memcpy(skb_put(skb, bcs->rcvidx), bcs->rcvbuf,
+ bcs->rcvidx);
+ DBG_SKB(1, skb);
+ B_L1L2(bcs, PH_DATA | INDICATION, skb);
+ }
+ bcs->rcvidx = 0;
+ } else {
+ DBG(0x10, "ch%d invalid frame %#x",
+ bcs->channel, stat);
+ bcs->rcvidx = 0;
+ }
+ }
+}
+
+static inline void hdlc_xdu_irq(struct fritz_bcs *bcs)
+{
+ struct fritz_adapter *adapter = bcs->adapter;
+
+
+ /* Here we lost an TX interrupt, so
+ * restart transmitting the whole frame.
+ */
+ bcs->ctrl.sr.xml = 0;
+ bcs->ctrl.sr.cmd |= HDLC_CMD_XRS;
+ adapter->write_ctrl(bcs, 1);
+ bcs->ctrl.sr.cmd &= ~HDLC_CMD_XRS;
+
+ if (!bcs->tx_skb) {
+ DBG(0x10, "XDU without skb");
+ adapter->write_ctrl(bcs, 1);
+ return;
+ }
+ /* only hdlc restarts the frame, transparent mode must continue */
+ if (bcs->mode == L1_MODE_HDLC) {
+ skb_push(bcs->tx_skb, bcs->tx_cnt);
+ bcs->tx_cnt = 0;
+ }
+}
+
+static inline void hdlc_xpr_irq(struct fritz_bcs *bcs)
+{
+ struct sk_buff *skb;
+
+ skb = bcs->tx_skb;
+ if (!skb)
+ return;
+
+ if (skb->len) {
+ hdlc_fill_fifo(bcs);
+ return;
+ }
+ bcs->tx_cnt = 0;
+ bcs->tx_skb = NULL;
+ B_L1L2(bcs, PH_DATA | CONFIRM, (void *)(unsigned long)skb->truesize);
+ dev_kfree_skb_irq(skb);
+}
+
+static void hdlc_irq_one(struct fritz_bcs *bcs, u32 stat)
+{
+ DBG(0x10, "ch%d stat %#x", bcs->channel, stat);
+ if (stat & HDLC_INT_RPR) {
+ DBG(0x10, "RPR");
+ hdlc_rpr_irq(bcs, stat);
+ }
+ if (stat & HDLC_INT_XDU) {
+ DBG(0x10, "XDU");
+ hdlc_xdu_irq(bcs);
+ hdlc_xpr_irq(bcs);
+ return;
+ }
+ if (stat & HDLC_INT_XPR) {
+ DBG(0x10, "XPR");
+ hdlc_xpr_irq(bcs);
+ }
+}
+
+static inline void hdlc_irq(struct fritz_adapter *adapter)
+{
+ int nr;
+ u32 stat;
+
+ for (nr = 0; nr < 2; nr++) {
+ stat = adapter->read_hdlc_status(adapter, nr);
+ DBG(0x10, "HDLC %c stat %#x", 'A' + nr, stat);
+ if (stat & HDLC_INT_MASK)
+ hdlc_irq_one(&adapter->bcs[nr], stat);
+ }
+}
+
+static void modehdlc(struct fritz_bcs *bcs, int mode)
+{
+ struct fritz_adapter *adapter = bcs->adapter;
+
+ DBG(0x40, "hdlc %c mode %d --> %d",
+ 'A' + bcs->channel, bcs->mode, mode);
+
+ if (bcs->mode == mode)
+ return;
+
+ bcs->fifo_size = 32;
+ bcs->ctrl.ctrl = 0;
+ bcs->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS;
+ switch (mode) {
+ case L1_MODE_NULL:
+ bcs->ctrl.sr.mode = HDLC_MODE_TRANS;
+ adapter->write_ctrl(bcs, 5);
+ break;
+ case L1_MODE_TRANS:
+ case L1_MODE_HDLC:
+ bcs->rcvidx = 0;
+ bcs->tx_cnt = 0;
+ bcs->tx_skb = NULL;
+ if (mode == L1_MODE_TRANS) {
+ bcs->ctrl.sr.mode = HDLC_MODE_TRANS;
+ } else {
+ bcs->ctrl.sr.mode = HDLC_MODE_ITF_FLG;
+ }
+ adapter->write_ctrl(bcs, 5);
+ bcs->ctrl.sr.cmd = HDLC_CMD_XRS;
+ adapter->write_ctrl(bcs, 1);
+ bcs->ctrl.sr.cmd = 0;
+ break;
+ }
+ bcs->mode = mode;
+}
+
+static void fritz_b_l2l1(struct hisax_if *ifc, int pr, void *arg)
+{
+ struct fritz_bcs *bcs = ifc->priv;
+ struct sk_buff *skb = arg;
+ int mode;
+
+ DBG(0x10, "pr %#x", pr);
+
+ switch (pr) {
+ case PH_DATA | REQUEST:
+ BUG_ON(bcs->tx_skb);
+ bcs->tx_skb = skb;
+ DBG_SKB(1, skb);
+ hdlc_fill_fifo(bcs);
+ break;
+ case PH_ACTIVATE | REQUEST:
+ mode = (long) arg;
+ DBG(4,"B%d,PH_ACTIVATE_REQUEST %d", bcs->channel + 1, mode);
+ modehdlc(bcs, mode);
+ B_L1L2(bcs, PH_ACTIVATE | INDICATION, NULL);
+ break;
+ case PH_DEACTIVATE | REQUEST:
+ DBG(4,"B%d,PH_DEACTIVATE_REQUEST", bcs->channel + 1);
+ modehdlc(bcs, L1_MODE_NULL);
+ B_L1L2(bcs, PH_DEACTIVATE | INDICATION, NULL);
+ break;
+ }
+}
+
+// ----------------------------------------------------------------------
+
+static irqreturn_t
+fcpci2_irq(int intno, void *dev)
+{
+ struct fritz_adapter *adapter = dev;
+ unsigned char val;
+
+ val = inb(adapter->io + AVM_STATUS0);
+ if (!(val & AVM_STATUS0_IRQ_MASK))
+ /* hopefully a shared IRQ reqest */
+ return IRQ_NONE;
+ DBG(2, "STATUS0 %#x", val);
+ if (val & AVM_STATUS0_IRQ_ISAC)
+ isacsx_irq(&adapter->isac);
+ if (val & AVM_STATUS0_IRQ_HDLC)
+ hdlc_irq(adapter);
+ if (val & AVM_STATUS0_IRQ_ISAC)
+ isacsx_irq(&adapter->isac);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t
+fcpci_irq(int intno, void *dev)
+{
+ struct fritz_adapter *adapter = dev;
+ unsigned char sval;
+
+ sval = inb(adapter->io + 2);
+ if ((sval & AVM_STATUS0_IRQ_MASK) == AVM_STATUS0_IRQ_MASK)
+ /* possibly a shared IRQ reqest */
+ return IRQ_NONE;
+ DBG(2, "sval %#x", sval);
+ if (!(sval & AVM_STATUS0_IRQ_ISAC))
+ isac_irq(&adapter->isac);
+
+ if (!(sval & AVM_STATUS0_IRQ_HDLC))
+ hdlc_irq(adapter);
+ return IRQ_HANDLED;
+}
+
+// ----------------------------------------------------------------------
+
+static inline void fcpci2_init(struct fritz_adapter *adapter)
+{
+ outb(AVM_STATUS0_RES_TIMER, adapter->io + AVM_STATUS0);
+ outb(AVM_STATUS0_ENA_IRQ, adapter->io + AVM_STATUS0);
+
+}
+
+static inline void fcpci_init(struct fritz_adapter *adapter)
+{
+ outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER |
+ AVM_STATUS0_ENA_IRQ, adapter->io + AVM_STATUS0);
+
+ outb(AVM_STATUS1_ENA_IOM | adapter->irq,
+ adapter->io + AVM_STATUS1);
+ mdelay(10);
+}
+
+// ----------------------------------------------------------------------
+
+static int __devinit fcpcipnp_setup(struct fritz_adapter *adapter)
+{
+ u32 val = 0;
+ int retval;
+
+ DBG(1,"");
+
+ isac_init(&adapter->isac); // FIXME is this okay now
+
+ retval = -EBUSY;
+ if (!request_region(adapter->io, 32, "fcpcipnp"))
+ goto err;
+
+ switch (adapter->type) {
+ case AVM_FRITZ_PCIV2:
+ case AVM_FRITZ_PCI:
+ val = inl(adapter->io);
+ break;
+ case AVM_FRITZ_PNP:
+ val = inb(adapter->io);
+ val |= inb(adapter->io + 1) << 8;
+ break;
+ }
+
+ DBG(1, "stat %#x Class %X Rev %d",
+ val, val & 0xff, (val>>8) & 0xff);
+
+ spin_lock_init(&adapter->hw_lock);
+ adapter->isac.priv = adapter;
+ switch (adapter->type) {
+ case AVM_FRITZ_PCIV2:
+ adapter->isac.read_isac = &fcpci2_read_isac;
+ adapter->isac.write_isac = &fcpci2_write_isac;
+ adapter->isac.read_isac_fifo = &fcpci2_read_isac_fifo;
+ adapter->isac.write_isac_fifo = &fcpci2_write_isac_fifo;
+
+ adapter->read_hdlc_status = &fcpci2_read_hdlc_status;
+ adapter->write_ctrl = &fcpci2_write_ctrl;
+ break;
+ case AVM_FRITZ_PCI:
+ adapter->isac.read_isac = &fcpci_read_isac;
+ adapter->isac.write_isac = &fcpci_write_isac;
+ adapter->isac.read_isac_fifo = &fcpci_read_isac_fifo;
+ adapter->isac.write_isac_fifo = &fcpci_write_isac_fifo;
+
+ adapter->read_hdlc_status = &fcpci_read_hdlc_status;
+ adapter->write_ctrl = &fcpci_write_ctrl;
+ break;
+ case AVM_FRITZ_PNP:
+ adapter->isac.read_isac = &fcpci_read_isac;
+ adapter->isac.write_isac = &fcpci_write_isac;
+ adapter->isac.read_isac_fifo = &fcpci_read_isac_fifo;
+ adapter->isac.write_isac_fifo = &fcpci_write_isac_fifo;
+
+ adapter->read_hdlc_status = &fcpnp_read_hdlc_status;
+ adapter->write_ctrl = &fcpnp_write_ctrl;
+ break;
+ }
+
+ // Reset
+ outb(0, adapter->io + AVM_STATUS0);
+ mdelay(10);
+ outb(AVM_STATUS0_RESET, adapter->io + AVM_STATUS0);
+ mdelay(10);
+ outb(0, adapter->io + AVM_STATUS0);
+ mdelay(10);
+
+ switch (adapter->type) {
+ case AVM_FRITZ_PCIV2:
+ retval = request_irq(adapter->irq, fcpci2_irq, IRQF_SHARED,
+ "fcpcipnp", adapter);
+ break;
+ case AVM_FRITZ_PCI:
+ retval = request_irq(adapter->irq, fcpci_irq, IRQF_SHARED,
+ "fcpcipnp", adapter);
+ break;
+ case AVM_FRITZ_PNP:
+ retval = request_irq(adapter->irq, fcpci_irq, 0,
+ "fcpcipnp", adapter);
+ break;
+ }
+ if (retval)
+ goto err_region;
+
+ switch (adapter->type) {
+ case AVM_FRITZ_PCIV2:
+ fcpci2_init(adapter);
+ isacsx_setup(&adapter->isac);
+ break;
+ case AVM_FRITZ_PCI:
+ case AVM_FRITZ_PNP:
+ fcpci_init(adapter);
+ isac_setup(&adapter->isac);
+ break;
+ }
+ val = adapter->read_hdlc_status(adapter, 0);
+ DBG(0x20, "HDLC A STA %x", val);
+ val = adapter->read_hdlc_status(adapter, 1);
+ DBG(0x20, "HDLC B STA %x", val);
+
+ adapter->bcs[0].mode = -1;
+ adapter->bcs[1].mode = -1;
+ modehdlc(&adapter->bcs[0], L1_MODE_NULL);
+ modehdlc(&adapter->bcs[1], L1_MODE_NULL);
+
+ return 0;
+
+ err_region:
+ release_region(adapter->io, 32);
+ err:
+ return retval;
+}
+
+static void __devexit fcpcipnp_release(struct fritz_adapter *adapter)
+{
+ DBG(1,"");
+
+ outb(0, adapter->io + AVM_STATUS0);
+ free_irq(adapter->irq, adapter);
+ release_region(adapter->io, 32);
+}
+
+// ----------------------------------------------------------------------
+
+static struct fritz_adapter * __devinit
+new_adapter(void)
+{
+ struct fritz_adapter *adapter;
+ struct hisax_b_if *b_if[2];
+ int i;
+
+ adapter = kzalloc(sizeof(struct fritz_adapter), GFP_KERNEL);
+ if (!adapter)
+ return NULL;
+
+ adapter->isac.hisax_d_if.owner = THIS_MODULE;
+ adapter->isac.hisax_d_if.ifc.priv = &adapter->isac;
+ adapter->isac.hisax_d_if.ifc.l2l1 = isac_d_l2l1;
+
+ for (i = 0; i < 2; i++) {
+ adapter->bcs[i].adapter = adapter;
+ adapter->bcs[i].channel = i;
+ adapter->bcs[i].b_if.ifc.priv = &adapter->bcs[i];
+ adapter->bcs[i].b_if.ifc.l2l1 = fritz_b_l2l1;
+ }
+
+ for (i = 0; i < 2; i++)
+ b_if[i] = &adapter->bcs[i].b_if;
+
+ if (hisax_register(&adapter->isac.hisax_d_if, b_if, "fcpcipnp",
+ protocol) != 0) {
+ kfree(adapter);
+ adapter = NULL;
+ }
+
+ return adapter;
+}
+
+static void delete_adapter(struct fritz_adapter *adapter)
+{
+ hisax_unregister(&adapter->isac.hisax_d_if);
+ kfree(adapter);
+}
+
+static int __devinit fcpci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct fritz_adapter *adapter;
+ int retval;
+
+ retval = -ENOMEM;
+ adapter = new_adapter();
+ if (!adapter)
+ goto err;
+
+ pci_set_drvdata(pdev, adapter);
+
+ if (pdev->device == PCI_DEVICE_ID_AVM_A1_V2)
+ adapter->type = AVM_FRITZ_PCIV2;
+ else
+ adapter->type = AVM_FRITZ_PCI;
+
+ retval = pci_enable_device(pdev);
+ if (retval)
+ goto err_free;
+
+ adapter->io = pci_resource_start(pdev, 1);
+ adapter->irq = pdev->irq;
+
+ printk(KERN_INFO "hisax_fcpcipnp: found adapter %s at %s\n",
+ (char *) ent->driver_data, pci_name(pdev));
+
+ retval = fcpcipnp_setup(adapter);
+ if (retval)
+ goto err_free;
+
+ return 0;
+
+ err_free:
+ delete_adapter(adapter);
+ err:
+ return retval;
+}
+
+#ifdef CONFIG_PNP
+static int __devinit fcpnp_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
+{
+ struct fritz_adapter *adapter;
+ int retval;
+
+ if (!pdev)
+ return(-ENODEV);
+
+ retval = -ENOMEM;
+ adapter = new_adapter();
+ if (!adapter)
+ goto err;
+
+ pnp_set_drvdata(pdev, adapter);
+
+ adapter->type = AVM_FRITZ_PNP;
+
+ pnp_disable_dev(pdev);
+ retval = pnp_activate_dev(pdev);
+ if (retval < 0) {
+ printk(KERN_WARNING "%s: pnp_activate_dev(%s) ret(%d)\n", __func__,
+ (char *)dev_id->driver_data, retval);
+ goto err_free;
+ }
+ adapter->io = pnp_port_start(pdev, 0);
+ adapter->irq = pnp_irq(pdev, 0);
+
+ printk(KERN_INFO "hisax_fcpcipnp: found adapter %s at IO %#x irq %d\n",
+ (char *) dev_id->driver_data, adapter->io, adapter->irq);
+
+ retval = fcpcipnp_setup(adapter);
+ if (retval)
+ goto err_free;
+
+ return 0;
+
+ err_free:
+ delete_adapter(adapter);
+ err:
+ return retval;
+}
+
+static void __devexit fcpnp_remove(struct pnp_dev *pdev)
+{
+ struct fritz_adapter *adapter = pnp_get_drvdata(pdev);
+
+ if (adapter) {
+ fcpcipnp_release(adapter);
+ delete_adapter(adapter);
+ }
+ pnp_disable_dev(pdev);
+}
+
+static struct pnp_driver fcpnp_driver = {
+ .name = "fcpnp",
+ .probe = fcpnp_probe,
+ .remove = __devexit_p(fcpnp_remove),
+ .id_table = fcpnp_ids,
+};
+#endif
+
+static void __devexit fcpci_remove(struct pci_dev *pdev)
+{
+ struct fritz_adapter *adapter = pci_get_drvdata(pdev);
+
+ fcpcipnp_release(adapter);
+ pci_disable_device(pdev);
+ delete_adapter(adapter);
+}
+
+static struct pci_driver fcpci_driver = {
+ .name = "fcpci",
+ .probe = fcpci_probe,
+ .remove = __devexit_p(fcpci_remove),
+ .id_table = fcpci_ids,
+};
+
+static int __init hisax_fcpcipnp_init(void)
+{
+ int retval;
+
+ printk(KERN_INFO "hisax_fcpcipnp: Fritz!Card PCI/PCIv2/PnP ISDN driver v0.0.1\n");
+
+ retval = pci_register_driver(&fcpci_driver);
+ if (retval)
+ return retval;
+#ifdef CONFIG_PNP
+ retval = pnp_register_driver(&fcpnp_driver);
+ if (retval < 0) {
+ pci_unregister_driver(&fcpci_driver);
+ return retval;
+ }
+#endif
+ return 0;
+}
+
+static void __exit hisax_fcpcipnp_exit(void)
+{
+#ifdef CONFIG_PNP
+ pnp_unregister_driver(&fcpnp_driver);
+#endif
+ pci_unregister_driver(&fcpci_driver);
+}
+
+module_init(hisax_fcpcipnp_init);
+module_exit(hisax_fcpcipnp_exit);
diff --git a/drivers/isdn/hisax/hisax_fcpcipnp.h b/drivers/isdn/hisax/hisax_fcpcipnp.h
new file mode 100644
index 0000000..21fbced
--- /dev/null
+++ b/drivers/isdn/hisax/hisax_fcpcipnp.h
@@ -0,0 +1,58 @@
+#include "hisax_if.h"
+#include "hisax_isac.h"
+#include <linux/pci.h>
+
+#define HSCX_BUFMAX 4096
+
+enum {
+ AVM_FRITZ_PCI,
+ AVM_FRITZ_PNP,
+ AVM_FRITZ_PCIV2,
+};
+
+struct hdlc_stat_reg {
+#ifdef __BIG_ENDIAN
+ u_char fill;
+ u_char mode;
+ u_char xml;
+ u_char cmd;
+#else
+ u_char cmd;
+ u_char xml;
+ u_char mode;
+ u_char fill;
+#endif
+} __attribute__((packed));
+
+struct fritz_bcs {
+ struct hisax_b_if b_if;
+ struct fritz_adapter *adapter;
+ int mode;
+ int channel;
+
+ union {
+ u_int ctrl;
+ struct hdlc_stat_reg sr;
+ } ctrl;
+ u_int stat;
+ int rcvidx;
+ int fifo_size;
+ u_char rcvbuf[HSCX_BUFMAX]; /* B-Channel receive Buffer */
+
+ int tx_cnt; /* B-Channel transmit counter */
+ struct sk_buff *tx_skb; /* B-Channel transmit Buffer */
+};
+
+struct fritz_adapter {
+ int type;
+ spinlock_t hw_lock;
+ unsigned int io;
+ unsigned int irq;
+ struct isac isac;
+
+ struct fritz_bcs bcs[2];
+
+ u32 (*read_hdlc_status) (struct fritz_adapter *adapter, int nr);
+ void (*write_ctrl) (struct fritz_bcs *bcs, int which);
+};
+
diff --git a/drivers/isdn/hisax/hisax_if.h b/drivers/isdn/hisax/hisax_if.h
new file mode 100644
index 0000000..aa7c940
--- /dev/null
+++ b/drivers/isdn/hisax/hisax_if.h
@@ -0,0 +1,66 @@
+/*
+ * Interface between low level (hardware) drivers and
+ * HiSax protocol stack
+ *
+ * Author Kai Germaschewski
+ * Copyright 2001 by Kai Germaschewski <kai.germaschewski@gmx.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef __HISAX_IF_H__
+#define __HISAX_IF_H__
+
+#include <linux/skbuff.h>
+
+#define REQUEST 0
+#define CONFIRM 1
+#define INDICATION 2
+#define RESPONSE 3
+
+#define PH_ACTIVATE 0x0100
+#define PH_DEACTIVATE 0x0110
+#define PH_DATA 0x0120
+#define PH_PULL 0x0130
+#define PH_DATA_E 0x0140
+
+#define L1_MODE_NULL 0
+#define L1_MODE_TRANS 1
+#define L1_MODE_HDLC 2
+#define L1_MODE_EXTRN 3
+#define L1_MODE_HDLC_56K 4
+#define L1_MODE_MODEM 7
+#define L1_MODE_V32 8
+#define L1_MODE_FAX 9
+
+struct hisax_if {
+ void *priv; // private to driver
+ void (*l1l2)(struct hisax_if *, int pr, void *arg);
+ void (*l2l1)(struct hisax_if *, int pr, void *arg);
+};
+
+struct hisax_b_if {
+ struct hisax_if ifc;
+
+ // private to hisax
+ struct BCState *bcs;
+};
+
+struct hisax_d_if {
+ struct hisax_if ifc;
+
+ // private to hisax
+ struct module *owner;
+ struct IsdnCardState *cs;
+ struct hisax_b_if *b_if[2];
+ struct sk_buff_head erq;
+ unsigned long ph_state;
+};
+
+int hisax_register(struct hisax_d_if *hisax_if, struct hisax_b_if *b_if[],
+ char *name, int protocol);
+void hisax_unregister(struct hisax_d_if *hisax_if);
+
+#endif
diff --git a/drivers/isdn/hisax/hisax_isac.c b/drivers/isdn/hisax/hisax_isac.c
new file mode 100644
index 0000000..d0fefcf
--- /dev/null
+++ b/drivers/isdn/hisax/hisax_isac.c
@@ -0,0 +1,894 @@
+/*
+ * Driver for ISAC-S and ISAC-SX
+ * ISDN Subscriber Access Controller for Terminals
+ *
+ * Author Kai Germaschewski
+ * Copyright 2001 by Kai Germaschewski <kai.germaschewski@gmx.de>
+ * 2001 by Karsten Keil <keil@isdn4linux.de>
+ *
+ * based upon Karsten Keil's original isac.c driver
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to Wizard Computersysteme GmbH, Bremervoerde and
+ * SoHaNet Technology GmbH, Berlin
+ * for supporting the development of this driver
+ */
+
+/* TODO:
+ * specifically handle level vs edge triggered?
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include "hisax_isac.h"
+
+// debugging cruft
+
+#define __debug_variable debug
+#include "hisax_debug.h"
+
+#ifdef CONFIG_HISAX_DEBUG
+static int debug = 1;
+module_param(debug, int, 0);
+
+static char *ISACVer[] = {
+ "2086/2186 V1.1",
+ "2085 B1",
+ "2085 B2",
+ "2085 V2.3"
+};
+#endif
+
+MODULE_AUTHOR("Kai Germaschewski <kai.germaschewski@gmx.de>/Karsten Keil <kkeil@suse.de>");
+MODULE_DESCRIPTION("ISAC/ISAC-SX driver");
+MODULE_LICENSE("GPL");
+
+#define DBG_WARN 0x0001
+#define DBG_IRQ 0x0002
+#define DBG_L1M 0x0004
+#define DBG_PR 0x0008
+#define DBG_RFIFO 0x0100
+#define DBG_RPACKET 0x0200
+#define DBG_XFIFO 0x1000
+#define DBG_XPACKET 0x2000
+
+// we need to distinguish ISAC-S and ISAC-SX
+#define TYPE_ISAC 0x00
+#define TYPE_ISACSX 0x01
+
+// registers etc.
+#define ISAC_MASK 0x20
+#define ISAC_ISTA 0x20
+#define ISAC_ISTA_EXI 0x01
+#define ISAC_ISTA_SIN 0x02
+#define ISAC_ISTA_CISQ 0x04
+#define ISAC_ISTA_XPR 0x10
+#define ISAC_ISTA_RSC 0x20
+#define ISAC_ISTA_RPF 0x40
+#define ISAC_ISTA_RME 0x80
+
+#define ISAC_STAR 0x21
+#define ISAC_CMDR 0x21
+#define ISAC_CMDR_XRES 0x01
+#define ISAC_CMDR_XME 0x02
+#define ISAC_CMDR_XTF 0x08
+#define ISAC_CMDR_RRES 0x40
+#define ISAC_CMDR_RMC 0x80
+
+#define ISAC_EXIR 0x24
+#define ISAC_EXIR_MOS 0x04
+#define ISAC_EXIR_XDU 0x40
+#define ISAC_EXIR_XMR 0x80
+
+#define ISAC_ADF2 0x39
+#define ISAC_SPCR 0x30
+#define ISAC_ADF1 0x38
+
+#define ISAC_CIR0 0x31
+#define ISAC_CIX0 0x31
+#define ISAC_CIR0_CIC0 0x02
+#define ISAC_CIR0_CIC1 0x01
+
+#define ISAC_CIR1 0x33
+#define ISAC_CIX1 0x33
+#define ISAC_STCR 0x37
+#define ISAC_MODE 0x22
+
+#define ISAC_RSTA 0x27
+#define ISAC_RSTA_RDO 0x40
+#define ISAC_RSTA_CRC 0x20
+#define ISAC_RSTA_RAB 0x10
+
+#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_RES 0x1
+#define ISAC_CMD_SSP 0x2
+#define ISAC_CMD_SCP 0x3
+#define ISAC_CMD_AR8 0x8
+#define ISAC_CMD_AR10 0x9
+#define ISAC_CMD_ARL 0xa
+#define ISAC_CMD_DI 0xf
+
+#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
+
+static struct Fsm l1fsm;
+
+enum {
+ ST_L1_RESET,
+ ST_L1_F3_PDOWN,
+ ST_L1_F3_PUP,
+ ST_L1_F3_PEND_DEACT,
+ ST_L1_F4,
+ ST_L1_F5,
+ ST_L1_F6,
+ ST_L1_F7,
+ ST_L1_F8,
+};
+
+#define L1_STATE_COUNT (ST_L1_F8+1)
+
+static char *strL1State[] =
+{
+ "ST_L1_RESET",
+ "ST_L1_F3_PDOWN",
+ "ST_L1_F3_PUP",
+ "ST_L1_F3_PEND_DEACT",
+ "ST_L1_F4",
+ "ST_L1_F5",
+ "ST_L1_F6",
+ "ST_L1_F7",
+ "ST_L1_F8",
+};
+
+enum {
+ EV_PH_DR, // 0000
+ EV_PH_RES, // 0001
+ EV_PH_TMA, // 0010
+ EV_PH_SLD, // 0011
+ EV_PH_RSY, // 0100
+ EV_PH_DR6, // 0101
+ EV_PH_EI, // 0110
+ EV_PH_PU, // 0111
+ EV_PH_AR, // 1000
+ EV_PH_9, // 1001
+ EV_PH_ARL, // 1010
+ EV_PH_CVR, // 1011
+ EV_PH_AI8, // 1100
+ EV_PH_AI10, // 1101
+ EV_PH_AIL, // 1110
+ EV_PH_DC, // 1111
+ EV_PH_ACTIVATE_REQ,
+ EV_PH_DEACTIVATE_REQ,
+ EV_TIMER3,
+};
+
+#define L1_EVENT_COUNT (EV_TIMER3 + 1)
+
+static char *strL1Event[] =
+{
+ "EV_PH_DR", // 0000
+ "EV_PH_RES", // 0001
+ "EV_PH_TMA", // 0010
+ "EV_PH_SLD", // 0011
+ "EV_PH_RSY", // 0100
+ "EV_PH_DR6", // 0101
+ "EV_PH_EI", // 0110
+ "EV_PH_PU", // 0111
+ "EV_PH_AR", // 1000
+ "EV_PH_9", // 1001
+ "EV_PH_ARL", // 1010
+ "EV_PH_CVR", // 1011
+ "EV_PH_AI8", // 1100
+ "EV_PH_AI10", // 1101
+ "EV_PH_AIL", // 1110
+ "EV_PH_DC", // 1111
+ "EV_PH_ACTIVATE_REQ",
+ "EV_PH_DEACTIVATE_REQ",
+ "EV_TIMER3",
+};
+
+static inline void D_L1L2(struct isac *isac, int pr, void *arg)
+{
+ struct hisax_if *ifc = (struct hisax_if *) &isac->hisax_d_if;
+
+ DBG(DBG_PR, "pr %#x", pr);
+ ifc->l1l2(ifc, pr, arg);
+}
+
+static void ph_command(struct isac *isac, unsigned int command)
+{
+ DBG(DBG_L1M, "ph_command %#x", command);
+ switch (isac->type) {
+ case TYPE_ISAC:
+ isac->write_isac(isac, ISAC_CIX0, (command << 2) | 3);
+ break;
+ case TYPE_ISACSX:
+ isac->write_isac(isac, ISACSX_CIX0, (command << 4) | (7 << 1));
+ break;
+ }
+}
+
+// ----------------------------------------------------------------------
+
+static void l1_di(struct FsmInst *fi, int event, void *arg)
+{
+ struct isac *isac = fi->userdata;
+
+ FsmChangeState(fi, ST_L1_RESET);
+ ph_command(isac, ISAC_CMD_DI);
+}
+
+static void l1_di_deact_ind(struct FsmInst *fi, int event, void *arg)
+{
+ struct isac *isac = fi->userdata;
+
+ FsmChangeState(fi, ST_L1_RESET);
+ D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL);
+ ph_command(isac, ISAC_CMD_DI);
+}
+
+static void l1_go_f3pdown(struct FsmInst *fi, int event, void *arg)
+{
+ FsmChangeState(fi, ST_L1_F3_PDOWN);
+}
+
+static void l1_go_f3pend_deact_ind(struct FsmInst *fi, int event, void *arg)
+{
+ struct isac *isac = fi->userdata;
+
+ FsmChangeState(fi, ST_L1_F3_PEND_DEACT);
+ D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL);
+ ph_command(isac, ISAC_CMD_DI);
+}
+
+static void l1_go_f3pend(struct FsmInst *fi, int event, void *arg)
+{
+ struct isac *isac = fi->userdata;
+
+ FsmChangeState(fi, ST_L1_F3_PEND_DEACT);
+ ph_command(isac, ISAC_CMD_DI);
+}
+
+static void l1_go_f4(struct FsmInst *fi, int event, void *arg)
+{
+ FsmChangeState(fi, ST_L1_F4);
+}
+
+static void l1_go_f5(struct FsmInst *fi, int event, void *arg)
+{
+ FsmChangeState(fi, ST_L1_F5);
+}
+
+static void l1_go_f6(struct FsmInst *fi, int event, void *arg)
+{
+ FsmChangeState(fi, ST_L1_F6);
+}
+
+static void l1_go_f6_deact_ind(struct FsmInst *fi, int event, void *arg)
+{
+ struct isac *isac = fi->userdata;
+
+ FsmChangeState(fi, ST_L1_F6);
+ D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL);
+}
+
+static void l1_go_f7_act_ind(struct FsmInst *fi, int event, void *arg)
+{
+ struct isac *isac = fi->userdata;
+
+ FsmDelTimer(&isac->timer, 0);
+ FsmChangeState(fi, ST_L1_F7);
+ ph_command(isac, ISAC_CMD_AR8);
+ D_L1L2(isac, PH_ACTIVATE | INDICATION, NULL);
+}
+
+static void l1_go_f8(struct FsmInst *fi, int event, void *arg)
+{
+ FsmChangeState(fi, ST_L1_F8);
+}
+
+static void l1_go_f8_deact_ind(struct FsmInst *fi, int event, void *arg)
+{
+ struct isac *isac = fi->userdata;
+
+ FsmChangeState(fi, ST_L1_F8);
+ D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL);
+}
+
+static void l1_ar8(struct FsmInst *fi, int event, void *arg)
+{
+ struct isac *isac = fi->userdata;
+
+ FsmRestartTimer(&isac->timer, TIMER3_VALUE, EV_TIMER3, NULL, 2);
+ ph_command(isac, ISAC_CMD_AR8);
+}
+
+static void l1_timer3(struct FsmInst *fi, int event, void *arg)
+{
+ struct isac *isac = fi->userdata;
+
+ ph_command(isac, ISAC_CMD_DI);
+ D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL);
+}
+
+// state machines according to data sheet PSB 2186 / 3186
+
+static struct FsmNode L1FnList[] __initdata =
+{
+ {ST_L1_RESET, EV_PH_RES, l1_di},
+ {ST_L1_RESET, EV_PH_EI, l1_di},
+ {ST_L1_RESET, EV_PH_DC, l1_go_f3pdown},
+ {ST_L1_RESET, EV_PH_AR, l1_go_f6},
+ {ST_L1_RESET, EV_PH_AI8, l1_go_f7_act_ind},
+
+ {ST_L1_F3_PDOWN, EV_PH_RES, l1_di},
+ {ST_L1_F3_PDOWN, EV_PH_EI, l1_di},
+ {ST_L1_F3_PDOWN, EV_PH_AR, l1_go_f6},
+ {ST_L1_F3_PDOWN, EV_PH_RSY, l1_go_f5},
+ {ST_L1_F3_PDOWN, EV_PH_PU, l1_go_f4},
+ {ST_L1_F3_PDOWN, EV_PH_AI8, l1_go_f7_act_ind},
+ {ST_L1_F3_PDOWN, EV_PH_ACTIVATE_REQ, l1_ar8},
+ {ST_L1_F3_PDOWN, EV_TIMER3, l1_timer3},
+
+ {ST_L1_F3_PEND_DEACT, EV_PH_RES, l1_di},
+ {ST_L1_F3_PEND_DEACT, EV_PH_EI, l1_di},
+ {ST_L1_F3_PEND_DEACT, EV_PH_DC, l1_go_f3pdown},
+ {ST_L1_F3_PEND_DEACT, EV_PH_RSY, l1_go_f5},
+ {ST_L1_F3_PEND_DEACT, EV_PH_AR, l1_go_f6},
+ {ST_L1_F3_PEND_DEACT, EV_PH_AI8, l1_go_f7_act_ind},
+
+ {ST_L1_F4, EV_PH_RES, l1_di},
+ {ST_L1_F4, EV_PH_EI, l1_di},
+ {ST_L1_F4, EV_PH_RSY, l1_go_f5},
+ {ST_L1_F4, EV_PH_AI8, l1_go_f7_act_ind},
+ {ST_L1_F4, EV_TIMER3, l1_timer3},
+ {ST_L1_F4, EV_PH_DC, l1_go_f3pdown},
+
+ {ST_L1_F5, EV_PH_RES, l1_di},
+ {ST_L1_F5, EV_PH_EI, l1_di},
+ {ST_L1_F5, EV_PH_AR, l1_go_f6},
+ {ST_L1_F5, EV_PH_AI8, l1_go_f7_act_ind},
+ {ST_L1_F5, EV_TIMER3, l1_timer3},
+ {ST_L1_F5, EV_PH_DR, l1_go_f3pend},
+ {ST_L1_F5, EV_PH_DC, l1_go_f3pdown},
+
+ {ST_L1_F6, EV_PH_RES, l1_di},
+ {ST_L1_F6, EV_PH_EI, l1_di},
+ {ST_L1_F6, EV_PH_RSY, l1_go_f8},
+ {ST_L1_F6, EV_PH_AI8, l1_go_f7_act_ind},
+ {ST_L1_F6, EV_PH_DR6, l1_go_f3pend},
+ {ST_L1_F6, EV_TIMER3, l1_timer3},
+ {ST_L1_F6, EV_PH_DC, l1_go_f3pdown},
+
+ {ST_L1_F7, EV_PH_RES, l1_di_deact_ind},
+ {ST_L1_F7, EV_PH_EI, l1_di_deact_ind},
+ {ST_L1_F7, EV_PH_AR, l1_go_f6_deact_ind},
+ {ST_L1_F7, EV_PH_RSY, l1_go_f8_deact_ind},
+ {ST_L1_F7, EV_PH_DR, l1_go_f3pend_deact_ind},
+
+ {ST_L1_F8, EV_PH_RES, l1_di},
+ {ST_L1_F8, EV_PH_EI, l1_di},
+ {ST_L1_F8, EV_PH_AR, l1_go_f6},
+ {ST_L1_F8, EV_PH_DR, l1_go_f3pend},
+ {ST_L1_F8, EV_PH_AI8, l1_go_f7_act_ind},
+ {ST_L1_F8, EV_TIMER3, l1_timer3},
+ {ST_L1_F8, EV_PH_DC, l1_go_f3pdown},
+};
+
+static void l1m_debug(struct FsmInst *fi, char *fmt, ...)
+{
+ va_list args;
+ char buf[256];
+
+ va_start(args, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, args);
+ DBG(DBG_L1M, "%s", buf);
+ va_end(args);
+}
+
+static void isac_version(struct isac *cs)
+{
+ int val;
+
+ val = cs->read_isac(cs, ISAC_RBCH);
+ DBG(1, "ISAC version (%x): %s", val, ISACVer[(val >> 5) & 3]);
+}
+
+static void isac_empty_fifo(struct isac *isac, int count)
+{
+ // this also works for isacsx, since
+ // CMDR(D) register works the same
+ u_char *ptr;
+
+ DBG(DBG_IRQ, "count %d", count);
+
+ if ((isac->rcvidx + count) >= MAX_DFRAME_LEN_L1) {
+ DBG(DBG_WARN, "overrun %d", isac->rcvidx + count);
+ isac->write_isac(isac, ISAC_CMDR, ISAC_CMDR_RMC);
+ isac->rcvidx = 0;
+ return;
+ }
+ ptr = isac->rcvbuf + isac->rcvidx;
+ isac->rcvidx += count;
+ isac->read_isac_fifo(isac, ptr, count);
+ isac->write_isac(isac, ISAC_CMDR, ISAC_CMDR_RMC);
+ DBG_PACKET(DBG_RFIFO, ptr, count);
+}
+
+static void isac_fill_fifo(struct isac *isac)
+{
+ // this also works for isacsx, since
+ // CMDR(D) register works the same
+
+ int count;
+ unsigned char cmd;
+ u_char *ptr;
+
+ BUG_ON(!isac->tx_skb);
+
+ count = isac->tx_skb->len;
+ BUG_ON(count <= 0);
+
+ DBG(DBG_IRQ, "count %d", count);
+
+ if (count > 0x20) {
+ count = 0x20;
+ cmd = ISAC_CMDR_XTF;
+ } else {
+ cmd = ISAC_CMDR_XTF | ISAC_CMDR_XME;
+ }
+
+ ptr = isac->tx_skb->data;
+ skb_pull(isac->tx_skb, count);
+ isac->tx_cnt += count;
+ DBG_PACKET(DBG_XFIFO, ptr, count);
+ isac->write_isac_fifo(isac, ptr, count);
+ isac->write_isac(isac, ISAC_CMDR, cmd);
+}
+
+static void isac_retransmit(struct isac *isac)
+{
+ if (!isac->tx_skb) {
+ DBG(DBG_WARN, "no skb");
+ return;
+ }
+ skb_push(isac->tx_skb, isac->tx_cnt);
+ isac->tx_cnt = 0;
+}
+
+
+static inline void isac_cisq_interrupt(struct isac *isac)
+{
+ unsigned char val;
+
+ val = isac->read_isac(isac, ISAC_CIR0);
+ DBG(DBG_IRQ, "CIR0 %#x", val);
+ if (val & ISAC_CIR0_CIC0) {
+ DBG(DBG_IRQ, "CODR0 %#x", (val >> 2) & 0xf);
+ FsmEvent(&isac->l1m, (val >> 2) & 0xf, NULL);
+ }
+ if (val & ISAC_CIR0_CIC1) {
+ val = isac->read_isac(isac, ISAC_CIR1);
+ DBG(DBG_WARN, "ISAC CIR1 %#x", val );
+ }
+}
+
+static inline void isac_rme_interrupt(struct isac *isac)
+{
+ unsigned char val;
+ int count;
+ struct sk_buff *skb;
+
+ val = isac->read_isac(isac, ISAC_RSTA);
+ if ((val & (ISAC_RSTA_RDO | ISAC_RSTA_CRC | ISAC_RSTA_RAB) )
+ != ISAC_RSTA_CRC) {
+ DBG(DBG_WARN, "RSTA %#x, dropped", val);
+ isac->write_isac(isac, ISAC_CMDR, ISAC_CMDR_RMC);
+ goto out;
+ }
+
+ count = isac->read_isac(isac, ISAC_RBCL) & 0x1f;
+ DBG(DBG_IRQ, "RBCL %#x", count);
+ if (count == 0)
+ count = 0x20;
+
+ isac_empty_fifo(isac, count);
+ count = isac->rcvidx;
+ if (count < 1) {
+ DBG(DBG_WARN, "count %d < 1", count);
+ goto out;
+ }
+
+ skb = alloc_skb(count, GFP_ATOMIC);
+ if (!skb) {
+ DBG(DBG_WARN, "no memory, dropping\n");
+ goto out;
+ }
+ memcpy(skb_put(skb, count), isac->rcvbuf, count);
+ DBG_SKB(DBG_RPACKET, skb);
+ D_L1L2(isac, PH_DATA | INDICATION, skb);
+ out:
+ isac->rcvidx = 0;
+}
+
+static inline void isac_xpr_interrupt(struct isac *isac)
+{
+ if (!isac->tx_skb)
+ return;
+
+ if (isac->tx_skb->len > 0) {
+ isac_fill_fifo(isac);
+ return;
+ }
+ dev_kfree_skb_irq(isac->tx_skb);
+ isac->tx_cnt = 0;
+ isac->tx_skb = NULL;
+ D_L1L2(isac, PH_DATA | CONFIRM, NULL);
+}
+
+static inline void isac_exi_interrupt(struct isac *isac)
+{
+ unsigned char val;
+
+ val = isac->read_isac(isac, ISAC_EXIR);
+ DBG(2, "EXIR %#x", val);
+
+ if (val & ISAC_EXIR_XMR) {
+ DBG(DBG_WARN, "ISAC XMR");
+ isac_retransmit(isac);
+ }
+ if (val & ISAC_EXIR_XDU) {
+ DBG(DBG_WARN, "ISAC XDU");
+ isac_retransmit(isac);
+ }
+ if (val & ISAC_EXIR_MOS) { /* MOS */
+ DBG(DBG_WARN, "MOS");
+ val = isac->read_isac(isac, ISAC_MOSR);
+ DBG(2, "ISAC MOSR %#x", val);
+ }
+}
+
+void isac_irq(struct isac *isac)
+{
+ unsigned char val;
+
+ val = isac->read_isac(isac, ISAC_ISTA);
+ DBG(DBG_IRQ, "ISTA %#x", val);
+
+ if (val & ISAC_ISTA_EXI) {
+ DBG(DBG_IRQ, "EXI");
+ isac_exi_interrupt(isac);
+ }
+ if (val & ISAC_ISTA_XPR) {
+ DBG(DBG_IRQ, "XPR");
+ isac_xpr_interrupt(isac);
+ }
+ if (val & ISAC_ISTA_RME) {
+ DBG(DBG_IRQ, "RME");
+ isac_rme_interrupt(isac);
+ }
+ if (val & ISAC_ISTA_RPF) {
+ DBG(DBG_IRQ, "RPF");
+ isac_empty_fifo(isac, 0x20);
+ }
+ if (val & ISAC_ISTA_CISQ) {
+ DBG(DBG_IRQ, "CISQ");
+ isac_cisq_interrupt(isac);
+ }
+ if (val & ISAC_ISTA_RSC) {
+ DBG(DBG_WARN, "RSC");
+ }
+ if (val & ISAC_ISTA_SIN) {
+ DBG(DBG_WARN, "SIN");
+ }
+ isac->write_isac(isac, ISAC_MASK, 0xff);
+ isac->write_isac(isac, ISAC_MASK, 0x00);
+}
+
+// ======================================================================
+
+static inline void isacsx_cic_interrupt(struct isac *isac)
+{
+ unsigned char val;
+
+ val = isac->read_isac(isac, ISACSX_CIR0);
+ DBG(DBG_IRQ, "CIR0 %#x", val);
+ if (val & ISACSX_CIR0_CIC0) {
+ DBG(DBG_IRQ, "CODR0 %#x", val >> 4);
+ FsmEvent(&isac->l1m, val >> 4, NULL);
+ }
+}
+
+static inline void isacsx_rme_interrupt(struct isac *isac)
+{
+ int count;
+ struct sk_buff *skb;
+ unsigned char val;
+
+ val = isac->read_isac(isac, ISACSX_RSTAD);
+ if ((val & (ISACSX_RSTAD_VFR |
+ ISACSX_RSTAD_RDO |
+ ISACSX_RSTAD_CRC |
+ ISACSX_RSTAD_RAB))
+ != (ISACSX_RSTAD_VFR | ISACSX_RSTAD_CRC)) {
+ DBG(DBG_WARN, "RSTAD %#x, dropped", val);
+ isac->write_isac(isac, ISACSX_CMDRD, ISACSX_CMDRD_RMC);
+ goto out;
+ }
+
+ count = isac->read_isac(isac, ISACSX_RBCLD) & 0x1f;
+ DBG(DBG_IRQ, "RBCLD %#x", count);
+ if (count == 0)
+ count = 0x20;
+
+ isac_empty_fifo(isac, count);
+ // strip trailing status byte
+ count = isac->rcvidx - 1;
+ if (count < 1) {
+ DBG(DBG_WARN, "count %d < 1", count);
+ goto out;
+ }
+
+ skb = dev_alloc_skb(count);
+ if (!skb) {
+ DBG(DBG_WARN, "no memory, dropping");
+ goto out;
+ }
+ memcpy(skb_put(skb, count), isac->rcvbuf, count);
+ DBG_SKB(DBG_RPACKET, skb);
+ D_L1L2(isac, PH_DATA | INDICATION, skb);
+ out:
+ isac->rcvidx = 0;
+}
+
+static inline void isacsx_xpr_interrupt(struct isac *isac)
+{
+ if (!isac->tx_skb)
+ return;
+
+ if (isac->tx_skb->len > 0) {
+ isac_fill_fifo(isac);
+ return;
+ }
+ dev_kfree_skb_irq(isac->tx_skb);
+ isac->tx_skb = NULL;
+ isac->tx_cnt = 0;
+ D_L1L2(isac, PH_DATA | CONFIRM, NULL);
+}
+
+static inline void isacsx_icd_interrupt(struct isac *isac)
+{
+ unsigned char val;
+
+ val = isac->read_isac(isac, ISACSX_ISTAD);
+ DBG(DBG_IRQ, "ISTAD %#x", val);
+ if (val & ISACSX_ISTAD_XDU) {
+ DBG(DBG_WARN, "ISTAD XDU");
+ isac_retransmit(isac);
+ }
+ if (val & ISACSX_ISTAD_XMR) {
+ DBG(DBG_WARN, "ISTAD XMR");
+ isac_retransmit(isac);
+ }
+ if (val & ISACSX_ISTAD_XPR) {
+ DBG(DBG_IRQ, "ISTAD XPR");
+ isacsx_xpr_interrupt(isac);
+ }
+ if (val & ISACSX_ISTAD_RFO) {
+ DBG(DBG_WARN, "ISTAD RFO");
+ isac->write_isac(isac, ISACSX_CMDRD, ISACSX_CMDRD_RMC);
+ }
+ if (val & ISACSX_ISTAD_RME) {
+ DBG(DBG_IRQ, "ISTAD RME");
+ isacsx_rme_interrupt(isac);
+ }
+ if (val & ISACSX_ISTAD_RPF) {
+ DBG(DBG_IRQ, "ISTAD RPF");
+ isac_empty_fifo(isac, 0x20);
+ }
+}
+
+void isacsx_irq(struct isac *isac)
+{
+ unsigned char val;
+
+ val = isac->read_isac(isac, ISACSX_ISTA);
+ DBG(DBG_IRQ, "ISTA %#x", val);
+
+ if (val & ISACSX_ISTA_ICD)
+ isacsx_icd_interrupt(isac);
+ if (val & ISACSX_ISTA_CIC)
+ isacsx_cic_interrupt(isac);
+}
+
+void isac_init(struct isac *isac)
+{
+ isac->tx_skb = NULL;
+ isac->l1m.fsm = &l1fsm;
+ isac->l1m.state = ST_L1_RESET;
+#ifdef CONFIG_HISAX_DEBUG
+ isac->l1m.debug = 1;
+#else
+ isac->l1m.debug = 0;
+#endif
+ isac->l1m.userdata = isac;
+ isac->l1m.printdebug = l1m_debug;
+ FsmInitTimer(&isac->l1m, &isac->timer);
+}
+
+void isac_setup(struct isac *isac)
+{
+ int val, eval;
+
+ isac->type = TYPE_ISAC;
+ isac_version(isac);
+
+ ph_command(isac, ISAC_CMD_RES);
+
+ isac->write_isac(isac, ISAC_MASK, 0xff);
+ isac->mocr = 0xaa;
+ if (test_bit(ISAC_IOM1, &isac->flags)) {
+ /* IOM 1 Mode */
+ isac->write_isac(isac, ISAC_ADF2, 0x0);
+ isac->write_isac(isac, ISAC_SPCR, 0xa);
+ isac->write_isac(isac, ISAC_ADF1, 0x2);
+ isac->write_isac(isac, ISAC_STCR, 0x70);
+ isac->write_isac(isac, ISAC_MODE, 0xc9);
+ } else {
+ /* IOM 2 Mode */
+ if (!isac->adf2)
+ isac->adf2 = 0x80;
+ isac->write_isac(isac, ISAC_ADF2, isac->adf2);
+ isac->write_isac(isac, ISAC_SQXR, 0x2f);
+ isac->write_isac(isac, ISAC_SPCR, 0x00);
+ isac->write_isac(isac, ISAC_STCR, 0x70);
+ isac->write_isac(isac, ISAC_MODE, 0xc9);
+ isac->write_isac(isac, ISAC_TIMR, 0x00);
+ isac->write_isac(isac, ISAC_ADF1, 0x00);
+ }
+ val = isac->read_isac(isac, ISAC_STAR);
+ DBG(2, "ISAC STAR %x", val);
+ val = isac->read_isac(isac, ISAC_MODE);
+ DBG(2, "ISAC MODE %x", val);
+ val = isac->read_isac(isac, ISAC_ADF2);
+ DBG(2, "ISAC ADF2 %x", val);
+ val = isac->read_isac(isac, ISAC_ISTA);
+ DBG(2, "ISAC ISTA %x", val);
+ if (val & 0x01) {
+ eval = isac->read_isac(isac, ISAC_EXIR);
+ DBG(2, "ISAC EXIR %x", eval);
+ }
+ val = isac->read_isac(isac, ISAC_CIR0);
+ DBG(2, "ISAC CIR0 %x", val);
+ FsmEvent(&isac->l1m, (val >> 2) & 0xf, NULL);
+
+ isac->write_isac(isac, ISAC_MASK, 0x0);
+ // RESET Receiver and Transmitter
+ isac->write_isac(isac, ISAC_CMDR, ISAC_CMDR_XRES | ISAC_CMDR_RRES);
+}
+
+void isacsx_setup(struct isac *isac)
+{
+ isac->type = TYPE_ISACSX;
+ // clear LDD
+ isac->write_isac(isac, ISACSX_TR_CONF0, 0x00);
+ // enable transmitter
+ isac->write_isac(isac, ISACSX_TR_CONF2, 0x00);
+ // transparent mode 0, RAC, stop/go
+ isac->write_isac(isac, ISACSX_MODED, 0xc9);
+ // all HDLC IRQ unmasked
+ isac->write_isac(isac, ISACSX_MASKD, 0x03);
+ // unmask ICD, CID IRQs
+ isac->write_isac(isac, ISACSX_MASK,
+ ~(ISACSX_ISTA_ICD | ISACSX_ISTA_CIC));
+}
+
+void isac_d_l2l1(struct hisax_if *hisax_d_if, int pr, void *arg)
+{
+ struct isac *isac = hisax_d_if->priv;
+ struct sk_buff *skb = arg;
+
+ DBG(DBG_PR, "pr %#x", pr);
+
+ switch (pr) {
+ case PH_ACTIVATE | REQUEST:
+ FsmEvent(&isac->l1m, EV_PH_ACTIVATE_REQ, NULL);
+ break;
+ case PH_DEACTIVATE | REQUEST:
+ FsmEvent(&isac->l1m, EV_PH_DEACTIVATE_REQ, NULL);
+ break;
+ case PH_DATA | REQUEST:
+ DBG(DBG_PR, "PH_DATA REQUEST len %d", skb->len);
+ DBG_SKB(DBG_XPACKET, skb);
+ if (isac->l1m.state != ST_L1_F7) {
+ DBG(1, "L1 wrong state %d\n", isac->l1m.state);
+ dev_kfree_skb(skb);
+ break;
+ }
+ BUG_ON(isac->tx_skb);
+
+ isac->tx_skb = skb;
+ isac_fill_fifo(isac);
+ break;
+ }
+}
+
+static int __init hisax_isac_init(void)
+{
+ printk(KERN_INFO "hisax_isac: ISAC-S/ISAC-SX ISDN driver v0.1.0\n");
+
+ l1fsm.state_count = L1_STATE_COUNT;
+ l1fsm.event_count = L1_EVENT_COUNT;
+ l1fsm.strState = strL1State;
+ l1fsm.strEvent = strL1Event;
+ return FsmNew(&l1fsm, L1FnList, ARRAY_SIZE(L1FnList));
+}
+
+static void __exit hisax_isac_exit(void)
+{
+ FsmFree(&l1fsm);
+}
+
+EXPORT_SYMBOL(isac_init);
+EXPORT_SYMBOL(isac_d_l2l1);
+
+EXPORT_SYMBOL(isacsx_setup);
+EXPORT_SYMBOL(isacsx_irq);
+
+EXPORT_SYMBOL(isac_setup);
+EXPORT_SYMBOL(isac_irq);
+
+module_init(hisax_isac_init);
+module_exit(hisax_isac_exit);
diff --git a/drivers/isdn/hisax/hisax_isac.h b/drivers/isdn/hisax/hisax_isac.h
new file mode 100644
index 0000000..08890cf
--- /dev/null
+++ b/drivers/isdn/hisax/hisax_isac.h
@@ -0,0 +1,45 @@
+#ifndef __HISAX_ISAC_H__
+#define __HISAX_ISAC_H__
+
+#include <linux/kernel.h>
+#include "fsm.h"
+#include "hisax_if.h"
+
+#define TIMER3_VALUE 7000
+#define MAX_DFRAME_LEN_L1 300
+
+#define ISAC_IOM1 0
+
+struct isac {
+ void *priv;
+
+ u_long flags;
+ struct hisax_d_if hisax_d_if;
+ struct FsmInst l1m;
+ struct FsmTimer timer;
+ u_char mocr;
+ u_char adf2;
+ int type;
+
+ u_char rcvbuf[MAX_DFRAME_LEN_L1];
+ int rcvidx;
+
+ struct sk_buff *tx_skb;
+ int tx_cnt;
+
+ u_char (*read_isac) (struct isac *, u_char);
+ void (*write_isac) (struct isac *, u_char, u_char);
+ void (*read_isac_fifo) (struct isac *, u_char *, int);
+ void (*write_isac_fifo)(struct isac *, u_char *, int);
+};
+
+void isac_init(struct isac *isac);
+void isac_d_l2l1(struct hisax_if *hisax_d_if, int pr, void *arg);
+
+void isac_setup(struct isac *isac);
+void isac_irq(struct isac *isac);
+
+void isacsx_setup(struct isac *isac);
+void isacsx_irq(struct isac *isac);
+
+#endif
diff --git a/drivers/isdn/hisax/hscx.c b/drivers/isdn/hisax/hscx.c
new file mode 100644
index 0000000..c8f9951
--- /dev/null
+++ b/drivers/isdn/hisax/hscx.c
@@ -0,0 +1,276 @@
+/* $Id: hscx.c,v 1.24.2.4 2004/01/24 20:47:23 keil Exp $
+ *
+ * HSCX specific routines
+ *
+ * Author Karsten Keil
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "hscx.h"
+#include "isac.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+
+static char *HSCXVer[] =
+{"A1", "?1", "A2", "?3", "A3", "V2.1", "?6", "?7",
+ "?8", "?9", "?10", "?11", "?12", "?13", "?14", "???"};
+
+int
+HscxVersion(struct IsdnCardState *cs, char *s)
+{
+ int verA, verB;
+
+ verA = cs->BC_Read_Reg(cs, 0, HSCX_VSTR) & 0xf;
+ verB = cs->BC_Read_Reg(cs, 1, HSCX_VSTR) & 0xf;
+ printk(KERN_INFO "%s HSCX version A: %s B: %s\n", s,
+ HSCXVer[verA], HSCXVer[verB]);
+ if ((verA == 0) | (verA == 0xf) | (verB == 0) | (verB == 0xf))
+ return (1);
+ else
+ return (0);
+}
+
+void
+modehscx(struct BCState *bcs, int mode, int bc)
+{
+ struct IsdnCardState *cs = bcs->cs;
+ int hscx = bcs->hw.hscx.hscx;
+
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "hscx %c mode %d ichan %d",
+ 'A' + hscx, mode, bc);
+ bcs->mode = mode;
+ bcs->channel = bc;
+ cs->BC_Write_Reg(cs, hscx, HSCX_XAD1, 0xFF);
+ cs->BC_Write_Reg(cs, hscx, HSCX_XAD2, 0xFF);
+ cs->BC_Write_Reg(cs, hscx, HSCX_RAH2, 0xFF);
+ cs->BC_Write_Reg(cs, hscx, HSCX_XBCH, 0x0);
+ cs->BC_Write_Reg(cs, hscx, HSCX_RLCR, 0x0);
+ cs->BC_Write_Reg(cs, hscx, HSCX_CCR1,
+ test_bit(HW_IPAC, &cs->HW_Flags) ? 0x82 : 0x85);
+ cs->BC_Write_Reg(cs, hscx, HSCX_CCR2, 0x30);
+ cs->BC_Write_Reg(cs, hscx, HSCX_XCCR, 7);
+ cs->BC_Write_Reg(cs, hscx, HSCX_RCCR, 7);
+
+ /* Switch IOM 1 SSI */
+ if (test_bit(HW_IOM1, &cs->HW_Flags) && (hscx == 0))
+ bc = 1 - bc;
+
+ if (bc == 0) {
+ cs->BC_Write_Reg(cs, hscx, HSCX_TSAX,
+ test_bit(HW_IOM1, &cs->HW_Flags) ? 0x7 : bcs->hw.hscx.tsaxr0);
+ cs->BC_Write_Reg(cs, hscx, HSCX_TSAR,
+ test_bit(HW_IOM1, &cs->HW_Flags) ? 0x7 : bcs->hw.hscx.tsaxr0);
+ } else {
+ cs->BC_Write_Reg(cs, hscx, HSCX_TSAX, bcs->hw.hscx.tsaxr1);
+ cs->BC_Write_Reg(cs, hscx, HSCX_TSAR, bcs->hw.hscx.tsaxr1);
+ }
+ switch (mode) {
+ case (L1_MODE_NULL):
+ cs->BC_Write_Reg(cs, hscx, HSCX_TSAX, 0x1f);
+ cs->BC_Write_Reg(cs, hscx, HSCX_TSAR, 0x1f);
+ cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0x84);
+ break;
+ case (L1_MODE_TRANS):
+ cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0xe4);
+ break;
+ case (L1_MODE_HDLC):
+ cs->BC_Write_Reg(cs, hscx, HSCX_CCR1,
+ test_bit(HW_IPAC, &cs->HW_Flags) ? 0x8a : 0x8d);
+ cs->BC_Write_Reg(cs, hscx, HSCX_MODE, 0x8c);
+ break;
+ }
+ if (mode)
+ cs->BC_Write_Reg(cs, hscx, HSCX_CMDR, 0x41);
+ cs->BC_Write_Reg(cs, hscx, HSCX_ISTA, 0x00);
+}
+
+void
+hscx_l2l1(struct PStack *st, int pr, void *arg)
+{
+ struct BCState *bcs = st->l1.bcs;
+ u_long flags;
+ struct sk_buff *skb = arg;
+
+ 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);
+ bcs->hw.hscx.count = 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 "hscx_l2l1: this shouldn't happen\n");
+ } else {
+ test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+ bcs->tx_skb = skb;
+ bcs->hw.hscx.count = 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);
+ modehscx(bcs, st->l1.mode, st->l1.bc);
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | REQUEST):
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | CONFIRM):
+ spin_lock_irqsave(&bcs->cs->lock, flags);
+ test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ modehscx(bcs, 0, st->l1.bc);
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
+ st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+ break;
+ }
+}
+
+static void
+close_hscxstate(struct BCState *bcs)
+{
+ modehscx(bcs, 0, bcs->channel);
+ if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+ kfree(bcs->hw.hscx.rcvbuf);
+ bcs->hw.hscx.rcvbuf = NULL;
+ kfree(bcs->blog);
+ bcs->blog = 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);
+ }
+ }
+}
+
+int
+open_hscxstate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+ if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+ if (!(bcs->hw.hscx.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "HiSax: No memory for hscx.rcvbuf\n");
+ test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+ return (1);
+ }
+ if (!(bcs->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "HiSax: No memory for bcs->blog\n");
+ test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+ kfree(bcs->hw.hscx.rcvbuf);
+ bcs->hw.hscx.rcvbuf = NULL;
+ return (2);
+ }
+ 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);
+ bcs->event = 0;
+ bcs->hw.hscx.rcvidx = 0;
+ bcs->tx_cnt = 0;
+ return (0);
+}
+
+static int
+setstack_hscx(struct PStack *st, struct BCState *bcs)
+{
+ bcs->channel = st->l1.bc;
+ if (open_hscxstate(st->l1.hardware, bcs))
+ return (-1);
+ st->l1.bcs = bcs;
+ st->l2.l2l1 = hscx_l2l1;
+ setstack_manager(st);
+ bcs->st = st;
+ setstack_l1_B(st);
+ return (0);
+}
+
+void
+clear_pending_hscx_ints(struct IsdnCardState *cs)
+{
+ int val, eval;
+
+ val = cs->BC_Read_Reg(cs, 1, HSCX_ISTA);
+ debugl1(cs, "HSCX B ISTA %x", val);
+ if (val & 0x01) {
+ eval = cs->BC_Read_Reg(cs, 1, HSCX_EXIR);
+ debugl1(cs, "HSCX B EXIR %x", eval);
+ }
+ if (val & 0x02) {
+ eval = cs->BC_Read_Reg(cs, 0, HSCX_EXIR);
+ debugl1(cs, "HSCX A EXIR %x", eval);
+ }
+ val = cs->BC_Read_Reg(cs, 0, HSCX_ISTA);
+ debugl1(cs, "HSCX A ISTA %x", val);
+ val = cs->BC_Read_Reg(cs, 1, HSCX_STAR);
+ debugl1(cs, "HSCX B STAR %x", val);
+ val = cs->BC_Read_Reg(cs, 0, HSCX_STAR);
+ debugl1(cs, "HSCX A STAR %x", val);
+ /* disable all IRQ */
+ cs->BC_Write_Reg(cs, 0, HSCX_MASK, 0xFF);
+ cs->BC_Write_Reg(cs, 1, HSCX_MASK, 0xFF);
+}
+
+void
+inithscx(struct IsdnCardState *cs)
+{
+ cs->bcs[0].BC_SetStack = setstack_hscx;
+ cs->bcs[1].BC_SetStack = setstack_hscx;
+ cs->bcs[0].BC_Close = close_hscxstate;
+ cs->bcs[1].BC_Close = close_hscxstate;
+ cs->bcs[0].hw.hscx.hscx = 0;
+ cs->bcs[1].hw.hscx.hscx = 1;
+ cs->bcs[0].hw.hscx.tsaxr0 = 0x2f;
+ cs->bcs[0].hw.hscx.tsaxr1 = 3;
+ cs->bcs[1].hw.hscx.tsaxr0 = 0x2f;
+ cs->bcs[1].hw.hscx.tsaxr1 = 3;
+ modehscx(cs->bcs, 0, 0);
+ modehscx(cs->bcs + 1, 0, 0);
+}
+
+void
+inithscxisac(struct IsdnCardState *cs, int part)
+{
+ if (part & 1) {
+ clear_pending_isac_ints(cs);
+ clear_pending_hscx_ints(cs);
+ initisac(cs);
+ inithscx(cs);
+ }
+ if (part & 2) {
+ /* Reenable all IRQ */
+ cs->writeisac(cs, ISAC_MASK, 0);
+ cs->BC_Write_Reg(cs, 0, HSCX_MASK, 0);
+ cs->BC_Write_Reg(cs, 1, HSCX_MASK, 0);
+ /* RESET Receiver and Transmitter */
+ cs->writeisac(cs, ISAC_CMDR, 0x41);
+ }
+}
diff --git a/drivers/isdn/hisax/hscx.h b/drivers/isdn/hisax/hscx.h
new file mode 100644
index 0000000..268bfd3
--- /dev/null
+++ b/drivers/isdn/hisax/hscx.h
@@ -0,0 +1,41 @@
+/* $Id: hscx.h,v 1.8.2.2 2004/01/12 22:52:26 keil Exp $
+ *
+ * HSCX specific defines
+ *
+ * Author Karsten Keil
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/* All Registers original Siemens Spec */
+
+#define HSCX_ISTA 0x20
+#define HSCX_CCR1 0x2f
+#define HSCX_CCR2 0x2c
+#define HSCX_TSAR 0x31
+#define HSCX_TSAX 0x30
+#define HSCX_XCCR 0x32
+#define HSCX_RCCR 0x33
+#define HSCX_MODE 0x22
+#define HSCX_CMDR 0x21
+#define HSCX_EXIR 0x24
+#define HSCX_XAD1 0x24
+#define HSCX_XAD2 0x25
+#define HSCX_RAH2 0x27
+#define HSCX_RSTA 0x27
+#define HSCX_TIMR 0x23
+#define HSCX_STAR 0x21
+#define HSCX_RBCL 0x25
+#define HSCX_XBCH 0x2d
+#define HSCX_VSTR 0x2e
+#define HSCX_RLCR 0x2e
+#define HSCX_MASK 0x20
+
+extern int HscxVersion(struct IsdnCardState *cs, char *s);
+extern void modehscx(struct BCState *bcs, int mode, int bc);
+extern void clear_pending_hscx_ints(struct IsdnCardState *cs);
+extern void inithscx(struct IsdnCardState *cs);
+extern void inithscxisac(struct IsdnCardState *cs, int part);
diff --git a/drivers/isdn/hisax/hscx_irq.c b/drivers/isdn/hisax/hscx_irq.c
new file mode 100644
index 0000000..7b1ad5e
--- /dev/null
+++ b/drivers/isdn/hisax/hscx_irq.c
@@ -0,0 +1,292 @@
+/* $Id: hscx_irq.c,v 1.18.2.3 2004/02/11 13:21:34 keil Exp $
+ *
+ * low level b-channel stuff for Siemens HSCX
+ *
+ * Author Karsten Keil
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * This is an include file for fast inline IRQ stuff
+ *
+ */
+
+
+static inline void
+waitforCEC(struct IsdnCardState *cs, int hscx)
+{
+ int to = 50;
+
+ while ((READHSCX(cs, hscx, HSCX_STAR) & 0x04) && to) {
+ udelay(1);
+ to--;
+ }
+ if (!to)
+ printk(KERN_WARNING "HiSax: waitforCEC timeout\n");
+}
+
+
+static inline void
+waitforXFW(struct IsdnCardState *cs, int hscx)
+{
+ int to = 50;
+
+ while ((!(READHSCX(cs, hscx, HSCX_STAR) & 0x44) == 0x40) && to) {
+ udelay(1);
+ to--;
+ }
+ if (!to)
+ printk(KERN_WARNING "HiSax: waitforXFW timeout\n");
+}
+
+static inline void
+WriteHSCXCMDR(struct IsdnCardState *cs, int hscx, u_char data)
+{
+ waitforCEC(cs, hscx);
+ WRITEHSCX(cs, hscx, HSCX_CMDR, data);
+}
+
+
+
+static void
+hscx_empty_fifo(struct BCState *bcs, int count)
+{
+ u_char *ptr;
+ struct IsdnCardState *cs = bcs->cs;
+
+ if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+ debugl1(cs, "hscx_empty_fifo");
+
+ if (bcs->hw.hscx.rcvidx + count > HSCX_BUFMAX) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "hscx_empty_fifo: incoming packet too large");
+ WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x80);
+ bcs->hw.hscx.rcvidx = 0;
+ return;
+ }
+ ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx;
+ bcs->hw.hscx.rcvidx += count;
+ READHSCXFIFO(cs, bcs->hw.hscx.hscx, ptr, count);
+ WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x80);
+ if (cs->debug & L1_DEB_HSCX_FIFO) {
+ char *t = bcs->blog;
+
+ t += sprintf(t, "hscx_empty_fifo %c cnt %d",
+ bcs->hw.hscx.hscx ? 'B' : 'A', count);
+ QuickHex(t, ptr, count);
+ debugl1(cs, bcs->blog);
+ }
+}
+
+static void
+hscx_fill_fifo(struct BCState *bcs)
+{
+ struct IsdnCardState *cs = bcs->cs;
+ int more, count;
+ int fifo_size = test_bit(HW_IPAC, &cs->HW_Flags)? 64: 32;
+ u_char *ptr;
+
+ if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+ debugl1(cs, "hscx_fill_fifo");
+
+ if (!bcs->tx_skb)
+ return;
+ if (bcs->tx_skb->len <= 0)
+ return;
+
+ more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0;
+ if (bcs->tx_skb->len > fifo_size) {
+ more = !0;
+ count = fifo_size;
+ } else
+ count = bcs->tx_skb->len;
+
+ waitforXFW(cs, bcs->hw.hscx.hscx);
+ ptr = bcs->tx_skb->data;
+ skb_pull(bcs->tx_skb, count);
+ bcs->tx_cnt -= count;
+ bcs->hw.hscx.count += count;
+ WRITEHSCXFIFO(cs, bcs->hw.hscx.hscx, ptr, count);
+ WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, more ? 0x8 : 0xa);
+ if (cs->debug & L1_DEB_HSCX_FIFO) {
+ char *t = bcs->blog;
+
+ t += sprintf(t, "hscx_fill_fifo %c cnt %d",
+ bcs->hw.hscx.hscx ? 'B' : 'A', count);
+ QuickHex(t, ptr, count);
+ debugl1(cs, bcs->blog);
+ }
+}
+
+static void
+hscx_interrupt(struct IsdnCardState *cs, u_char val, u_char hscx)
+{
+ u_char r;
+ struct BCState *bcs = cs->bcs + hscx;
+ struct sk_buff *skb;
+ int fifo_size = test_bit(HW_IPAC, &cs->HW_Flags)? 64: 32;
+ int count;
+
+ if (!test_bit(BC_FLG_INIT, &bcs->Flag))
+ return;
+
+ if (val & 0x80) { /* RME */
+ r = READHSCX(cs, hscx, HSCX_RSTA);
+ if ((r & 0xf0) != 0xa0) {
+ if (!(r & 0x80)) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "HSCX invalid frame");
+#ifdef ERROR_STATISTIC
+ bcs->err_inv++;
+#endif
+ }
+ if ((r & 0x40) && bcs->mode) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "HSCX RDO mode=%d",
+ bcs->mode);
+#ifdef ERROR_STATISTIC
+ bcs->err_rdo++;
+#endif
+ }
+ if (!(r & 0x20)) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "HSCX CRC error");
+#ifdef ERROR_STATISTIC
+ bcs->err_crc++;
+#endif
+ }
+ WriteHSCXCMDR(cs, hscx, 0x80);
+ } else {
+ count = READHSCX(cs, hscx, HSCX_RBCL) & (
+ test_bit(HW_IPAC, &cs->HW_Flags)? 0x3f: 0x1f);
+ if (count == 0)
+ count = fifo_size;
+ hscx_empty_fifo(bcs, count);
+ if ((count = bcs->hw.hscx.rcvidx - 1) > 0) {
+ if (cs->debug & L1_DEB_HSCX_FIFO)
+ debugl1(cs, "HX Frame %d", count);
+ if (!(skb = dev_alloc_skb(count)))
+ printk(KERN_WARNING "HSCX: receive out of memory\n");
+ else {
+ memcpy(skb_put(skb, count), bcs->hw.hscx.rcvbuf, count);
+ skb_queue_tail(&bcs->rqueue, skb);
+ }
+ }
+ }
+ bcs->hw.hscx.rcvidx = 0;
+ schedule_event(bcs, B_RCVBUFREADY);
+ }
+ if (val & 0x40) { /* RPF */
+ hscx_empty_fifo(bcs, fifo_size);
+ if (bcs->mode == L1_MODE_TRANS) {
+ /* receive audio data */
+ if (!(skb = dev_alloc_skb(fifo_size)))
+ printk(KERN_WARNING "HiSax: receive out of memory\n");
+ else {
+ memcpy(skb_put(skb, fifo_size), bcs->hw.hscx.rcvbuf, fifo_size);
+ skb_queue_tail(&bcs->rqueue, skb);
+ }
+ bcs->hw.hscx.rcvidx = 0;
+ schedule_event(bcs, B_RCVBUFREADY);
+ }
+ }
+ if (val & 0x10) { /* XPR */
+ if (bcs->tx_skb) {
+ if (bcs->tx_skb->len) {
+ hscx_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.hscx.count;
+ spin_unlock_irqrestore(&bcs->aclock, flags);
+ schedule_event(bcs, B_ACKPENDING);
+ }
+ dev_kfree_skb_irq(bcs->tx_skb);
+ bcs->hw.hscx.count = 0;
+ bcs->tx_skb = NULL;
+ }
+ }
+ if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+ bcs->hw.hscx.count = 0;
+ test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+ hscx_fill_fifo(bcs);
+ } else {
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ schedule_event(bcs, B_XMTBUFREADY);
+ }
+ }
+}
+
+static void
+hscx_int_main(struct IsdnCardState *cs, u_char val)
+{
+
+ u_char exval;
+ struct BCState *bcs;
+
+ if (val & 0x01) {
+ bcs = cs->bcs + 1;
+ exval = READHSCX(cs, 1, HSCX_EXIR);
+ if (exval & 0x40) {
+ if (bcs->mode == 1)
+ hscx_fill_fifo(bcs);
+ else {
+#ifdef ERROR_STATISTIC
+ bcs->err_tx++;
+#endif
+ /* Here we lost an TX interrupt, so
+ * restart transmitting the whole frame.
+ */
+ if (bcs->tx_skb) {
+ skb_push(bcs->tx_skb, bcs->hw.hscx.count);
+ bcs->tx_cnt += bcs->hw.hscx.count;
+ bcs->hw.hscx.count = 0;
+ }
+ WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x01);
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "HSCX B EXIR %x Lost TX", exval);
+ }
+ } else if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "HSCX B EXIR %x", exval);
+ }
+ if (val & 0xf8) {
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "HSCX B interrupt %x", val);
+ hscx_interrupt(cs, val, 1);
+ }
+ if (val & 0x02) {
+ bcs = cs->bcs;
+ exval = READHSCX(cs, 0, HSCX_EXIR);
+ if (exval & 0x40) {
+ if (bcs->mode == L1_MODE_TRANS)
+ hscx_fill_fifo(bcs);
+ else {
+ /* Here we lost an TX interrupt, so
+ * restart transmitting the whole frame.
+ */
+#ifdef ERROR_STATISTIC
+ bcs->err_tx++;
+#endif
+ if (bcs->tx_skb) {
+ skb_push(bcs->tx_skb, bcs->hw.hscx.count);
+ bcs->tx_cnt += bcs->hw.hscx.count;
+ bcs->hw.hscx.count = 0;
+ }
+ WriteHSCXCMDR(cs, bcs->hw.hscx.hscx, 0x01);
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "HSCX A EXIR %x Lost TX", exval);
+ }
+ } else if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "HSCX A EXIR %x", exval);
+ }
+ if (val & 0x04) {
+ exval = READHSCX(cs, 0, HSCX_ISTA);
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "HSCX A interrupt %x", exval);
+ hscx_interrupt(cs, exval, 0);
+ }
+}
diff --git a/drivers/isdn/hisax/icc.c b/drivers/isdn/hisax/icc.c
new file mode 100644
index 0000000..682cac3
--- /dev/null
+++ b/drivers/isdn/hisax/icc.c
@@ -0,0 +1,683 @@
+/* $Id: icc.c,v 1.8.2.3 2004/01/13 14:31:25 keil Exp $
+ *
+ * ICC specific routines
+ *
+ * Author Matt Henderson & Guy Ellis
+ * Copyright by Traverse Technologies Pty Ltd, www.travers.com.au
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * 1999.6.25 Initial implementation of routines for Siemens ISDN
+ * Communication Controller PEB 2070 based on the ISAC routines
+ * written by Karsten Keil.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "icc.h"
+// #include "arcofi.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+
+#define DBUSY_TIMER_VALUE 80
+#define ARCOFI_USE 0
+
+static char *ICCVer[] =
+{"2070 A1/A3", "2070 B1", "2070 B2/B3", "2070 V2.4"};
+
+void
+ICCVersion(struct IsdnCardState *cs, char *s)
+{
+ int val;
+
+ val = cs->readisac(cs, ICC_RBCH);
+ printk(KERN_INFO "%s ICC version (%x): %s\n", s, val, ICCVer[(val >> 5) & 3]);
+}
+
+static void
+ph_command(struct IsdnCardState *cs, unsigned int command)
+{
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ph_command %x", command);
+ cs->writeisac(cs, ICC_CIX0, (command << 2) | 3);
+}
+
+
+static void
+icc_new_ph(struct IsdnCardState *cs)
+{
+ switch (cs->dc.icc.ph_state) {
+ case (ICC_IND_EI1):
+ ph_command(cs, ICC_CMD_DI);
+ l1_msg(cs, HW_RESET | INDICATION, NULL);
+ break;
+ case (ICC_IND_DC):
+ l1_msg(cs, HW_DEACTIVATE | CONFIRM, NULL);
+ break;
+ case (ICC_IND_DR):
+ l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
+ break;
+ case (ICC_IND_PU):
+ l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
+ break;
+ case (ICC_IND_FJ):
+ l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+ break;
+ case (ICC_IND_AR):
+ l1_msg(cs, HW_INFO2 | INDICATION, NULL);
+ break;
+ case (ICC_IND_AI):
+ l1_msg(cs, HW_INFO4 | INDICATION, NULL);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+icc_bh(struct work_struct *work)
+{
+ struct IsdnCardState *cs =
+ container_of(work, struct IsdnCardState, tqueue);
+ struct PStack *stptr;
+
+ if (!cs)
+ return;
+ if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) {
+ if (cs->debug)
+ debugl1(cs, "D-Channel Busy cleared");
+ stptr = cs->stlist;
+ while (stptr != NULL) {
+ stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL);
+ stptr = stptr->next;
+ }
+ }
+ if (test_and_clear_bit(D_L1STATECHANGE, &cs->event))
+ icc_new_ph(cs);
+ if (test_and_clear_bit(D_RCVBUFREADY, &cs->event))
+ DChannel_proc_rcv(cs);
+ if (test_and_clear_bit(D_XMTBUFREADY, &cs->event))
+ DChannel_proc_xmt(cs);
+#if ARCOFI_USE
+ if (!test_bit(HW_ARCOFI, &cs->HW_Flags))
+ return;
+ if (test_and_clear_bit(D_RX_MON1, &cs->event))
+ arcofi_fsm(cs, ARCOFI_RX_END, NULL);
+ if (test_and_clear_bit(D_TX_MON1, &cs->event))
+ arcofi_fsm(cs, ARCOFI_TX_END, NULL);
+#endif
+}
+
+static void
+icc_empty_fifo(struct IsdnCardState *cs, int count)
+{
+ u_char *ptr;
+
+ if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
+ debugl1(cs, "icc_empty_fifo");
+
+ if ((cs->rcvidx + count) >= MAX_DFRAME_LEN_L1) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "icc_empty_fifo overrun %d",
+ cs->rcvidx + count);
+ cs->writeisac(cs, ICC_CMDR, 0x80);
+ cs->rcvidx = 0;
+ return;
+ }
+ ptr = cs->rcvbuf + cs->rcvidx;
+ cs->rcvidx += count;
+ cs->readisacfifo(cs, ptr, count);
+ cs->writeisac(cs, ICC_CMDR, 0x80);
+ if (cs->debug & L1_DEB_ISAC_FIFO) {
+ char *t = cs->dlog;
+
+ t += sprintf(t, "icc_empty_fifo cnt %d", count);
+ QuickHex(t, ptr, count);
+ debugl1(cs, cs->dlog);
+ }
+}
+
+static void
+icc_fill_fifo(struct IsdnCardState *cs)
+{
+ int count, more;
+ u_char *ptr;
+
+ if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
+ debugl1(cs, "icc_fill_fifo");
+
+ if (!cs->tx_skb)
+ return;
+
+ count = cs->tx_skb->len;
+ if (count <= 0)
+ return;
+
+ more = 0;
+ if (count > 32) {
+ more = !0;
+ count = 32;
+ }
+ ptr = cs->tx_skb->data;
+ skb_pull(cs->tx_skb, count);
+ cs->tx_cnt += count;
+ cs->writeisacfifo(cs, ptr, count);
+ cs->writeisac(cs, ICC_CMDR, more ? 0x8 : 0xa);
+ if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+ debugl1(cs, "icc_fill_fifo dbusytimer running");
+ del_timer(&cs->dbusytimer);
+ }
+ init_timer(&cs->dbusytimer);
+ cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
+ add_timer(&cs->dbusytimer);
+ if (cs->debug & L1_DEB_ISAC_FIFO) {
+ char *t = cs->dlog;
+
+ t += sprintf(t, "icc_fill_fifo cnt %d", count);
+ QuickHex(t, ptr, count);
+ debugl1(cs, cs->dlog);
+ }
+}
+
+void
+icc_interrupt(struct IsdnCardState *cs, u_char val)
+{
+ u_char exval, v1;
+ struct sk_buff *skb;
+ unsigned int count;
+
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ICC interrupt %x", val);
+ if (val & 0x80) { /* RME */
+ exval = cs->readisac(cs, ICC_RSTA);
+ if ((exval & 0x70) != 0x20) {
+ if (exval & 0x40) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "ICC RDO");
+#ifdef ERROR_STATISTIC
+ cs->err_rx++;
+#endif
+ }
+ if (!(exval & 0x20)) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "ICC CRC error");
+#ifdef ERROR_STATISTIC
+ cs->err_crc++;
+#endif
+ }
+ cs->writeisac(cs, ICC_CMDR, 0x80);
+ } else {
+ count = cs->readisac(cs, ICC_RBCL) & 0x1f;
+ if (count == 0)
+ count = 32;
+ icc_empty_fifo(cs, count);
+ if ((count = cs->rcvidx) > 0) {
+ cs->rcvidx = 0;
+ if (!(skb = alloc_skb(count, GFP_ATOMIC)))
+ printk(KERN_WARNING "HiSax: D receive out of memory\n");
+ else {
+ memcpy(skb_put(skb, count), cs->rcvbuf, count);
+ skb_queue_tail(&cs->rq, skb);
+ }
+ }
+ }
+ cs->rcvidx = 0;
+ schedule_event(cs, D_RCVBUFREADY);
+ }
+ if (val & 0x40) { /* RPF */
+ icc_empty_fifo(cs, 32);
+ }
+ if (val & 0x20) { /* RSC */
+ /* never */
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "ICC RSC interrupt");
+ }
+ if (val & 0x10) { /* XPR */
+ if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+ del_timer(&cs->dbusytimer);
+ if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+ schedule_event(cs, D_CLEARBUSY);
+ if (cs->tx_skb) {
+ if (cs->tx_skb->len) {
+ icc_fill_fifo(cs);
+ goto afterXPR;
+ } else {
+ dev_kfree_skb_irq(cs->tx_skb);
+ cs->tx_cnt = 0;
+ cs->tx_skb = NULL;
+ }
+ }
+ if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
+ cs->tx_cnt = 0;
+ icc_fill_fifo(cs);
+ } else
+ schedule_event(cs, D_XMTBUFREADY);
+ }
+ afterXPR:
+ if (val & 0x04) { /* CISQ */
+ exval = cs->readisac(cs, ICC_CIR0);
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ICC CIR0 %02X", exval );
+ if (exval & 2) {
+ cs->dc.icc.ph_state = (exval >> 2) & 0xf;
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ph_state change %x", cs->dc.icc.ph_state);
+ schedule_event(cs, D_L1STATECHANGE);
+ }
+ if (exval & 1) {
+ exval = cs->readisac(cs, ICC_CIR1);
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ICC CIR1 %02X", exval );
+ }
+ }
+ if (val & 0x02) { /* SIN */
+ /* never */
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "ICC SIN interrupt");
+ }
+ if (val & 0x01) { /* EXI */
+ exval = cs->readisac(cs, ICC_EXIR);
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "ICC EXIR %02x", exval);
+ if (exval & 0x80) { /* XMR */
+ debugl1(cs, "ICC XMR");
+ printk(KERN_WARNING "HiSax: ICC XMR\n");
+ }
+ if (exval & 0x40) { /* XDU */
+ debugl1(cs, "ICC XDU");
+ printk(KERN_WARNING "HiSax: ICC XDU\n");
+#ifdef ERROR_STATISTIC
+ cs->err_tx++;
+#endif
+ if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+ del_timer(&cs->dbusytimer);
+ if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+ schedule_event(cs, D_CLEARBUSY);
+ if (cs->tx_skb) { /* Restart frame */
+ skb_push(cs->tx_skb, cs->tx_cnt);
+ cs->tx_cnt = 0;
+ icc_fill_fifo(cs);
+ } else {
+ printk(KERN_WARNING "HiSax: ICC XDU no skb\n");
+ debugl1(cs, "ICC XDU no skb");
+ }
+ }
+ if (exval & 0x04) { /* MOS */
+ v1 = cs->readisac(cs, ICC_MOSR);
+ if (cs->debug & L1_DEB_MONITOR)
+ debugl1(cs, "ICC MOSR %02x", v1);
+#if ARCOFI_USE
+ if (v1 & 0x08) {
+ if (!cs->dc.icc.mon_rx) {
+ if (!(cs->dc.icc.mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "ICC MON RX out of memory!");
+ cs->dc.icc.mocr &= 0xf0;
+ cs->dc.icc.mocr |= 0x0a;
+ cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+ goto afterMONR0;
+ } else
+ cs->dc.icc.mon_rxp = 0;
+ }
+ if (cs->dc.icc.mon_rxp >= MAX_MON_FRAME) {
+ cs->dc.icc.mocr &= 0xf0;
+ cs->dc.icc.mocr |= 0x0a;
+ cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+ cs->dc.icc.mon_rxp = 0;
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "ICC MON RX overflow!");
+ goto afterMONR0;
+ }
+ cs->dc.icc.mon_rx[cs->dc.icc.mon_rxp++] = cs->readisac(cs, ICC_MOR0);
+ if (cs->debug & L1_DEB_MONITOR)
+ debugl1(cs, "ICC MOR0 %02x", cs->dc.icc.mon_rx[cs->dc.icc.mon_rxp -1]);
+ if (cs->dc.icc.mon_rxp == 1) {
+ cs->dc.icc.mocr |= 0x04;
+ cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+ }
+ }
+ afterMONR0:
+ if (v1 & 0x80) {
+ if (!cs->dc.icc.mon_rx) {
+ if (!(cs->dc.icc.mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "ICC MON RX out of memory!");
+ cs->dc.icc.mocr &= 0x0f;
+ cs->dc.icc.mocr |= 0xa0;
+ cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+ goto afterMONR1;
+ } else
+ cs->dc.icc.mon_rxp = 0;
+ }
+ if (cs->dc.icc.mon_rxp >= MAX_MON_FRAME) {
+ cs->dc.icc.mocr &= 0x0f;
+ cs->dc.icc.mocr |= 0xa0;
+ cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+ cs->dc.icc.mon_rxp = 0;
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "ICC MON RX overflow!");
+ goto afterMONR1;
+ }
+ cs->dc.icc.mon_rx[cs->dc.icc.mon_rxp++] = cs->readisac(cs, ICC_MOR1);
+ if (cs->debug & L1_DEB_MONITOR)
+ debugl1(cs, "ICC MOR1 %02x", cs->dc.icc.mon_rx[cs->dc.icc.mon_rxp -1]);
+ cs->dc.icc.mocr |= 0x40;
+ cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+ }
+ afterMONR1:
+ if (v1 & 0x04) {
+ cs->dc.icc.mocr &= 0xf0;
+ cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+ cs->dc.icc.mocr |= 0x0a;
+ cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+ schedule_event(cs, D_RX_MON0);
+ }
+ if (v1 & 0x40) {
+ cs->dc.icc.mocr &= 0x0f;
+ cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+ cs->dc.icc.mocr |= 0xa0;
+ cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+ schedule_event(cs, D_RX_MON1);
+ }
+ if (v1 & 0x02) {
+ if ((!cs->dc.icc.mon_tx) || (cs->dc.icc.mon_txc &&
+ (cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc) &&
+ !(v1 & 0x08))) {
+ cs->dc.icc.mocr &= 0xf0;
+ cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+ cs->dc.icc.mocr |= 0x0a;
+ cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+ if (cs->dc.icc.mon_txc &&
+ (cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc))
+ schedule_event(cs, D_TX_MON0);
+ goto AfterMOX0;
+ }
+ if (cs->dc.icc.mon_txc && (cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc)) {
+ schedule_event(cs, D_TX_MON0);
+ goto AfterMOX0;
+ }
+ cs->writeisac(cs, ICC_MOX0,
+ cs->dc.icc.mon_tx[cs->dc.icc.mon_txp++]);
+ if (cs->debug & L1_DEB_MONITOR)
+ debugl1(cs, "ICC %02x -> MOX0", cs->dc.icc.mon_tx[cs->dc.icc.mon_txp -1]);
+ }
+ AfterMOX0:
+ if (v1 & 0x20) {
+ if ((!cs->dc.icc.mon_tx) || (cs->dc.icc.mon_txc &&
+ (cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc) &&
+ !(v1 & 0x80))) {
+ cs->dc.icc.mocr &= 0x0f;
+ cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+ cs->dc.icc.mocr |= 0xa0;
+ cs->writeisac(cs, ICC_MOCR, cs->dc.icc.mocr);
+ if (cs->dc.icc.mon_txc &&
+ (cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc))
+ schedule_event(cs, D_TX_MON1);
+ goto AfterMOX1;
+ }
+ if (cs->dc.icc.mon_txc && (cs->dc.icc.mon_txp >= cs->dc.icc.mon_txc)) {
+ schedule_event(cs, D_TX_MON1);
+ goto AfterMOX1;
+ }
+ cs->writeisac(cs, ICC_MOX1,
+ cs->dc.icc.mon_tx[cs->dc.icc.mon_txp++]);
+ if (cs->debug & L1_DEB_MONITOR)
+ debugl1(cs, "ICC %02x -> MOX1", cs->dc.icc.mon_tx[cs->dc.icc.mon_txp -1]);
+ }
+ AfterMOX1:
+#endif
+ }
+ }
+}
+
+static void
+ICC_l1hw(struct PStack *st, int pr, void *arg)
+{
+ struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+ struct sk_buff *skb = arg;
+ u_long flags;
+ int val;
+
+ switch (pr) {
+ case (PH_DATA |REQUEST):
+ if (cs->debug & DEB_DLOG_HEX)
+ LogFrame(cs, skb->data, skb->len);
+ if (cs->debug & DEB_DLOG_VERBOSE)
+ dlogframe(cs, skb, 0);
+ spin_lock_irqsave(&cs->lock, flags);
+ if (cs->tx_skb) {
+ skb_queue_tail(&cs->sq, skb);
+#ifdef L2FRAME_DEBUG /* psa */
+ if (cs->debug & L1_DEB_LAPD)
+ Logl2Frame(cs, skb, "PH_DATA Queued", 0);
+#endif
+ } else {
+ cs->tx_skb = skb;
+ cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG /* psa */
+ if (cs->debug & L1_DEB_LAPD)
+ Logl2Frame(cs, skb, "PH_DATA", 0);
+#endif
+ icc_fill_fifo(cs);
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (PH_PULL |INDICATION):
+ spin_lock_irqsave(&cs->lock, flags);
+ if (cs->tx_skb) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
+ skb_queue_tail(&cs->sq, skb);
+ break;
+ }
+ if (cs->debug & DEB_DLOG_HEX)
+ LogFrame(cs, skb->data, skb->len);
+ if (cs->debug & DEB_DLOG_VERBOSE)
+ dlogframe(cs, skb, 0);
+ cs->tx_skb = skb;
+ cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG /* psa */
+ if (cs->debug & L1_DEB_LAPD)
+ Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
+#endif
+ icc_fill_fifo(cs);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (PH_PULL | REQUEST):
+#ifdef L2FRAME_DEBUG /* psa */
+ if (cs->debug & L1_DEB_LAPD)
+ debugl1(cs, "-> PH_REQUEST_PULL");
+#endif
+ if (!cs->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 (HW_RESET | REQUEST):
+ spin_lock_irqsave(&cs->lock, flags);
+ if ((cs->dc.icc.ph_state == ICC_IND_EI1) ||
+ (cs->dc.icc.ph_state == ICC_IND_DR))
+ ph_command(cs, ICC_CMD_DI);
+ else
+ ph_command(cs, ICC_CMD_RES);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (HW_ENABLE | REQUEST):
+ spin_lock_irqsave(&cs->lock, flags);
+ ph_command(cs, ICC_CMD_DI);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (HW_INFO1 | REQUEST):
+ spin_lock_irqsave(&cs->lock, flags);
+ ph_command(cs, ICC_CMD_AR);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (HW_INFO3 | REQUEST):
+ spin_lock_irqsave(&cs->lock, flags);
+ ph_command(cs, ICC_CMD_AI);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (HW_TESTLOOP | REQUEST):
+ spin_lock_irqsave(&cs->lock, flags);
+ val = 0;
+ if (1 & (long) arg)
+ val |= 0x0c;
+ if (2 & (long) arg)
+ val |= 0x3;
+ if (test_bit(HW_IOM1, &cs->HW_Flags)) {
+ /* IOM 1 Mode */
+ if (!val) {
+ cs->writeisac(cs, ICC_SPCR, 0xa);
+ cs->writeisac(cs, ICC_ADF1, 0x2);
+ } else {
+ cs->writeisac(cs, ICC_SPCR, val);
+ cs->writeisac(cs, ICC_ADF1, 0xa);
+ }
+ } else {
+ /* IOM 2 Mode */
+ cs->writeisac(cs, ICC_SPCR, val);
+ if (val)
+ cs->writeisac(cs, ICC_ADF1, 0x8);
+ else
+ cs->writeisac(cs, ICC_ADF1, 0x0);
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (HW_DEACTIVATE | RESPONSE):
+ skb_queue_purge(&cs->rq);
+ skb_queue_purge(&cs->sq);
+ if (cs->tx_skb) {
+ dev_kfree_skb_any(cs->tx_skb);
+ cs->tx_skb = NULL;
+ }
+ if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+ del_timer(&cs->dbusytimer);
+ if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+ schedule_event(cs, D_CLEARBUSY);
+ break;
+ default:
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "icc_l1hw unknown %04x", pr);
+ break;
+ }
+}
+
+static void
+setstack_icc(struct PStack *st, struct IsdnCardState *cs)
+{
+ st->l1.l1hw = ICC_l1hw;
+}
+
+static void
+DC_Close_icc(struct IsdnCardState *cs) {
+ kfree(cs->dc.icc.mon_rx);
+ cs->dc.icc.mon_rx = NULL;
+ kfree(cs->dc.icc.mon_tx);
+ cs->dc.icc.mon_tx = NULL;
+}
+
+static void
+dbusy_timer_handler(struct IsdnCardState *cs)
+{
+ struct PStack *stptr;
+ int rbch, star;
+
+ if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+ rbch = cs->readisac(cs, ICC_RBCH);
+ star = cs->readisac(cs, ICC_STAR);
+ if (cs->debug)
+ debugl1(cs, "D-Channel Busy RBCH %02x STAR %02x",
+ rbch, star);
+ if (rbch & ICC_RBCH_XAC) { /* D-Channel Busy */
+ test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
+ stptr = cs->stlist;
+ while (stptr != NULL) {
+ stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL);
+ stptr = stptr->next;
+ }
+ } else {
+ /* discard frame; reset transceiver */
+ test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags);
+ if (cs->tx_skb) {
+ dev_kfree_skb_any(cs->tx_skb);
+ cs->tx_cnt = 0;
+ cs->tx_skb = NULL;
+ } else {
+ printk(KERN_WARNING "HiSax: ICC D-Channel Busy no skb\n");
+ debugl1(cs, "D-Channel Busy no skb");
+ }
+ cs->writeisac(cs, ICC_CMDR, 0x01); /* Transmitter reset */
+ cs->irq_func(cs->irq, cs);
+ }
+ }
+}
+
+void
+initicc(struct IsdnCardState *cs)
+{
+ cs->setstack_d = setstack_icc;
+ cs->DC_Close = DC_Close_icc;
+ cs->dc.icc.mon_tx = NULL;
+ cs->dc.icc.mon_rx = NULL;
+ cs->writeisac(cs, ICC_MASK, 0xff);
+ cs->dc.icc.mocr = 0xaa;
+ if (test_bit(HW_IOM1, &cs->HW_Flags)) {
+ /* IOM 1 Mode */
+ cs->writeisac(cs, ICC_ADF2, 0x0);
+ cs->writeisac(cs, ICC_SPCR, 0xa);
+ cs->writeisac(cs, ICC_ADF1, 0x2);
+ cs->writeisac(cs, ICC_STCR, 0x70);
+ cs->writeisac(cs, ICC_MODE, 0xc9);
+ } else {
+ /* IOM 2 Mode */
+ if (!cs->dc.icc.adf2)
+ cs->dc.icc.adf2 = 0x80;
+ cs->writeisac(cs, ICC_ADF2, cs->dc.icc.adf2);
+ cs->writeisac(cs, ICC_SQXR, 0xa0);
+ cs->writeisac(cs, ICC_SPCR, 0x20);
+ cs->writeisac(cs, ICC_STCR, 0x70);
+ cs->writeisac(cs, ICC_MODE, 0xca);
+ cs->writeisac(cs, ICC_TIMR, 0x00);
+ cs->writeisac(cs, ICC_ADF1, 0x20);
+ }
+ ph_command(cs, ICC_CMD_RES);
+ cs->writeisac(cs, ICC_MASK, 0x0);
+ ph_command(cs, ICC_CMD_DI);
+}
+
+void
+clear_pending_icc_ints(struct IsdnCardState *cs)
+{
+ int val, eval;
+
+ val = cs->readisac(cs, ICC_STAR);
+ debugl1(cs, "ICC STAR %x", val);
+ val = cs->readisac(cs, ICC_MODE);
+ debugl1(cs, "ICC MODE %x", val);
+ val = cs->readisac(cs, ICC_ADF2);
+ debugl1(cs, "ICC ADF2 %x", val);
+ val = cs->readisac(cs, ICC_ISTA);
+ debugl1(cs, "ICC ISTA %x", val);
+ if (val & 0x01) {
+ eval = cs->readisac(cs, ICC_EXIR);
+ debugl1(cs, "ICC EXIR %x", eval);
+ }
+ val = cs->readisac(cs, ICC_CIR0);
+ debugl1(cs, "ICC CIR0 %x", val);
+ cs->dc.icc.ph_state = (val >> 2) & 0xf;
+ schedule_event(cs, D_L1STATECHANGE);
+ /* Disable all IRQ */
+ cs->writeisac(cs, ICC_MASK, 0xFF);
+}
+
+void __devinit
+setup_icc(struct IsdnCardState *cs)
+{
+ INIT_WORK(&cs->tqueue, icc_bh);
+ cs->dbusytimer.function = (void *) dbusy_timer_handler;
+ cs->dbusytimer.data = (long) cs;
+ init_timer(&cs->dbusytimer);
+}
diff --git a/drivers/isdn/hisax/icc.h b/drivers/isdn/hisax/icc.h
new file mode 100644
index 0000000..e7f5939
--- /dev/null
+++ b/drivers/isdn/hisax/icc.h
@@ -0,0 +1,72 @@
+/* $Id: icc.h,v 1.4.2.2 2004/01/12 22:52:26 keil Exp $
+ *
+ * ICC specific routines
+ *
+ * Author Matt Henderson & Guy Ellis
+ * Copyright by Traverse Technologies Pty Ltd, www.travers.com.au
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * 1999.7.14 Initial implementation of routines for Siemens ISDN
+ * Communication Controller PEB 2070 based on the ISAC routines
+ * written by Karsten Keil.
+ */
+
+/* All Registers original Siemens Spec */
+
+#define ICC_MASK 0x20
+#define ICC_ISTA 0x20
+#define ICC_STAR 0x21
+#define ICC_CMDR 0x21
+#define ICC_EXIR 0x24
+#define ICC_ADF2 0x39
+#define ICC_SPCR 0x30
+#define ICC_ADF1 0x38
+#define ICC_CIR0 0x31
+#define ICC_CIX0 0x31
+#define ICC_CIR1 0x33
+#define ICC_CIX1 0x33
+#define ICC_STCR 0x37
+#define ICC_MODE 0x22
+#define ICC_RSTA 0x27
+#define ICC_RBCL 0x25
+#define ICC_RBCH 0x2A
+#define ICC_TIMR 0x23
+#define ICC_SQXR 0x3b
+#define ICC_MOSR 0x3a
+#define ICC_MOCR 0x3a
+#define ICC_MOR0 0x32
+#define ICC_MOX0 0x32
+#define ICC_MOR1 0x34
+#define ICC_MOX1 0x34
+
+#define ICC_RBCH_XAC 0x80
+
+#define ICC_CMD_TIM 0x0
+#define ICC_CMD_RES 0x1
+#define ICC_CMD_DU 0x3
+#define ICC_CMD_EI1 0x4
+#define ICC_CMD_SSP 0x5
+#define ICC_CMD_DT 0x6
+#define ICC_CMD_AR 0x8
+#define ICC_CMD_ARL 0xA
+#define ICC_CMD_AI 0xC
+#define ICC_CMD_DI 0xF
+
+#define ICC_IND_DR 0x0
+#define ICC_IND_FJ 0x2
+#define ICC_IND_EI1 0x4
+#define ICC_IND_INT 0x6
+#define ICC_IND_PU 0x7
+#define ICC_IND_AR 0x8
+#define ICC_IND_ARL 0xA
+#define ICC_IND_AI 0xC
+#define ICC_IND_AIL 0xE
+#define ICC_IND_DC 0xF
+
+extern void ICCVersion(struct IsdnCardState *cs, char *s);
+extern void initicc(struct IsdnCardState *cs);
+extern void icc_interrupt(struct IsdnCardState *cs, u_char val);
+extern void clear_pending_icc_ints(struct IsdnCardState *cs);
+extern void setup_icc(struct IsdnCardState *);
diff --git a/drivers/isdn/hisax/ipac.h b/drivers/isdn/hisax/ipac.h
new file mode 100644
index 0000000..f92a04a
--- /dev/null
+++ b/drivers/isdn/hisax/ipac.h
@@ -0,0 +1,29 @@
+/* $Id: ipac.h,v 1.7.2.2 2004/01/12 22:52:26 keil Exp $
+ *
+ * IPAC specific defines
+ *
+ * Author Karsten Keil
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/* All Registers original Siemens Spec */
+
+#define IPAC_CONF 0xC0
+#define IPAC_MASK 0xC1
+#define IPAC_ISTA 0xC1
+#define IPAC_ID 0xC2
+#define IPAC_ACFG 0xC3
+#define IPAC_AOE 0xC4
+#define IPAC_ARX 0xC5
+#define IPAC_ATX 0xC5
+#define IPAC_PITA1 0xC6
+#define IPAC_PITA2 0xC7
+#define IPAC_POTA1 0xC8
+#define IPAC_POTA2 0xC9
+#define IPAC_PCFG 0xCA
+#define IPAC_SCFG 0xCB
+#define IPAC_TIMR2 0xCC
diff --git a/drivers/isdn/hisax/ipacx.c b/drivers/isdn/hisax/ipacx.c
new file mode 100644
index 0000000..00afd55
--- /dev/null
+++ b/drivers/isdn/hisax/ipacx.c
@@ -0,0 +1,913 @@
+/*
+ *
+ * IPACX specific routines
+ *
+ * Author Joerg Petersohn
+ * Derived from hisax_isac.c, isac.c, hscx.c and others
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include "hisax_if.h"
+#include "hisax.h"
+#include "isdnl1.h"
+#include "ipacx.h"
+
+#define DBUSY_TIMER_VALUE 80
+#define TIMER3_VALUE 7000
+#define MAX_DFRAME_LEN_L1 300
+#define B_FIFO_SIZE 64
+#define D_FIFO_SIZE 32
+
+
+// ipacx interrupt mask values
+#define _MASK_IMASK 0x2E // global mask
+#define _MASKB_IMASK 0x0B
+#define _MASKD_IMASK 0x03 // all on
+
+//----------------------------------------------------------
+// local function declarations
+//----------------------------------------------------------
+static void ph_command(struct IsdnCardState *cs, unsigned int command);
+static inline void cic_int(struct IsdnCardState *cs);
+static void dch_l2l1(struct PStack *st, int pr, void *arg);
+static void dbusy_timer_handler(struct IsdnCardState *cs);
+static void dch_empty_fifo(struct IsdnCardState *cs, int count);
+static void dch_fill_fifo(struct IsdnCardState *cs);
+static inline void dch_int(struct IsdnCardState *cs);
+static void dch_setstack(struct PStack *st, struct IsdnCardState *cs);
+static void dch_init(struct IsdnCardState *cs);
+static void bch_l2l1(struct PStack *st, int pr, void *arg);
+static void bch_empty_fifo(struct BCState *bcs, int count);
+static void bch_fill_fifo(struct BCState *bcs);
+static void bch_int(struct IsdnCardState *cs, u_char hscx);
+static void bch_mode(struct BCState *bcs, int mode, int bc);
+static void bch_close_state(struct BCState *bcs);
+static int bch_open_state(struct IsdnCardState *cs, struct BCState *bcs);
+static int bch_setstack(struct PStack *st, struct BCState *bcs);
+static void bch_init(struct IsdnCardState *cs, int hscx);
+static void clear_pending_ints(struct IsdnCardState *cs);
+
+//----------------------------------------------------------
+// Issue Layer 1 command to chip
+//----------------------------------------------------------
+static void
+ph_command(struct IsdnCardState *cs, unsigned int command)
+{
+ if (cs->debug &L1_DEB_ISAC)
+ debugl1(cs, "ph_command (%#x) in (%#x)", command,
+ cs->dc.isac.ph_state);
+//###################################
+// printk(KERN_INFO "ph_command (%#x)\n", command);
+//###################################
+ cs->writeisac(cs, IPACX_CIX0, (command << 4) | 0x0E);
+}
+
+//----------------------------------------------------------
+// Transceiver interrupt handler
+//----------------------------------------------------------
+static inline void
+cic_int(struct IsdnCardState *cs)
+{
+ u_char event;
+
+ event = cs->readisac(cs, IPACX_CIR0) >> 4;
+ if (cs->debug &L1_DEB_ISAC) debugl1(cs, "cic_int(event=%#x)", event);
+//#########################################
+// printk(KERN_INFO "cic_int(%x)\n", event);
+//#########################################
+ cs->dc.isac.ph_state = event;
+ schedule_event(cs, D_L1STATECHANGE);
+}
+
+//==========================================================
+// D channel functions
+//==========================================================
+
+//----------------------------------------------------------
+// Command entry point
+//----------------------------------------------------------
+static void
+dch_l2l1(struct PStack *st, int pr, void *arg)
+{
+ struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+ struct sk_buff *skb = arg;
+ u_char cda1_cr, cda2_cr;
+
+ switch (pr) {
+ case (PH_DATA |REQUEST):
+ if (cs->debug &DEB_DLOG_HEX) LogFrame(cs, skb->data, skb->len);
+ if (cs->debug &DEB_DLOG_VERBOSE) dlogframe(cs, skb, 0);
+ if (cs->tx_skb) {
+ skb_queue_tail(&cs->sq, skb);
+#ifdef L2FRAME_DEBUG
+ if (cs->debug &L1_DEB_LAPD) Logl2Frame(cs, skb, "PH_DATA Queued", 0);
+#endif
+ } else {
+ cs->tx_skb = skb;
+ cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG
+ if (cs->debug &L1_DEB_LAPD) Logl2Frame(cs, skb, "PH_DATA", 0);
+#endif
+ dch_fill_fifo(cs);
+ }
+ break;
+
+ case (PH_PULL |INDICATION):
+ if (cs->tx_skb) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
+ skb_queue_tail(&cs->sq, skb);
+ break;
+ }
+ if (cs->debug & DEB_DLOG_HEX) LogFrame(cs, skb->data, skb->len);
+ if (cs->debug & DEB_DLOG_VERBOSE) dlogframe(cs, skb, 0);
+ cs->tx_skb = skb;
+ cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG
+ if (cs->debug & L1_DEB_LAPD) Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
+#endif
+ dch_fill_fifo(cs);
+ break;
+
+ case (PH_PULL | REQUEST):
+#ifdef L2FRAME_DEBUG
+ if (cs->debug & L1_DEB_LAPD) debugl1(cs, "-> PH_REQUEST_PULL");
+#endif
+ if (!cs->tx_skb) {
+ clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+ st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+ } else
+ set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+ break;
+
+ case (HW_RESET | REQUEST):
+ case (HW_ENABLE | REQUEST):
+ if ((cs->dc.isac.ph_state == IPACX_IND_RES) ||
+ (cs->dc.isac.ph_state == IPACX_IND_DR) ||
+ (cs->dc.isac.ph_state == IPACX_IND_DC))
+ ph_command(cs, IPACX_CMD_TIM);
+ else
+ ph_command(cs, IPACX_CMD_RES);
+ break;
+
+ case (HW_INFO3 | REQUEST):
+ ph_command(cs, IPACX_CMD_AR8);
+ break;
+
+ case (HW_TESTLOOP | REQUEST):
+ cs->writeisac(cs, IPACX_CDA_TSDP10, 0x80); // Timeslot 0 is B1
+ cs->writeisac(cs, IPACX_CDA_TSDP11, 0x81); // Timeslot 0 is B1
+ cda1_cr = cs->readisac(cs, IPACX_CDA1_CR);
+ cda2_cr = cs->readisac(cs, IPACX_CDA2_CR);
+ if ((long)arg &1) { // loop B1
+ cs->writeisac(cs, IPACX_CDA1_CR, cda1_cr |0x0a);
+ }
+ else { // B1 off
+ cs->writeisac(cs, IPACX_CDA1_CR, cda1_cr &~0x0a);
+ }
+ if ((long)arg &2) { // loop B2
+ cs->writeisac(cs, IPACX_CDA1_CR, cda1_cr |0x14);
+ }
+ else { // B2 off
+ cs->writeisac(cs, IPACX_CDA1_CR, cda1_cr &~0x14);
+ }
+ break;
+
+ case (HW_DEACTIVATE | RESPONSE):
+ skb_queue_purge(&cs->rq);
+ skb_queue_purge(&cs->sq);
+ if (cs->tx_skb) {
+ dev_kfree_skb_any(cs->tx_skb);
+ cs->tx_skb = NULL;
+ }
+ if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+ del_timer(&cs->dbusytimer);
+ break;
+
+ default:
+ if (cs->debug &L1_DEB_WARN) debugl1(cs, "dch_l2l1 unknown %04x", pr);
+ break;
+ }
+}
+
+//----------------------------------------------------------
+//----------------------------------------------------------
+static void
+dbusy_timer_handler(struct IsdnCardState *cs)
+{
+ struct PStack *st;
+ int rbchd, stard;
+
+ if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+ rbchd = cs->readisac(cs, IPACX_RBCHD);
+ stard = cs->readisac(cs, IPACX_STARD);
+ if (cs->debug)
+ debugl1(cs, "D-Channel Busy RBCHD %02x STARD %02x", rbchd, stard);
+ if (!(stard &0x40)) { // D-Channel Busy
+ set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
+ for (st = cs->stlist; st; st = st->next) {
+ st->l1.l1l2(st, PH_PAUSE | INDICATION, NULL); // flow control on
+ }
+ } else {
+ // seems we lost an interrupt; reset transceiver */
+ clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags);
+ if (cs->tx_skb) {
+ dev_kfree_skb_any(cs->tx_skb);
+ cs->tx_cnt = 0;
+ cs->tx_skb = NULL;
+ } else {
+ printk(KERN_WARNING "HiSax: ISAC D-Channel Busy no skb\n");
+ debugl1(cs, "D-Channel Busy no skb");
+ }
+ cs->writeisac(cs, IPACX_CMDRD, 0x01); // Tx reset, generates XPR
+ }
+ }
+}
+
+//----------------------------------------------------------
+// Fill buffer from receive FIFO
+//----------------------------------------------------------
+static void
+dch_empty_fifo(struct IsdnCardState *cs, int count)
+{
+ u_char *ptr;
+
+ if ((cs->debug &L1_DEB_ISAC) && !(cs->debug &L1_DEB_ISAC_FIFO))
+ debugl1(cs, "dch_empty_fifo()");
+
+ // message too large, remove
+ if ((cs->rcvidx + count) >= MAX_DFRAME_LEN_L1) {
+ if (cs->debug &L1_DEB_WARN)
+ debugl1(cs, "dch_empty_fifo() incoming message too large");
+ cs->writeisac(cs, IPACX_CMDRD, 0x80); // RMC
+ cs->rcvidx = 0;
+ return;
+ }
+
+ ptr = cs->rcvbuf + cs->rcvidx;
+ cs->rcvidx += count;
+
+ cs->readisacfifo(cs, ptr, count);
+ cs->writeisac(cs, IPACX_CMDRD, 0x80); // RMC
+
+ if (cs->debug &L1_DEB_ISAC_FIFO) {
+ char *t = cs->dlog;
+
+ t += sprintf(t, "dch_empty_fifo() cnt %d", count);
+ QuickHex(t, ptr, count);
+ debugl1(cs, cs->dlog);
+ }
+}
+
+//----------------------------------------------------------
+// Fill transmit FIFO
+//----------------------------------------------------------
+static void
+dch_fill_fifo(struct IsdnCardState *cs)
+{
+ int count;
+ u_char cmd, *ptr;
+
+ if ((cs->debug &L1_DEB_ISAC) && !(cs->debug &L1_DEB_ISAC_FIFO))
+ debugl1(cs, "dch_fill_fifo()");
+
+ if (!cs->tx_skb) return;
+ count = cs->tx_skb->len;
+ if (count <= 0) return;
+
+ if (count > D_FIFO_SIZE) {
+ count = D_FIFO_SIZE;
+ cmd = 0x08; // XTF
+ } else {
+ cmd = 0x0A; // XTF | XME
+ }
+
+ ptr = cs->tx_skb->data;
+ skb_pull(cs->tx_skb, count);
+ cs->tx_cnt += count;
+ cs->writeisacfifo(cs, ptr, count);
+ cs->writeisac(cs, IPACX_CMDRD, cmd);
+
+ // set timeout for transmission contol
+ if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+ debugl1(cs, "dch_fill_fifo dbusytimer running");
+ del_timer(&cs->dbusytimer);
+ }
+ init_timer(&cs->dbusytimer);
+ cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
+ add_timer(&cs->dbusytimer);
+
+ if (cs->debug &L1_DEB_ISAC_FIFO) {
+ char *t = cs->dlog;
+
+ t += sprintf(t, "dch_fill_fifo() cnt %d", count);
+ QuickHex(t, ptr, count);
+ debugl1(cs, cs->dlog);
+ }
+}
+
+//----------------------------------------------------------
+// D channel interrupt handler
+//----------------------------------------------------------
+static inline void
+dch_int(struct IsdnCardState *cs)
+{
+ struct sk_buff *skb;
+ u_char istad, rstad;
+ int count;
+
+ istad = cs->readisac(cs, IPACX_ISTAD);
+//##############################################
+// printk(KERN_WARNING "dch_int(istad=%02x)\n", istad);
+//##############################################
+
+ if (istad &0x80) { // RME
+ rstad = cs->readisac(cs, IPACX_RSTAD);
+ if ((rstad &0xf0) != 0xa0) { // !(VFR && !RDO && CRC && !RAB)
+ if (!(rstad &0x80))
+ if (cs->debug &L1_DEB_WARN)
+ debugl1(cs, "dch_int(): invalid frame");
+ if ((rstad &0x40))
+ if (cs->debug &L1_DEB_WARN)
+ debugl1(cs, "dch_int(): RDO");
+ if (!(rstad &0x20))
+ if (cs->debug &L1_DEB_WARN)
+ debugl1(cs, "dch_int(): CRC error");
+ cs->writeisac(cs, IPACX_CMDRD, 0x80); // RMC
+ } else { // received frame ok
+ count = cs->readisac(cs, IPACX_RBCLD);
+ if (count) count--; // RSTAB is last byte
+ count &= D_FIFO_SIZE-1;
+ if (count == 0) count = D_FIFO_SIZE;
+ dch_empty_fifo(cs, count);
+ if ((count = cs->rcvidx) > 0) {
+ cs->rcvidx = 0;
+ if (!(skb = dev_alloc_skb(count)))
+ printk(KERN_WARNING "HiSax dch_int(): receive out of memory\n");
+ else {
+ memcpy(skb_put(skb, count), cs->rcvbuf, count);
+ skb_queue_tail(&cs->rq, skb);
+ }
+ }
+ }
+ cs->rcvidx = 0;
+ schedule_event(cs, D_RCVBUFREADY);
+ }
+
+ if (istad &0x40) { // RPF
+ dch_empty_fifo(cs, D_FIFO_SIZE);
+ }
+
+ if (istad &0x20) { // RFO
+ if (cs->debug &L1_DEB_WARN) debugl1(cs, "dch_int(): RFO");
+ cs->writeisac(cs, IPACX_CMDRD, 0x40); //RRES
+ }
+
+ if (istad &0x10) { // XPR
+ if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+ del_timer(&cs->dbusytimer);
+ if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+ schedule_event(cs, D_CLEARBUSY);
+ if (cs->tx_skb) {
+ if (cs->tx_skb->len) {
+ dch_fill_fifo(cs);
+ goto afterXPR;
+ }
+ else {
+ dev_kfree_skb_irq(cs->tx_skb);
+ cs->tx_skb = NULL;
+ cs->tx_cnt = 0;
+ }
+ }
+ if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
+ cs->tx_cnt = 0;
+ dch_fill_fifo(cs);
+ }
+ else {
+ schedule_event(cs, D_XMTBUFREADY);
+ }
+ }
+ afterXPR:
+
+ if (istad &0x0C) { // XDU or XMR
+ if (cs->debug &L1_DEB_WARN) debugl1(cs, "dch_int(): XDU");
+ if (cs->tx_skb) {
+ skb_push(cs->tx_skb, cs->tx_cnt); // retransmit
+ cs->tx_cnt = 0;
+ dch_fill_fifo(cs);
+ } else {
+ printk(KERN_WARNING "HiSax: ISAC XDU no skb\n");
+ debugl1(cs, "ISAC XDU no skb");
+ }
+ }
+}
+
+//----------------------------------------------------------
+//----------------------------------------------------------
+static void
+dch_setstack(struct PStack *st, struct IsdnCardState *cs)
+{
+ st->l1.l1hw = dch_l2l1;
+}
+
+//----------------------------------------------------------
+//----------------------------------------------------------
+static void
+dch_init(struct IsdnCardState *cs)
+{
+ printk(KERN_INFO "HiSax: IPACX ISDN driver v0.1.0\n");
+
+ cs->setstack_d = dch_setstack;
+
+ cs->dbusytimer.function = (void *) dbusy_timer_handler;
+ cs->dbusytimer.data = (long) cs;
+ init_timer(&cs->dbusytimer);
+
+ cs->writeisac(cs, IPACX_TR_CONF0, 0x00); // clear LDD
+ cs->writeisac(cs, IPACX_TR_CONF2, 0x00); // enable transmitter
+ cs->writeisac(cs, IPACX_MODED, 0xC9); // transparent mode 0, RAC, stop/go
+ cs->writeisac(cs, IPACX_MON_CR, 0x00); // disable monitor channel
+}
+
+
+//==========================================================
+// B channel functions
+//==========================================================
+
+//----------------------------------------------------------
+// Entry point for commands
+//----------------------------------------------------------
+static void
+bch_l2l1(struct PStack *st, int pr, void *arg)
+{
+ struct BCState *bcs = st->l1.bcs;
+ struct sk_buff *skb = arg;
+ 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;
+ set_bit(BC_FLG_BUSY, &bcs->Flag);
+ bcs->hw.hscx.count = 0;
+ bch_fill_fifo(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 "HiSax bch_l2l1(): this shouldn't happen\n");
+ } else {
+ set_bit(BC_FLG_BUSY, &bcs->Flag);
+ bcs->tx_skb = skb;
+ bcs->hw.hscx.count = 0;
+ bch_fill_fifo(bcs);
+ }
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
+ break;
+ case (PH_PULL | REQUEST):
+ if (!bcs->tx_skb) {
+ clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+ st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+ } else
+ set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
+ break;
+ case (PH_ACTIVATE | REQUEST):
+ spin_lock_irqsave(&bcs->cs->lock, flags);
+ set_bit(BC_FLG_ACTIV, &bcs->Flag);
+ bch_mode(bcs, st->l1.mode, st->l1.bc);
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | REQUEST):
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | CONFIRM):
+ spin_lock_irqsave(&bcs->cs->lock, flags);
+ clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+ clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ bch_mode(bcs, 0, st->l1.bc);
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
+ st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+ break;
+ }
+}
+
+//----------------------------------------------------------
+// Read B channel fifo to receive buffer
+//----------------------------------------------------------
+static void
+bch_empty_fifo(struct BCState *bcs, int count)
+{
+ u_char *ptr, hscx;
+ struct IsdnCardState *cs;
+ int cnt;
+
+ cs = bcs->cs;
+ hscx = bcs->hw.hscx.hscx;
+ if ((cs->debug &L1_DEB_HSCX) && !(cs->debug &L1_DEB_HSCX_FIFO))
+ debugl1(cs, "bch_empty_fifo()");
+
+ // message too large, remove
+ if (bcs->hw.hscx.rcvidx + count > HSCX_BUFMAX) {
+ if (cs->debug &L1_DEB_WARN)
+ debugl1(cs, "bch_empty_fifo() incoming packet too large");
+ cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x80); // RMC
+ bcs->hw.hscx.rcvidx = 0;
+ return;
+ }
+
+ ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx;
+ cnt = count;
+ while (cnt--) *ptr++ = cs->BC_Read_Reg(cs, hscx, IPACX_RFIFOB);
+ cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x80); // RMC
+
+ ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx;
+ bcs->hw.hscx.rcvidx += count;
+
+ if (cs->debug &L1_DEB_HSCX_FIFO) {
+ char *t = bcs->blog;
+
+ t += sprintf(t, "bch_empty_fifo() B-%d cnt %d", hscx, count);
+ QuickHex(t, ptr, count);
+ debugl1(cs, bcs->blog);
+ }
+}
+
+//----------------------------------------------------------
+// Fill buffer to transmit FIFO
+//----------------------------------------------------------
+static void
+bch_fill_fifo(struct BCState *bcs)
+{
+ struct IsdnCardState *cs;
+ int more, count, cnt;
+ u_char *ptr, *p, hscx;
+
+ cs = bcs->cs;
+ if ((cs->debug &L1_DEB_HSCX) && !(cs->debug &L1_DEB_HSCX_FIFO))
+ debugl1(cs, "bch_fill_fifo()");
+
+ if (!bcs->tx_skb) return;
+ if (bcs->tx_skb->len <= 0) return;
+
+ hscx = bcs->hw.hscx.hscx;
+ more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0;
+ if (bcs->tx_skb->len > B_FIFO_SIZE) {
+ more = 1;
+ count = B_FIFO_SIZE;
+ } else {
+ count = bcs->tx_skb->len;
+ }
+ cnt = count;
+
+ p = ptr = bcs->tx_skb->data;
+ skb_pull(bcs->tx_skb, count);
+ bcs->tx_cnt -= count;
+ bcs->hw.hscx.count += count;
+ while (cnt--) cs->BC_Write_Reg(cs, hscx, IPACX_XFIFOB, *p++);
+ cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, (more ? 0x08 : 0x0a));
+
+ if (cs->debug &L1_DEB_HSCX_FIFO) {
+ char *t = bcs->blog;
+
+ t += sprintf(t, "chb_fill_fifo() B-%d cnt %d", hscx, count);
+ QuickHex(t, ptr, count);
+ debugl1(cs, bcs->blog);
+ }
+}
+
+//----------------------------------------------------------
+// B channel interrupt handler
+//----------------------------------------------------------
+static void
+bch_int(struct IsdnCardState *cs, u_char hscx)
+{
+ u_char istab;
+ struct BCState *bcs;
+ struct sk_buff *skb;
+ int count;
+ u_char rstab;
+
+ bcs = cs->bcs + hscx;
+ istab = cs->BC_Read_Reg(cs, hscx, IPACX_ISTAB);
+//##############################################
+// printk(KERN_WARNING "bch_int(istab=%02x)\n", istab);
+//##############################################
+ if (!test_bit(BC_FLG_INIT, &bcs->Flag)) return;
+
+ if (istab &0x80) { // RME
+ rstab = cs->BC_Read_Reg(cs, hscx, IPACX_RSTAB);
+ if ((rstab &0xf0) != 0xa0) { // !(VFR && !RDO && CRC && !RAB)
+ if (!(rstab &0x80))
+ if (cs->debug &L1_DEB_WARN)
+ debugl1(cs, "bch_int() B-%d: invalid frame", hscx);
+ if ((rstab &0x40) && (bcs->mode != L1_MODE_NULL))
+ if (cs->debug &L1_DEB_WARN)
+ debugl1(cs, "bch_int() B-%d: RDO mode=%d", hscx, bcs->mode);
+ if (!(rstab &0x20))
+ if (cs->debug &L1_DEB_WARN)
+ debugl1(cs, "bch_int() B-%d: CRC error", hscx);
+ cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x80); // RMC
+ }
+ else { // received frame ok
+ count = cs->BC_Read_Reg(cs, hscx, IPACX_RBCLB) &(B_FIFO_SIZE-1);
+ if (count == 0) count = B_FIFO_SIZE;
+ bch_empty_fifo(bcs, count);
+ if ((count = bcs->hw.hscx.rcvidx - 1) > 0) {
+ if (cs->debug &L1_DEB_HSCX_FIFO)
+ debugl1(cs, "bch_int Frame %d", count);
+ if (!(skb = dev_alloc_skb(count)))
+ printk(KERN_WARNING "HiSax bch_int(): receive frame out of memory\n");
+ else {
+ memcpy(skb_put(skb, count), bcs->hw.hscx.rcvbuf, count);
+ skb_queue_tail(&bcs->rqueue, skb);
+ }
+ }
+ }
+ bcs->hw.hscx.rcvidx = 0;
+ schedule_event(bcs, B_RCVBUFREADY);
+ }
+
+ if (istab &0x40) { // RPF
+ bch_empty_fifo(bcs, B_FIFO_SIZE);
+
+ if (bcs->mode == L1_MODE_TRANS) { // queue every chunk
+ // receive transparent audio data
+ if (!(skb = dev_alloc_skb(B_FIFO_SIZE)))
+ printk(KERN_WARNING "HiSax bch_int(): receive transparent out of memory\n");
+ else {
+ memcpy(skb_put(skb, B_FIFO_SIZE), bcs->hw.hscx.rcvbuf, B_FIFO_SIZE);
+ skb_queue_tail(&bcs->rqueue, skb);
+ }
+ bcs->hw.hscx.rcvidx = 0;
+ schedule_event(bcs, B_RCVBUFREADY);
+ }
+ }
+
+ if (istab &0x20) { // RFO
+ if (cs->debug &L1_DEB_WARN)
+ debugl1(cs, "bch_int() B-%d: RFO error", hscx);
+ cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x40); // RRES
+ }
+
+ if (istab &0x10) { // XPR
+ if (bcs->tx_skb) {
+ if (bcs->tx_skb->len) {
+ bch_fill_fifo(bcs);
+ goto afterXPR;
+ } 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.hscx.count;
+ spin_unlock_irqrestore(&bcs->aclock, flags);
+ schedule_event(bcs, B_ACKPENDING);
+ }
+ }
+ dev_kfree_skb_irq(bcs->tx_skb);
+ bcs->hw.hscx.count = 0;
+ bcs->tx_skb = NULL;
+ }
+ if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+ bcs->hw.hscx.count = 0;
+ set_bit(BC_FLG_BUSY, &bcs->Flag);
+ bch_fill_fifo(bcs);
+ } else {
+ clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ schedule_event(bcs, B_XMTBUFREADY);
+ }
+ }
+ afterXPR:
+
+ if (istab &0x04) { // XDU
+ if (bcs->mode == L1_MODE_TRANS) {
+ bch_fill_fifo(bcs);
+ }
+ else {
+ if (bcs->tx_skb) { // restart transmitting the whole frame
+ skb_push(bcs->tx_skb, bcs->hw.hscx.count);
+ bcs->tx_cnt += bcs->hw.hscx.count;
+ bcs->hw.hscx.count = 0;
+ }
+ cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x01); // XRES
+ if (cs->debug &L1_DEB_WARN)
+ debugl1(cs, "bch_int() B-%d XDU error", hscx);
+ }
+ }
+}
+
+//----------------------------------------------------------
+//----------------------------------------------------------
+static void
+bch_mode(struct BCState *bcs, int mode, int bc)
+{
+ struct IsdnCardState *cs = bcs->cs;
+ int hscx = bcs->hw.hscx.hscx;
+
+ bc = bc ? 1 : 0; // in case bc is greater than 1
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "mode_bch() switch B-% mode %d chan %d", hscx, mode, bc);
+ bcs->mode = mode;
+ bcs->channel = bc;
+
+ // map controller to according timeslot
+ if (!hscx)
+ {
+ cs->writeisac(cs, IPACX_BCHA_TSDP_BC1, 0x80 | bc);
+ cs->writeisac(cs, IPACX_BCHA_CR, 0x88);
+ }
+ else
+ {
+ cs->writeisac(cs, IPACX_BCHB_TSDP_BC1, 0x80 | bc);
+ cs->writeisac(cs, IPACX_BCHB_CR, 0x88);
+ }
+
+ switch (mode) {
+ case (L1_MODE_NULL):
+ cs->BC_Write_Reg(cs, hscx, IPACX_MODEB, 0xC0); // rec off
+ cs->BC_Write_Reg(cs, hscx, IPACX_EXMB, 0x30); // std adj.
+ cs->BC_Write_Reg(cs, hscx, IPACX_MASKB, 0xFF); // ints off
+ cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x41); // validate adjustments
+ break;
+ case (L1_MODE_TRANS):
+ cs->BC_Write_Reg(cs, hscx, IPACX_MODEB, 0x88); // ext transp mode
+ cs->BC_Write_Reg(cs, hscx, IPACX_EXMB, 0x00); // xxx00000
+ cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x41); // validate adjustments
+ cs->BC_Write_Reg(cs, hscx, IPACX_MASKB, _MASKB_IMASK);
+ break;
+ case (L1_MODE_HDLC):
+ cs->BC_Write_Reg(cs, hscx, IPACX_MODEB, 0xC8); // transp mode 0
+ cs->BC_Write_Reg(cs, hscx, IPACX_EXMB, 0x01); // idle=hdlc flags crc enabled
+ cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x41); // validate adjustments
+ cs->BC_Write_Reg(cs, hscx, IPACX_MASKB, _MASKB_IMASK);
+ break;
+ }
+}
+
+//----------------------------------------------------------
+//----------------------------------------------------------
+static void
+bch_close_state(struct BCState *bcs)
+{
+ bch_mode(bcs, 0, bcs->channel);
+ if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+ kfree(bcs->hw.hscx.rcvbuf);
+ bcs->hw.hscx.rcvbuf = NULL;
+ kfree(bcs->blog);
+ bcs->blog = 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;
+ clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ }
+ }
+}
+
+//----------------------------------------------------------
+//----------------------------------------------------------
+static int
+bch_open_state(struct IsdnCardState *cs, struct BCState *bcs)
+{
+ if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+ if (!(bcs->hw.hscx.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "HiSax open_bchstate(): No memory for hscx.rcvbuf\n");
+ clear_bit(BC_FLG_INIT, &bcs->Flag);
+ return (1);
+ }
+ if (!(bcs->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "HiSax open_bchstate: No memory for bcs->blog\n");
+ clear_bit(BC_FLG_INIT, &bcs->Flag);
+ kfree(bcs->hw.hscx.rcvbuf);
+ bcs->hw.hscx.rcvbuf = NULL;
+ return (2);
+ }
+ skb_queue_head_init(&bcs->rqueue);
+ skb_queue_head_init(&bcs->squeue);
+ }
+ bcs->tx_skb = NULL;
+ clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ bcs->event = 0;
+ bcs->hw.hscx.rcvidx = 0;
+ bcs->tx_cnt = 0;
+ return (0);
+}
+
+//----------------------------------------------------------
+//----------------------------------------------------------
+static int
+bch_setstack(struct PStack *st, struct BCState *bcs)
+{
+ bcs->channel = st->l1.bc;
+ if (bch_open_state(st->l1.hardware, bcs)) return (-1);
+ st->l1.bcs = bcs;
+ st->l2.l2l1 = bch_l2l1;
+ setstack_manager(st);
+ bcs->st = st;
+ setstack_l1_B(st);
+ return (0);
+}
+
+//----------------------------------------------------------
+//----------------------------------------------------------
+static void
+bch_init(struct IsdnCardState *cs, int hscx)
+{
+ cs->bcs[hscx].BC_SetStack = bch_setstack;
+ cs->bcs[hscx].BC_Close = bch_close_state;
+ cs->bcs[hscx].hw.hscx.hscx = hscx;
+ cs->bcs[hscx].cs = cs;
+ bch_mode(cs->bcs + hscx, 0, hscx);
+}
+
+
+//==========================================================
+// Shared functions
+//==========================================================
+
+//----------------------------------------------------------
+// Main interrupt handler
+//----------------------------------------------------------
+void
+interrupt_ipacx(struct IsdnCardState *cs)
+{
+ u_char ista;
+
+ while ((ista = cs->readisac(cs, IPACX_ISTA))) {
+//#################################################
+// printk(KERN_WARNING "interrupt_ipacx(ista=%02x)\n", ista);
+//#################################################
+ if (ista &0x80) bch_int(cs, 0); // B channel interrupts
+ if (ista &0x40) bch_int(cs, 1);
+
+ if (ista &0x01) dch_int(cs); // D channel
+ if (ista &0x10) cic_int(cs); // Layer 1 state
+ }
+}
+
+//----------------------------------------------------------
+// Clears chip interrupt status
+//----------------------------------------------------------
+static void
+clear_pending_ints(struct IsdnCardState *cs)
+{
+ int ista;
+
+ // all interrupts off
+ cs->writeisac(cs, IPACX_MASK, 0xff);
+ cs->writeisac(cs, IPACX_MASKD, 0xff);
+ cs->BC_Write_Reg(cs, 0, IPACX_MASKB, 0xff);
+ cs->BC_Write_Reg(cs, 1, IPACX_MASKB, 0xff);
+
+ ista = cs->readisac(cs, IPACX_ISTA);
+ if (ista &0x80) cs->BC_Read_Reg(cs, 0, IPACX_ISTAB);
+ if (ista &0x40) cs->BC_Read_Reg(cs, 1, IPACX_ISTAB);
+ if (ista &0x10) cs->readisac(cs, IPACX_CIR0);
+ if (ista &0x01) cs->readisac(cs, IPACX_ISTAD);
+}
+
+//----------------------------------------------------------
+// Does chip configuration work
+// Work to do depends on bit mask in part
+//----------------------------------------------------------
+void
+init_ipacx(struct IsdnCardState *cs, int part)
+{
+ if (part &1) { // initialise chip
+//##################################################
+// printk(KERN_INFO "init_ipacx(%x)\n", part);
+//##################################################
+ clear_pending_ints(cs);
+ bch_init(cs, 0);
+ bch_init(cs, 1);
+ dch_init(cs);
+ }
+ if (part &2) { // reenable all interrupts and start chip
+ cs->BC_Write_Reg(cs, 0, IPACX_MASKB, _MASKB_IMASK);
+ cs->BC_Write_Reg(cs, 1, IPACX_MASKB, _MASKB_IMASK);
+ cs->writeisac(cs, IPACX_MASKD, _MASKD_IMASK);
+ cs->writeisac(cs, IPACX_MASK, _MASK_IMASK); // global mask register
+
+ // reset HDLC Transmitters/receivers
+ cs->writeisac(cs, IPACX_CMDRD, 0x41);
+ cs->BC_Write_Reg(cs, 0, IPACX_CMDRB, 0x41);
+ cs->BC_Write_Reg(cs, 1, IPACX_CMDRB, 0x41);
+ ph_command(cs, IPACX_CMD_RES);
+ }
+}
+
+//----------------- end of file -----------------------
+
diff --git a/drivers/isdn/hisax/ipacx.h b/drivers/isdn/hisax/ipacx.h
new file mode 100644
index 0000000..e8a22e8
--- /dev/null
+++ b/drivers/isdn/hisax/ipacx.h
@@ -0,0 +1,162 @@
+/*
+ *
+ * IPACX specific defines
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/* All Registers original Siemens Spec */
+
+#ifndef INCLUDE_IPACX_H
+#define INCLUDE_IPACX_H
+
+/* D-channel registers */
+#define IPACX_RFIFOD 0x00 /* RD */
+#define IPACX_XFIFOD 0x00 /* WR */
+#define IPACX_ISTAD 0x20 /* RD */
+#define IPACX_MASKD 0x20 /* WR */
+#define IPACX_STARD 0x21 /* RD */
+#define IPACX_CMDRD 0x21 /* WR */
+#define IPACX_MODED 0x22 /* RD/WR */
+#define IPACX_EXMD1 0x23 /* RD/WR */
+#define IPACX_TIMR1 0x24 /* RD/WR */
+#define IPACX_SAP1 0x25 /* WR */
+#define IPACX_SAP2 0x26 /* WR */
+#define IPACX_RBCLD 0x26 /* RD */
+#define IPACX_RBCHD 0x27 /* RD */
+#define IPACX_TEI1 0x27 /* WR */
+#define IPACX_TEI2 0x28 /* WR */
+#define IPACX_RSTAD 0x28 /* RD */
+#define IPACX_TMD 0x29 /* RD/WR */
+#define IPACX_CIR0 0x2E /* RD */
+#define IPACX_CIX0 0x2E /* WR */
+#define IPACX_CIR1 0x2F /* RD */
+#define IPACX_CIX1 0x2F /* WR */
+
+/* Transceiver registers */
+#define IPACX_TR_CONF0 0x30 /* RD/WR */
+#define IPACX_TR_CONF1 0x31 /* RD/WR */
+#define IPACX_TR_CONF2 0x32 /* RD/WR */
+#define IPACX_TR_STA 0x33 /* RD */
+#define IPACX_TR_CMD 0x34 /* RD/WR */
+#define IPACX_SQRR1 0x35 /* RD */
+#define IPACX_SQXR1 0x35 /* WR */
+#define IPACX_SQRR2 0x36 /* RD */
+#define IPACX_SQXR2 0x36 /* WR */
+#define IPACX_SQRR3 0x37 /* RD */
+#define IPACX_SQXR3 0x37 /* WR */
+#define IPACX_ISTATR 0x38 /* RD */
+#define IPACX_MASKTR 0x39 /* RD/WR */
+#define IPACX_TR_MODE 0x3A /* RD/WR */
+#define IPACX_ACFG1 0x3C /* RD/WR */
+#define IPACX_ACFG2 0x3D /* RD/WR */
+#define IPACX_AOE 0x3E /* RD/WR */
+#define IPACX_ARX 0x3F /* RD */
+#define IPACX_ATX 0x3F /* WR */
+
+/* IOM: Timeslot, DPS, CDA */
+#define IPACX_CDA10 0x40 /* RD/WR */
+#define IPACX_CDA11 0x41 /* RD/WR */
+#define IPACX_CDA20 0x42 /* RD/WR */
+#define IPACX_CDA21 0x43 /* RD/WR */
+#define IPACX_CDA_TSDP10 0x44 /* RD/WR */
+#define IPACX_CDA_TSDP11 0x45 /* RD/WR */
+#define IPACX_CDA_TSDP20 0x46 /* RD/WR */
+#define IPACX_CDA_TSDP21 0x47 /* RD/WR */
+#define IPACX_BCHA_TSDP_BC1 0x48 /* RD/WR */
+#define IPACX_BCHA_TSDP_BC2 0x49 /* RD/WR */
+#define IPACX_BCHB_TSDP_BC1 0x4A /* RD/WR */
+#define IPACX_BCHB_TSDP_BC2 0x4B /* RD/WR */
+#define IPACX_TR_TSDP_BC1 0x4C /* RD/WR */
+#define IPACX_TR_TSDP_BC2 0x4D /* RD/WR */
+#define IPACX_CDA1_CR 0x4E /* RD/WR */
+#define IPACX_CDA2_CR 0x4F /* RD/WR */
+
+/* IOM: Contol, Sync transfer, Monitor */
+#define IPACX_TR_CR 0x50 /* RD/WR */
+#define IPACX_TRC_CR 0x50 /* RD/WR */
+#define IPACX_BCHA_CR 0x51 /* RD/WR */
+#define IPACX_BCHB_CR 0x52 /* RD/WR */
+#define IPACX_DCI_CR 0x53 /* RD/WR */
+#define IPACX_DCIC_CR 0x53 /* RD/WR */
+#define IPACX_MON_CR 0x54 /* RD/WR */
+#define IPACX_SDS1_CR 0x55 /* RD/WR */
+#define IPACX_SDS2_CR 0x56 /* RD/WR */
+#define IPACX_IOM_CR 0x57 /* RD/WR */
+#define IPACX_STI 0x58 /* RD */
+#define IPACX_ASTI 0x58 /* WR */
+#define IPACX_MSTI 0x59 /* RD/WR */
+#define IPACX_SDS_CONF 0x5A /* RD/WR */
+#define IPACX_MCDA 0x5B /* RD */
+#define IPACX_MOR 0x5C /* RD */
+#define IPACX_MOX 0x5C /* WR */
+#define IPACX_MOSR 0x5D /* RD */
+#define IPACX_MOCR 0x5E /* RD/WR */
+#define IPACX_MSTA 0x5F /* RD */
+#define IPACX_MCONF 0x5F /* WR */
+
+/* Interrupt and general registers */
+#define IPACX_ISTA 0x60 /* RD */
+#define IPACX_MASK 0x60 /* WR */
+#define IPACX_AUXI 0x61 /* RD */
+#define IPACX_AUXM 0x61 /* WR */
+#define IPACX_MODE1 0x62 /* RD/WR */
+#define IPACX_MODE2 0x63 /* RD/WR */
+#define IPACX_ID 0x64 /* RD */
+#define IPACX_SRES 0x64 /* WR */
+#define IPACX_TIMR2 0x65 /* RD/WR */
+
+/* B-channel registers */
+#define IPACX_OFF_B1 0x70
+#define IPACX_OFF_B2 0x80
+
+#define IPACX_ISTAB 0x00 /* RD */
+#define IPACX_MASKB 0x00 /* WR */
+#define IPACX_STARB 0x01 /* RD */
+#define IPACX_CMDRB 0x01 /* WR */
+#define IPACX_MODEB 0x02 /* RD/WR */
+#define IPACX_EXMB 0x03 /* RD/WR */
+#define IPACX_RAH1 0x05 /* WR */
+#define IPACX_RAH2 0x06 /* WR */
+#define IPACX_RBCLB 0x06 /* RD */
+#define IPACX_RBCHB 0x07 /* RD */
+#define IPACX_RAL1 0x07 /* WR */
+#define IPACX_RAL2 0x08 /* WR */
+#define IPACX_RSTAB 0x08 /* RD */
+#define IPACX_TMB 0x09 /* RD/WR */
+#define IPACX_RFIFOB 0x0A /*- RD */
+#define IPACX_XFIFOB 0x0A /*- WR */
+
+/* Layer 1 Commands */
+#define IPACX_CMD_TIM 0x0
+#define IPACX_CMD_RES 0x1
+#define IPACX_CMD_SSP 0x2
+#define IPACX_CMD_SCP 0x3
+#define IPACX_CMD_AR8 0x8
+#define IPACX_CMD_AR10 0x9
+#define IPACX_CMD_ARL 0xa
+#define IPACX_CMD_DI 0xf
+
+/* Layer 1 Indications */
+#define IPACX_IND_DR 0x0
+#define IPACX_IND_RES 0x1
+#define IPACX_IND_TMA 0x2
+#define IPACX_IND_SLD 0x3
+#define IPACX_IND_RSY 0x4
+#define IPACX_IND_DR6 0x5
+#define IPACX_IND_PU 0x7
+#define IPACX_IND_AR 0x8
+#define IPACX_IND_ARL 0xa
+#define IPACX_IND_CVR 0xb
+#define IPACX_IND_AI8 0xc
+#define IPACX_IND_AI10 0xd
+#define IPACX_IND_AIL 0xe
+#define IPACX_IND_DC 0xf
+
+extern void init_ipacx(struct IsdnCardState *, int);
+extern void interrupt_ipacx(struct IsdnCardState *);
+extern void setup_isac(struct IsdnCardState *);
+
+#endif
diff --git a/drivers/isdn/hisax/isac.c b/drivers/isdn/hisax/isac.c
new file mode 100644
index 0000000..07b1673
--- /dev/null
+++ b/drivers/isdn/hisax/isac.c
@@ -0,0 +1,680 @@
+/* $Id: isac.c,v 1.31.2.3 2004/01/13 14:31:25 keil Exp $
+ *
+ * ISAC specific routines
+ *
+ * Author Karsten Keil
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ */
+
+#include "hisax.h"
+#include "isac.h"
+#include "arcofi.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+#include <linux/init.h>
+
+#define DBUSY_TIMER_VALUE 80
+#define ARCOFI_USE 1
+
+static char *ISACVer[] __devinitdata =
+{"2086/2186 V1.1", "2085 B1", "2085 B2",
+ "2085 V2.3"};
+
+void __devinit ISACVersion(struct IsdnCardState *cs, char *s)
+{
+ int val;
+
+ val = cs->readisac(cs, ISAC_RBCH);
+ printk(KERN_INFO "%s ISAC version (%x): %s\n", s, val, ISACVer[(val >> 5) & 3]);
+}
+
+static void
+ph_command(struct IsdnCardState *cs, unsigned int command)
+{
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ph_command %x", command);
+ cs->writeisac(cs, ISAC_CIX0, (command << 2) | 3);
+}
+
+
+static void
+isac_new_ph(struct IsdnCardState *cs)
+{
+ switch (cs->dc.isac.ph_state) {
+ case (ISAC_IND_RS):
+ case (ISAC_IND_EI):
+ ph_command(cs, ISAC_CMD_DUI);
+ l1_msg(cs, HW_RESET | INDICATION, NULL);
+ break;
+ case (ISAC_IND_DID):
+ l1_msg(cs, HW_DEACTIVATE | CONFIRM, NULL);
+ break;
+ case (ISAC_IND_DR):
+ l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
+ break;
+ case (ISAC_IND_PU):
+ l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
+ break;
+ case (ISAC_IND_RSY):
+ l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+ break;
+ case (ISAC_IND_ARD):
+ l1_msg(cs, HW_INFO2 | INDICATION, NULL);
+ break;
+ case (ISAC_IND_AI8):
+ l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
+ break;
+ case (ISAC_IND_AI10):
+ l1_msg(cs, HW_INFO4_P10 | INDICATION, NULL);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+isac_bh(struct work_struct *work)
+{
+ struct IsdnCardState *cs =
+ container_of(work, struct IsdnCardState, tqueue);
+ struct PStack *stptr;
+
+ if (!cs)
+ return;
+ if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) {
+ if (cs->debug)
+ debugl1(cs, "D-Channel Busy cleared");
+ stptr = cs->stlist;
+ while (stptr != NULL) {
+ stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL);
+ stptr = stptr->next;
+ }
+ }
+ if (test_and_clear_bit(D_L1STATECHANGE, &cs->event))
+ isac_new_ph(cs);
+ if (test_and_clear_bit(D_RCVBUFREADY, &cs->event))
+ DChannel_proc_rcv(cs);
+ if (test_and_clear_bit(D_XMTBUFREADY, &cs->event))
+ DChannel_proc_xmt(cs);
+#if ARCOFI_USE
+ if (!test_bit(HW_ARCOFI, &cs->HW_Flags))
+ return;
+ if (test_and_clear_bit(D_RX_MON1, &cs->event))
+ arcofi_fsm(cs, ARCOFI_RX_END, NULL);
+ if (test_and_clear_bit(D_TX_MON1, &cs->event))
+ arcofi_fsm(cs, ARCOFI_TX_END, NULL);
+#endif
+}
+
+static void
+isac_empty_fifo(struct IsdnCardState *cs, int count)
+{
+ u_char *ptr;
+
+ if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
+ debugl1(cs, "isac_empty_fifo");
+
+ if ((cs->rcvidx + count) >= MAX_DFRAME_LEN_L1) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "isac_empty_fifo overrun %d",
+ cs->rcvidx + count);
+ cs->writeisac(cs, ISAC_CMDR, 0x80);
+ cs->rcvidx = 0;
+ return;
+ }
+ ptr = cs->rcvbuf + cs->rcvidx;
+ cs->rcvidx += count;
+ cs->readisacfifo(cs, ptr, count);
+ cs->writeisac(cs, ISAC_CMDR, 0x80);
+ if (cs->debug & L1_DEB_ISAC_FIFO) {
+ char *t = cs->dlog;
+
+ t += sprintf(t, "isac_empty_fifo cnt %d", count);
+ QuickHex(t, ptr, count);
+ debugl1(cs, cs->dlog);
+ }
+}
+
+static void
+isac_fill_fifo(struct IsdnCardState *cs)
+{
+ int count, more;
+ u_char *ptr;
+
+ if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
+ debugl1(cs, "isac_fill_fifo");
+
+ if (!cs->tx_skb)
+ return;
+
+ count = cs->tx_skb->len;
+ if (count <= 0)
+ return;
+
+ more = 0;
+ if (count > 32) {
+ more = !0;
+ count = 32;
+ }
+ ptr = cs->tx_skb->data;
+ skb_pull(cs->tx_skb, count);
+ cs->tx_cnt += count;
+ cs->writeisacfifo(cs, ptr, count);
+ cs->writeisac(cs, ISAC_CMDR, more ? 0x8 : 0xa);
+ if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+ debugl1(cs, "isac_fill_fifo dbusytimer running");
+ del_timer(&cs->dbusytimer);
+ }
+ init_timer(&cs->dbusytimer);
+ cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
+ add_timer(&cs->dbusytimer);
+ if (cs->debug & L1_DEB_ISAC_FIFO) {
+ char *t = cs->dlog;
+
+ t += sprintf(t, "isac_fill_fifo cnt %d", count);
+ QuickHex(t, ptr, count);
+ debugl1(cs, cs->dlog);
+ }
+}
+
+void
+isac_interrupt(struct IsdnCardState *cs, u_char val)
+{
+ u_char exval, v1;
+ struct sk_buff *skb;
+ unsigned int count;
+
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ISAC interrupt %x", val);
+ if (val & 0x80) { /* RME */
+ exval = cs->readisac(cs, ISAC_RSTA);
+ if ((exval & 0x70) != 0x20) {
+ if (exval & 0x40) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "ISAC RDO");
+#ifdef ERROR_STATISTIC
+ cs->err_rx++;
+#endif
+ }
+ if (!(exval & 0x20)) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "ISAC CRC error");
+#ifdef ERROR_STATISTIC
+ cs->err_crc++;
+#endif
+ }
+ cs->writeisac(cs, ISAC_CMDR, 0x80);
+ } else {
+ count = cs->readisac(cs, ISAC_RBCL) & 0x1f;
+ if (count == 0)
+ count = 32;
+ isac_empty_fifo(cs, count);
+ if ((count = cs->rcvidx) > 0) {
+ cs->rcvidx = 0;
+ if (!(skb = alloc_skb(count, GFP_ATOMIC)))
+ printk(KERN_WARNING "HiSax: D receive out of memory\n");
+ else {
+ memcpy(skb_put(skb, count), cs->rcvbuf, count);
+ skb_queue_tail(&cs->rq, skb);
+ }
+ }
+ }
+ cs->rcvidx = 0;
+ schedule_event(cs, D_RCVBUFREADY);
+ }
+ if (val & 0x40) { /* RPF */
+ isac_empty_fifo(cs, 32);
+ }
+ if (val & 0x20) { /* RSC */
+ /* never */
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "ISAC RSC interrupt");
+ }
+ if (val & 0x10) { /* XPR */
+ if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+ del_timer(&cs->dbusytimer);
+ if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+ schedule_event(cs, D_CLEARBUSY);
+ if (cs->tx_skb) {
+ if (cs->tx_skb->len) {
+ isac_fill_fifo(cs);
+ goto afterXPR;
+ } else {
+ dev_kfree_skb_irq(cs->tx_skb);
+ cs->tx_cnt = 0;
+ cs->tx_skb = NULL;
+ }
+ }
+ if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
+ cs->tx_cnt = 0;
+ isac_fill_fifo(cs);
+ } else
+ schedule_event(cs, D_XMTBUFREADY);
+ }
+ afterXPR:
+ if (val & 0x04) { /* CISQ */
+ exval = cs->readisac(cs, ISAC_CIR0);
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ISAC CIR0 %02X", exval );
+ if (exval & 2) {
+ cs->dc.isac.ph_state = (exval >> 2) & 0xf;
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ph_state change %x", cs->dc.isac.ph_state);
+ schedule_event(cs, D_L1STATECHANGE);
+ }
+ if (exval & 1) {
+ exval = cs->readisac(cs, ISAC_CIR1);
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ISAC CIR1 %02X", exval );
+ }
+ }
+ if (val & 0x02) { /* SIN */
+ /* never */
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "ISAC SIN interrupt");
+ }
+ if (val & 0x01) { /* EXI */
+ exval = cs->readisac(cs, ISAC_EXIR);
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "ISAC EXIR %02x", exval);
+ if (exval & 0x80) { /* XMR */
+ debugl1(cs, "ISAC XMR");
+ printk(KERN_WARNING "HiSax: ISAC XMR\n");
+ }
+ if (exval & 0x40) { /* XDU */
+ debugl1(cs, "ISAC XDU");
+ printk(KERN_WARNING "HiSax: ISAC XDU\n");
+#ifdef ERROR_STATISTIC
+ cs->err_tx++;
+#endif
+ if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+ del_timer(&cs->dbusytimer);
+ if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+ schedule_event(cs, D_CLEARBUSY);
+ if (cs->tx_skb) { /* Restart frame */
+ skb_push(cs->tx_skb, cs->tx_cnt);
+ cs->tx_cnt = 0;
+ isac_fill_fifo(cs);
+ } else {
+ printk(KERN_WARNING "HiSax: ISAC XDU no skb\n");
+ debugl1(cs, "ISAC XDU no skb");
+ }
+ }
+ if (exval & 0x04) { /* MOS */
+ v1 = cs->readisac(cs, ISAC_MOSR);
+ if (cs->debug & L1_DEB_MONITOR)
+ debugl1(cs, "ISAC MOSR %02x", v1);
+#if ARCOFI_USE
+ if (v1 & 0x08) {
+ if (!cs->dc.isac.mon_rx) {
+ if (!(cs->dc.isac.mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "ISAC MON RX out of memory!");
+ cs->dc.isac.mocr &= 0xf0;
+ cs->dc.isac.mocr |= 0x0a;
+ cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+ goto afterMONR0;
+ } else
+ cs->dc.isac.mon_rxp = 0;
+ }
+ if (cs->dc.isac.mon_rxp >= MAX_MON_FRAME) {
+ cs->dc.isac.mocr &= 0xf0;
+ cs->dc.isac.mocr |= 0x0a;
+ cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+ cs->dc.isac.mon_rxp = 0;
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "ISAC MON RX overflow!");
+ goto afterMONR0;
+ }
+ cs->dc.isac.mon_rx[cs->dc.isac.mon_rxp++] = cs->readisac(cs, ISAC_MOR0);
+ if (cs->debug & L1_DEB_MONITOR)
+ debugl1(cs, "ISAC MOR0 %02x", cs->dc.isac.mon_rx[cs->dc.isac.mon_rxp -1]);
+ if (cs->dc.isac.mon_rxp == 1) {
+ cs->dc.isac.mocr |= 0x04;
+ cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+ }
+ }
+ afterMONR0:
+ if (v1 & 0x80) {
+ if (!cs->dc.isac.mon_rx) {
+ if (!(cs->dc.isac.mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC))) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "ISAC MON RX out of memory!");
+ cs->dc.isac.mocr &= 0x0f;
+ cs->dc.isac.mocr |= 0xa0;
+ cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+ goto afterMONR1;
+ } else
+ cs->dc.isac.mon_rxp = 0;
+ }
+ if (cs->dc.isac.mon_rxp >= MAX_MON_FRAME) {
+ cs->dc.isac.mocr &= 0x0f;
+ cs->dc.isac.mocr |= 0xa0;
+ cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+ cs->dc.isac.mon_rxp = 0;
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "ISAC MON RX overflow!");
+ goto afterMONR1;
+ }
+ cs->dc.isac.mon_rx[cs->dc.isac.mon_rxp++] = cs->readisac(cs, ISAC_MOR1);
+ if (cs->debug & L1_DEB_MONITOR)
+ debugl1(cs, "ISAC MOR1 %02x", cs->dc.isac.mon_rx[cs->dc.isac.mon_rxp -1]);
+ cs->dc.isac.mocr |= 0x40;
+ cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+ }
+ afterMONR1:
+ if (v1 & 0x04) {
+ cs->dc.isac.mocr &= 0xf0;
+ cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+ cs->dc.isac.mocr |= 0x0a;
+ cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+ schedule_event(cs, D_RX_MON0);
+ }
+ if (v1 & 0x40) {
+ cs->dc.isac.mocr &= 0x0f;
+ cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+ cs->dc.isac.mocr |= 0xa0;
+ cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+ schedule_event(cs, D_RX_MON1);
+ }
+ if (v1 & 0x02) {
+ if ((!cs->dc.isac.mon_tx) || (cs->dc.isac.mon_txc &&
+ (cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc) &&
+ !(v1 & 0x08))) {
+ cs->dc.isac.mocr &= 0xf0;
+ cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+ cs->dc.isac.mocr |= 0x0a;
+ cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+ if (cs->dc.isac.mon_txc &&
+ (cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc))
+ schedule_event(cs, D_TX_MON0);
+ goto AfterMOX0;
+ }
+ if (cs->dc.isac.mon_txc && (cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc)) {
+ schedule_event(cs, D_TX_MON0);
+ goto AfterMOX0;
+ }
+ cs->writeisac(cs, ISAC_MOX0,
+ cs->dc.isac.mon_tx[cs->dc.isac.mon_txp++]);
+ if (cs->debug & L1_DEB_MONITOR)
+ debugl1(cs, "ISAC %02x -> MOX0", cs->dc.isac.mon_tx[cs->dc.isac.mon_txp -1]);
+ }
+ AfterMOX0:
+ if (v1 & 0x20) {
+ if ((!cs->dc.isac.mon_tx) || (cs->dc.isac.mon_txc &&
+ (cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc) &&
+ !(v1 & 0x80))) {
+ cs->dc.isac.mocr &= 0x0f;
+ cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+ cs->dc.isac.mocr |= 0xa0;
+ cs->writeisac(cs, ISAC_MOCR, cs->dc.isac.mocr);
+ if (cs->dc.isac.mon_txc &&
+ (cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc))
+ schedule_event(cs, D_TX_MON1);
+ goto AfterMOX1;
+ }
+ if (cs->dc.isac.mon_txc && (cs->dc.isac.mon_txp >= cs->dc.isac.mon_txc)) {
+ schedule_event(cs, D_TX_MON1);
+ goto AfterMOX1;
+ }
+ cs->writeisac(cs, ISAC_MOX1,
+ cs->dc.isac.mon_tx[cs->dc.isac.mon_txp++]);
+ if (cs->debug & L1_DEB_MONITOR)
+ debugl1(cs, "ISAC %02x -> MOX1", cs->dc.isac.mon_tx[cs->dc.isac.mon_txp -1]);
+ }
+ AfterMOX1:;
+#endif
+ }
+ }
+}
+
+static void
+ISAC_l1hw(struct PStack *st, int pr, void *arg)
+{
+ struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+ struct sk_buff *skb = arg;
+ u_long flags;
+ int val;
+
+ switch (pr) {
+ case (PH_DATA |REQUEST):
+ if (cs->debug & DEB_DLOG_HEX)
+ LogFrame(cs, skb->data, skb->len);
+ if (cs->debug & DEB_DLOG_VERBOSE)
+ dlogframe(cs, skb, 0);
+ spin_lock_irqsave(&cs->lock, flags);
+ if (cs->tx_skb) {
+ skb_queue_tail(&cs->sq, skb);
+#ifdef L2FRAME_DEBUG /* psa */
+ if (cs->debug & L1_DEB_LAPD)
+ Logl2Frame(cs, skb, "PH_DATA Queued", 0);
+#endif
+ } else {
+ cs->tx_skb = skb;
+ cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG /* psa */
+ if (cs->debug & L1_DEB_LAPD)
+ Logl2Frame(cs, skb, "PH_DATA", 0);
+#endif
+ isac_fill_fifo(cs);
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (PH_PULL |INDICATION):
+ spin_lock_irqsave(&cs->lock, flags);
+ if (cs->tx_skb) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
+ skb_queue_tail(&cs->sq, skb);
+ } else {
+ if (cs->debug & DEB_DLOG_HEX)
+ LogFrame(cs, skb->data, skb->len);
+ if (cs->debug & DEB_DLOG_VERBOSE)
+ dlogframe(cs, skb, 0);
+ cs->tx_skb = skb;
+ cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG /* psa */
+ if (cs->debug & L1_DEB_LAPD)
+ Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
+#endif
+ isac_fill_fifo(cs);
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (PH_PULL | REQUEST):
+#ifdef L2FRAME_DEBUG /* psa */
+ if (cs->debug & L1_DEB_LAPD)
+ debugl1(cs, "-> PH_REQUEST_PULL");
+#endif
+ if (!cs->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 (HW_RESET | REQUEST):
+ spin_lock_irqsave(&cs->lock, flags);
+ if ((cs->dc.isac.ph_state == ISAC_IND_EI) ||
+ (cs->dc.isac.ph_state == ISAC_IND_DR) ||
+ (cs->dc.isac.ph_state == ISAC_IND_RS))
+ ph_command(cs, ISAC_CMD_TIM);
+ else
+ ph_command(cs, ISAC_CMD_RS);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (HW_ENABLE | REQUEST):
+ spin_lock_irqsave(&cs->lock, flags);
+ ph_command(cs, ISAC_CMD_TIM);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (HW_INFO3 | REQUEST):
+ spin_lock_irqsave(&cs->lock, flags);
+ ph_command(cs, ISAC_CMD_AR8);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (HW_TESTLOOP | REQUEST):
+ spin_lock_irqsave(&cs->lock, flags);
+ val = 0;
+ if (1 & (long) arg)
+ val |= 0x0c;
+ if (2 & (long) arg)
+ val |= 0x3;
+ if (test_bit(HW_IOM1, &cs->HW_Flags)) {
+ /* IOM 1 Mode */
+ if (!val) {
+ cs->writeisac(cs, ISAC_SPCR, 0xa);
+ cs->writeisac(cs, ISAC_ADF1, 0x2);
+ } else {
+ cs->writeisac(cs, ISAC_SPCR, val);
+ cs->writeisac(cs, ISAC_ADF1, 0xa);
+ }
+ } else {
+ /* IOM 2 Mode */
+ cs->writeisac(cs, ISAC_SPCR, val);
+ if (val)
+ cs->writeisac(cs, ISAC_ADF1, 0x8);
+ else
+ cs->writeisac(cs, ISAC_ADF1, 0x0);
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (HW_DEACTIVATE | RESPONSE):
+ skb_queue_purge(&cs->rq);
+ skb_queue_purge(&cs->sq);
+ if (cs->tx_skb) {
+ dev_kfree_skb_any(cs->tx_skb);
+ cs->tx_skb = NULL;
+ }
+ if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+ del_timer(&cs->dbusytimer);
+ if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+ schedule_event(cs, D_CLEARBUSY);
+ break;
+ default:
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "isac_l1hw unknown %04x", pr);
+ break;
+ }
+}
+
+static void
+setstack_isac(struct PStack *st, struct IsdnCardState *cs)
+{
+ st->l1.l1hw = ISAC_l1hw;
+}
+
+static void
+DC_Close_isac(struct IsdnCardState *cs)
+{
+ kfree(cs->dc.isac.mon_rx);
+ cs->dc.isac.mon_rx = NULL;
+ kfree(cs->dc.isac.mon_tx);
+ cs->dc.isac.mon_tx = NULL;
+}
+
+static void
+dbusy_timer_handler(struct IsdnCardState *cs)
+{
+ struct PStack *stptr;
+ int rbch, star;
+
+ if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+ rbch = cs->readisac(cs, ISAC_RBCH);
+ star = cs->readisac(cs, ISAC_STAR);
+ if (cs->debug)
+ debugl1(cs, "D-Channel Busy RBCH %02x STAR %02x",
+ rbch, star);
+ if (rbch & ISAC_RBCH_XAC) { /* D-Channel Busy */
+ test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
+ stptr = cs->stlist;
+ while (stptr != NULL) {
+ stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL);
+ stptr = stptr->next;
+ }
+ } else {
+ /* discard frame; reset transceiver */
+ test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags);
+ if (cs->tx_skb) {
+ dev_kfree_skb_any(cs->tx_skb);
+ cs->tx_cnt = 0;
+ cs->tx_skb = NULL;
+ } else {
+ printk(KERN_WARNING "HiSax: ISAC D-Channel Busy no skb\n");
+ debugl1(cs, "D-Channel Busy no skb");
+ }
+ cs->writeisac(cs, ISAC_CMDR, 0x01); /* Transmitter reset */
+ cs->irq_func(cs->irq, cs);
+ }
+ }
+}
+
+void initisac(struct IsdnCardState *cs)
+{
+ cs->setstack_d = setstack_isac;
+ cs->DC_Close = DC_Close_isac;
+ cs->dc.isac.mon_tx = NULL;
+ cs->dc.isac.mon_rx = NULL;
+ cs->writeisac(cs, ISAC_MASK, 0xff);
+ cs->dc.isac.mocr = 0xaa;
+ if (test_bit(HW_IOM1, &cs->HW_Flags)) {
+ /* IOM 1 Mode */
+ cs->writeisac(cs, ISAC_ADF2, 0x0);
+ cs->writeisac(cs, ISAC_SPCR, 0xa);
+ cs->writeisac(cs, ISAC_ADF1, 0x2);
+ cs->writeisac(cs, ISAC_STCR, 0x70);
+ cs->writeisac(cs, ISAC_MODE, 0xc9);
+ } else {
+ /* IOM 2 Mode */
+ if (!cs->dc.isac.adf2)
+ cs->dc.isac.adf2 = 0x80;
+ cs->writeisac(cs, ISAC_ADF2, cs->dc.isac.adf2);
+ cs->writeisac(cs, ISAC_SQXR, 0x2f);
+ cs->writeisac(cs, ISAC_SPCR, 0x00);
+ cs->writeisac(cs, ISAC_STCR, 0x70);
+ cs->writeisac(cs, ISAC_MODE, 0xc9);
+ cs->writeisac(cs, ISAC_TIMR, 0x00);
+ cs->writeisac(cs, ISAC_ADF1, 0x00);
+ }
+ ph_command(cs, ISAC_CMD_RS);
+ cs->writeisac(cs, ISAC_MASK, 0x0);
+}
+
+void clear_pending_isac_ints(struct IsdnCardState *cs)
+{
+ int val, eval;
+
+ val = cs->readisac(cs, ISAC_STAR);
+ debugl1(cs, "ISAC STAR %x", val);
+ val = cs->readisac(cs, ISAC_MODE);
+ debugl1(cs, "ISAC MODE %x", val);
+ val = cs->readisac(cs, ISAC_ADF2);
+ debugl1(cs, "ISAC ADF2 %x", val);
+ val = cs->readisac(cs, ISAC_ISTA);
+ debugl1(cs, "ISAC ISTA %x", val);
+ if (val & 0x01) {
+ eval = cs->readisac(cs, ISAC_EXIR);
+ debugl1(cs, "ISAC EXIR %x", eval);
+ }
+ val = cs->readisac(cs, ISAC_CIR0);
+ debugl1(cs, "ISAC CIR0 %x", val);
+ cs->dc.isac.ph_state = (val >> 2) & 0xf;
+ schedule_event(cs, D_L1STATECHANGE);
+ /* Disable all IRQ */
+ cs->writeisac(cs, ISAC_MASK, 0xFF);
+}
+
+void __devinit
+setup_isac(struct IsdnCardState *cs)
+{
+ INIT_WORK(&cs->tqueue, isac_bh);
+ cs->dbusytimer.function = (void *) dbusy_timer_handler;
+ cs->dbusytimer.data = (long) cs;
+ init_timer(&cs->dbusytimer);
+}
diff --git a/drivers/isdn/hisax/isac.h b/drivers/isdn/hisax/isac.h
new file mode 100644
index 0000000..8f8331e
--- /dev/null
+++ b/drivers/isdn/hisax/isac.h
@@ -0,0 +1,70 @@
+/* $Id: isac.h,v 1.9.2.2 2004/01/12 22:52:27 keil Exp $
+ *
+ * ISAC specific defines
+ *
+ * Author Karsten Keil
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/* 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
+
+extern void ISACVersion(struct IsdnCardState *, char *);
+extern void setup_isac(struct IsdnCardState *);
+extern void initisac(struct IsdnCardState *);
+extern void isac_interrupt(struct IsdnCardState *, u_char);
+extern void clear_pending_isac_ints(struct IsdnCardState *);
diff --git a/drivers/isdn/hisax/isar.c b/drivers/isdn/hisax/isar.c
new file mode 100644
index 0000000..bfeb9b6
--- /dev/null
+++ b/drivers/isdn/hisax/isar.c
@@ -0,0 +1,1909 @@
+/* $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>
+
+#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, 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, 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 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
+
+ if ((ret = copy_from_user(&size, p, sizeof(int)))) {
+ printk(KERN_ERR"isar_load_firmware copy_from_user ret %d\n", ret);
+ return ret;
+ }
+ 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))) {
+ memcpy(skb_put(skb, 2), 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 {
+ memcpy(skb_put(skb, bcs->hw.isar.rcvidx-2),
+ 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", devt);
+ 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;
+ 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);
+ 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, 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 BCState *bcs) {
+ if (bcs->cs->debug)
+ debugl1(bcs->cs, "ftimer flags %04x",
+ 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 pathes 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 funktions only with DP1\n");
+ debugl1(cs, "isar modeisar analog funktions 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/%d", 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;
+ cs->bcs[0].hw.isar.ftimer.function = (void *) ftimer_handler;
+ cs->bcs[0].hw.isar.ftimer.data = (long) &cs->bcs[0];
+ init_timer(&cs->bcs[0].hw.isar.ftimer);
+ cs->bcs[1].hw.isar.ftimer.function = (void *) ftimer_handler;
+ cs->bcs[1].hw.isar.ftimer.data = (long) &cs->bcs[1];
+ init_timer(&cs->bcs[1].hw.isar.ftimer);
+}
diff --git a/drivers/isdn/hisax/isar.h b/drivers/isdn/hisax/isar.h
new file mode 100644
index 0000000..bf76765
--- /dev/null
+++ b/drivers/isdn/hisax/isar.h
@@ -0,0 +1,222 @@
+/* $Id: isar.h,v 1.11.2.2 2004/01/12 22:52:27 keil Exp $
+ *
+ * ISAR (Siemens PSB 7110) specific defines
+ *
+ * Author Karsten Keil
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#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_WAITSTATE 0x27
+#define ISAR_HIS_TIMERIRQ 0x25
+#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_CMD_TIMERIRQ_OFF 0x20
+#define ISAR_CMD_TIMERIRQ_ON 0x21
+
+
+#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_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
+
+#define ISDN_FAXPUMP_HALT 100
+
+extern int ISARVersion(struct IsdnCardState *cs, char *s);
+extern void isar_int_main(struct IsdnCardState *cs);
+extern void initisar(struct IsdnCardState *cs);
+extern void isar_fill_fifo(struct BCState *bcs);
+extern int isar_auxcmd(struct IsdnCardState *cs, isdn_ctrl *ic);
diff --git a/drivers/isdn/hisax/isdnhdlc.c b/drivers/isdn/hisax/isdnhdlc.c
new file mode 100644
index 0000000..c69a77a
--- /dev/null
+++ b/drivers/isdn/hisax/isdnhdlc.c
@@ -0,0 +1,603 @@
+/*
+ * isdnhdlc.c -- General purpose ISDN HDLC decoder.
+ *
+ *Copyright (C) 2002 Wolfgang Mües <wolfgang@iksw-muees.de>
+ * 2001 Frode Isaksen <fisaksen@bewan.com>
+ * 2001 Kai Germaschewski <kai.germaschewski@gmx.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 of the License, 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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/crc-ccitt.h>
+#include "isdnhdlc.h"
+
+/*-------------------------------------------------------------------*/
+
+MODULE_AUTHOR("Wolfgang Mües <wolfgang@iksw-muees.de>, "
+ "Frode Isaksen <fisaksen@bewan.com>, "
+ "Kai Germaschewski <kai.germaschewski@gmx.de>");
+MODULE_DESCRIPTION("General purpose ISDN HDLC decoder");
+MODULE_LICENSE("GPL");
+
+/*-------------------------------------------------------------------*/
+
+enum {
+ HDLC_FAST_IDLE,HDLC_GET_FLAG_B0,HDLC_GETFLAG_B1A6,HDLC_GETFLAG_B7,
+ HDLC_GET_DATA,HDLC_FAST_FLAG
+};
+
+enum {
+ HDLC_SEND_DATA,HDLC_SEND_CRC1,HDLC_SEND_FAST_FLAG,
+ HDLC_SEND_FIRST_FLAG,HDLC_SEND_CRC2,HDLC_SEND_CLOSING_FLAG,
+ HDLC_SEND_IDLE1,HDLC_SEND_FAST_IDLE,HDLC_SENDFLAG_B0,
+ HDLC_SENDFLAG_B1A6,HDLC_SENDFLAG_B7,STOPPED
+};
+
+void isdnhdlc_rcv_init (struct isdnhdlc_vars *hdlc, int do_adapt56)
+{
+ hdlc->bit_shift = 0;
+ hdlc->hdlc_bits1 = 0;
+ hdlc->data_bits = 0;
+ hdlc->ffbit_shift = 0;
+ hdlc->data_received = 0;
+ hdlc->state = HDLC_GET_DATA;
+ hdlc->do_adapt56 = do_adapt56;
+ hdlc->dchannel = 0;
+ hdlc->crc = 0;
+ hdlc->cbin = 0;
+ hdlc->shift_reg = 0;
+ hdlc->ffvalue = 0;
+ hdlc->dstpos = 0;
+}
+
+void isdnhdlc_out_init (struct isdnhdlc_vars *hdlc, int is_d_channel, int do_adapt56)
+{
+ hdlc->bit_shift = 0;
+ hdlc->hdlc_bits1 = 0;
+ hdlc->data_bits = 0;
+ hdlc->ffbit_shift = 0;
+ hdlc->data_received = 0;
+ hdlc->do_closing = 0;
+ hdlc->ffvalue = 0;
+ if (is_d_channel) {
+ hdlc->dchannel = 1;
+ hdlc->state = HDLC_SEND_FIRST_FLAG;
+ } else {
+ hdlc->dchannel = 0;
+ hdlc->state = HDLC_SEND_FAST_FLAG;
+ hdlc->ffvalue = 0x7e;
+ }
+ hdlc->cbin = 0x7e;
+ hdlc->bit_shift = 0;
+ if(do_adapt56){
+ hdlc->do_adapt56 = 1;
+ hdlc->data_bits = 0;
+ hdlc->state = HDLC_SENDFLAG_B0;
+ } else {
+ hdlc->do_adapt56 = 0;
+ hdlc->data_bits = 8;
+ }
+ hdlc->shift_reg = 0;
+}
+
+/*
+ isdnhdlc_decode - decodes HDLC frames from a transparent bit stream.
+
+ The source buffer is scanned for valid HDLC frames looking for
+ flags (01111110) to indicate the start of a frame. If the start of
+ the frame is found, the bit stuffing is removed (0 after 5 1's).
+ When a new flag is found, the complete frame has been received
+ and the CRC is checked.
+ If a valid frame is found, the function returns the frame length
+ excluding the CRC with the bit HDLC_END_OF_FRAME set.
+ If the beginning of a valid frame is found, the function returns
+ the length.
+ If a framing error is found (too many 1s and not a flag) the function
+ returns the length with the bit HDLC_FRAMING_ERROR set.
+ If a CRC error is found the function returns the length with the
+ bit HDLC_CRC_ERROR set.
+ If the frame length exceeds the destination buffer size, the function
+ returns the length with the bit HDLC_LENGTH_ERROR set.
+
+ src - source buffer
+ slen - source buffer length
+ count - number of bytes removed (decoded) from the source buffer
+ dst _ destination buffer
+ dsize - destination buffer size
+ returns - number of decoded bytes in the destination buffer and status
+ flag.
+ */
+int isdnhdlc_decode (struct isdnhdlc_vars *hdlc, const unsigned char *src,
+ int slen, int *count, unsigned char *dst, int dsize)
+{
+ int status=0;
+
+ static const unsigned char fast_flag[]={
+ 0x00,0x00,0x00,0x20,0x30,0x38,0x3c,0x3e,0x3f
+ };
+
+ static const unsigned char fast_flag_value[]={
+ 0x00,0x7e,0xfc,0xf9,0xf3,0xe7,0xcf,0x9f,0x3f
+ };
+
+ static const unsigned char fast_abort[]={
+ 0x00,0x00,0x80,0xc0,0xe0,0xf0,0xf8,0xfc,0xfe,0xff
+ };
+
+ *count = slen;
+
+ while(slen > 0){
+ if(hdlc->bit_shift==0){
+ hdlc->cbin = *src++;
+ slen--;
+ hdlc->bit_shift = 8;
+ if(hdlc->do_adapt56){
+ hdlc->bit_shift --;
+ }
+ }
+
+ switch(hdlc->state){
+ case STOPPED:
+ return 0;
+ case HDLC_FAST_IDLE:
+ if(hdlc->cbin == 0xff){
+ hdlc->bit_shift = 0;
+ break;
+ }
+ hdlc->state = HDLC_GET_FLAG_B0;
+ hdlc->hdlc_bits1 = 0;
+ hdlc->bit_shift = 8;
+ break;
+ case HDLC_GET_FLAG_B0:
+ if(!(hdlc->cbin & 0x80)) {
+ hdlc->state = HDLC_GETFLAG_B1A6;
+ hdlc->hdlc_bits1 = 0;
+ } else {
+ if(!hdlc->do_adapt56){
+ if(++hdlc->hdlc_bits1 >=8 ) if(hdlc->bit_shift==1)
+ hdlc->state = HDLC_FAST_IDLE;
+ }
+ }
+ hdlc->cbin<<=1;
+ hdlc->bit_shift --;
+ break;
+ case HDLC_GETFLAG_B1A6:
+ if(hdlc->cbin & 0x80){
+ hdlc->hdlc_bits1++;
+ if(hdlc->hdlc_bits1==6){
+ hdlc->state = HDLC_GETFLAG_B7;
+ }
+ } else {
+ hdlc->hdlc_bits1 = 0;
+ }
+ hdlc->cbin<<=1;
+ hdlc->bit_shift --;
+ break;
+ case HDLC_GETFLAG_B7:
+ if(hdlc->cbin & 0x80) {
+ hdlc->state = HDLC_GET_FLAG_B0;
+ } else {
+ hdlc->state = HDLC_GET_DATA;
+ hdlc->crc = 0xffff;
+ hdlc->shift_reg = 0;
+ hdlc->hdlc_bits1 = 0;
+ hdlc->data_bits = 0;
+ hdlc->data_received = 0;
+ }
+ hdlc->cbin<<=1;
+ hdlc->bit_shift --;
+ break;
+ case HDLC_GET_DATA:
+ if(hdlc->cbin & 0x80){
+ hdlc->hdlc_bits1++;
+ switch(hdlc->hdlc_bits1){
+ case 6:
+ break;
+ case 7:
+ if(hdlc->data_received) {
+ // bad frame
+ status = -HDLC_FRAMING_ERROR;
+ }
+ if(!hdlc->do_adapt56){
+ if(hdlc->cbin==fast_abort[hdlc->bit_shift+1]){
+ hdlc->state = HDLC_FAST_IDLE;
+ hdlc->bit_shift=1;
+ break;
+ }
+ } else {
+ hdlc->state = HDLC_GET_FLAG_B0;
+ }
+ break;
+ default:
+ hdlc->shift_reg>>=1;
+ hdlc->shift_reg |= 0x80;
+ hdlc->data_bits++;
+ break;
+ }
+ } else {
+ switch(hdlc->hdlc_bits1){
+ case 5:
+ break;
+ case 6:
+ if(hdlc->data_received){
+ if (hdlc->dstpos < 2) {
+ status = -HDLC_FRAMING_ERROR;
+ } else if (hdlc->crc != 0xf0b8){
+ // crc error
+ status = -HDLC_CRC_ERROR;
+ } else {
+ // remove CRC
+ hdlc->dstpos -= 2;
+ // good frame
+ status = hdlc->dstpos;
+ }
+ }
+ hdlc->crc = 0xffff;
+ hdlc->shift_reg = 0;
+ hdlc->data_bits = 0;
+ if(!hdlc->do_adapt56){
+ if(hdlc->cbin==fast_flag[hdlc->bit_shift]){
+ hdlc->ffvalue = fast_flag_value[hdlc->bit_shift];
+ hdlc->state = HDLC_FAST_FLAG;
+ hdlc->ffbit_shift = hdlc->bit_shift;
+ hdlc->bit_shift = 1;
+ } else {
+ hdlc->state = HDLC_GET_DATA;
+ hdlc->data_received = 0;
+ }
+ } else {
+ hdlc->state = HDLC_GET_DATA;
+ hdlc->data_received = 0;
+ }
+ break;
+ default:
+ hdlc->shift_reg>>=1;
+ hdlc->data_bits++;
+ break;
+ }
+ hdlc->hdlc_bits1 = 0;
+ }
+ if (status) {
+ hdlc->dstpos = 0;
+ *count -= slen;
+ hdlc->cbin <<= 1;
+ hdlc->bit_shift--;
+ return status;
+ }
+ if(hdlc->data_bits==8){
+ hdlc->data_bits = 0;
+ hdlc->data_received = 1;
+ hdlc->crc = crc_ccitt_byte(hdlc->crc, hdlc->shift_reg);
+
+ // good byte received
+ if (hdlc->dstpos < dsize) {
+ dst[hdlc->dstpos++] = hdlc->shift_reg;
+ } else {
+ // frame too long
+ status = -HDLC_LENGTH_ERROR;
+ hdlc->dstpos = 0;
+ }
+ }
+ hdlc->cbin <<= 1;
+ hdlc->bit_shift--;
+ break;
+ case HDLC_FAST_FLAG:
+ if(hdlc->cbin==hdlc->ffvalue){
+ hdlc->bit_shift = 0;
+ break;
+ } else {
+ if(hdlc->cbin == 0xff){
+ hdlc->state = HDLC_FAST_IDLE;
+ hdlc->bit_shift=0;
+ } else if(hdlc->ffbit_shift==8){
+ hdlc->state = HDLC_GETFLAG_B7;
+ break;
+ } else {
+ hdlc->shift_reg = fast_abort[hdlc->ffbit_shift-1];
+ hdlc->hdlc_bits1 = hdlc->ffbit_shift-2;
+ if(hdlc->hdlc_bits1<0)hdlc->hdlc_bits1 = 0;
+ hdlc->data_bits = hdlc->ffbit_shift-1;
+ hdlc->state = HDLC_GET_DATA;
+ hdlc->data_received = 0;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ *count -= slen;
+ return 0;
+}
+
+/*
+ isdnhdlc_encode - encodes HDLC frames to a transparent bit stream.
+
+ The bit stream starts with a beginning flag (01111110). After
+ that each byte is added to the bit stream with bit stuffing added
+ (0 after 5 1's).
+ When the last byte has been removed from the source buffer, the
+ CRC (2 bytes is added) and the frame terminates with the ending flag.
+ For the dchannel, the idle character (all 1's) is also added at the end.
+ If this function is called with empty source buffer (slen=0), flags or
+ idle character will be generated.
+
+ src - source buffer
+ slen - source buffer length
+ count - number of bytes removed (encoded) from source buffer
+ dst _ destination buffer
+ dsize - destination buffer size
+ returns - number of encoded bytes in the destination buffer
+*/
+int isdnhdlc_encode(struct isdnhdlc_vars *hdlc, const unsigned char *src,
+ unsigned short slen, int *count,
+ unsigned char *dst, int dsize)
+{
+ static const unsigned char xfast_flag_value[] = {
+ 0x7e,0x3f,0x9f,0xcf,0xe7,0xf3,0xf9,0xfc,0x7e
+ };
+
+ int len = 0;
+
+ *count = slen;
+
+ while (dsize > 0) {
+ if(hdlc->bit_shift==0){
+ if(slen && !hdlc->do_closing){
+ hdlc->shift_reg = *src++;
+ slen--;
+ if (slen == 0)
+ hdlc->do_closing = 1; /* closing sequence, CRC + flag(s) */
+ hdlc->bit_shift = 8;
+ } else {
+ if(hdlc->state == HDLC_SEND_DATA){
+ if(hdlc->data_received){
+ hdlc->state = HDLC_SEND_CRC1;
+ hdlc->crc ^= 0xffff;
+ hdlc->bit_shift = 8;
+ hdlc->shift_reg = hdlc->crc & 0xff;
+ } else if(!hdlc->do_adapt56){
+ hdlc->state = HDLC_SEND_FAST_FLAG;
+ } else {
+ hdlc->state = HDLC_SENDFLAG_B0;
+ }
+ }
+
+ }
+ }
+
+ switch(hdlc->state){
+ case STOPPED:
+ while (dsize--)
+ *dst++ = 0xff;
+
+ return dsize;
+ case HDLC_SEND_FAST_FLAG:
+ hdlc->do_closing = 0;
+ if(slen == 0){
+ *dst++ = hdlc->ffvalue;
+ len++;
+ dsize--;
+ break;
+ }
+ if(hdlc->bit_shift==8){
+ hdlc->cbin = hdlc->ffvalue>>(8-hdlc->data_bits);
+ hdlc->state = HDLC_SEND_DATA;
+ hdlc->crc = 0xffff;
+ hdlc->hdlc_bits1 = 0;
+ hdlc->data_received = 1;
+ }
+ break;
+ case HDLC_SENDFLAG_B0:
+ hdlc->do_closing = 0;
+ hdlc->cbin <<= 1;
+ hdlc->data_bits++;
+ hdlc->hdlc_bits1 = 0;
+ hdlc->state = HDLC_SENDFLAG_B1A6;
+ break;
+ case HDLC_SENDFLAG_B1A6:
+ hdlc->cbin <<= 1;
+ hdlc->data_bits++;
+ hdlc->cbin++;
+ if(++hdlc->hdlc_bits1 == 6)
+ hdlc->state = HDLC_SENDFLAG_B7;
+ break;
+ case HDLC_SENDFLAG_B7:
+ hdlc->cbin <<= 1;
+ hdlc->data_bits++;
+ if(slen == 0){
+ hdlc->state = HDLC_SENDFLAG_B0;
+ break;
+ }
+ if(hdlc->bit_shift==8){
+ hdlc->state = HDLC_SEND_DATA;
+ hdlc->crc = 0xffff;
+ hdlc->hdlc_bits1 = 0;
+ hdlc->data_received = 1;
+ }
+ break;
+ case HDLC_SEND_FIRST_FLAG:
+ hdlc->data_received = 1;
+ if(hdlc->data_bits==8){
+ hdlc->state = HDLC_SEND_DATA;
+ hdlc->crc = 0xffff;
+ hdlc->hdlc_bits1 = 0;
+ break;
+ }
+ hdlc->cbin <<= 1;
+ hdlc->data_bits++;
+ if(hdlc->shift_reg & 0x01)
+ hdlc->cbin++;
+ hdlc->shift_reg >>= 1;
+ hdlc->bit_shift--;
+ if(hdlc->bit_shift==0){
+ hdlc->state = HDLC_SEND_DATA;
+ hdlc->crc = 0xffff;
+ hdlc->hdlc_bits1 = 0;
+ }
+ break;
+ case HDLC_SEND_DATA:
+ hdlc->cbin <<= 1;
+ hdlc->data_bits++;
+ if(hdlc->hdlc_bits1 == 5){
+ hdlc->hdlc_bits1 = 0;
+ break;
+ }
+ if(hdlc->bit_shift==8){
+ hdlc->crc = crc_ccitt_byte(hdlc->crc, hdlc->shift_reg);
+ }
+ if(hdlc->shift_reg & 0x01){
+ hdlc->hdlc_bits1++;
+ hdlc->cbin++;
+ hdlc->shift_reg >>= 1;
+ hdlc->bit_shift--;
+ } else {
+ hdlc->hdlc_bits1 = 0;
+ hdlc->shift_reg >>= 1;
+ hdlc->bit_shift--;
+ }
+ break;
+ case HDLC_SEND_CRC1:
+ hdlc->cbin <<= 1;
+ hdlc->data_bits++;
+ if(hdlc->hdlc_bits1 == 5){
+ hdlc->hdlc_bits1 = 0;
+ break;
+ }
+ if(hdlc->shift_reg & 0x01){
+ hdlc->hdlc_bits1++;
+ hdlc->cbin++;
+ hdlc->shift_reg >>= 1;
+ hdlc->bit_shift--;
+ } else {
+ hdlc->hdlc_bits1 = 0;
+ hdlc->shift_reg >>= 1;
+ hdlc->bit_shift--;
+ }
+ if(hdlc->bit_shift==0){
+ hdlc->shift_reg = (hdlc->crc >> 8);
+ hdlc->state = HDLC_SEND_CRC2;
+ hdlc->bit_shift = 8;
+ }
+ break;
+ case HDLC_SEND_CRC2:
+ hdlc->cbin <<= 1;
+ hdlc->data_bits++;
+ if(hdlc->hdlc_bits1 == 5){
+ hdlc->hdlc_bits1 = 0;
+ break;
+ }
+ if(hdlc->shift_reg & 0x01){
+ hdlc->hdlc_bits1++;
+ hdlc->cbin++;
+ hdlc->shift_reg >>= 1;
+ hdlc->bit_shift--;
+ } else {
+ hdlc->hdlc_bits1 = 0;
+ hdlc->shift_reg >>= 1;
+ hdlc->bit_shift--;
+ }
+ if(hdlc->bit_shift==0){
+ hdlc->shift_reg = 0x7e;
+ hdlc->state = HDLC_SEND_CLOSING_FLAG;
+ hdlc->bit_shift = 8;
+ }
+ break;
+ case HDLC_SEND_CLOSING_FLAG:
+ hdlc->cbin <<= 1;
+ hdlc->data_bits++;
+ if(hdlc->hdlc_bits1 == 5){
+ hdlc->hdlc_bits1 = 0;
+ break;
+ }
+ if(hdlc->shift_reg & 0x01){
+ hdlc->cbin++;
+ }
+ hdlc->shift_reg >>= 1;
+ hdlc->bit_shift--;
+ if(hdlc->bit_shift==0){
+ hdlc->ffvalue = xfast_flag_value[hdlc->data_bits];
+ if(hdlc->dchannel){
+ hdlc->ffvalue = 0x7e;
+ hdlc->state = HDLC_SEND_IDLE1;
+ hdlc->bit_shift = 8-hdlc->data_bits;
+ if(hdlc->bit_shift==0)
+ hdlc->state = HDLC_SEND_FAST_IDLE;
+ } else {
+ if(!hdlc->do_adapt56){
+ hdlc->state = HDLC_SEND_FAST_FLAG;
+ hdlc->data_received = 0;
+ } else {
+ hdlc->state = HDLC_SENDFLAG_B0;
+ hdlc->data_received = 0;
+ }
+ // Finished with this frame, send flags
+ if (dsize > 1) dsize = 1;
+ }
+ }
+ break;
+ case HDLC_SEND_IDLE1:
+ hdlc->do_closing = 0;
+ hdlc->cbin <<= 1;
+ hdlc->cbin++;
+ hdlc->data_bits++;
+ hdlc->bit_shift--;
+ if(hdlc->bit_shift==0){
+ hdlc->state = HDLC_SEND_FAST_IDLE;
+ hdlc->bit_shift = 0;
+ }
+ break;
+ case HDLC_SEND_FAST_IDLE:
+ hdlc->do_closing = 0;
+ hdlc->cbin = 0xff;
+ hdlc->data_bits = 8;
+ if(hdlc->bit_shift == 8){
+ hdlc->cbin = 0x7e;
+ hdlc->state = HDLC_SEND_FIRST_FLAG;
+ } else {
+ *dst++ = hdlc->cbin;
+ hdlc->bit_shift = hdlc->data_bits = 0;
+ len++;
+ dsize = 0;
+ }
+ break;
+ default:
+ break;
+ }
+ if(hdlc->do_adapt56){
+ if(hdlc->data_bits==7){
+ hdlc->cbin <<= 1;
+ hdlc->cbin++;
+ hdlc->data_bits++;
+ }
+ }
+ if(hdlc->data_bits==8){
+ *dst++ = hdlc->cbin;
+ hdlc->data_bits = 0;
+ len++;
+ dsize--;
+ }
+ }
+ *count -= slen;
+
+ return len;
+}
+
+EXPORT_SYMBOL(isdnhdlc_rcv_init);
+EXPORT_SYMBOL(isdnhdlc_decode);
+EXPORT_SYMBOL(isdnhdlc_out_init);
+EXPORT_SYMBOL(isdnhdlc_encode);
diff --git a/drivers/isdn/hisax/isdnhdlc.h b/drivers/isdn/hisax/isdnhdlc.h
new file mode 100644
index 0000000..cf0a95a
--- /dev/null
+++ b/drivers/isdn/hisax/isdnhdlc.h
@@ -0,0 +1,70 @@
+/*
+ * isdnhdlc.h -- General purpose ISDN HDLC decoder.
+ *
+ * Implementation of a HDLC decoder/encoder in software.
+ * Neccessary because some ISDN devices don't have HDLC
+ * controllers. Also included: a bit reversal table.
+ *
+ *Copyright (C) 2002 Wolfgang Mües <wolfgang@iksw-muees.de>
+ * 2001 Frode Isaksen <fisaksen@bewan.com>
+ * 2001 Kai Germaschewski <kai.germaschewski@gmx.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 of the License, 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.
+ */
+
+#ifndef __ISDNHDLC_H__
+#define __ISDNHDLC_H__
+
+struct isdnhdlc_vars {
+ int bit_shift;
+ int hdlc_bits1;
+ int data_bits;
+ int ffbit_shift; // encoding only
+ int state;
+ int dstpos;
+
+ unsigned short crc;
+
+ unsigned char cbin;
+ unsigned char shift_reg;
+ unsigned char ffvalue;
+
+ unsigned int data_received:1; // set if transferring data
+ unsigned int dchannel:1; // set if D channel (send idle instead of flags)
+ unsigned int do_adapt56:1; // set if 56K adaptation
+ unsigned int do_closing:1; // set if in closing phase (need to send CRC + flag
+};
+
+
+/*
+ The return value from isdnhdlc_decode is
+ the frame length, 0 if no complete frame was decoded,
+ or a negative error number
+*/
+#define HDLC_FRAMING_ERROR 1
+#define HDLC_CRC_ERROR 2
+#define HDLC_LENGTH_ERROR 3
+
+extern void isdnhdlc_rcv_init (struct isdnhdlc_vars *hdlc, int do_adapt56);
+
+extern int isdnhdlc_decode (struct isdnhdlc_vars *hdlc, const unsigned char *src, int slen,int *count,
+ unsigned char *dst, int dsize);
+
+extern void isdnhdlc_out_init (struct isdnhdlc_vars *hdlc,int is_d_channel,int do_adapt56);
+
+extern int isdnhdlc_encode (struct isdnhdlc_vars *hdlc,const unsigned char *src,unsigned short slen,int *count,
+ unsigned char *dst,int dsize);
+
+#endif /* __ISDNHDLC_H__ */
diff --git a/drivers/isdn/hisax/isdnl1.c b/drivers/isdn/hisax/isdnl1.c
new file mode 100644
index 0000000..a14204e
--- /dev/null
+++ b/drivers/isdn/hisax/isdnl1.c
@@ -0,0 +1,935 @@
+/* $Id: isdnl1.c,v 2.46.2.5 2004/02/11 13:21:34 keil Exp $
+ *
+ * common low level stuff for Siemens Chipsetbased isdn cards
+ *
+ * Author Karsten Keil
+ * based on the teles driver from Jan den Ouden
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ * Thanks to Jan den Ouden
+ * Fritz Elfert
+ * Beat Doebeli
+ *
+ */
+
+const char *l1_revision = "$Revision: 2.46.2.5 $";
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isdnl1.h"
+
+#define TIMER3_VALUE 7000
+
+static struct Fsm l1fsm_b;
+static struct Fsm l1fsm_s;
+
+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 HISAX_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_RSYNC_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_RSYNC_IND",
+ "EV_INFO2_IND",
+ "EV_INFO4_IND",
+ "EV_TIMER_DEACT",
+ "EV_TIMER_ACT",
+ "EV_TIMER3",
+};
+
+void
+debugl1(struct IsdnCardState *cs, char *fmt, ...)
+{
+ va_list args;
+ char tmp[8];
+
+ va_start(args, fmt);
+ sprintf(tmp, "Card%d ", cs->cardnr + 1);
+ VHiSax_putstatus(cs, tmp, fmt, args);
+ va_end(args);
+}
+
+static void
+l1m_debug(struct FsmInst *fi, char *fmt, ...)
+{
+ va_list args;
+ struct PStack *st = fi->userdata;
+ struct IsdnCardState *cs = st->l1.hardware;
+ char tmp[8];
+
+ va_start(args, fmt);
+ sprintf(tmp, "Card%d ", cs->cardnr + 1);
+ VHiSax_putstatus(cs, tmp, fmt, args);
+ va_end(args);
+}
+
+static void
+L1activated(struct IsdnCardState *cs)
+{
+ struct PStack *st;
+
+ st = cs->stlist;
+ while (st) {
+ if (test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags))
+ st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+ else
+ st->l1.l1l2(st, PH_ACTIVATE | INDICATION, NULL);
+ st = st->next;
+ }
+}
+
+static void
+L1deactivated(struct IsdnCardState *cs)
+{
+ struct PStack *st;
+
+ st = cs->stlist;
+ while (st) {
+ if (test_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+ st->l1.l1l2(st, PH_PAUSE | CONFIRM, NULL);
+ st->l1.l1l2(st, PH_DEACTIVATE | INDICATION, NULL);
+ st = st->next;
+ }
+ test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags);
+}
+
+void
+DChannel_proc_xmt(struct IsdnCardState *cs)
+{
+ struct PStack *stptr;
+
+ if (cs->tx_skb)
+ return;
+
+ stptr = cs->stlist;
+ while (stptr != NULL) {
+ if (test_and_clear_bit(FLG_L1_PULL_REQ, &stptr->l1.Flags)) {
+ stptr->l1.l1l2(stptr, PH_PULL | CONFIRM, NULL);
+ break;
+ } else
+ stptr = stptr->next;
+ }
+}
+
+void
+DChannel_proc_rcv(struct IsdnCardState *cs)
+{
+ struct sk_buff *skb, *nskb;
+ struct PStack *stptr = cs->stlist;
+ int found, tei, sapi;
+
+ if (stptr)
+ if (test_bit(FLG_L1_ACTTIMER, &stptr->l1.Flags))
+ FsmEvent(&stptr->l1.l1m, EV_TIMER_ACT, NULL);
+ while ((skb = skb_dequeue(&cs->rq))) {
+#ifdef L2FRAME_DEBUG /* psa */
+ if (cs->debug & L1_DEB_LAPD)
+ Logl2Frame(cs, skb, "PH_DATA", 1);
+#endif
+ stptr = cs->stlist;
+ if (skb->len<3) {
+ debugl1(cs, "D-channel frame too short(%d)",skb->len);
+ dev_kfree_skb(skb);
+ return;
+ }
+ if ((skb->data[0] & 1) || !(skb->data[1] &1)) {
+ debugl1(cs, "D-channel frame wrong EA0/EA1");
+ dev_kfree_skb(skb);
+ return;
+ }
+ sapi = skb->data[0] >> 2;
+ tei = skb->data[1] >> 1;
+ if (cs->debug & DEB_DLOG_HEX)
+ LogFrame(cs, skb->data, skb->len);
+ if (cs->debug & DEB_DLOG_VERBOSE)
+ dlogframe(cs, skb, 1);
+ if (tei == GROUP_TEI) {
+ if (sapi == CTRL_SAPI) { /* sapi 0 */
+ while (stptr != NULL) {
+ if ((nskb = skb_clone(skb, GFP_ATOMIC)))
+ stptr->l1.l1l2(stptr, PH_DATA | INDICATION, nskb);
+ else
+ printk(KERN_WARNING "HiSax: isdn broadcast buffer shortage\n");
+ stptr = stptr->next;
+ }
+ } else if (sapi == TEI_SAPI) {
+ while (stptr != NULL) {
+ if ((nskb = skb_clone(skb, GFP_ATOMIC)))
+ stptr->l1.l1tei(stptr, PH_DATA | INDICATION, nskb);
+ else
+ printk(KERN_WARNING "HiSax: tei broadcast buffer shortage\n");
+ stptr = stptr->next;
+ }
+ }
+ dev_kfree_skb(skb);
+ } else if (sapi == CTRL_SAPI) { /* sapi 0 */
+ found = 0;
+ while (stptr != NULL)
+ if (tei == stptr->l2.tei) {
+ stptr->l1.l1l2(stptr, PH_DATA | INDICATION, skb);
+ found = !0;
+ break;
+ } else
+ stptr = stptr->next;
+ if (!found)
+ dev_kfree_skb(skb);
+ } else
+ dev_kfree_skb(skb);
+ }
+}
+
+static void
+BChannel_proc_xmt(struct BCState *bcs)
+{
+ struct PStack *st = bcs->st;
+
+ if (test_bit(BC_FLG_BUSY, &bcs->Flag)) {
+ debugl1(bcs->cs, "BC_BUSY Error");
+ return;
+ }
+
+ if (test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags))
+ st->l1.l1l2(st, PH_PULL | CONFIRM, NULL);
+ if (!test_bit(BC_FLG_ACTIV, &bcs->Flag)) {
+ if (!test_bit(BC_FLG_BUSY, &bcs->Flag) &&
+ skb_queue_empty(&bcs->squeue)) {
+ st->l2.l2l1(st, PH_DEACTIVATE | CONFIRM, NULL);
+ }
+ }
+}
+
+static void
+BChannel_proc_rcv(struct BCState *bcs)
+{
+ struct sk_buff *skb;
+
+ if (bcs->st->l1.l1m.state == ST_L1_WAIT_ACT) {
+ FsmDelTimer(&bcs->st->l1.timer, 4);
+ FsmEvent(&bcs->st->l1.l1m, EV_TIMER_ACT, NULL);
+ }
+ while ((skb = skb_dequeue(&bcs->rqueue))) {
+ bcs->st->l1.l1l2(bcs->st, PH_DATA | INDICATION, skb);
+ }
+}
+
+static void
+BChannel_proc_ack(struct BCState *bcs)
+{
+ u_long flags;
+ int ack;
+
+ spin_lock_irqsave(&bcs->aclock, flags);
+ ack = bcs->ackcnt;
+ bcs->ackcnt = 0;
+ spin_unlock_irqrestore(&bcs->aclock, flags);
+ if (ack)
+ lli_writewakeup(bcs->st, ack);
+}
+
+void
+BChannel_bh(struct work_struct *work)
+{
+ struct BCState *bcs = container_of(work, struct BCState, tqueue);
+
+ if (!bcs)
+ return;
+ if (test_and_clear_bit(B_RCVBUFREADY, &bcs->event))
+ BChannel_proc_rcv(bcs);
+ if (test_and_clear_bit(B_XMTBUFREADY, &bcs->event))
+ BChannel_proc_xmt(bcs);
+ if (test_and_clear_bit(B_ACKPENDING, &bcs->event))
+ BChannel_proc_ack(bcs);
+}
+
+void
+HiSax_addlist(struct IsdnCardState *cs,
+ struct PStack *st)
+{
+ st->next = cs->stlist;
+ cs->stlist = st;
+}
+
+void
+HiSax_rmlist(struct IsdnCardState *cs,
+ struct PStack *st)
+{
+ struct PStack *p;
+
+ FsmDelTimer(&st->l1.timer, 0);
+ if (cs->stlist == st)
+ cs->stlist = st->next;
+ else {
+ p = cs->stlist;
+ while (p)
+ if (p->next == st) {
+ p->next = st->next;
+ return;
+ } else
+ p = p->next;
+ }
+}
+
+void
+init_bcstate(struct IsdnCardState *cs, int bc)
+{
+ struct BCState *bcs = cs->bcs + bc;
+
+ bcs->cs = cs;
+ bcs->channel = bc;
+ INIT_WORK(&bcs->tqueue, BChannel_bh);
+ spin_lock_init(&bcs->aclock);
+ bcs->BC_SetStack = NULL;
+ bcs->BC_Close = NULL;
+ bcs->Flag = 0;
+}
+
+#ifdef L2FRAME_DEBUG /* psa */
+
+static char *
+l2cmd(u_char cmd)
+{
+ switch (cmd & ~0x10) {
+ case 1:
+ return "RR";
+ case 5:
+ return "RNR";
+ case 9:
+ return "REJ";
+ case 0x6f:
+ return "SABME";
+ case 0x0f:
+ return "DM";
+ case 3:
+ return "UI";
+ case 0x43:
+ return "DISC";
+ case 0x63:
+ return "UA";
+ case 0x87:
+ return "FRMR";
+ case 0xaf:
+ return "XID";
+ default:
+ if (!(cmd & 1))
+ return "I";
+ else
+ return "invalid command";
+ }
+}
+
+static char tmpdeb[32];
+
+static char *
+l2frames(u_char * ptr)
+{
+ switch (ptr[2] & ~0x10) {
+ case 1:
+ case 5:
+ case 9:
+ sprintf(tmpdeb, "%s[%d](nr %d)", l2cmd(ptr[2]), ptr[3] & 1, ptr[3] >> 1);
+ break;
+ case 0x6f:
+ case 0x0f:
+ case 3:
+ case 0x43:
+ case 0x63:
+ case 0x87:
+ case 0xaf:
+ sprintf(tmpdeb, "%s[%d]", l2cmd(ptr[2]), (ptr[2] & 0x10) >> 4);
+ break;
+ default:
+ if (!(ptr[2] & 1)) {
+ sprintf(tmpdeb, "I[%d](ns %d, nr %d)", ptr[3] & 1, ptr[2] >> 1, ptr[3] >> 1);
+ break;
+ } else
+ return "invalid command";
+ }
+
+
+ return tmpdeb;
+}
+
+void
+Logl2Frame(struct IsdnCardState *cs, struct sk_buff *skb, char *buf, int dir)
+{
+ u_char *ptr;
+
+ ptr = skb->data;
+
+ if (ptr[0] & 1 || !(ptr[1] & 1))
+ debugl1(cs, "Address not LAPD");
+ else
+ debugl1(cs, "%s %s: %s%c (sapi %d, tei %d)",
+ (dir ? "<-" : "->"), buf, l2frames(ptr),
+ ((ptr[0] & 2) >> 1) == dir ? 'C' : 'R', ptr[0] >> 2, ptr[1] >> 1);
+}
+#endif
+
+static void
+l1_reset(struct FsmInst *fi, int event, void *arg)
+{
+ FsmChangeState(fi, ST_L1_F3);
+}
+
+static void
+l1_deact_cnf(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ FsmChangeState(fi, ST_L1_F3);
+ if (test_bit(FLG_L1_ACTIVATING, &st->l1.Flags))
+ st->l1.l1hw(st, HW_ENABLE | REQUEST, NULL);
+}
+
+static void
+l1_deact_req_s(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ FsmChangeState(fi, ST_L1_F3);
+ FsmRestartTimer(&st->l1.timer, 550, EV_TIMER_DEACT, NULL, 2);
+ test_and_set_bit(FLG_L1_DEACTTIMER, &st->l1.Flags);
+}
+
+static void
+l1_power_up_s(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ if (test_bit(FLG_L1_ACTIVATING, &st->l1.Flags)) {
+ FsmChangeState(fi, ST_L1_F4);
+ st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL);
+ FsmRestartTimer(&st->l1.timer, TIMER3_VALUE, EV_TIMER3, NULL, 2);
+ test_and_set_bit(FLG_L1_T3RUN, &st->l1.Flags);
+ } else
+ FsmChangeState(fi, ST_L1_F3);
+}
+
+static void
+l1_go_F5(struct FsmInst *fi, int event, void *arg)
+{
+ FsmChangeState(fi, ST_L1_F5);
+}
+
+static void
+l1_go_F8(struct FsmInst *fi, int event, void *arg)
+{
+ FsmChangeState(fi, ST_L1_F8);
+}
+
+static void
+l1_info2_ind(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+#ifdef HISAX_UINTERFACE
+ if (test_bit(FLG_L1_UINT, &st->l1.Flags))
+ FsmChangeState(fi, ST_L1_SYNC2);
+ else
+#endif
+ FsmChangeState(fi, ST_L1_F6);
+ st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL);
+}
+
+static void
+l1_info4_ind(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+#ifdef HISAX_UINTERFACE
+ if (test_bit(FLG_L1_UINT, &st->l1.Flags))
+ FsmChangeState(fi, ST_L1_TRANS);
+ else
+#endif
+ FsmChangeState(fi, ST_L1_F7);
+ st->l1.l1hw(st, HW_INFO3 | REQUEST, NULL);
+ if (test_and_clear_bit(FLG_L1_DEACTTIMER, &st->l1.Flags))
+ FsmDelTimer(&st->l1.timer, 4);
+ if (!test_bit(FLG_L1_ACTIVATED, &st->l1.Flags)) {
+ if (test_and_clear_bit(FLG_L1_T3RUN, &st->l1.Flags))
+ FsmDelTimer(&st->l1.timer, 3);
+ FsmRestartTimer(&st->l1.timer, 110, EV_TIMER_ACT, NULL, 2);
+ test_and_set_bit(FLG_L1_ACTTIMER, &st->l1.Flags);
+ }
+}
+
+static void
+l1_timer3(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ test_and_clear_bit(FLG_L1_T3RUN, &st->l1.Flags);
+ if (test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags))
+ L1deactivated(st->l1.hardware);
+
+#ifdef HISAX_UINTERFACE
+ if (!test_bit(FLG_L1_UINT, &st->l1.Flags))
+#endif
+ if (st->l1.l1m.state != ST_L1_F6) {
+ FsmChangeState(fi, ST_L1_F3);
+ st->l1.l1hw(st, HW_ENABLE | REQUEST, NULL);
+ }
+}
+
+static void
+l1_timer_act(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ test_and_clear_bit(FLG_L1_ACTTIMER, &st->l1.Flags);
+ test_and_set_bit(FLG_L1_ACTIVATED, &st->l1.Flags);
+ L1activated(st->l1.hardware);
+}
+
+static void
+l1_timer_deact(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ test_and_clear_bit(FLG_L1_DEACTTIMER, &st->l1.Flags);
+ test_and_clear_bit(FLG_L1_ACTIVATED, &st->l1.Flags);
+ L1deactivated(st->l1.hardware);
+ st->l1.l1hw(st, HW_DEACTIVATE | RESPONSE, NULL);
+}
+
+static void
+l1_activate_s(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ st->l1.l1hw(st, HW_RESET | REQUEST, NULL);
+}
+
+static void
+l1_activate_no(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ if ((!test_bit(FLG_L1_DEACTTIMER, &st->l1.Flags)) && (!test_bit(FLG_L1_T3RUN, &st->l1.Flags))) {
+ test_and_clear_bit(FLG_L1_ACTIVATING, &st->l1.Flags);
+ L1deactivated(st->l1.hardware);
+ }
+}
+
+static struct FsmNode L1SFnList[] __initdata =
+{
+ {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_RSYNC_IND, l1_go_F5},
+ {ST_L1_F6, EV_RSYNC_IND, l1_go_F8},
+ {ST_L1_F7, EV_RSYNC_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 HISAX_UINTERFACE
+static void
+l1_deact_req_u(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ FsmChangeState(fi, ST_L1_RESET);
+ FsmRestartTimer(&st->l1.timer, 550, EV_TIMER_DEACT, NULL, 2);
+ test_and_set_bit(FLG_L1_DEACTTIMER, &st->l1.Flags);
+ st->l1.l1hw(st, HW_ENABLE | REQUEST, NULL);
+}
+
+static void
+l1_power_up_u(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ FsmRestartTimer(&st->l1.timer, TIMER3_VALUE, EV_TIMER3, NULL, 2);
+ test_and_set_bit(FLG_L1_T3RUN, &st->l1.Flags);
+}
+
+static void
+l1_info0_ind(struct FsmInst *fi, int event, void *arg)
+{
+ FsmChangeState(fi, ST_L1_DEACT);
+}
+
+static void
+l1_activate_u(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ st->l1.l1hw(st, HW_INFO1 | REQUEST, NULL);
+}
+
+static struct FsmNode L1UFnList[] __initdata =
+{
+ {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)
+{
+ struct PStack *st = fi->userdata;
+
+ FsmChangeState(fi, ST_L1_WAIT_ACT);
+ FsmRestartTimer(&st->l1.timer, st->l1.delay, EV_TIMER_ACT, NULL, 2);
+}
+
+static void
+l1b_deactivate(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ FsmChangeState(fi, ST_L1_WAIT_DEACT);
+ FsmRestartTimer(&st->l1.timer, 10, EV_TIMER_DEACT, NULL, 2);
+}
+
+static void
+l1b_timer_act(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ FsmChangeState(fi, ST_L1_ACTIV);
+ st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+}
+
+static void
+l1b_timer_deact(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ FsmChangeState(fi, ST_L1_NULL);
+ st->l2.l2l1(st, PH_DEACTIVATE | CONFIRM, NULL);
+}
+
+static struct FsmNode L1BFnList[] __initdata =
+{
+ {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))
+
+int __init
+Isdnl1New(void)
+{
+ int retval;
+
+ l1fsm_s.state_count = L1S_STATE_COUNT;
+ l1fsm_s.event_count = L1_EVENT_COUNT;
+ l1fsm_s.strEvent = strL1Event;
+ l1fsm_s.strState = strL1SState;
+ retval = FsmNew(&l1fsm_s, L1SFnList, L1S_FN_COUNT);
+ if (retval)
+ return retval;
+
+ l1fsm_b.state_count = L1B_STATE_COUNT;
+ l1fsm_b.event_count = L1_EVENT_COUNT;
+ l1fsm_b.strEvent = strL1Event;
+ l1fsm_b.strState = strL1BState;
+ retval = FsmNew(&l1fsm_b, L1BFnList, L1B_FN_COUNT);
+ if (retval) {
+ FsmFree(&l1fsm_s);
+ return retval;
+ }
+#ifdef HISAX_UINTERFACE
+ l1fsm_u.state_count = L1U_STATE_COUNT;
+ l1fsm_u.event_count = L1_EVENT_COUNT;
+ l1fsm_u.strEvent = strL1Event;
+ l1fsm_u.strState = strL1UState;
+ retval = FsmNew(&l1fsm_u, L1UFnList, L1U_FN_COUNT);
+ if (retval) {
+ FsmFree(&l1fsm_s);
+ FsmFree(&l1fsm_b);
+ return retval;
+ }
+#endif
+ return 0;
+}
+
+void Isdnl1Free(void)
+{
+#ifdef HISAX_UINTERFACE
+ FsmFree(&l1fsm_u);
+#endif
+ FsmFree(&l1fsm_s);
+ FsmFree(&l1fsm_b);
+}
+
+static void
+dch_l2l1(struct PStack *st, int pr, void *arg)
+{
+ struct IsdnCardState *cs = (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):
+ if (cs->debug)
+ debugl1(cs, "PH_ACTIVATE_REQ %s",
+ st->l1.l1m.fsm->strState[st->l1.l1m.state]);
+ if (test_bit(FLG_L1_ACTIVATED, &st->l1.Flags))
+ st->l1.l1l2(st, PH_ACTIVATE | CONFIRM, NULL);
+ else {
+ test_and_set_bit(FLG_L1_ACTIVATING, &st->l1.Flags);
+ FsmEvent(&st->l1.l1m, EV_PH_ACTIVATE, arg);
+ }
+ break;
+ case (PH_TESTLOOP | REQUEST):
+ if (1 & (long) arg)
+ debugl1(cs, "PH_TEST_LOOP B1");
+ if (2 & (long) arg)
+ debugl1(cs, "PH_TEST_LOOP B2");
+ if (!(3 & (long) arg))
+ debugl1(cs, "PH_TEST_LOOP DISABLED");
+ st->l1.l1hw(st, HW_TESTLOOP | REQUEST, arg);
+ break;
+ default:
+ if (cs->debug)
+ debugl1(cs, "dch_l2l1 msg %04X unhandled", pr);
+ break;
+ }
+}
+
+void
+l1_msg(struct IsdnCardState *cs, int pr, void *arg) {
+ struct PStack *st;
+
+ st = cs->stlist;
+
+ while (st) {
+ switch(pr) {
+ case (HW_RESET | INDICATION):
+ FsmEvent(&st->l1.l1m, EV_RESET_IND, arg);
+ break;
+ case (HW_DEACTIVATE | CONFIRM):
+ FsmEvent(&st->l1.l1m, EV_DEACT_CNF, arg);
+ break;
+ case (HW_DEACTIVATE | INDICATION):
+ FsmEvent(&st->l1.l1m, EV_DEACT_IND, arg);
+ break;
+ case (HW_POWERUP | CONFIRM):
+ FsmEvent(&st->l1.l1m, EV_POWER_UP, arg);
+ break;
+ case (HW_RSYNC | INDICATION):
+ FsmEvent(&st->l1.l1m, EV_RSYNC_IND, arg);
+ break;
+ case (HW_INFO2 | INDICATION):
+ FsmEvent(&st->l1.l1m, EV_INFO2_IND, arg);
+ break;
+ case (HW_INFO4_P8 | INDICATION):
+ case (HW_INFO4_P10 | INDICATION):
+ FsmEvent(&st->l1.l1m, EV_INFO4_IND, arg);
+ break;
+ default:
+ if (cs->debug)
+ debugl1(cs, "l1msg %04X unhandled", pr);
+ break;
+ }
+ st = st->next;
+ }
+}
+
+void
+l1_msg_b(struct PStack *st, int pr, void *arg) {
+ switch(pr) {
+ case (PH_ACTIVATE | REQUEST):
+ FsmEvent(&st->l1.l1m, EV_PH_ACTIVATE, NULL);
+ break;
+ case (PH_DEACTIVATE | REQUEST):
+ FsmEvent(&st->l1.l1m, EV_PH_DEACTIVATE, NULL);
+ break;
+ }
+}
+
+void
+setstack_HiSax(struct PStack *st, struct IsdnCardState *cs)
+{
+ st->l1.hardware = cs;
+ st->protocol = cs->protocol;
+ st->l1.l1m.fsm = &l1fsm_s;
+ st->l1.l1m.state = ST_L1_F3;
+ st->l1.Flags = 0;
+#ifdef HISAX_UINTERFACE
+ if (test_bit(FLG_HW_L1_UINT, &cs->HW_Flags)) {
+ st->l1.l1m.fsm = &l1fsm_u;
+ st->l1.l1m.state = ST_L1_RESET;
+ st->l1.Flags = FLG_L1_UINT;
+ }
+#endif
+ st->l1.l1m.debug = cs->debug;
+ st->l1.l1m.userdata = st;
+ st->l1.l1m.userint = 0;
+ st->l1.l1m.printdebug = l1m_debug;
+ FsmInitTimer(&st->l1.l1m, &st->l1.timer);
+ setstack_tei(st);
+ setstack_manager(st);
+ st->l1.stlistp = &(cs->stlist);
+ st->l2.l2l1 = dch_l2l1;
+ if (cs->setstack_d)
+ cs->setstack_d(st, cs);
+}
+
+void
+setstack_l1_B(struct PStack *st)
+{
+ struct IsdnCardState *cs = st->l1.hardware;
+
+ st->l1.l1m.fsm = &l1fsm_b;
+ st->l1.l1m.state = ST_L1_NULL;
+ st->l1.l1m.debug = cs->debug;
+ st->l1.l1m.userdata = st;
+ st->l1.l1m.userint = 0;
+ st->l1.l1m.printdebug = l1m_debug;
+ st->l1.Flags = 0;
+ FsmInitTimer(&st->l1.l1m, &st->l1.timer);
+}
diff --git a/drivers/isdn/hisax/isdnl1.h b/drivers/isdn/hisax/isdnl1.h
new file mode 100644
index 0000000..172ad4c
--- /dev/null
+++ b/drivers/isdn/hisax/isdnl1.h
@@ -0,0 +1,31 @@
+/* $Id: isdnl1.h,v 2.12.2.3 2004/02/11 13:21:34 keil Exp $
+ *
+ * Layer 1 defines
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#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 E_RCVBUFREADY 8
+
+#define B_RCVBUFREADY 0
+#define B_XMTBUFREADY 1
+#define B_ACKPENDING 2
+
+void debugl1(struct IsdnCardState *cs, char *fmt, ...);
+void DChannel_proc_xmt(struct IsdnCardState *cs);
+void DChannel_proc_rcv(struct IsdnCardState *cs);
+void l1_msg(struct IsdnCardState *cs, int pr, void *arg);
+void l1_msg_b(struct PStack *st, int pr, void *arg);
+void Logl2Frame(struct IsdnCardState *cs, struct sk_buff *skb, char *buf,
+ int dir);
+void BChannel_bh(struct work_struct *work);
diff --git a/drivers/isdn/hisax/isdnl2.c b/drivers/isdn/hisax/isdnl2.c
new file mode 100644
index 0000000..3446f24
--- /dev/null
+++ b/drivers/isdn/hisax/isdnl2.c
@@ -0,0 +1,1846 @@
+/* $Id: isdnl2.c,v 2.30.2.4 2004/02/11 13:21:34 keil Exp $
+ *
+ * Author Karsten Keil
+ * based on the teles driver from Jan den Ouden
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ * Thanks to Jan den Ouden
+ * Fritz Elfert
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isdnl2.h"
+
+const char *l2_revision = "$Revision: 2.30.2.4 $";
+
+static void l2m_debug(struct FsmInst *fi, char *fmt, ...);
+
+static struct Fsm l2fsm;
+
+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_UNIT_DATA,
+ 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_UNIT_DATA",
+ "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",
+};
+
+static int l2addrsize(struct Layer2 *l2);
+
+static void
+set_peer_busy(struct Layer2 *l2) {
+ test_and_set_bit(FLG_PEER_BUSY, &l2->flag);
+ if (!skb_queue_empty(&l2->i_queue) ||
+ !skb_queue_empty(&l2->ui_queue))
+ test_and_set_bit(FLG_L2BLOCK, &l2->flag);
+}
+
+static void
+clear_peer_busy(struct Layer2 *l2) {
+ if (test_and_clear_bit(FLG_PEER_BUSY, &l2->flag))
+ test_and_clear_bit(FLG_L2BLOCK, &l2->flag);
+}
+
+static void
+InitWin(struct Layer2 *l2)
+{
+ int i;
+
+ for (i = 0; i < MAX_WINDOW; i++)
+ l2->windowar[i] = NULL;
+}
+
+static int
+freewin1(struct Layer2 *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 inline void
+freewin(struct PStack *st)
+{
+ freewin1(&st->l2);
+}
+
+static void
+ReleaseWin(struct Layer2 *l2)
+{
+ int cnt;
+
+ if((cnt = freewin1(l2)))
+ printk(KERN_WARNING "isdl2 freed %d skbuffs in release\n", cnt);
+}
+
+static inline unsigned int
+cansend(struct PStack *st)
+{
+ unsigned int p1;
+
+ if(test_bit(FLG_MOD128, &st->l2.flag))
+ p1 = (st->l2.vs - st->l2.va) % 128;
+ else
+ p1 = (st->l2.vs - st->l2.va) % 8;
+ return ((p1 < st->l2.window) && !test_bit(FLG_PEER_BUSY, &st->l2.flag));
+}
+
+static inline void
+clear_exception(struct Layer2 *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 inline int
+l2headersize(struct Layer2 *l2, int ui)
+{
+ return (((test_bit(FLG_MOD128, &l2->flag) && (!ui)) ? 2 : 1) +
+ (test_bit(FLG_LAPD, &l2->flag) ? 2 : 1));
+}
+
+inline int
+l2addrsize(struct Layer2 *l2)
+{
+ return (test_bit(FLG_LAPD, &l2->flag) ? 2 : 1);
+}
+
+static int
+sethdraddr(struct Layer2 *l2, u_char * header, int rsp)
+{
+ u_char *ptr = header;
+ int crbit = rsp;
+
+ if (test_bit(FLG_LAPD, &l2->flag)) {
+ *ptr++ = (l2->sap << 2) | (rsp ? 2 : 0);
+ *ptr++ = (l2->tei << 1) | 1;
+ return (2);
+ } else {
+ if (test_bit(FLG_ORIG, &l2->flag))
+ crbit = !crbit;
+ if (crbit)
+ *ptr++ = 1;
+ else
+ *ptr++ = 3;
+ return (1);
+ }
+}
+
+static inline void
+enqueue_super(struct PStack *st,
+ struct sk_buff *skb)
+{
+ if (test_bit(FLG_LAPB, &st->l2.flag))
+ st->l1.bcs->tx_cnt += skb->len;
+ st->l2.l2l1(st, PH_DATA | REQUEST, skb);
+}
+
+#define enqueue_ui(a, b) enqueue_super(a, b)
+
+static inline int
+IsUI(u_char * data)
+{
+ return ((data[0] & 0xef) == UI);
+}
+
+static inline int
+IsUA(u_char * data)
+{
+ return ((data[0] & 0xef) == UA);
+}
+
+static inline int
+IsDM(u_char * data)
+{
+ return ((data[0] & 0xef) == DM);
+}
+
+static inline int
+IsDISC(u_char * data)
+{
+ return ((data[0] & 0xef) == DISC);
+}
+
+static inline int
+IsSFrame(u_char * data, struct PStack *st)
+{
+ register u_char d = *data;
+
+ if (!test_bit(FLG_MOD128, &st->l2.flag))
+ d &= 0xf;
+ return(((d & 0xf3) == 1) && ((d & 0x0c) != 0x0c));
+}
+
+static inline int
+IsSABME(u_char * data, struct PStack *st)
+{
+ u_char d = data[0] & ~0x10;
+
+ return (test_bit(FLG_MOD128, &st->l2.flag) ? d == SABME : d == SABM);
+}
+
+static inline int
+IsREJ(u_char * data, struct PStack *st)
+{
+ return (test_bit(FLG_MOD128, &st->l2.flag) ? data[0] == REJ : (data[0] & 0xf) == REJ);
+}
+
+static inline int
+IsFRMR(u_char * data)
+{
+ return ((data[0] & 0xef) == FRMR);
+}
+
+static inline int
+IsRNR(u_char * data, struct PStack *st)
+{
+ return (test_bit(FLG_MOD128, &st->l2.flag) ? data[0] == RNR : (data[0] & 0xf) == RNR);
+}
+
+static int
+iframe_error(struct PStack *st, struct sk_buff *skb)
+{
+ int i = l2addrsize(&st->l2) + (test_bit(FLG_MOD128, &st->l2.flag) ? 2 : 1);
+ int rsp = *skb->data & 0x2;
+
+ if (test_bit(FLG_ORIG, &st->l2.flag))
+ rsp = !rsp;
+
+ if (rsp)
+ return 'L';
+
+
+ if (skb->len < i)
+ return 'N';
+
+ if ((skb->len - i) > st->l2.maxlen)
+ return 'O';
+
+
+ return 0;
+}
+
+static int
+super_error(struct PStack *st, struct sk_buff *skb)
+{
+ if (skb->len != l2addrsize(&st->l2) +
+ (test_bit(FLG_MOD128, &st->l2.flag) ? 2 : 1))
+ return 'N';
+
+ return 0;
+}
+
+static int
+unnum_error(struct PStack *st, struct sk_buff *skb, int wantrsp)
+{
+ int rsp = (*skb->data & 0x2) >> 1;
+ if (test_bit(FLG_ORIG, &st->l2.flag))
+ rsp = !rsp;
+
+ if (rsp != wantrsp)
+ return 'L';
+
+ if (skb->len != l2addrsize(&st->l2) + 1)
+ return 'N';
+
+ return 0;
+}
+
+static int
+UI_error(struct PStack *st, struct sk_buff *skb)
+{
+ int rsp = *skb->data & 0x2;
+ if (test_bit(FLG_ORIG, &st->l2.flag))
+ rsp = !rsp;
+
+ if (rsp)
+ return 'L';
+
+ if (skb->len > st->l2.maxlen + l2addrsize(&st->l2) + 1)
+ return 'O';
+
+ return 0;
+}
+
+static int
+FRMR_error(struct PStack *st, struct sk_buff *skb)
+{
+ int headers = l2addrsize(&st->l2) + 1;
+ u_char *datap = skb->data + headers;
+ int rsp = *skb->data & 0x2;
+
+ if (test_bit(FLG_ORIG, &st->l2.flag))
+ rsp = !rsp;
+
+ if (!rsp)
+ return 'L';
+
+ if (test_bit(FLG_MOD128, &st->l2.flag)) {
+ if (skb->len < headers + 5)
+ return 'N';
+ else
+ l2m_debug(&st->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
+ l2m_debug(&st->l2.l2m, "FRMR information %2x %2x %2x",
+ datap[0], datap[1], datap[2]);
+ }
+
+ return 0;
+}
+
+static unsigned int
+legalnr(struct PStack *st, unsigned int nr)
+{
+ struct Layer2 *l2 = &st->l2;
+
+ 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(struct PStack *st, unsigned int nr)
+{
+ struct Layer2 *l2 = &st->l2;
+ int len;
+ u_long flags;
+
+ 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;
+ len = l2->windowar[l2->sow]->len;
+ if (PACKET_NOACK == l2->windowar[l2->sow]->pkt_type)
+ len = -1;
+ dev_kfree_skb(l2->windowar[l2->sow]);
+ l2->windowar[l2->sow] = NULL;
+ l2->sow = (l2->sow + 1) % l2->window;
+ spin_unlock_irqrestore(&l2->lock, flags);
+ if (test_bit(FLG_LLI_L2WAKEUP, &st->lli.flag) && (len >=0))
+ lli_writewakeup(st, len);
+ spin_lock_irqsave(&l2->lock, flags);
+ }
+ spin_unlock_irqrestore(&l2->lock, flags);
+}
+
+static void
+send_uframe(struct PStack *st, u_char cmd, u_char cr)
+{
+ struct sk_buff *skb;
+ u_char tmp[MAX_HEADER_LEN];
+ int i;
+
+ i = sethdraddr(&st->l2, tmp, cr);
+ tmp[i++] = cmd;
+ if (!(skb = alloc_skb(i, GFP_ATOMIC))) {
+ printk(KERN_WARNING "isdl2 can't alloc sbbuff for send_uframe\n");
+ return;
+ }
+ memcpy(skb_put(skb, i), tmp, i);
+ enqueue_super(st, skb);
+}
+
+static inline u_char
+get_PollFlag(struct PStack * st, struct sk_buff * skb)
+{
+ return (skb->data[l2addrsize(&(st->l2))] & 0x10);
+}
+
+static inline u_char
+get_PollFlagFree(struct PStack *st, struct sk_buff *skb)
+{
+ u_char PF;
+
+ PF = get_PollFlag(st, skb);
+ dev_kfree_skb(skb);
+ return (PF);
+}
+
+static inline void
+start_t200(struct PStack *st, int i)
+{
+ FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, i);
+ test_and_set_bit(FLG_T200_RUN, &st->l2.flag);
+}
+
+static inline void
+restart_t200(struct PStack *st, int i)
+{
+ FsmRestartTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, i);
+ test_and_set_bit(FLG_T200_RUN, &st->l2.flag);
+}
+
+static inline void
+stop_t200(struct PStack *st, int i)
+{
+ if(test_and_clear_bit(FLG_T200_RUN, &st->l2.flag))
+ FsmDelTimer(&st->l2.t200, i);
+}
+
+static inline void
+st5_dl_release_l2l3(struct PStack *st)
+{
+ int pr;
+
+ if(test_and_clear_bit(FLG_PEND_REL, &st->l2.flag))
+ pr = DL_RELEASE | CONFIRM;
+ else
+ pr = DL_RELEASE | INDICATION;
+
+ st->l2.l2l3(st, pr, NULL);
+}
+
+static inline void
+lapb_dl_release_l2l3(struct PStack *st, int f)
+{
+ if (test_bit(FLG_LAPB, &st->l2.flag))
+ st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+ st->l2.l2l3(st, DL_RELEASE | f, NULL);
+}
+
+static void
+establishlink(struct FsmInst *fi)
+{
+ struct PStack *st = fi->userdata;
+ u_char cmd;
+
+ clear_exception(&st->l2);
+ st->l2.rc = 0;
+ cmd = (test_bit(FLG_MOD128, &st->l2.flag) ? SABME : SABM) | 0x10;
+ send_uframe(st, cmd, CMD);
+ FsmDelTimer(&st->l2.t203, 1);
+ restart_t200(st, 1);
+ test_and_clear_bit(FLG_PEND_REL, &st->l2.flag);
+ freewin(st);
+ FsmChangeState(fi, ST_L2_5);
+}
+
+static void
+l2_mdl_error_ua(struct FsmInst *fi, int event, void *arg)
+{
+ struct sk_buff *skb = arg;
+ struct PStack *st = fi->userdata;
+
+ if (get_PollFlagFree(st, skb))
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'C');
+ else
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'D');
+}
+
+static void
+l2_mdl_error_dm(struct FsmInst *fi, int event, void *arg)
+{
+ struct sk_buff *skb = arg;
+ struct PStack *st = fi->userdata;
+
+ if (get_PollFlagFree(st, skb))
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'B');
+ else {
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'E');
+ establishlink(fi);
+ test_and_clear_bit(FLG_L3_INIT, &st->l2.flag);
+ }
+}
+
+static void
+l2_st8_mdl_error_dm(struct FsmInst *fi, int event, void *arg)
+{
+ struct sk_buff *skb = arg;
+ struct PStack *st = fi->userdata;
+
+ if (get_PollFlagFree(st, skb))
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'B');
+ else {
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'E');
+ }
+ establishlink(fi);
+ test_and_clear_bit(FLG_L3_INIT, &st->l2.flag);
+}
+
+static void
+l2_go_st3(struct FsmInst *fi, int event, void *arg)
+{
+ FsmChangeState(fi, ST_L2_3);
+}
+
+static void
+l2_mdl_assign(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ FsmChangeState(fi, ST_L2_3);
+ st->l2.l2tei(st, MDL_ASSIGN | INDICATION, NULL);
+}
+
+static void
+l2_queue_ui_assign(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct sk_buff *skb = arg;
+
+ skb_queue_tail(&st->l2.ui_queue, skb);
+ FsmChangeState(fi, ST_L2_2);
+ st->l2.l2tei(st, MDL_ASSIGN | INDICATION, NULL);
+}
+
+static void
+l2_queue_ui(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct sk_buff *skb = arg;
+
+ skb_queue_tail(&st->l2.ui_queue, skb);
+}
+
+static void
+tx_ui(struct PStack *st)
+{
+ struct sk_buff *skb;
+ u_char header[MAX_HEADER_LEN];
+ int i;
+
+ i = sethdraddr(&(st->l2), header, CMD);
+ header[i++] = UI;
+ while ((skb = skb_dequeue(&st->l2.ui_queue))) {
+ memcpy(skb_push(skb, i), header, i);
+ enqueue_ui(st, skb);
+ }
+}
+
+static void
+l2_send_ui(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct sk_buff *skb = arg;
+
+ skb_queue_tail(&st->l2.ui_queue, skb);
+ tx_ui(st);
+}
+
+static void
+l2_got_ui(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct sk_buff *skb = arg;
+
+ skb_pull(skb, l2headersize(&st->l2, 1));
+ st->l2.l2l3(st, DL_UNIT_DATA | INDICATION, skb);
+/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ * in states 1-3 for broadcast
+ */
+
+
+}
+
+static void
+l2_establish(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ establishlink(fi);
+ test_and_set_bit(FLG_L3_INIT, &st->l2.flag);
+}
+
+static void
+l2_discard_i_setl3(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ skb_queue_purge(&st->l2.i_queue);
+ test_and_set_bit(FLG_L3_INIT, &st->l2.flag);
+ test_and_clear_bit(FLG_PEND_REL, &st->l2.flag);
+}
+
+static void
+l2_l3_reestablish(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ skb_queue_purge(&st->l2.i_queue);
+ establishlink(fi);
+ test_and_set_bit(FLG_L3_INIT, &st->l2.flag);
+}
+
+static void
+l2_release(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL);
+}
+
+static void
+l2_pend_rel(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ test_and_set_bit(FLG_PEND_REL, &st->l2.flag);
+}
+
+static void
+l2_disconnect(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ skb_queue_purge(&st->l2.i_queue);
+ freewin(st);
+ FsmChangeState(fi, ST_L2_6);
+ st->l2.rc = 0;
+ send_uframe(st, DISC | 0x10, CMD);
+ FsmDelTimer(&st->l2.t203, 1);
+ restart_t200(st, 2);
+}
+
+static void
+l2_start_multi(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct sk_buff *skb = arg;
+
+ send_uframe(st, UA | get_PollFlagFree(st, skb), RSP);
+
+ clear_exception(&st->l2);
+ st->l2.vs = 0;
+ st->l2.va = 0;
+ st->l2.vr = 0;
+ st->l2.sow = 0;
+ FsmChangeState(fi, ST_L2_7);
+ FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 3);
+
+ st->l2.l2l3(st, DL_ESTABLISH | INDICATION, NULL);
+}
+
+static void
+l2_send_UA(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct sk_buff *skb = arg;
+
+ send_uframe(st, UA | get_PollFlagFree(st, skb), RSP);
+}
+
+static void
+l2_send_DM(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct sk_buff *skb = arg;
+
+ send_uframe(st, DM | get_PollFlagFree(st, skb), RSP);
+}
+
+static void
+l2_restart_multi(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct sk_buff *skb = arg;
+ int est = 0, state;
+
+ state = fi->state;
+
+ send_uframe(st, UA | get_PollFlagFree(st, skb), RSP);
+
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'F');
+
+ if (st->l2.vs != st->l2.va) {
+ skb_queue_purge(&st->l2.i_queue);
+ est = 1;
+ }
+
+ clear_exception(&st->l2);
+ st->l2.vs = 0;
+ st->l2.va = 0;
+ st->l2.vr = 0;
+ st->l2.sow = 0;
+ FsmChangeState(fi, ST_L2_7);
+ stop_t200(st, 3);
+ FsmRestartTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 3);
+
+ if (est)
+ st->l2.l2l3(st, DL_ESTABLISH | INDICATION, NULL);
+
+ if ((ST_L2_7==state) || (ST_L2_8 == state))
+ if (!skb_queue_empty(&st->l2.i_queue) && cansend(st))
+ st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+}
+
+static void
+l2_stop_multi(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct sk_buff *skb = arg;
+
+ FsmChangeState(fi, ST_L2_4);
+ FsmDelTimer(&st->l2.t203, 3);
+ stop_t200(st, 4);
+
+ send_uframe(st, UA | get_PollFlagFree(st, skb), RSP);
+
+ skb_queue_purge(&st->l2.i_queue);
+ freewin(st);
+ lapb_dl_release_l2l3(st, INDICATION);
+}
+
+static void
+l2_connected(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct sk_buff *skb = arg;
+ int pr=-1;
+
+ if (!get_PollFlag(st, skb)) {
+ l2_mdl_error_ua(fi, event, arg);
+ return;
+ }
+ dev_kfree_skb(skb);
+
+ if (test_and_clear_bit(FLG_PEND_REL, &st->l2.flag))
+ l2_disconnect(fi, event, arg);
+
+ if (test_and_clear_bit(FLG_L3_INIT, &st->l2.flag)) {
+ pr = DL_ESTABLISH | CONFIRM;
+ } else if (st->l2.vs != st->l2.va) {
+ skb_queue_purge(&st->l2.i_queue);
+ pr = DL_ESTABLISH | INDICATION;
+ }
+
+ stop_t200(st, 5);
+
+ st->l2.vr = 0;
+ st->l2.vs = 0;
+ st->l2.va = 0;
+ st->l2.sow = 0;
+ FsmChangeState(fi, ST_L2_7);
+ FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 4);
+
+ if (pr != -1)
+ st->l2.l2l3(st, pr, NULL);
+
+ if (!skb_queue_empty(&st->l2.i_queue) && cansend(st))
+ st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+}
+
+static void
+l2_released(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct sk_buff *skb = arg;
+
+ if (!get_PollFlag(st, skb)) {
+ l2_mdl_error_ua(fi, event, arg);
+ return;
+ }
+ dev_kfree_skb(skb);
+
+ stop_t200(st, 6);
+ lapb_dl_release_l2l3(st, CONFIRM);
+ FsmChangeState(fi, ST_L2_4);
+}
+
+static void
+l2_reestablish(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct sk_buff *skb = arg;
+
+ if (!get_PollFlagFree(st, skb)) {
+ establishlink(fi);
+ test_and_set_bit(FLG_L3_INIT, &st->l2.flag);
+ }
+}
+
+static void
+l2_st5_dm_release(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct sk_buff *skb = arg;
+
+ if (get_PollFlagFree(st, skb)) {
+ stop_t200(st, 7);
+ if (!test_bit(FLG_L3_INIT, &st->l2.flag))
+ skb_queue_purge(&st->l2.i_queue);
+ if (test_bit(FLG_LAPB, &st->l2.flag))
+ st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+ st5_dl_release_l2l3(st);
+ FsmChangeState(fi, ST_L2_4);
+ }
+}
+
+static void
+l2_st6_dm_release(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct sk_buff *skb = arg;
+
+ if (get_PollFlagFree(st, skb)) {
+ stop_t200(st, 8);
+ lapb_dl_release_l2l3(st, CONFIRM);
+ FsmChangeState(fi, ST_L2_4);
+ }
+}
+
+static inline void
+enquiry_cr(struct PStack *st, u_char typ, u_char cr, u_char pf)
+{
+ struct sk_buff *skb;
+ struct Layer2 *l2;
+ u_char tmp[MAX_HEADER_LEN];
+ int i;
+
+ l2 = &st->l2;
+ 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 "isdl2 can't alloc sbbuff for enquiry_cr\n");
+ return;
+ }
+ memcpy(skb_put(skb, i), tmp, i);
+ enqueue_super(st, skb);
+}
+
+static inline void
+enquiry_response(struct PStack *st)
+{
+ if (test_bit(FLG_OWN_BUSY, &st->l2.flag))
+ enquiry_cr(st, RNR, RSP, 1);
+ else
+ enquiry_cr(st, RR, RSP, 1);
+ test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag);
+}
+
+static inline void
+transmit_enquiry(struct PStack *st)
+{
+ if (test_bit(FLG_OWN_BUSY, &st->l2.flag))
+ enquiry_cr(st, RNR, CMD, 1);
+ else
+ enquiry_cr(st, RR, CMD, 1);
+ test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag);
+ start_t200(st, 9);
+}
+
+
+static void
+nrerrorrecovery(struct FsmInst *fi)
+{
+ struct PStack *st = fi->userdata;
+
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'J');
+ establishlink(fi);
+ test_and_clear_bit(FLG_L3_INIT, &st->l2.flag);
+}
+
+static void
+invoke_retransmission(struct PStack *st, unsigned int nr)
+{
+ struct Layer2 *l2 = &st->l2;
+ 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 (test_bit(FLG_LAPB, &l2->flag))
+ st->l1.bcs->tx_cnt += l2->windowar[p1]->len + l2headersize(l2, 0);
+ skb_queue_head(&l2->i_queue, l2->windowar[p1]);
+ l2->windowar[p1] = NULL;
+ }
+ spin_unlock_irqrestore(&l2->lock, flags);
+ st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+ return;
+ }
+ spin_unlock_irqrestore(&l2->lock, flags);
+}
+
+static void
+l2_st7_got_super(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct sk_buff *skb = arg;
+ int PollFlag, rsp, typ = RR;
+ unsigned int nr;
+ struct Layer2 *l2 = &st->l2;
+
+ rsp = *skb->data & 0x2;
+ if (test_bit(FLG_ORIG, &l2->flag))
+ rsp = !rsp;
+
+ skb_pull(skb, l2addrsize(l2));
+ if (IsRNR(skb->data, st)) {
+ set_peer_busy(l2);
+ typ = RNR;
+ } else
+ clear_peer_busy(l2);
+ if (IsREJ(skb->data, st))
+ 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)
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'A');
+ else
+ enquiry_response(st);
+ }
+ if (legalnr(st, nr)) {
+ if (typ == REJ) {
+ setva(st, nr);
+ invoke_retransmission(st, nr);
+ stop_t200(st, 10);
+ if (FsmAddTimer(&st->l2.t203, st->l2.T203,
+ EV_L2_T203, NULL, 6))
+ l2m_debug(&st->l2.l2m, "Restart T203 ST7 REJ");
+ } else if ((nr == l2->vs) && (typ == RR)) {
+ setva(st, nr);
+ stop_t200(st, 11);
+ FsmRestartTimer(&st->l2.t203, st->l2.T203,
+ EV_L2_T203, NULL, 7);
+ } else if ((l2->va != nr) || (typ == RNR)) {
+ setva(st, nr);
+ if(typ != RR) FsmDelTimer(&st->l2.t203, 9);
+ restart_t200(st, 12);
+ }
+ if (!skb_queue_empty(&st->l2.i_queue) && (typ == RR))
+ st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+ } else
+ nrerrorrecovery(fi);
+}
+
+static void
+l2_feed_i_if_reest(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct sk_buff *skb = arg;
+
+ if (test_bit(FLG_LAPB, &st->l2.flag))
+ st->l1.bcs->tx_cnt += skb->len + l2headersize(&st->l2, 0);
+ if (!test_bit(FLG_L3_INIT, &st->l2.flag))
+ skb_queue_tail(&st->l2.i_queue, skb);
+ else
+ dev_kfree_skb(skb);
+}
+
+static void
+l2_feed_i_pull(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct sk_buff *skb = arg;
+
+ if (test_bit(FLG_LAPB, &st->l2.flag))
+ st->l1.bcs->tx_cnt += skb->len + l2headersize(&st->l2, 0);
+ skb_queue_tail(&st->l2.i_queue, skb);
+ st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+}
+
+static void
+l2_feed_iqueue(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct sk_buff *skb = arg;
+
+ if (test_bit(FLG_LAPB, &st->l2.flag))
+ st->l1.bcs->tx_cnt += skb->len + l2headersize(&st->l2, 0);
+ skb_queue_tail(&st->l2.i_queue, skb);
+}
+
+static void
+l2_got_iframe(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct sk_buff *skb = arg;
+ struct Layer2 *l2 = &(st->l2);
+ int PollFlag, ns, i;
+ unsigned int nr;
+
+ 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(st);
+ } else 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);
+
+ if (PollFlag)
+ enquiry_response(st);
+ else
+ test_and_set_bit(FLG_ACK_PEND, &l2->flag);
+ skb_pull(skb, l2headersize(l2, 0));
+ st->l2.l2l3(st, DL_DATA | INDICATION, skb);
+ } else {
+ /* n(s)!=v(r) */
+ dev_kfree_skb(skb);
+ if (test_and_set_bit(FLG_REJEXC, &l2->flag)) {
+ if (PollFlag)
+ enquiry_response(st);
+ } else {
+ enquiry_cr(st, REJ, RSP, PollFlag);
+ test_and_clear_bit(FLG_ACK_PEND, &l2->flag);
+ }
+ }
+
+ if (legalnr(st, nr)) {
+ if (!test_bit(FLG_PEER_BUSY, &st->l2.flag) && (fi->state == ST_L2_7)) {
+ if (nr == st->l2.vs) {
+ stop_t200(st, 13);
+ FsmRestartTimer(&st->l2.t203, st->l2.T203,
+ EV_L2_T203, NULL, 7);
+ } else if (nr != st->l2.va)
+ restart_t200(st, 14);
+ }
+ setva(st, nr);
+ } else {
+ nrerrorrecovery(fi);
+ return;
+ }
+
+ if (!skb_queue_empty(&st->l2.i_queue) && (fi->state == ST_L2_7))
+ st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+ if (test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag))
+ enquiry_cr(st, RR, RSP, 0);
+}
+
+static void
+l2_got_tei(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ st->l2.tei = (long) arg;
+
+ if (fi->state == ST_L2_3) {
+ establishlink(fi);
+ test_and_set_bit(FLG_L3_INIT, &st->l2.flag);
+ } else
+ FsmChangeState(fi, ST_L2_4);
+ if (!skb_queue_empty(&st->l2.ui_queue))
+ tx_ui(st);
+}
+
+static void
+l2_st5_tout_200(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ if (test_bit(FLG_LAPD, &st->l2.flag) &&
+ test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) {
+ FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9);
+ } else if (st->l2.rc == st->l2.N200) {
+ FsmChangeState(fi, ST_L2_4);
+ test_and_clear_bit(FLG_T200_RUN, &st->l2.flag);
+ skb_queue_purge(&st->l2.i_queue);
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'G');
+ if (test_bit(FLG_LAPB, &st->l2.flag))
+ st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+ st5_dl_release_l2l3(st);
+ } else {
+ st->l2.rc++;
+ FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9);
+ send_uframe(st, (test_bit(FLG_MOD128, &st->l2.flag) ? SABME : SABM)
+ | 0x10, CMD);
+ }
+}
+
+static void
+l2_st6_tout_200(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ if (test_bit(FLG_LAPD, &st->l2.flag) &&
+ test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) {
+ FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9);
+ } else if (st->l2.rc == st->l2.N200) {
+ FsmChangeState(fi, ST_L2_4);
+ test_and_clear_bit(FLG_T200_RUN, &st->l2.flag);
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'H');
+ lapb_dl_release_l2l3(st, CONFIRM);
+ } else {
+ st->l2.rc++;
+ FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200,
+ NULL, 9);
+ send_uframe(st, DISC | 0x10, CMD);
+ }
+}
+
+static void
+l2_st7_tout_200(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ if (test_bit(FLG_LAPD, &st->l2.flag) &&
+ test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) {
+ FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9);
+ return;
+ }
+ test_and_clear_bit(FLG_T200_RUN, &st->l2.flag);
+ st->l2.rc = 0;
+ FsmChangeState(fi, ST_L2_8);
+
+ transmit_enquiry(st);
+ st->l2.rc++;
+}
+
+static void
+l2_st8_tout_200(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ if (test_bit(FLG_LAPD, &st->l2.flag) &&
+ test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) {
+ FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 9);
+ return;
+ }
+ test_and_clear_bit(FLG_T200_RUN, &st->l2.flag);
+ if (st->l2.rc == st->l2.N200) {
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'I');
+ establishlink(fi);
+ test_and_clear_bit(FLG_L3_INIT, &st->l2.flag);
+ } else {
+ transmit_enquiry(st);
+ st->l2.rc++;
+ }
+}
+
+static void
+l2_st7_tout_203(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ if (test_bit(FLG_LAPD, &st->l2.flag) &&
+ test_bit(FLG_DCHAN_BUSY, &st->l2.flag)) {
+ FsmAddTimer(&st->l2.t203, st->l2.T203, EV_L2_T203, NULL, 9);
+ return;
+ }
+ FsmChangeState(fi, ST_L2_8);
+ transmit_enquiry(st);
+ st->l2.rc = 0;
+}
+
+static void
+l2_pull_iqueue(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct sk_buff *skb, *oskb;
+ struct Layer2 *l2 = &st->l2;
+ u_char header[MAX_HEADER_LEN];
+ int i;
+ int unsigned p1;
+ u_long flags;
+
+ if (!cansend(st))
+ 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_clone(skb, GFP_ATOMIC);
+
+ i = sethdraddr(&st->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);
+ p1 = skb->data - skb->head;
+ if (p1 >= i)
+ memcpy(skb_push(skb, i), header, i);
+ else {
+ printk(KERN_WARNING
+ "isdl2 pull_iqueue skb header(%d/%d) too short\n", i, p1);
+ oskb = skb;
+ skb = alloc_skb(oskb->len + i, GFP_ATOMIC);
+ memcpy(skb_put(skb, i), header, i);
+ skb_copy_from_linear_data(oskb,
+ skb_put(skb, oskb->len), oskb->len);
+ dev_kfree_skb(oskb);
+ }
+ st->l2.l2l1(st, PH_PULL | INDICATION, skb);
+ test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag);
+ if (!test_and_set_bit(FLG_T200_RUN, &st->l2.flag)) {
+ FsmDelTimer(&st->l2.t203, 13);
+ FsmAddTimer(&st->l2.t200, st->l2.T200, EV_L2_T200, NULL, 11);
+ }
+ if (!skb_queue_empty(&l2->i_queue) && cansend(st))
+ st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+}
+
+static void
+l2_st8_got_super(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct sk_buff *skb = arg;
+ int PollFlag, rsp, rnr = 0;
+ unsigned int nr;
+ struct Layer2 *l2 = &st->l2;
+
+ rsp = *skb->data & 0x2;
+ if (test_bit(FLG_ORIG, &l2->flag))
+ rsp = !rsp;
+
+ skb_pull(skb, l2addrsize(l2));
+
+ if (IsRNR(skb->data, st)) {
+ 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(st, nr)) {
+ if (rnr) {
+ restart_t200(st, 15);
+ } else {
+ stop_t200(st, 16);
+ FsmAddTimer(&l2->t203, l2->T203,
+ EV_L2_T203, NULL, 5);
+ setva(st, nr);
+ }
+ invoke_retransmission(st, nr);
+ FsmChangeState(fi, ST_L2_7);
+ if (!skb_queue_empty(&l2->i_queue) && cansend(st))
+ st->l2.l2l1(st, PH_PULL | REQUEST, NULL);
+ } else
+ nrerrorrecovery(fi);
+ } else {
+ if (!rsp && PollFlag)
+ enquiry_response(st);
+ if (legalnr(st, nr)) {
+ setva(st, nr);
+ } else
+ nrerrorrecovery(fi);
+ }
+}
+
+static void
+l2_got_FRMR(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct sk_buff *skb = arg;
+
+ skb_pull(skb, l2addrsize(&st->l2) + 1);
+
+ if (!(skb->data[0] & 1) || ((skb->data[0] & 3) == 1) || /* I or S */
+ (IsUA(skb->data) && (fi->state == ST_L2_7))) {
+ st->ma.layer(st, MDL_ERROR | INDICATION, (void *) 'K');
+ establishlink(fi);
+ test_and_clear_bit(FLG_L3_INIT, &st->l2.flag);
+ }
+ dev_kfree_skb(skb);
+}
+
+static void
+l2_st24_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ skb_queue_purge(&st->l2.ui_queue);
+ st->l2.tei = -1;
+ FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_st3_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ skb_queue_purge(&st->l2.ui_queue);
+ st->l2.tei = -1;
+ st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
+ FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_st5_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ skb_queue_purge(&st->l2.i_queue);
+ skb_queue_purge(&st->l2.ui_queue);
+ freewin(st);
+ st->l2.tei = -1;
+ stop_t200(st, 17);
+ st5_dl_release_l2l3(st);
+ FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_st6_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ skb_queue_purge(&st->l2.ui_queue);
+ st->l2.tei = -1;
+ stop_t200(st, 18);
+ st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL);
+ FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ skb_queue_purge(&st->l2.i_queue);
+ skb_queue_purge(&st->l2.ui_queue);
+ freewin(st);
+ st->l2.tei = -1;
+ stop_t200(st, 17);
+ FsmDelTimer(&st->l2.t203, 19);
+ st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
+ FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_st14_persistent_da(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ skb_queue_purge(&st->l2.i_queue);
+ skb_queue_purge(&st->l2.ui_queue);
+ if (test_and_clear_bit(FLG_ESTAB_PEND, &st->l2.flag))
+ st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
+}
+
+static void
+l2_st5_persistent_da(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ skb_queue_purge(&st->l2.i_queue);
+ skb_queue_purge(&st->l2.ui_queue);
+ freewin(st);
+ stop_t200(st, 19);
+ st5_dl_release_l2l3(st);
+ FsmChangeState(fi, ST_L2_4);
+}
+
+static void
+l2_st6_persistent_da(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ skb_queue_purge(&st->l2.ui_queue);
+ stop_t200(st, 20);
+ st->l2.l2l3(st, DL_RELEASE | CONFIRM, NULL);
+ FsmChangeState(fi, ST_L2_4);
+}
+
+static void
+l2_persistent_da(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ skb_queue_purge(&st->l2.i_queue);
+ skb_queue_purge(&st->l2.ui_queue);
+ freewin(st);
+ stop_t200(st, 19);
+ FsmDelTimer(&st->l2.t203, 19);
+ st->l2.l2l3(st, DL_RELEASE | INDICATION, NULL);
+ FsmChangeState(fi, ST_L2_4);
+}
+
+static void
+l2_set_own_busy(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ if(!test_and_set_bit(FLG_OWN_BUSY, &st->l2.flag)) {
+ enquiry_cr(st, RNR, RSP, 0);
+ test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag);
+ }
+}
+
+static void
+l2_clear_own_busy(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ if(!test_and_clear_bit(FLG_OWN_BUSY, &st->l2.flag)) {
+ enquiry_cr(st, RR, RSP, 0);
+ test_and_clear_bit(FLG_ACK_PEND, &st->l2.flag);
+ }
+}
+
+static void
+l2_frame_error(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ st->ma.layer(st, MDL_ERROR | INDICATION, arg);
+}
+
+static void
+l2_frame_error_reest(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ st->ma.layer(st, MDL_ERROR | INDICATION, arg);
+ establishlink(fi);
+ test_and_clear_bit(FLG_L3_INIT, &st->l2.flag);
+}
+
+static struct FsmNode L2FnList[] __initdata =
+{
+ {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_UNIT_DATA, l2_queue_ui_assign},
+ {ST_L2_2, EV_L2_DL_UNIT_DATA, l2_queue_ui},
+ {ST_L2_3, EV_L2_DL_UNIT_DATA, l2_queue_ui},
+ {ST_L2_4, EV_L2_DL_UNIT_DATA, l2_send_ui},
+ {ST_L2_5, EV_L2_DL_UNIT_DATA, l2_send_ui},
+ {ST_L2_6, EV_L2_DL_UNIT_DATA, l2_send_ui},
+ {ST_L2_7, EV_L2_DL_UNIT_DATA, l2_send_ui},
+ {ST_L2_8, EV_L2_DL_UNIT_DATA, 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_persistent_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_persistent_da},
+ {ST_L2_5, EV_L1_DEACTIVATE, l2_st5_persistent_da},
+ {ST_L2_6, EV_L1_DEACTIVATE, l2_st6_persistent_da},
+ {ST_L2_7, EV_L1_DEACTIVATE, l2_persistent_da},
+ {ST_L2_8, EV_L1_DEACTIVATE, l2_persistent_da},
+};
+
+#define L2_FN_COUNT (sizeof(L2FnList)/sizeof(struct FsmNode))
+
+static void
+isdnl2_l1l2(struct PStack *st, int pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ u_char *datap;
+ int ret = 1, len;
+ int c = 0;
+
+ switch (pr) {
+ case (PH_DATA | INDICATION):
+ datap = skb->data;
+ len = l2addrsize(&st->l2);
+ if (skb->len > len)
+ datap += len;
+ else {
+ FsmEvent(&st->l2.l2m, EV_L2_FRAME_ERROR, (void *) 'N');
+ dev_kfree_skb(skb);
+ return;
+ }
+ if (!(*datap & 1)) { /* I-Frame */
+ if(!(c = iframe_error(st, skb)))
+ ret = FsmEvent(&st->l2.l2m, EV_L2_I, skb);
+ } else if (IsSFrame(datap, st)) { /* S-Frame */
+ if(!(c = super_error(st, skb)))
+ ret = FsmEvent(&st->l2.l2m, EV_L2_SUPER, skb);
+ } else if (IsUI(datap)) {
+ if(!(c = UI_error(st, skb)))
+ ret = FsmEvent(&st->l2.l2m, EV_L2_UI, skb);
+ } else if (IsSABME(datap, st)) {
+ if(!(c = unnum_error(st, skb, CMD)))
+ ret = FsmEvent(&st->l2.l2m, EV_L2_SABME, skb);
+ } else if (IsUA(datap)) {
+ if(!(c = unnum_error(st, skb, RSP)))
+ ret = FsmEvent(&st->l2.l2m, EV_L2_UA, skb);
+ } else if (IsDISC(datap)) {
+ if(!(c = unnum_error(st, skb, CMD)))
+ ret = FsmEvent(&st->l2.l2m, EV_L2_DISC, skb);
+ } else if (IsDM(datap)) {
+ if(!(c = unnum_error(st, skb, RSP)))
+ ret = FsmEvent(&st->l2.l2m, EV_L2_DM, skb);
+ } else if (IsFRMR(datap)) {
+ if(!(c = FRMR_error(st,skb)))
+ ret = FsmEvent(&st->l2.l2m, EV_L2_FRMR, skb);
+ } else {
+ FsmEvent(&st->l2.l2m, EV_L2_FRAME_ERROR, (void *) 'L');
+ dev_kfree_skb(skb);
+ ret = 0;
+ }
+ if(c) {
+ dev_kfree_skb(skb);
+ FsmEvent(&st->l2.l2m, EV_L2_FRAME_ERROR, (void *)(long)c);
+ ret = 0;
+ }
+ if (ret)
+ dev_kfree_skb(skb);
+ break;
+ case (PH_PULL | CONFIRM):
+ FsmEvent(&st->l2.l2m, EV_L2_ACK_PULL, arg);
+ break;
+ case (PH_PAUSE | INDICATION):
+ test_and_set_bit(FLG_DCHAN_BUSY, &st->l2.flag);
+ break;
+ case (PH_PAUSE | CONFIRM):
+ test_and_clear_bit(FLG_DCHAN_BUSY, &st->l2.flag);
+ break;
+ case (PH_ACTIVATE | CONFIRM):
+ case (PH_ACTIVATE | INDICATION):
+ test_and_set_bit(FLG_L1_ACTIV, &st->l2.flag);
+ if (test_and_clear_bit(FLG_ESTAB_PEND, &st->l2.flag))
+ FsmEvent(&st->l2.l2m, EV_L2_DL_ESTABLISH_REQ, arg);
+ break;
+ case (PH_DEACTIVATE | INDICATION):
+ case (PH_DEACTIVATE | CONFIRM):
+ test_and_clear_bit(FLG_L1_ACTIV, &st->l2.flag);
+ FsmEvent(&st->l2.l2m, EV_L1_DEACTIVATE, arg);
+ break;
+ default:
+ l2m_debug(&st->l2.l2m, "l2 unknown pr %04x", pr);
+ break;
+ }
+}
+
+static void
+isdnl2_l3l2(struct PStack *st, int pr, void *arg)
+{
+ switch (pr) {
+ case (DL_DATA | REQUEST):
+ if (FsmEvent(&st->l2.l2m, EV_L2_DL_DATA, arg)) {
+ dev_kfree_skb((struct sk_buff *) arg);
+ }
+ break;
+ case (DL_UNIT_DATA | REQUEST):
+ if (FsmEvent(&st->l2.l2m, EV_L2_DL_UNIT_DATA, arg)) {
+ dev_kfree_skb((struct sk_buff *) arg);
+ }
+ break;
+ case (DL_ESTABLISH | REQUEST):
+ if (test_bit(FLG_L1_ACTIV, &st->l2.flag)) {
+ if (test_bit(FLG_LAPD, &st->l2.flag) ||
+ test_bit(FLG_ORIG, &st->l2.flag)) {
+ FsmEvent(&st->l2.l2m, EV_L2_DL_ESTABLISH_REQ, arg);
+ }
+ } else {
+ if (test_bit(FLG_LAPD, &st->l2.flag) ||
+ test_bit(FLG_ORIG, &st->l2.flag)) {
+ test_and_set_bit(FLG_ESTAB_PEND, &st->l2.flag);
+ }
+ st->l2.l2l1(st, PH_ACTIVATE, NULL);
+ }
+ break;
+ case (DL_RELEASE | REQUEST):
+ if (test_bit(FLG_LAPB, &st->l2.flag)) {
+ st->l2.l2l1(st, PH_DEACTIVATE, NULL);
+ }
+ FsmEvent(&st->l2.l2m, EV_L2_DL_RELEASE_REQ, arg);
+ break;
+ case (MDL_ASSIGN | REQUEST):
+ FsmEvent(&st->l2.l2m, EV_L2_MDL_ASSIGN, arg);
+ break;
+ case (MDL_REMOVE | REQUEST):
+ FsmEvent(&st->l2.l2m, EV_L2_MDL_REMOVE, arg);
+ break;
+ case (MDL_ERROR | RESPONSE):
+ FsmEvent(&st->l2.l2m, EV_L2_MDL_ERROR, arg);
+ break;
+ }
+}
+
+void
+releasestack_isdnl2(struct PStack *st)
+{
+ FsmDelTimer(&st->l2.t200, 21);
+ FsmDelTimer(&st->l2.t203, 16);
+ skb_queue_purge(&st->l2.i_queue);
+ skb_queue_purge(&st->l2.ui_queue);
+ ReleaseWin(&st->l2);
+}
+
+static void
+l2m_debug(struct FsmInst *fi, char *fmt, ...)
+{
+ va_list args;
+ struct PStack *st = fi->userdata;
+
+ va_start(args, fmt);
+ VHiSax_putstatus(st->l1.hardware, st->l2.debug_id, fmt, args);
+ va_end(args);
+}
+
+void
+setstack_isdnl2(struct PStack *st, char *debug_id)
+{
+ spin_lock_init(&st->l2.lock);
+ st->l1.l1l2 = isdnl2_l1l2;
+ st->l3.l3l2 = isdnl2_l3l2;
+
+ skb_queue_head_init(&st->l2.i_queue);
+ skb_queue_head_init(&st->l2.ui_queue);
+ InitWin(&st->l2);
+ st->l2.debug = 0;
+
+ st->l2.l2m.fsm = &l2fsm;
+ if (test_bit(FLG_LAPB, &st->l2.flag))
+ st->l2.l2m.state = ST_L2_4;
+ else
+ st->l2.l2m.state = ST_L2_1;
+ st->l2.l2m.debug = 0;
+ st->l2.l2m.userdata = st;
+ st->l2.l2m.userint = 0;
+ st->l2.l2m.printdebug = l2m_debug;
+ strcpy(st->l2.debug_id, debug_id);
+
+ FsmInitTimer(&st->l2.l2m, &st->l2.t200);
+ FsmInitTimer(&st->l2.l2m, &st->l2.t203);
+}
+
+static void
+transl2_l3l2(struct PStack *st, int pr, void *arg)
+{
+ switch (pr) {
+ case (DL_DATA | REQUEST):
+ case (DL_UNIT_DATA | REQUEST):
+ st->l2.l2l1(st, PH_DATA | REQUEST, arg);
+ break;
+ case (DL_ESTABLISH | REQUEST):
+ st->l2.l2l1(st, PH_ACTIVATE | REQUEST, NULL);
+ break;
+ case (DL_RELEASE | REQUEST):
+ st->l2.l2l1(st, PH_DEACTIVATE | REQUEST, NULL);
+ break;
+ }
+}
+
+void
+setstack_transl2(struct PStack *st)
+{
+ st->l3.l3l2 = transl2_l3l2;
+}
+
+void
+releasestack_transl2(struct PStack *st)
+{
+}
+
+int __init
+Isdnl2New(void)
+{
+ l2fsm.state_count = L2_STATE_COUNT;
+ l2fsm.event_count = L2_EVENT_COUNT;
+ l2fsm.strEvent = strL2Event;
+ l2fsm.strState = strL2State;
+ return FsmNew(&l2fsm, L2FnList, L2_FN_COUNT);
+}
+
+void
+Isdnl2Free(void)
+{
+ FsmFree(&l2fsm);
+}
diff --git a/drivers/isdn/hisax/isdnl2.h b/drivers/isdn/hisax/isdnl2.h
new file mode 100644
index 0000000..0cdab1b
--- /dev/null
+++ b/drivers/isdn/hisax/isdnl2.h
@@ -0,0 +1,26 @@
+/* $Id: isdnl2.h,v 1.3.6.2 2001/09/23 22:24:49 kai Exp $
+ *
+ * Layer 2 defines
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#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
+
diff --git a/drivers/isdn/hisax/isdnl3.c b/drivers/isdn/hisax/isdnl3.c
new file mode 100644
index 0000000..935f233
--- /dev/null
+++ b/drivers/isdn/hisax/isdnl3.c
@@ -0,0 +1,597 @@
+/* $Id: isdnl3.c,v 2.22.2.3 2004/01/13 14:31:25 keil Exp $
+ *
+ * Author Karsten Keil
+ * based on the teles driver from Jan den Ouden
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ * Thanks to Jan den Ouden
+ * Fritz Elfert
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isdnl3.h"
+
+const char *l3_revision = "$Revision: 2.22.2.3 $";
+
+static struct Fsm l3fsm;
+
+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, ...)
+{
+ va_list args;
+ struct PStack *st = fi->userdata;
+
+ va_start(args, fmt);
+ VHiSax_putstatus(st->l1.hardware, st->l3.debug_id, fmt, args);
+ va_end(args);
+}
+
+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 (*p & 0x80)
+ p++;
+ else {
+ if (codeset == wanted_set) {
+ if (*p == ie)
+ { /* improved length check (Werner Cornelius) */
+ if ((pend - p) < 2)
+ return(NULL);
+ if (*(p+1) > (pend - (p+2)))
+ return(NULL);
+ return (p);
+ }
+
+ if (*p > ie)
+ return (NULL);
+ }
+ p++;
+ l = *p++;
+ p += l;
+ codeset = maincodeset;
+ }
+ }
+ 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);
+}
+
+static int OrigCallRef = 0;
+
+int
+newcallref(void)
+{
+ if (OrigCallRef == 127)
+ OrigCallRef = 1;
+ else
+ OrigCallRef++;
+ return (OrigCallRef);
+}
+
+void
+newl3state(struct l3_process *pc, int state)
+{
+ if (pc->debug & L3_DEB_STATE)
+ l3_debug(pc->st, "newstate cr %d %d --> %d",
+ pc->callref & 0x7F,
+ pc->state, state);
+ pc->state = state;
+}
+
+static void
+L3ExpireTimer(struct L3Timer *t)
+{
+ t->pc->st->lli.l4l3(t->pc->st, t->event, t->pc);
+}
+
+void
+L3InitTimer(struct l3_process *pc, struct L3Timer *t)
+{
+ t->pc = pc;
+ t->tl.function = (void *) L3ExpireTimer;
+ t->tl.data = (long) t;
+ init_timer(&t->tl);
+}
+
+void
+L3DelTimer(struct L3Timer *t)
+{
+ del_timer(&t->tl);
+}
+
+int
+L3AddTimer(struct L3Timer *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(struct l3_process *pc)
+{
+ L3DelTimer(&pc->timer);
+}
+
+struct sk_buff *
+l3_alloc_skb(int len)
+{
+ struct sk_buff *skb;
+
+ if (!(skb = alloc_skb(len + MAX_HEADER_LEN, GFP_ATOMIC))) {
+ printk(KERN_WARNING "HiSax: No skb for D-channel\n");
+ return (NULL);
+ }
+ skb_reserve(skb, MAX_HEADER_LEN);
+ return (skb);
+}
+
+static void
+no_l3_proto(struct PStack *st, int pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+
+ HiSax_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 "HiSax: no specific protocol handler for proto %lu\n",ic->arg & 0xFF);
+ return(-1);
+}
+
+struct l3_process
+*getl3proc(struct PStack *st, int cr)
+{
+ struct l3_process *p = st->l3.proc;
+
+ while (p)
+ if (p->callref == cr)
+ return (p);
+ else
+ p = p->next;
+ return (NULL);
+}
+
+struct l3_process
+*new_l3_process(struct PStack *st, int cr)
+{
+ struct l3_process *p, *np;
+
+ if (!(p = kmalloc(sizeof(struct l3_process), GFP_ATOMIC))) {
+ printk(KERN_ERR "HiSax can't get memory for cr %d\n", cr);
+ return (NULL);
+ }
+ if (!st->l3.proc)
+ st->l3.proc = p;
+ else {
+ np = st->l3.proc;
+ while (np->next)
+ np = np->next;
+ np->next = p;
+ }
+ p->next = NULL;
+ p->debug = st->l3.debug;
+ p->callref = cr;
+ p->state = 0;
+ p->chan = NULL;
+ p->st = st;
+ p->N303 = st->l3.N303;
+ L3InitTimer(p, &p->timer);
+ return (p);
+};
+
+void
+release_l3_process(struct l3_process *p)
+{
+ struct l3_process *np, *pp = NULL;
+
+ if (!p)
+ return;
+ np = p->st->l3.proc;
+ while (np) {
+ if (np == p) {
+ StopAllL3Timer(p);
+ if (pp)
+ pp->next = np->next;
+ else if (!(p->st->l3.proc = np->next) &&
+ !test_bit(FLG_PTP, &p->st->l2.flag)) {
+ if (p->debug)
+ l3_debug(p->st, "release_l3_process: last process");
+ if (skb_queue_empty(&p->st->l3.squeue)) {
+ if (p->debug)
+ l3_debug(p->st, "release_l3_process: release link");
+ if (p->st->protocol != ISDN_PTYPE_NI1)
+ FsmEvent(&p->st->l3.l3m, EV_RELEASE_REQ, NULL);
+ else
+ FsmEvent(&p->st->l3.l3m, EV_RELEASE_IND, NULL);
+ } else {
+ if (p->debug)
+ l3_debug(p->st, "release_l3_process: not release link");
+ }
+ }
+ kfree(p);
+ return;
+ }
+ pp = np;
+ np = np->next;
+ }
+ printk(KERN_ERR "HiSax internal L3 error CR(%d) not in list\n", p->callref);
+ l3_debug(p->st, "HiSax internal L3 error CR(%d) not in list", p->callref);
+};
+
+static void
+l3ml3p(struct PStack *st, int pr)
+{
+ struct l3_process *p = st->l3.proc;
+ struct l3_process *np;
+
+ while (p) {
+ /* p might be kfreed under us, so we need to save where we want to go on */
+ np = p->next;
+ st->l3.l3ml3(st, pr, p);
+ p = np;
+ }
+}
+
+void
+setstack_l3dc(struct PStack *st, struct Channel *chanp)
+{
+ char tmp[64];
+
+ st->l3.proc = NULL;
+ st->l3.global = NULL;
+ skb_queue_head_init(&st->l3.squeue);
+ st->l3.l3m.fsm = &l3fsm;
+ st->l3.l3m.state = ST_L3_LC_REL;
+ st->l3.l3m.debug = 1;
+ st->l3.l3m.userdata = st;
+ st->l3.l3m.userint = 0;
+ st->l3.l3m.printdebug = l3m_debug;
+ FsmInitTimer(&st->l3.l3m, &st->l3.l3m_timer);
+ strcpy(st->l3.debug_id, "L3DC ");
+ st->lli.l4l3_proto = no_l3_proto_spec;
+
+#ifdef CONFIG_HISAX_EURO
+ if (st->protocol == ISDN_PTYPE_EURO) {
+ setstack_dss1(st);
+ } else
+#endif
+#ifdef CONFIG_HISAX_NI1
+ if (st->protocol == ISDN_PTYPE_NI1) {
+ setstack_ni1(st);
+ } else
+#endif
+#ifdef CONFIG_HISAX_1TR6
+ if (st->protocol == ISDN_PTYPE_1TR6) {
+ setstack_1tr6(st);
+ } else
+#endif
+ if (st->protocol == ISDN_PTYPE_LEASED) {
+ st->lli.l4l3 = no_l3_proto;
+ st->l2.l2l3 = no_l3_proto;
+ st->l3.l3ml3 = no_l3_proto;
+ printk(KERN_INFO "HiSax: Leased line mode\n");
+ } else {
+ st->lli.l4l3 = no_l3_proto;
+ st->l2.l2l3 = no_l3_proto;
+ st->l3.l3ml3 = no_l3_proto;
+ sprintf(tmp, "protocol %s not supported",
+ (st->protocol == ISDN_PTYPE_1TR6) ? "1tr6" :
+ (st->protocol == ISDN_PTYPE_EURO) ? "euro" :
+ (st->protocol == ISDN_PTYPE_NI1) ? "ni1" :
+ "unknown");
+ printk(KERN_WARNING "HiSax: %s\n", tmp);
+ st->protocol = -1;
+ }
+}
+
+static void
+isdnl3_trans(struct PStack *st, int pr, void *arg) {
+ st->l3.l3l2(st, pr, arg);
+}
+
+void
+releasestack_isdnl3(struct PStack *st)
+{
+ while (st->l3.proc)
+ release_l3_process(st->l3.proc);
+ if (st->l3.global) {
+ StopAllL3Timer(st->l3.global);
+ kfree(st->l3.global);
+ st->l3.global = NULL;
+ }
+ FsmDelTimer(&st->l3.l3m_timer, 54);
+ skb_queue_purge(&st->l3.squeue);
+}
+
+void
+setstack_l3bc(struct PStack *st, struct Channel *chanp)
+{
+
+ st->l3.proc = NULL;
+ st->l3.global = NULL;
+ skb_queue_head_init(&st->l3.squeue);
+ st->l3.l3m.fsm = &l3fsm;
+ st->l3.l3m.state = ST_L3_LC_REL;
+ st->l3.l3m.debug = 1;
+ st->l3.l3m.userdata = st;
+ st->l3.l3m.userint = 0;
+ st->l3.l3m.printdebug = l3m_debug;
+ strcpy(st->l3.debug_id, "L3BC ");
+ st->lli.l4l3 = isdnl3_trans;
+}
+
+#define DREL_TIMER_VALUE 40000
+
+static void
+lc_activate(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ FsmChangeState(fi, ST_L3_LC_ESTAB_WAIT);
+ st->l3.l3l2(st, DL_ESTABLISH | REQUEST, NULL);
+}
+
+static void
+lc_connect(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct sk_buff *skb = arg;
+ int dequeued = 0;
+
+ FsmChangeState(fi, ST_L3_LC_ESTAB);
+ while ((skb = skb_dequeue(&st->l3.squeue))) {
+ st->l3.l3l2(st, DL_DATA | REQUEST, skb);
+ dequeued++;
+ }
+ if ((!st->l3.proc) && dequeued) {
+ if (st->l3.debug)
+ l3_debug(st, "lc_connect: release link");
+ FsmEvent(&st->l3.l3m, EV_RELEASE_REQ, NULL);
+ } else
+ l3ml3p(st, DL_ESTABLISH | INDICATION);
+}
+
+static void
+lc_connected(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct sk_buff *skb = arg;
+ int dequeued = 0;
+
+ FsmDelTimer(&st->l3.l3m_timer, 51);
+ FsmChangeState(fi, ST_L3_LC_ESTAB);
+ while ((skb = skb_dequeue(&st->l3.squeue))) {
+ st->l3.l3l2(st, DL_DATA | REQUEST, skb);
+ dequeued++;
+ }
+ if ((!st->l3.proc) && dequeued) {
+ if (st->l3.debug)
+ l3_debug(st, "lc_connected: release link");
+ FsmEvent(&st->l3.l3m, EV_RELEASE_REQ, NULL);
+ } else
+ l3ml3p(st, DL_ESTABLISH | CONFIRM);
+}
+
+static void
+lc_start_delay(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ FsmChangeState(fi, ST_L3_LC_REL_DELAY);
+ FsmAddTimer(&st->l3.l3m_timer, DREL_TIMER_VALUE, EV_TIMEOUT, NULL, 50);
+}
+
+static void
+lc_start_delay_check(struct FsmInst *fi, int event, void *arg)
+/* 20/09/00 - GE timer not user for NI-1 as layer 2 should stay up */
+{
+ struct PStack *st = fi->userdata;
+
+ FsmChangeState(fi, ST_L3_LC_REL_DELAY);
+ /* 19/09/00 - GE timer not user for NI-1 */
+ if (st->protocol != ISDN_PTYPE_NI1)
+ FsmAddTimer(&st->l3.l3m_timer, DREL_TIMER_VALUE, EV_TIMEOUT, NULL, 50);
+}
+
+static void
+lc_release_req(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ if (test_bit(FLG_L2BLOCK, &st->l2.flag)) {
+ if (st->l3.debug)
+ l3_debug(st, "lc_release_req: l2 blocked");
+ /* restart release timer */
+ FsmAddTimer(&st->l3.l3m_timer, DREL_TIMER_VALUE, EV_TIMEOUT, NULL, 51);
+ } else {
+ FsmChangeState(fi, ST_L3_LC_REL_WAIT);
+ st->l3.l3l2(st, DL_RELEASE | REQUEST, NULL);
+ }
+}
+
+static void
+lc_release_ind(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ FsmDelTimer(&st->l3.l3m_timer, 52);
+ FsmChangeState(fi, ST_L3_LC_REL);
+ skb_queue_purge(&st->l3.squeue);
+ l3ml3p(st, DL_RELEASE | INDICATION);
+}
+
+static void
+lc_release_cnf(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ FsmChangeState(fi, ST_L3_LC_REL);
+ skb_queue_purge(&st->l3.squeue);
+ l3ml3p(st, DL_RELEASE | CONFIRM);
+}
+
+
+/* *INDENT-OFF* */
+static struct FsmNode L3FnList[] __initdata =
+{
+ {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_check},
+ {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))
+
+void
+l3_msg(struct PStack *st, int pr, void *arg)
+{
+ switch (pr) {
+ case (DL_DATA | REQUEST):
+ if (st->l3.l3m.state == ST_L3_LC_ESTAB) {
+ st->l3.l3l2(st, pr, arg);
+ } else {
+ struct sk_buff *skb = arg;
+
+ skb_queue_tail(&st->l3.squeue, skb);
+ FsmEvent(&st->l3.l3m, EV_ESTABLISH_REQ, NULL);
+ }
+ break;
+ case (DL_ESTABLISH | REQUEST):
+ FsmEvent(&st->l3.l3m, EV_ESTABLISH_REQ, NULL);
+ break;
+ case (DL_ESTABLISH | CONFIRM):
+ FsmEvent(&st->l3.l3m, EV_ESTABLISH_CNF, NULL);
+ break;
+ case (DL_ESTABLISH | INDICATION):
+ FsmEvent(&st->l3.l3m, EV_ESTABLISH_IND, NULL);
+ break;
+ case (DL_RELEASE | INDICATION):
+ FsmEvent(&st->l3.l3m, EV_RELEASE_IND, NULL);
+ break;
+ case (DL_RELEASE | CONFIRM):
+ FsmEvent(&st->l3.l3m, EV_RELEASE_CNF, NULL);
+ break;
+ case (DL_RELEASE | REQUEST):
+ FsmEvent(&st->l3.l3m, EV_RELEASE_REQ, NULL);
+ break;
+ }
+}
+
+int __init
+Isdnl3New(void)
+{
+ l3fsm.state_count = L3_STATE_COUNT;
+ l3fsm.event_count = L3_EVENT_COUNT;
+ l3fsm.strEvent = strL3Event;
+ l3fsm.strState = strL3State;
+ return FsmNew(&l3fsm, L3FnList, L3_FN_COUNT);
+}
+
+void
+Isdnl3Free(void)
+{
+ FsmFree(&l3fsm);
+}
diff --git a/drivers/isdn/hisax/isdnl3.h b/drivers/isdn/hisax/isdnl3.h
new file mode 100644
index 0000000..749498f
--- /dev/null
+++ b/drivers/isdn/hisax/isdnl3.h
@@ -0,0 +1,43 @@
+/* $Id: isdnl3.h,v 2.6.6.2 2001/09/23 22:24:49 kai Exp $
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#define SBIT(state) (1<<state)
+#define ALL_STATES 0x03ffffff
+
+#define PROTO_DIS_EURO 0x08
+
+#define L3_DEB_WARN 0x01
+#define L3_DEB_PROTERR 0x02
+#define L3_DEB_STATE 0x04
+#define L3_DEB_CHARGE 0x08
+#define L3_DEB_CHECK 0x10
+#define L3_DEB_SI 0x20
+
+struct stateentry {
+ int state;
+ int primitive;
+ void (*rout) (struct l3_process *, u8, void *);
+};
+
+#define l3_debug(st, fmt, args...) HiSax_putstatus(st->l1.hardware, "l3 ", fmt, ## args)
+
+struct PStack;
+
+void newl3state(struct l3_process *pc, int state);
+void L3InitTimer(struct l3_process *pc, struct L3Timer *t);
+void L3DelTimer(struct L3Timer *t);
+int L3AddTimer(struct L3Timer *t, int millisec, int event);
+void StopAllL3Timer(struct l3_process *pc);
+struct sk_buff *l3_alloc_skb(int len);
+struct l3_process *new_l3_process(struct PStack *st, int cr);
+void release_l3_process(struct l3_process *p);
+struct l3_process *getl3proc(struct PStack *st, int cr);
+void l3_msg(struct PStack *st, int pr, void *arg);
+void setstack_dss1(struct PStack *st);
+void setstack_ni1(struct PStack *st);
+void setstack_1tr6(struct PStack *st);
+
diff --git a/drivers/isdn/hisax/isurf.c b/drivers/isdn/hisax/isurf.c
new file mode 100644
index 0000000..ca41617
--- /dev/null
+++ b/drivers/isdn/hisax/isurf.c
@@ -0,0 +1,301 @@
+/* $Id: isurf.c,v 1.12.2.4 2004/01/13 21:46:03 keil Exp $
+ *
+ * low level stuff for Siemens I-Surf/I-Talk cards
+ *
+ * Author Karsten Keil
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "isar.h"
+#include "isdnl1.h"
+#include <linux/isapnp.h>
+
+static const char *ISurf_revision = "$Revision: 1.12.2.4 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define ISURF_ISAR_RESET 1
+#define ISURF_ISAC_RESET 2
+#define ISURF_ISAR_EA 4
+#define ISURF_ARCOFI_RESET 8
+#define ISURF_RESET (ISURF_ISAR_RESET | ISURF_ISAC_RESET | ISURF_ARCOFI_RESET)
+
+#define ISURF_ISAR_OFFSET 0
+#define ISURF_ISAC_OFFSET 0x100
+#define ISURF_IOMEM_SIZE 0x400
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+ return (readb(cs->hw.isurf.isac + offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ writeb(value, cs->hw.isurf.isac + offset); mb();
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ register int i;
+ for (i = 0; i < size; i++)
+ data[i] = readb(cs->hw.isurf.isac);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ register int i;
+ for (i = 0; i < size; i++){
+ writeb(data[i], cs->hw.isurf.isac);mb();
+ }
+}
+
+/* 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(struct IsdnCardState *cs, int mode, u_char offset)
+{
+ return(readb(cs->hw.isurf.isar + offset));
+}
+
+static void
+WriteISAR(struct IsdnCardState *cs, int mode, u_char offset, u_char value)
+{
+ writeb(value, cs->hw.isurf.isar + offset);mb();
+}
+
+static irqreturn_t
+isurf_interrupt(int intno, void *dev_id)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char val;
+ int cnt = 5;
+ u_long flags;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ val = readb(cs->hw.isurf.isar + ISAR_IRQBIT);
+ Start_ISAR:
+ if (val & ISAR_IRQSTA)
+ isar_int_main(cs);
+ val = readb(cs->hw.isurf.isac + ISAC_ISTA);
+ Start_ISAC:
+ if (val)
+ isac_interrupt(cs, val);
+ val = readb(cs->hw.isurf.isar + ISAR_IRQBIT);
+ if ((val & ISAR_IRQSTA) && --cnt) {
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "ISAR IntStat after IntRoutine");
+ goto Start_ISAR;
+ }
+ val = readb(cs->hw.isurf.isac + ISAC_ISTA);
+ if (val && --cnt) {
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ISAC IntStat after IntRoutine");
+ goto Start_ISAC;
+ }
+ if (!cnt)
+ printk(KERN_WARNING "ISurf IRQ LOOP\n");
+
+ writeb(0, cs->hw.isurf.isar + ISAR_IRQBIT); mb();
+ writeb(0xFF, cs->hw.isurf.isac + ISAC_MASK);mb();
+ writeb(0, cs->hw.isurf.isac + ISAC_MASK);mb();
+ writeb(ISAR_IRQMSK, cs->hw.isurf.isar + ISAR_IRQBIT); mb();
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static void
+release_io_isurf(struct IsdnCardState *cs)
+{
+ release_region(cs->hw.isurf.reset, 1);
+ iounmap(cs->hw.isurf.isar);
+ release_mem_region(cs->hw.isurf.phymem, ISURF_IOMEM_SIZE);
+}
+
+static void
+reset_isurf(struct IsdnCardState *cs, u_char chips)
+{
+ printk(KERN_INFO "ISurf: resetting card\n");
+
+ byteout(cs->hw.isurf.reset, chips); /* Reset On */
+ mdelay(10);
+ byteout(cs->hw.isurf.reset, ISURF_ISAR_EA); /* Reset Off */
+ mdelay(10);
+}
+
+static int
+ISurf_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ u_long flags;
+
+ switch (mt) {
+ case CARD_RESET:
+ spin_lock_irqsave(&cs->lock, flags);
+ reset_isurf(cs, ISURF_RESET);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return(0);
+ case CARD_RELEASE:
+ release_io_isurf(cs);
+ return(0);
+ case CARD_INIT:
+ spin_lock_irqsave(&cs->lock, flags);
+ reset_isurf(cs, ISURF_RESET);
+ clear_pending_isac_ints(cs);
+ writeb(0, cs->hw.isurf.isar+ISAR_IRQBIT);mb();
+ initisac(cs);
+ initisar(cs);
+ /* Reenable ISAC IRQ */
+ cs->writeisac(cs, ISAC_MASK, 0);
+ /* RESET Receiver and Transmitter */
+ cs->writeisac(cs, ISAC_CMDR, 0x41);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return(0);
+ case CARD_TEST:
+ return(0);
+ }
+ return(0);
+}
+
+static int
+isurf_auxcmd(struct IsdnCardState *cs, isdn_ctrl *ic) {
+ int ret;
+ u_long flags;
+
+ if ((ic->command == ISDN_CMD_IOCTL) && (ic->arg == 9)) {
+ ret = isar_auxcmd(cs, ic);
+ spin_lock_irqsave(&cs->lock, flags);
+ if (!ret) {
+ reset_isurf(cs, ISURF_ISAR_EA | ISURF_ISAC_RESET |
+ ISURF_ARCOFI_RESET);
+ initisac(cs);
+ cs->writeisac(cs, ISAC_MASK, 0);
+ cs->writeisac(cs, ISAC_CMDR, 0x41);
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return(ret);
+ }
+ return(isar_auxcmd(cs, ic));
+}
+
+#ifdef __ISAPNP__
+static struct pnp_card *pnp_c __devinitdata = NULL;
+#endif
+
+int __devinit
+setup_isurf(struct IsdnCard *card)
+{
+ int ver;
+ struct IsdnCardState *cs = card->cs;
+ char tmp[64];
+
+ strcpy(tmp, ISurf_revision);
+ printk(KERN_INFO "HiSax: ISurf driver Rev. %s\n", HiSax_getrev(tmp));
+
+ if (cs->typ != ISDN_CTYPE_ISURF)
+ return(0);
+ if (card->para[1] && card->para[2]) {
+ cs->hw.isurf.reset = card->para[1];
+ cs->hw.isurf.phymem = card->para[2];
+ cs->irq = card->para[0];
+ } else {
+#ifdef __ISAPNP__
+ if (isapnp_present()) {
+ struct pnp_dev *pnp_d = NULL;
+ int err;
+
+ cs->subtyp = 0;
+ if ((pnp_c = pnp_find_card(
+ ISAPNP_VENDOR('S', 'I', 'E'),
+ ISAPNP_FUNCTION(0x0010), pnp_c))) {
+ if (!(pnp_d = pnp_find_dev(pnp_c,
+ ISAPNP_VENDOR('S', 'I', 'E'),
+ ISAPNP_FUNCTION(0x0010), pnp_d))) {
+ printk(KERN_ERR "ISurfPnP: PnP error card found, no device\n");
+ return (0);
+ }
+ pnp_disable_dev(pnp_d);
+ err = pnp_activate_dev(pnp_d);
+ cs->hw.isurf.reset = pnp_port_start(pnp_d, 0);
+ cs->hw.isurf.phymem = pnp_mem_start(pnp_d, 1);
+ cs->irq = pnp_irq(pnp_d, 0);
+ if (!cs->irq || !cs->hw.isurf.reset || !cs->hw.isurf.phymem) {
+ printk(KERN_ERR "ISurfPnP:some resources are missing %d/%x/%lx\n",
+ cs->irq, cs->hw.isurf.reset, cs->hw.isurf.phymem);
+ pnp_disable_dev(pnp_d);
+ return(0);
+ }
+ } else {
+ printk(KERN_INFO "ISurfPnP: no ISAPnP card found\n");
+ return(0);
+ }
+ } else {
+ printk(KERN_INFO "ISurfPnP: no ISAPnP bus found\n");
+ return(0);
+ }
+#else
+ printk(KERN_WARNING "HiSax: Siemens I-Surf port/mem not set\n");
+ return (0);
+#endif
+ }
+ if (!request_region(cs->hw.isurf.reset, 1, "isurf isdn")) {
+ printk(KERN_WARNING
+ "HiSax: Siemens I-Surf config port %x already in use\n",
+ cs->hw.isurf.reset);
+ return (0);
+ }
+ if (!request_region(cs->hw.isurf.phymem, ISURF_IOMEM_SIZE, "isurf iomem")) {
+ printk(KERN_WARNING "HiSax: Siemens I-Surf memory region "
+ "%lx-%lx already in use\n",
+ cs->hw.isurf.phymem,
+ cs->hw.isurf.phymem + ISURF_IOMEM_SIZE);
+ release_region(cs->hw.isurf.reset, 1);
+ return (0);
+ }
+ cs->hw.isurf.isar = ioremap(cs->hw.isurf.phymem, ISURF_IOMEM_SIZE);
+ cs->hw.isurf.isac = cs->hw.isurf.isar + ISURF_ISAC_OFFSET;
+ printk(KERN_INFO
+ "ISurf: defined at 0x%x 0x%lx IRQ %d\n",
+ cs->hw.isurf.reset,
+ cs->hw.isurf.phymem,
+ cs->irq);
+
+ setup_isac(cs);
+ cs->cardmsg = &ISurf_card_msg;
+ cs->irq_func = &isurf_interrupt;
+ cs->auxcmd = &isurf_auxcmd;
+ cs->readisac = &ReadISAC;
+ cs->writeisac = &WriteISAC;
+ cs->readisacfifo = &ReadISACfifo;
+ cs->writeisacfifo = &WriteISACfifo;
+ cs->bcs[0].hw.isar.reg = &cs->hw.isurf.isar_r;
+ cs->bcs[1].hw.isar.reg = &cs->hw.isurf.isar_r;
+ test_and_set_bit(HW_ISAR, &cs->HW_Flags);
+ ISACVersion(cs, "ISurf:");
+ cs->BC_Read_Reg = &ReadISAR;
+ cs->BC_Write_Reg = &WriteISAR;
+ cs->BC_Send_Data = &isar_fill_fifo;
+ ver = ISARVersion(cs, "ISurf:");
+ if (ver < 0) {
+ printk(KERN_WARNING
+ "ISurf: wrong ISAR version (ret = %d)\n", ver);
+ release_io_isurf(cs);
+ return (0);
+ }
+ return (1);
+}
diff --git a/drivers/isdn/hisax/ix1_micro.c b/drivers/isdn/hisax/ix1_micro.c
new file mode 100644
index 0000000..a92bf0d
--- /dev/null
+++ b/drivers/isdn/hisax/ix1_micro.c
@@ -0,0 +1,317 @@
+/* $Id: ix1_micro.c,v 2.12.2.4 2004/01/13 23:48:39 keil Exp $
+ *
+ * low level stuff for ITK ix1-micro Rev.2 isdn cards
+ * derived from the original file teles3.c from Karsten Keil
+ *
+ * Author Klaus-Peter Nischke
+ * Copyright by Klaus-Peter Nischke, ITK AG
+ * <klaus@nischke.do.eunet.de>
+ * by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Klaus-Peter Nischke
+ * Deusener Str. 287
+ * 44369 Dortmund
+ * Germany
+ */
+
+#include <linux/init.h>
+#include <linux/isapnp.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+static const char *ix1_revision = "$Revision: 2.12.2.4 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define SPECIAL_PORT_OFFSET 3
+
+#define ISAC_COMMAND_OFFSET 2
+#define ISAC_DATA_OFFSET 0
+#define HSCX_COMMAND_OFFSET 2
+#define HSCX_DATA_OFFSET 1
+
+#define TIMEOUT 50
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+ register u_char ret;
+
+ byteout(ale, off);
+ ret = bytein(adr);
+ return (ret);
+}
+
+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(struct IsdnCardState *cs, u_char offset)
+{
+ return (readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ readfifo(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ writefifo(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, 0, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+ return (readreg(cs->hw.ix1.hscx_ale,
+ cs->hw.ix1.hscx, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+ writereg(cs->hw.ix1.hscx_ale,
+ cs->hw.ix1.hscx, offset + (hscx ? 0x40 : 0), value);
+}
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.ix1.hscx_ale, \
+ cs->hw.ix1.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.ix1.hscx_ale, \
+ cs->hw.ix1.hscx, reg + (nr ? 0x40 : 0), data)
+
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.ix1.hscx_ale, \
+ cs->hw.ix1.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.ix1.hscx_ale, \
+ cs->hw.ix1.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+ix1micro_interrupt(int intno, void *dev_id)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char val;
+ u_long flags;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ val = readreg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_ISTA + 0x40);
+ Start_HSCX:
+ if (val)
+ hscx_int_main(cs, val);
+ val = readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_ISTA);
+ Start_ISAC:
+ if (val)
+ isac_interrupt(cs, val);
+ val = readreg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_ISTA + 0x40);
+ if (val) {
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "HSCX IntStat after IntRoutine");
+ goto Start_HSCX;
+ }
+ val = readreg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_ISTA);
+ if (val) {
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ISAC IntStat after IntRoutine");
+ goto Start_ISAC;
+ }
+ writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK, 0xFF);
+ writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK + 0x40, 0xFF);
+ writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_MASK, 0xFF);
+ writereg(cs->hw.ix1.isac_ale, cs->hw.ix1.isac, ISAC_MASK, 0);
+ writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK, 0);
+ writereg(cs->hw.ix1.hscx_ale, cs->hw.ix1.hscx, HSCX_MASK + 0x40, 0);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static void
+release_io_ix1micro(struct IsdnCardState *cs)
+{
+ if (cs->hw.ix1.cfg_reg)
+ release_region(cs->hw.ix1.cfg_reg, 4);
+}
+
+static void
+ix1_reset(struct IsdnCardState *cs)
+{
+ int cnt;
+
+ /* reset isac */
+ cnt = 3 * (HZ / 10) + 1;
+ while (cnt--) {
+ byteout(cs->hw.ix1.cfg_reg + SPECIAL_PORT_OFFSET, 1);
+ HZDELAY(1); /* wait >=10 ms */
+ }
+ byteout(cs->hw.ix1.cfg_reg + SPECIAL_PORT_OFFSET, 0);
+}
+
+static int
+ix1_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ u_long flags;
+
+ switch (mt) {
+ case CARD_RESET:
+ spin_lock_irqsave(&cs->lock, flags);
+ ix1_reset(cs);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return(0);
+ case CARD_RELEASE:
+ release_io_ix1micro(cs);
+ return(0);
+ case CARD_INIT:
+ spin_lock_irqsave(&cs->lock, flags);
+ ix1_reset(cs);
+ inithscxisac(cs, 3);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return(0);
+ case CARD_TEST:
+ return(0);
+ }
+ return(0);
+}
+
+#ifdef __ISAPNP__
+static struct isapnp_device_id itk_ids[] __devinitdata = {
+ { ISAPNP_VENDOR('I', 'T', 'K'), ISAPNP_FUNCTION(0x25),
+ ISAPNP_VENDOR('I', 'T', 'K'), ISAPNP_FUNCTION(0x25),
+ (unsigned long) "ITK micro 2" },
+ { ISAPNP_VENDOR('I', 'T', 'K'), ISAPNP_FUNCTION(0x29),
+ ISAPNP_VENDOR('I', 'T', 'K'), ISAPNP_FUNCTION(0x29),
+ (unsigned long) "ITK micro 2." },
+ { 0, }
+};
+
+static struct isapnp_device_id *ipid __devinitdata = &itk_ids[0];
+static struct pnp_card *pnp_c __devinitdata = NULL;
+#endif
+
+
+int __devinit
+setup_ix1micro(struct IsdnCard *card)
+{
+ struct IsdnCardState *cs = card->cs;
+ char tmp[64];
+
+ strcpy(tmp, ix1_revision);
+ printk(KERN_INFO "HiSax: ITK IX1 driver Rev. %s\n", HiSax_getrev(tmp));
+ if (cs->typ != ISDN_CTYPE_IX1MICROR2)
+ return (0);
+
+#ifdef __ISAPNP__
+ if (!card->para[1] && isapnp_present()) {
+ struct pnp_dev *pnp_d;
+ while(ipid->card_vendor) {
+ if ((pnp_c = pnp_find_card(ipid->card_vendor,
+ ipid->card_device, pnp_c))) {
+ pnp_d = NULL;
+ if ((pnp_d = pnp_find_dev(pnp_c,
+ ipid->vendor, ipid->function, pnp_d))) {
+ int err;
+
+ printk(KERN_INFO "HiSax: %s detected\n",
+ (char *)ipid->driver_data);
+ pnp_disable_dev(pnp_d);
+ err = pnp_activate_dev(pnp_d);
+ if (err<0) {
+ printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
+ __func__, err);
+ return(0);
+ }
+ card->para[1] = pnp_port_start(pnp_d, 0);
+ card->para[0] = pnp_irq(pnp_d, 0);
+ if (!card->para[0] || !card->para[1]) {
+ printk(KERN_ERR "ITK PnP:some resources are missing %ld/%lx\n",
+ card->para[0], card->para[1]);
+ pnp_disable_dev(pnp_d);
+ return(0);
+ }
+ break;
+ } else {
+ printk(KERN_ERR "ITK PnP: PnP error card found, no device\n");
+ }
+ }
+ ipid++;
+ pnp_c = NULL;
+ }
+ if (!ipid->card_vendor) {
+ printk(KERN_INFO "ITK PnP: no ISAPnP card found\n");
+ return(0);
+ }
+ }
+#endif
+ /* IO-Ports */
+ cs->hw.ix1.isac_ale = card->para[1] + ISAC_COMMAND_OFFSET;
+ cs->hw.ix1.hscx_ale = card->para[1] + HSCX_COMMAND_OFFSET;
+ cs->hw.ix1.isac = card->para[1] + ISAC_DATA_OFFSET;
+ cs->hw.ix1.hscx = card->para[1] + HSCX_DATA_OFFSET;
+ cs->hw.ix1.cfg_reg = card->para[1];
+ cs->irq = card->para[0];
+ if (cs->hw.ix1.cfg_reg) {
+ if (!request_region(cs->hw.ix1.cfg_reg, 4, "ix1micro cfg")) {
+ printk(KERN_WARNING
+ "HiSax: ITK ix1-micro Rev.2 config port "
+ "%x-%x already in use\n",
+ cs->hw.ix1.cfg_reg,
+ cs->hw.ix1.cfg_reg + 4);
+ return (0);
+ }
+ }
+ printk(KERN_INFO "HiSax: ITK ix1-micro Rev.2 config irq:%d io:0x%X\n",
+ cs->irq, cs->hw.ix1.cfg_reg);
+ setup_isac(cs);
+ cs->readisac = &ReadISAC;
+ cs->writeisac = &WriteISAC;
+ cs->readisacfifo = &ReadISACfifo;
+ cs->writeisacfifo = &WriteISACfifo;
+ cs->BC_Read_Reg = &ReadHSCX;
+ cs->BC_Write_Reg = &WriteHSCX;
+ cs->BC_Send_Data = &hscx_fill_fifo;
+ cs->cardmsg = &ix1_card_msg;
+ cs->irq_func = &ix1micro_interrupt;
+ ISACVersion(cs, "ix1-Micro:");
+ if (HscxVersion(cs, "ix1-Micro:")) {
+ printk(KERN_WARNING
+ "ix1-Micro: wrong HSCX versions check IO address\n");
+ release_io_ix1micro(cs);
+ return (0);
+ }
+ return (1);
+}
diff --git a/drivers/isdn/hisax/jade.c b/drivers/isdn/hisax/jade.c
new file mode 100644
index 0000000..70840a7
--- /dev/null
+++ b/drivers/isdn/hisax/jade.c
@@ -0,0 +1,314 @@
+/* $Id: jade.c,v 1.9.2.4 2004/01/14 16:04:48 keil Exp $
+ *
+ * JADE stuff (derived from original hscx.c)
+ *
+ * Author Roland Klabunde
+ * Copyright by Roland Klabunde <R.Klabunde@Berkom.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "hscx.h"
+#include "jade.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+
+
+int
+JadeVersion(struct IsdnCardState *cs, char *s)
+{
+ int ver,i;
+ int to = 50;
+ cs->BC_Write_Reg(cs, -1, 0x50, 0x19);
+ i=0;
+ while (to) {
+ udelay(1);
+ ver = cs->BC_Read_Reg(cs, -1, 0x60);
+ to--;
+ if (ver)
+ break;
+ if (!to) {
+ printk(KERN_INFO "%s JADE version not obtainable\n", s);
+ return (0);
+ }
+ }
+ /* Wait for the JADE */
+ udelay(10);
+ /* Read version */
+ ver = cs->BC_Read_Reg(cs, -1, 0x60);
+ printk(KERN_INFO "%s JADE version: %d\n", s, ver);
+ return (1);
+}
+
+/* Write to indirect accessible jade register set */
+static void
+jade_write_indirect(struct IsdnCardState *cs, u_char reg, u_char value)
+{
+ int to = 50;
+ u_char ret;
+
+ /* Write the data */
+ cs->BC_Write_Reg(cs, -1, COMM_JADE+1, value);
+ /* Say JADE we wanna write indirect reg 'reg' */
+ cs->BC_Write_Reg(cs, -1, COMM_JADE, reg);
+ to = 50;
+ /* Wait for RDY goes high */
+ while (to) {
+ udelay(1);
+ ret = cs->BC_Read_Reg(cs, -1, COMM_JADE);
+ to--;
+ if (ret & 1)
+ /* Got acknowledge */
+ break;
+ if (!to) {
+ printk(KERN_INFO "Can not see ready bit from JADE DSP (reg=0x%X, value=0x%X)\n", reg, value);
+ return;
+ }
+ }
+}
+
+
+
+static void
+modejade(struct BCState *bcs, int mode, int bc)
+{
+ struct IsdnCardState *cs = bcs->cs;
+ int jade = bcs->hw.hscx.hscx;
+
+ if (cs->debug & L1_DEB_HSCX) {
+ char tmp[40];
+ sprintf(tmp, "jade %c mode %d ichan %d",
+ 'A' + jade, mode, bc);
+ debugl1(cs, tmp);
+ }
+ bcs->mode = mode;
+ bcs->channel = bc;
+
+ cs->BC_Write_Reg(cs, jade, jade_HDLC_MODE, (mode == L1_MODE_TRANS ? jadeMODE_TMO:0x00));
+ cs->BC_Write_Reg(cs, jade, jade_HDLC_CCR0, (jadeCCR0_PU|jadeCCR0_ITF));
+ cs->BC_Write_Reg(cs, jade, jade_HDLC_CCR1, 0x00);
+
+ jade_write_indirect(cs, jade_HDLC1SERRXPATH, 0x08);
+ jade_write_indirect(cs, jade_HDLC2SERRXPATH, 0x08);
+ jade_write_indirect(cs, jade_HDLC1SERTXPATH, 0x00);
+ jade_write_indirect(cs, jade_HDLC2SERTXPATH, 0x00);
+
+ cs->BC_Write_Reg(cs, jade, jade_HDLC_XCCR, 0x07);
+ cs->BC_Write_Reg(cs, jade, jade_HDLC_RCCR, 0x07);
+
+ if (bc == 0) {
+ cs->BC_Write_Reg(cs, jade, jade_HDLC_TSAX, 0x00);
+ cs->BC_Write_Reg(cs, jade, jade_HDLC_TSAR, 0x00);
+ } else {
+ cs->BC_Write_Reg(cs, jade, jade_HDLC_TSAX, 0x04);
+ cs->BC_Write_Reg(cs, jade, jade_HDLC_TSAR, 0x04);
+ }
+ switch (mode) {
+ case (L1_MODE_NULL):
+ cs->BC_Write_Reg(cs, jade, jade_HDLC_MODE, jadeMODE_TMO);
+ break;
+ case (L1_MODE_TRANS):
+ cs->BC_Write_Reg(cs, jade, jade_HDLC_MODE, (jadeMODE_TMO|jadeMODE_RAC|jadeMODE_XAC));
+ break;
+ case (L1_MODE_HDLC):
+ cs->BC_Write_Reg(cs, jade, jade_HDLC_MODE, (jadeMODE_RAC|jadeMODE_XAC));
+ break;
+ }
+ if (mode) {
+ cs->BC_Write_Reg(cs, jade, jade_HDLC_RCMD, (jadeRCMD_RRES|jadeRCMD_RMC));
+ cs->BC_Write_Reg(cs, jade, jade_HDLC_XCMD, jadeXCMD_XRES);
+ /* Unmask ints */
+ cs->BC_Write_Reg(cs, jade, jade_HDLC_IMR, 0xF8);
+ }
+ else
+ /* Mask ints */
+ cs->BC_Write_Reg(cs, jade, jade_HDLC_IMR, 0x00);
+}
+
+static void
+jade_l2l1(struct PStack *st, int pr, void *arg)
+{
+ struct BCState *bcs = st->l1.bcs;
+ struct sk_buff *skb = arg;
+ 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);
+ bcs->hw.hscx.count = 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 "jade_l2l1: this shouldn't happen\n");
+ } else {
+ test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+ bcs->tx_skb = skb;
+ bcs->hw.hscx.count = 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);
+ modejade(bcs, st->l1.mode, st->l1.bc);
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | REQUEST):
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | CONFIRM):
+ spin_lock_irqsave(&bcs->cs->lock, flags);
+ test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ modejade(bcs, 0, st->l1.bc);
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
+ st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+ break;
+ }
+}
+
+static void
+close_jadestate(struct BCState *bcs)
+{
+ modejade(bcs, 0, bcs->channel);
+ if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+ kfree(bcs->hw.hscx.rcvbuf);
+ bcs->hw.hscx.rcvbuf = NULL;
+ kfree(bcs->blog);
+ bcs->blog = 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);
+ }
+ }
+}
+
+static int
+open_jadestate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+ if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+ if (!(bcs->hw.hscx.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "HiSax: No memory for hscx.rcvbuf\n");
+ test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+ return (1);
+ }
+ if (!(bcs->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "HiSax: No memory for bcs->blog\n");
+ test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+ kfree(bcs->hw.hscx.rcvbuf);
+ bcs->hw.hscx.rcvbuf = NULL;
+ return (2);
+ }
+ 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);
+ bcs->event = 0;
+ bcs->hw.hscx.rcvidx = 0;
+ bcs->tx_cnt = 0;
+ return (0);
+}
+
+
+static int
+setstack_jade(struct PStack *st, struct BCState *bcs)
+{
+ bcs->channel = st->l1.bc;
+ if (open_jadestate(st->l1.hardware, bcs))
+ return (-1);
+ st->l1.bcs = bcs;
+ st->l2.l2l1 = jade_l2l1;
+ setstack_manager(st);
+ bcs->st = st;
+ setstack_l1_B(st);
+ return (0);
+}
+
+void
+clear_pending_jade_ints(struct IsdnCardState *cs)
+{
+ int val;
+ char tmp[64];
+
+ cs->BC_Write_Reg(cs, 0, jade_HDLC_IMR, 0x00);
+ cs->BC_Write_Reg(cs, 1, jade_HDLC_IMR, 0x00);
+
+ val = cs->BC_Read_Reg(cs, 1, jade_HDLC_ISR);
+ sprintf(tmp, "jade B ISTA %x", val);
+ debugl1(cs, tmp);
+ val = cs->BC_Read_Reg(cs, 0, jade_HDLC_ISR);
+ sprintf(tmp, "jade A ISTA %x", val);
+ debugl1(cs, tmp);
+ val = cs->BC_Read_Reg(cs, 1, jade_HDLC_STAR);
+ sprintf(tmp, "jade B STAR %x", val);
+ debugl1(cs, tmp);
+ val = cs->BC_Read_Reg(cs, 0, jade_HDLC_STAR);
+ sprintf(tmp, "jade A STAR %x", val);
+ debugl1(cs, tmp);
+ /* Unmask ints */
+ cs->BC_Write_Reg(cs, 0, jade_HDLC_IMR, 0xF8);
+ cs->BC_Write_Reg(cs, 1, jade_HDLC_IMR, 0xF8);
+}
+
+void
+initjade(struct IsdnCardState *cs)
+{
+ cs->bcs[0].BC_SetStack = setstack_jade;
+ cs->bcs[1].BC_SetStack = setstack_jade;
+ cs->bcs[0].BC_Close = close_jadestate;
+ cs->bcs[1].BC_Close = close_jadestate;
+ cs->bcs[0].hw.hscx.hscx = 0;
+ cs->bcs[1].hw.hscx.hscx = 1;
+
+ /* Stop DSP audio tx/rx */
+ jade_write_indirect(cs, 0x11, 0x0f);
+ jade_write_indirect(cs, 0x17, 0x2f);
+
+ /* Transparent Mode, RxTx inactive, No Test, No RFS/TFS */
+ cs->BC_Write_Reg(cs, 0, jade_HDLC_MODE, jadeMODE_TMO);
+ cs->BC_Write_Reg(cs, 1, jade_HDLC_MODE, jadeMODE_TMO);
+ /* Power down, 1-Idle, RxTx least significant bit first */
+ cs->BC_Write_Reg(cs, 0, jade_HDLC_CCR0, 0x00);
+ cs->BC_Write_Reg(cs, 1, jade_HDLC_CCR0, 0x00);
+ /* Mask all interrupts */
+ cs->BC_Write_Reg(cs, 0, jade_HDLC_IMR, 0x00);
+ cs->BC_Write_Reg(cs, 1, jade_HDLC_IMR, 0x00);
+ /* Setup host access to hdlc controller */
+ jade_write_indirect(cs, jade_HDLCCNTRACCESS, (jadeINDIRECT_HAH1|jadeINDIRECT_HAH2));
+ /* Unmask HDLC int (don't forget DSP int later on)*/
+ cs->BC_Write_Reg(cs, -1,jade_INT, (jadeINT_HDLC1|jadeINT_HDLC2));
+
+ /* once again TRANSPARENT */
+ modejade(cs->bcs, 0, 0);
+ modejade(cs->bcs + 1, 0, 0);
+}
+
diff --git a/drivers/isdn/hisax/jade.h b/drivers/isdn/hisax/jade.h
new file mode 100644
index 0000000..29055e1
--- /dev/null
+++ b/drivers/isdn/hisax/jade.h
@@ -0,0 +1,134 @@
+/* $Id: jade.h,v 1.5.2.3 2004/01/14 16:04:48 keil Exp $
+ *
+ * JADE specific defines
+ *
+ * Author Roland Klabunde
+ * Copyright by Roland Klabunde <R.Klabunde@Berkom.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/* All Registers original Siemens Spec */
+#ifndef __JADE_H__
+#define __JADE_H__
+
+/* Special registers for access to indirect accessible JADE regs */
+#define DIRECT_IO_JADE 0x0000 /* Jade direct io access area */
+#define COMM_JADE 0x0040 /* Jade communication area */
+
+/********************************************************************/
+/* JADE-HDLC registers */
+/********************************************************************/
+#define jade_HDLC_RFIFO 0x00 /* R */
+#define jade_HDLC_XFIFO 0x00 /* W */
+
+#define jade_HDLC_STAR 0x20 /* R */
+ #define jadeSTAR_XDOV 0x80
+ #define jadeSTAR_XFW 0x40 /* Does not work*/
+ #define jadeSTAR_XCEC 0x20
+ #define jadeSTAR_RCEC 0x10
+ #define jadeSTAR_BSY 0x08
+ #define jadeSTAR_RNA 0x04
+ #define jadeSTAR_STR 0x02
+ #define jadeSTAR_STX 0x01
+
+#define jade_HDLC_XCMD 0x20 /* W */
+ #define jadeXCMD_XF 0x80
+ #define jadeXCMD_XME 0x40
+ #define jadeXCMD_XRES 0x20
+ #define jadeXCMD_STX 0x01
+
+#define jade_HDLC_RSTA 0x21 /* R */
+ #define jadeRSTA_VFR 0x80
+ #define jadeRSTA_RDO 0x40
+ #define jadeRSTA_CRC 0x20
+ #define jadeRSTA_RAB 0x10
+ #define jadeRSTA_MASK 0xF0
+
+#define jade_HDLC_MODE 0x22 /* RW*/
+ #define jadeMODE_TMO 0x80
+ #define jadeMODE_RAC 0x40
+ #define jadeMODE_XAC 0x20
+ #define jadeMODE_TLP 0x10
+ #define jadeMODE_ERFS 0x02
+ #define jadeMODE_ETFS 0x01
+
+#define jade_HDLC_RBCH 0x24 /* R */
+
+#define jade_HDLC_RBCL 0x25 /* R */
+#define jade_HDLC_RCMD 0x25 /* W */
+ #define jadeRCMD_RMC 0x80
+ #define jadeRCMD_RRES 0x40
+ #define jadeRCMD_RMD 0x20
+ #define jadeRCMD_STR 0x02
+
+#define jade_HDLC_CCR0 0x26 /* RW*/
+ #define jadeCCR0_PU 0x80
+ #define jadeCCR0_ITF 0x40
+ #define jadeCCR0_C32 0x20
+ #define jadeCCR0_CRL 0x10
+ #define jadeCCR0_RCRC 0x08
+ #define jadeCCR0_XCRC 0x04
+ #define jadeCCR0_RMSB 0x02
+ #define jadeCCR0_XMSB 0x01
+
+#define jade_HDLC_CCR1 0x27 /* RW*/
+ #define jadeCCR1_RCS0 0x80
+ #define jadeCCR1_RCONT 0x40
+ #define jadeCCR1_RFDIS 0x20
+ #define jadeCCR1_XCS0 0x10
+ #define jadeCCR1_XCONT 0x08
+ #define jadeCCR1_XFDIS 0x04
+
+#define jade_HDLC_TSAR 0x28 /* RW*/
+#define jade_HDLC_TSAX 0x29 /* RW*/
+#define jade_HDLC_RCCR 0x2A /* RW*/
+#define jade_HDLC_XCCR 0x2B /* RW*/
+
+#define jade_HDLC_ISR 0x2C /* R */
+#define jade_HDLC_IMR 0x2C /* W */
+ #define jadeISR_RME 0x80
+ #define jadeISR_RPF 0x40
+ #define jadeISR_RFO 0x20
+ #define jadeISR_XPR 0x10
+ #define jadeISR_XDU 0x08
+ #define jadeISR_ALLS 0x04
+
+#define jade_INT 0x75
+ #define jadeINT_HDLC1 0x02
+ #define jadeINT_HDLC2 0x01
+ #define jadeINT_DSP 0x04
+#define jade_INTR 0x70
+
+/********************************************************************/
+/* Indirect accessible JADE registers of common interest */
+/********************************************************************/
+#define jade_CHIPVERSIONNR 0x00 /* Does not work*/
+
+#define jade_HDLCCNTRACCESS 0x10
+ #define jadeINDIRECT_HAH1 0x02
+ #define jadeINDIRECT_HAH2 0x01
+
+#define jade_HDLC1SERRXPATH 0x1D
+#define jade_HDLC1SERTXPATH 0x1E
+#define jade_HDLC2SERRXPATH 0x1F
+#define jade_HDLC2SERTXPATH 0x20
+ #define jadeINDIRECT_SLIN1 0x10
+ #define jadeINDIRECT_SLIN0 0x08
+ #define jadeINDIRECT_LMOD1 0x04
+ #define jadeINDIRECT_LMOD0 0x02
+ #define jadeINDIRECT_HHR 0x01
+ #define jadeINDIRECT_HHX 0x01
+
+#define jade_RXAUDIOCH1CFG 0x11
+#define jade_RXAUDIOCH2CFG 0x14
+#define jade_TXAUDIOCH1CFG 0x17
+#define jade_TXAUDIOCH2CFG 0x1A
+
+extern int JadeVersion(struct IsdnCardState *cs, char *s);
+extern void clear_pending_jade_ints(struct IsdnCardState *cs);
+extern void initjade(struct IsdnCardState *cs);
+
+#endif /* __JADE_H__ */
diff --git a/drivers/isdn/hisax/jade_irq.c b/drivers/isdn/hisax/jade_irq.c
new file mode 100644
index 0000000..1f201af
--- /dev/null
+++ b/drivers/isdn/hisax/jade_irq.c
@@ -0,0 +1,236 @@
+/* $Id: jade_irq.c,v 1.7.2.4 2004/02/11 13:21:34 keil Exp $
+ *
+ * Low level JADE IRQ stuff (derived from original hscx_irq.c)
+ *
+ * Author Roland Klabunde
+ * Copyright by Roland Klabunde <R.Klabunde@Berkom.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+static inline void
+waitforCEC(struct IsdnCardState *cs, int jade, int reg)
+{
+ int to = 50;
+ int mask = (reg == jade_HDLC_XCMD ? jadeSTAR_XCEC : jadeSTAR_RCEC);
+ while ((READJADE(cs, jade, jade_HDLC_STAR) & mask) && to) {
+ udelay(1);
+ to--;
+ }
+ if (!to)
+ printk(KERN_WARNING "HiSax: waitforCEC (jade) timeout\n");
+}
+
+
+static inline void
+waitforXFW(struct IsdnCardState *cs, int jade)
+{
+ /* Does not work on older jade versions, don't care */
+}
+
+static inline void
+WriteJADECMDR(struct IsdnCardState *cs, int jade, int reg, u_char data)
+{
+ waitforCEC(cs, jade, reg);
+ WRITEJADE(cs, jade, reg, data);
+}
+
+
+
+static void
+jade_empty_fifo(struct BCState *bcs, int count)
+{
+ u_char *ptr;
+ struct IsdnCardState *cs = bcs->cs;
+
+ if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+ debugl1(cs, "jade_empty_fifo");
+
+ if (bcs->hw.hscx.rcvidx + count > HSCX_BUFMAX) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "jade_empty_fifo: incoming packet too large");
+ WriteJADECMDR(cs, bcs->hw.hscx.hscx, jade_HDLC_RCMD, jadeRCMD_RMC);
+ bcs->hw.hscx.rcvidx = 0;
+ return;
+ }
+ ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx;
+ bcs->hw.hscx.rcvidx += count;
+ READJADEFIFO(cs, bcs->hw.hscx.hscx, ptr, count);
+ WriteJADECMDR(cs, bcs->hw.hscx.hscx, jade_HDLC_RCMD, jadeRCMD_RMC);
+ if (cs->debug & L1_DEB_HSCX_FIFO) {
+ char *t = bcs->blog;
+
+ t += sprintf(t, "jade_empty_fifo %c cnt %d",
+ bcs->hw.hscx.hscx ? 'B' : 'A', count);
+ QuickHex(t, ptr, count);
+ debugl1(cs, bcs->blog);
+ }
+}
+
+static void
+jade_fill_fifo(struct BCState *bcs)
+{
+ struct IsdnCardState *cs = bcs->cs;
+ int more, count;
+ int fifo_size = 32;
+ u_char *ptr;
+
+ if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+ debugl1(cs, "jade_fill_fifo");
+
+ if (!bcs->tx_skb)
+ return;
+ if (bcs->tx_skb->len <= 0)
+ return;
+
+ more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0;
+ if (bcs->tx_skb->len > fifo_size) {
+ more = !0;
+ count = fifo_size;
+ } else
+ count = bcs->tx_skb->len;
+
+ waitforXFW(cs, bcs->hw.hscx.hscx);
+ ptr = bcs->tx_skb->data;
+ skb_pull(bcs->tx_skb, count);
+ bcs->tx_cnt -= count;
+ bcs->hw.hscx.count += count;
+ WRITEJADEFIFO(cs, bcs->hw.hscx.hscx, ptr, count);
+ WriteJADECMDR(cs, bcs->hw.hscx.hscx, jade_HDLC_XCMD, more ? jadeXCMD_XF : (jadeXCMD_XF|jadeXCMD_XME));
+ if (cs->debug & L1_DEB_HSCX_FIFO) {
+ char *t = bcs->blog;
+
+ t += sprintf(t, "jade_fill_fifo %c cnt %d",
+ bcs->hw.hscx.hscx ? 'B' : 'A', count);
+ QuickHex(t, ptr, count);
+ debugl1(cs, bcs->blog);
+ }
+}
+
+
+static void
+jade_interrupt(struct IsdnCardState *cs, u_char val, u_char jade)
+{
+ u_char r;
+ struct BCState *bcs = cs->bcs + jade;
+ struct sk_buff *skb;
+ int fifo_size = 32;
+ int count;
+ int i_jade = (int) jade; /* To satisfy the compiler */
+
+ if (!test_bit(BC_FLG_INIT, &bcs->Flag))
+ return;
+
+ if (val & 0x80) { /* RME */
+ r = READJADE(cs, i_jade, jade_HDLC_RSTA);
+ if ((r & 0xf0) != 0xa0) {
+ if (!(r & 0x80))
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "JADE %s invalid frame", (jade ? "B":"A"));
+ if ((r & 0x40) && bcs->mode)
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "JADE %c RDO mode=%d", 'A'+jade, bcs->mode);
+ if (!(r & 0x20))
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "JADE %c CRC error", 'A'+jade);
+ WriteJADECMDR(cs, jade, jade_HDLC_RCMD, jadeRCMD_RMC);
+ } else {
+ count = READJADE(cs, i_jade, jade_HDLC_RBCL) & 0x1F;
+ if (count == 0)
+ count = fifo_size;
+ jade_empty_fifo(bcs, count);
+ if ((count = bcs->hw.hscx.rcvidx - 1) > 0) {
+ if (cs->debug & L1_DEB_HSCX_FIFO)
+ debugl1(cs, "HX Frame %d", count);
+ if (!(skb = dev_alloc_skb(count)))
+ printk(KERN_WARNING "JADE %s receive out of memory\n", (jade ? "B":"A"));
+ else {
+ memcpy(skb_put(skb, count), bcs->hw.hscx.rcvbuf, count);
+ skb_queue_tail(&bcs->rqueue, skb);
+ }
+ }
+ }
+ bcs->hw.hscx.rcvidx = 0;
+ schedule_event(bcs, B_RCVBUFREADY);
+ }
+ if (val & 0x40) { /* RPF */
+ jade_empty_fifo(bcs, fifo_size);
+ if (bcs->mode == L1_MODE_TRANS) {
+ /* receive audio data */
+ if (!(skb = dev_alloc_skb(fifo_size)))
+ printk(KERN_WARNING "HiSax: receive out of memory\n");
+ else {
+ memcpy(skb_put(skb, fifo_size), bcs->hw.hscx.rcvbuf, fifo_size);
+ skb_queue_tail(&bcs->rqueue, skb);
+ }
+ bcs->hw.hscx.rcvidx = 0;
+ schedule_event(bcs, B_RCVBUFREADY);
+ }
+ }
+ if (val & 0x10) { /* XPR */
+ if (bcs->tx_skb) {
+ if (bcs->tx_skb->len) {
+ jade_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.hscx.count;
+ spin_unlock_irqrestore(&bcs->aclock, flags);
+ schedule_event(bcs, B_ACKPENDING);
+ }
+ dev_kfree_skb_irq(bcs->tx_skb);
+ bcs->hw.hscx.count = 0;
+ bcs->tx_skb = NULL;
+ }
+ }
+ if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+ bcs->hw.hscx.count = 0;
+ test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+ jade_fill_fifo(bcs);
+ } else {
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ schedule_event(bcs, B_XMTBUFREADY);
+ }
+ }
+}
+
+static inline void
+jade_int_main(struct IsdnCardState *cs, u_char val, int jade)
+{
+ struct BCState *bcs;
+ bcs = cs->bcs + jade;
+
+ if (val & jadeISR_RFO) {
+ /* handled with RDO */
+ val &= ~jadeISR_RFO;
+ }
+ if (val & jadeISR_XDU) {
+ /* relevant in HDLC mode only */
+ /* don't reset XPR here */
+ if (bcs->mode == 1)
+ jade_fill_fifo(bcs);
+ else {
+ /* Here we lost an TX interrupt, so
+ * restart transmitting the whole frame.
+ */
+ if (bcs->tx_skb) {
+ skb_push(bcs->tx_skb, bcs->hw.hscx.count);
+ bcs->tx_cnt += bcs->hw.hscx.count;
+ bcs->hw.hscx.count = 0;
+ }
+ WriteJADECMDR(cs, bcs->hw.hscx.hscx, jade_HDLC_XCMD, jadeXCMD_XRES);
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "JADE %c EXIR %x Lost TX", 'A'+jade, val);
+ }
+ }
+ if (val & (jadeISR_RME|jadeISR_RPF|jadeISR_XPR)) {
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "JADE %c interrupt %x", 'A'+jade, val);
+ jade_interrupt(cs, val, jade);
+ }
+}
diff --git a/drivers/isdn/hisax/l3_1tr6.c b/drivers/isdn/hisax/l3_1tr6.c
new file mode 100644
index 0000000..c5c36ee
--- /dev/null
+++ b/drivers/isdn/hisax/l3_1tr6.c
@@ -0,0 +1,955 @@
+/* $Id: l3_1tr6.c,v 2.15.2.3 2004/01/13 14:31:25 keil Exp $
+ *
+ * German 1TR6 D-channel protocol
+ *
+ * Author Karsten Keil
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ */
+
+#include "hisax.h"
+#include "l3_1tr6.h"
+#include "isdnl3.h"
+#include <linux/ctype.h>
+
+extern char *HiSax_getrev(const char *revision);
+static const char *l3_1tr6_revision = "$Revision: 2.15.2.3 $";
+
+#define MsgHead(ptr, cref, mty, dis) \
+ *ptr++ = dis; \
+ *ptr++ = 0x1; \
+ *ptr++ = cref ^ 0x80; \
+ *ptr++ = mty
+
+static void
+l3_1TR6_message(struct l3_process *pc, u_char mt, u_char pd)
+{
+ struct sk_buff *skb;
+ u_char *p;
+
+ if (!(skb = l3_alloc_skb(4)))
+ return;
+ p = skb_put(skb, 4);
+ MsgHead(p, pc->callref, mt, pd);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3_1tr6_release_req(struct l3_process *pc, u_char pr, void *arg)
+{
+ StopAllL3Timer(pc);
+ newl3state(pc, 19);
+ l3_1TR6_message(pc, MT_N1_REL, PROTO_DIS_N1);
+ L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3_1tr6_invalid(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+
+ dev_kfree_skb(skb);
+ l3_1tr6_release_req(pc, 0, NULL);
+}
+
+static void
+l3_1tr6_error(struct l3_process *pc, u_char *msg, struct sk_buff *skb)
+{
+ dev_kfree_skb(skb);
+ if (pc->st->l3.debug & L3_DEB_WARN)
+ l3_debug(pc->st, msg);
+ l3_1tr6_release_req(pc, 0, NULL);
+}
+
+static void
+l3_1tr6_setup_req(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb;
+ u_char tmp[128];
+ u_char *p = tmp;
+ u_char *teln;
+ u_char *eaz;
+ u_char channel = 0;
+ int l;
+
+ MsgHead(p, pc->callref, MT_N1_SETUP, PROTO_DIS_N1);
+ teln = pc->para.setup.phone;
+ pc->para.spv = 0;
+ if (!isdigit(*teln)) {
+ switch (0x5f & *teln) {
+ case 'S':
+ pc->para.spv = 1;
+ break;
+ case 'C':
+ channel = 0x08;
+ case 'P':
+ channel |= 0x80;
+ teln++;
+ if (*teln == '1')
+ channel |= 0x01;
+ else
+ channel |= 0x02;
+ break;
+ default:
+ if (pc->st->l3.debug & L3_DEB_WARN)
+ l3_debug(pc->st, "Wrong MSN Code");
+ break;
+ }
+ teln++;
+ }
+ if (channel) {
+ *p++ = 0x18; /* channel indicator */
+ *p++ = 1;
+ *p++ = channel;
+ }
+ if (pc->para.spv) { /* SPV ? */
+ /* NSF SPV */
+ *p++ = WE0_netSpecFac;
+ *p++ = 4; /* Laenge */
+ *p++ = 0;
+ *p++ = FAC_SPV; /* SPV */
+ *p++ = pc->para.setup.si1; /* 0 for all Services */
+ *p++ = pc->para.setup.si2; /* 0 for all Services */
+ *p++ = WE0_netSpecFac;
+ *p++ = 4; /* Laenge */
+ *p++ = 0;
+ *p++ = FAC_Activate; /* aktiviere SPV (default) */
+ *p++ = pc->para.setup.si1; /* 0 for all Services */
+ *p++ = pc->para.setup.si2; /* 0 for all Services */
+ }
+ eaz = pc->para.setup.eazmsn;
+ if (*eaz) {
+ *p++ = WE0_origAddr;
+ *p++ = strlen(eaz) + 1;
+ /* Classify as AnyPref. */
+ *p++ = 0x81; /* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
+ while (*eaz)
+ *p++ = *eaz++ & 0x7f;
+ }
+ *p++ = WE0_destAddr;
+ *p++ = strlen(teln) + 1;
+ /* Classify as AnyPref. */
+ *p++ = 0x81; /* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
+ while (*teln)
+ *p++ = *teln++ & 0x7f;
+
+ *p++ = WE_Shift_F6;
+ /* Codesatz 6 fuer Service */
+ *p++ = WE6_serviceInd;
+ *p++ = 2; /* len=2 info,info2 */
+ *p++ = pc->para.setup.si1;
+ *p++ = pc->para.setup.si2;
+
+ l = p - tmp;
+ if (!(skb = l3_alloc_skb(l)))
+ return;
+ memcpy(skb_put(skb, l), tmp, l);
+ L3DelTimer(&pc->timer);
+ L3AddTimer(&pc->timer, T303, CC_T303);
+ newl3state(pc, 1);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3_1tr6_setup(struct l3_process *pc, u_char pr, void *arg)
+{
+ u_char *p;
+ int bcfound = 0;
+ char tmp[80];
+ struct sk_buff *skb = arg;
+
+ p = skb->data;
+
+ /* Channel Identification */
+ p = skb->data;
+ if ((p = findie(p, skb->len, WE0_chanID, 0))) {
+ if (p[1] != 1) {
+ l3_1tr6_error(pc, "setup wrong chanID len", skb);
+ return;
+ }
+ if ((p[2] & 0xf4) != 0x80) {
+ l3_1tr6_error(pc, "setup wrong WE0_chanID", skb);
+ return;
+ }
+ if ((pc->para.bchannel = p[2] & 0x3))
+ bcfound++;
+ } else {
+ l3_1tr6_error(pc, "missing setup chanID", skb);
+ return;
+ }
+
+ p = skb->data;
+ if ((p = findie(p, skb->len, WE6_serviceInd, 6))) {
+ pc->para.setup.si1 = p[2];
+ pc->para.setup.si2 = p[3];
+ } else {
+ l3_1tr6_error(pc, "missing setup SI", skb);
+ return;
+ }
+
+ p = skb->data;
+ if ((p = findie(p, skb->len, WE0_destAddr, 0)))
+ iecpy(pc->para.setup.eazmsn, p, 1);
+ else
+ pc->para.setup.eazmsn[0] = 0;
+
+ p = skb->data;
+ if ((p = findie(p, skb->len, WE0_origAddr, 0))) {
+ iecpy(pc->para.setup.phone, p, 1);
+ } else
+ pc->para.setup.phone[0] = 0;
+
+ p = skb->data;
+ pc->para.spv = 0;
+ if ((p = findie(p, skb->len, WE0_netSpecFac, 0))) {
+ if ((FAC_SPV == p[3]) || (FAC_Activate == p[3]))
+ pc->para.spv = 1;
+ }
+ dev_kfree_skb(skb);
+
+ /* Signal all services, linklevel takes care of Service-Indicator */
+ if (bcfound) {
+ if ((pc->para.setup.si1 != 7) && (pc->st->l3.debug & L3_DEB_WARN)) {
+ sprintf(tmp, "non-digital call: %s -> %s",
+ pc->para.setup.phone,
+ pc->para.setup.eazmsn);
+ l3_debug(pc->st, tmp);
+ }
+ newl3state(pc, 6);
+ pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc);
+ } else
+ release_l3_process(pc);
+}
+
+static void
+l3_1tr6_setup_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+ u_char *p;
+ struct sk_buff *skb = arg;
+
+ L3DelTimer(&pc->timer);
+ p = skb->data;
+ newl3state(pc, 2);
+ if ((p = findie(p, skb->len, WE0_chanID, 0))) {
+ if (p[1] != 1) {
+ l3_1tr6_error(pc, "setup_ack wrong chanID len", skb);
+ return;
+ }
+ if ((p[2] & 0xf4) != 0x80) {
+ l3_1tr6_error(pc, "setup_ack wrong WE0_chanID", skb);
+ return;
+ }
+ pc->para.bchannel = p[2] & 0x3;
+ } else {
+ l3_1tr6_error(pc, "missing setup_ack WE0_chanID", skb);
+ return;
+ }
+ dev_kfree_skb(skb);
+ L3AddTimer(&pc->timer, T304, CC_T304);
+ pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
+}
+
+static void
+l3_1tr6_call_sent(struct l3_process *pc, u_char pr, void *arg)
+{
+ u_char *p;
+ struct sk_buff *skb = arg;
+
+ L3DelTimer(&pc->timer);
+ p = skb->data;
+ if ((p = findie(p, skb->len, WE0_chanID, 0))) {
+ if (p[1] != 1) {
+ l3_1tr6_error(pc, "call sent wrong chanID len", skb);
+ return;
+ }
+ if ((p[2] & 0xf4) != 0x80) {
+ l3_1tr6_error(pc, "call sent wrong WE0_chanID", skb);
+ return;
+ }
+ if ((pc->state == 2) && (pc->para.bchannel != (p[2] & 0x3))) {
+ l3_1tr6_error(pc, "call sent wrong chanID value", skb);
+ return;
+ }
+ pc->para.bchannel = p[2] & 0x3;
+ } else {
+ l3_1tr6_error(pc, "missing call sent WE0_chanID", skb);
+ return;
+ }
+ dev_kfree_skb(skb);
+ L3AddTimer(&pc->timer, T310, CC_T310);
+ newl3state(pc, 3);
+ pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc);
+}
+
+static void
+l3_1tr6_alert(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+
+ dev_kfree_skb(skb);
+ L3DelTimer(&pc->timer); /* T304 */
+ newl3state(pc, 4);
+ pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc);
+}
+
+static void
+l3_1tr6_info(struct l3_process *pc, u_char pr, void *arg)
+{
+ u_char *p;
+ int i, tmpcharge = 0;
+ char a_charge[8], tmp[32];
+ struct sk_buff *skb = arg;
+
+ p = skb->data;
+ if ((p = findie(p, skb->len, WE6_chargingInfo, 6))) {
+ iecpy(a_charge, p, 1);
+ for (i = 0; i < strlen(a_charge); i++) {
+ tmpcharge *= 10;
+ tmpcharge += a_charge[i] & 0xf;
+ }
+ if (tmpcharge > pc->para.chargeinfo) {
+ pc->para.chargeinfo = tmpcharge;
+ pc->st->l3.l3l4(pc->st, CC_CHARGE | INDICATION, pc);
+ }
+ if (pc->st->l3.debug & L3_DEB_CHARGE) {
+ sprintf(tmp, "charging info %d", pc->para.chargeinfo);
+ l3_debug(pc->st, tmp);
+ }
+ } else if (pc->st->l3.debug & L3_DEB_CHARGE)
+ l3_debug(pc->st, "charging info not found");
+ dev_kfree_skb(skb);
+
+}
+
+static void
+l3_1tr6_info_s2(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+
+ dev_kfree_skb(skb);
+}
+
+static void
+l3_1tr6_connect(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+
+ L3DelTimer(&pc->timer); /* T310 */
+ if (!findie(skb->data, skb->len, WE6_date, 6)) {
+ l3_1tr6_error(pc, "missing connect date", skb);
+ return;
+ }
+ newl3state(pc, 10);
+ dev_kfree_skb(skb);
+ pc->para.chargeinfo = 0;
+ pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc);
+}
+
+static void
+l3_1tr6_rel(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ u_char *p;
+
+ p = skb->data;
+ if ((p = findie(p, skb->len, WE0_cause, 0))) {
+ if (p[1] > 0) {
+ pc->para.cause = p[2];
+ if (p[1] > 1)
+ pc->para.loc = p[3];
+ else
+ pc->para.loc = 0;
+ } else {
+ pc->para.cause = 0;
+ pc->para.loc = 0;
+ }
+ } else {
+ pc->para.cause = NO_CAUSE;
+ l3_1tr6_error(pc, "missing REL cause", skb);
+ return;
+ }
+ dev_kfree_skb(skb);
+ StopAllL3Timer(pc);
+ newl3state(pc, 0);
+ l3_1TR6_message(pc, MT_N1_REL_ACK, PROTO_DIS_N1);
+ pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+ release_l3_process(pc);
+}
+
+static void
+l3_1tr6_rel_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+
+ dev_kfree_skb(skb);
+ StopAllL3Timer(pc);
+ newl3state(pc, 0);
+ pc->para.cause = NO_CAUSE;
+ pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc);
+ release_l3_process(pc);
+}
+
+static void
+l3_1tr6_disc(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ u_char *p;
+ int i, tmpcharge = 0;
+ char a_charge[8], tmp[32];
+
+ StopAllL3Timer(pc);
+ p = skb->data;
+ if ((p = findie(p, skb->len, WE6_chargingInfo, 6))) {
+ iecpy(a_charge, p, 1);
+ for (i = 0; i < strlen(a_charge); i++) {
+ tmpcharge *= 10;
+ tmpcharge += a_charge[i] & 0xf;
+ }
+ if (tmpcharge > pc->para.chargeinfo) {
+ pc->para.chargeinfo = tmpcharge;
+ pc->st->l3.l3l4(pc->st, CC_CHARGE | INDICATION, pc);
+ }
+ if (pc->st->l3.debug & L3_DEB_CHARGE) {
+ sprintf(tmp, "charging info %d", pc->para.chargeinfo);
+ l3_debug(pc->st, tmp);
+ }
+ } else if (pc->st->l3.debug & L3_DEB_CHARGE)
+ l3_debug(pc->st, "charging info not found");
+
+
+ p = skb->data;
+ if ((p = findie(p, skb->len, WE0_cause, 0))) {
+ if (p[1] > 0) {
+ pc->para.cause = p[2];
+ if (p[1] > 1)
+ pc->para.loc = p[3];
+ else
+ pc->para.loc = 0;
+ } else {
+ pc->para.cause = 0;
+ pc->para.loc = 0;
+ }
+ } else {
+ if (pc->st->l3.debug & L3_DEB_WARN)
+ l3_debug(pc->st, "cause not found");
+ pc->para.cause = NO_CAUSE;
+ }
+ if (!findie(skb->data, skb->len, WE6_date, 6)) {
+ l3_1tr6_error(pc, "missing connack date", skb);
+ return;
+ }
+ dev_kfree_skb(skb);
+ newl3state(pc, 12);
+ pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc);
+}
+
+
+static void
+l3_1tr6_connect_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+
+ if (!findie(skb->data, skb->len, WE6_date, 6)) {
+ l3_1tr6_error(pc, "missing connack date", skb);
+ return;
+ }
+ dev_kfree_skb(skb);
+ newl3state(pc, 10);
+ pc->para.chargeinfo = 0;
+ L3DelTimer(&pc->timer);
+ pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc);
+}
+
+static void
+l3_1tr6_alert_req(struct l3_process *pc, u_char pr, void *arg)
+{
+ newl3state(pc, 7);
+ l3_1TR6_message(pc, MT_N1_ALERT, PROTO_DIS_N1);
+}
+
+static void
+l3_1tr6_setup_rsp(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb;
+ u_char tmp[24];
+ u_char *p = tmp;
+ int l;
+
+ MsgHead(p, pc->callref, MT_N1_CONN, PROTO_DIS_N1);
+ if (pc->para.spv) { /* SPV ? */
+ /* NSF SPV */
+ *p++ = WE0_netSpecFac;
+ *p++ = 4; /* Laenge */
+ *p++ = 0;
+ *p++ = FAC_SPV; /* SPV */
+ *p++ = pc->para.setup.si1;
+ *p++ = pc->para.setup.si2;
+ *p++ = WE0_netSpecFac;
+ *p++ = 4; /* Laenge */
+ *p++ = 0;
+ *p++ = FAC_Activate; /* aktiviere SPV */
+ *p++ = pc->para.setup.si1;
+ *p++ = pc->para.setup.si2;
+ }
+ newl3state(pc, 8);
+ l = p - tmp;
+ if (!(skb = l3_alloc_skb(l)))
+ return;
+ memcpy(skb_put(skb, l), tmp, l);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+ L3DelTimer(&pc->timer);
+ L3AddTimer(&pc->timer, T313, CC_T313);
+}
+
+static void
+l3_1tr6_reset(struct l3_process *pc, u_char pr, void *arg)
+{
+ release_l3_process(pc);
+}
+
+static void
+l3_1tr6_disconnect_req(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb;
+ u_char tmp[16];
+ u_char *p = tmp;
+ int l;
+ u_char cause = 0x10;
+ u_char clen = 1;
+
+ if (pc->para.cause > 0)
+ cause = pc->para.cause;
+ /* Map DSS1 causes */
+ switch (cause & 0x7f) {
+ case 0x10:
+ clen = 0;
+ break;
+ case 0x11:
+ cause = CAUSE_UserBusy;
+ break;
+ case 0x15:
+ cause = CAUSE_CallRejected;
+ break;
+ }
+ StopAllL3Timer(pc);
+ MsgHead(p, pc->callref, MT_N1_DISC, PROTO_DIS_N1);
+ *p++ = WE0_cause;
+ *p++ = clen; /* Laenge */
+ if (clen)
+ *p++ = cause | 0x80;
+ newl3state(pc, 11);
+ l = p - tmp;
+ if (!(skb = l3_alloc_skb(l)))
+ return;
+ memcpy(skb_put(skb, l), tmp, l);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+ L3AddTimer(&pc->timer, T305, CC_T305);
+}
+
+static void
+l3_1tr6_t303(struct l3_process *pc, u_char pr, void *arg)
+{
+ if (pc->N303 > 0) {
+ pc->N303--;
+ L3DelTimer(&pc->timer);
+ l3_1tr6_setup_req(pc, pr, arg);
+ } else {
+ L3DelTimer(&pc->timer);
+ pc->para.cause = 0;
+ l3_1tr6_disconnect_req(pc, 0, NULL);
+ }
+}
+
+static void
+l3_1tr6_t304(struct l3_process *pc, u_char pr, void *arg)
+{
+ L3DelTimer(&pc->timer);
+ pc->para.cause = 0xE6;
+ l3_1tr6_disconnect_req(pc, pr, NULL);
+ pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+}
+
+static void
+l3_1tr6_t305(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb;
+ u_char tmp[16];
+ u_char *p = tmp;
+ int l;
+ u_char cause = 0x90;
+ u_char clen = 1;
+
+ L3DelTimer(&pc->timer);
+ if (pc->para.cause != NO_CAUSE)
+ cause = pc->para.cause;
+ /* Map DSS1 causes */
+ switch (cause & 0x7f) {
+ case 0x10:
+ clen = 0;
+ break;
+ case 0x15:
+ cause = CAUSE_CallRejected;
+ break;
+ }
+ MsgHead(p, pc->callref, MT_N1_REL, PROTO_DIS_N1);
+ *p++ = WE0_cause;
+ *p++ = clen; /* Laenge */
+ if (clen)
+ *p++ = cause;
+ newl3state(pc, 19);
+ l = p - tmp;
+ if (!(skb = l3_alloc_skb(l)))
+ return;
+ memcpy(skb_put(skb, l), tmp, l);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+ L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3_1tr6_t310(struct l3_process *pc, u_char pr, void *arg)
+{
+ L3DelTimer(&pc->timer);
+ pc->para.cause = 0xE6;
+ l3_1tr6_disconnect_req(pc, pr, NULL);
+ pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+}
+
+static void
+l3_1tr6_t313(struct l3_process *pc, u_char pr, void *arg)
+{
+ L3DelTimer(&pc->timer);
+ pc->para.cause = 0xE6;
+ l3_1tr6_disconnect_req(pc, pr, NULL);
+ pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc);
+}
+
+static void
+l3_1tr6_t308_1(struct l3_process *pc, u_char pr, void *arg)
+{
+ L3DelTimer(&pc->timer);
+ l3_1TR6_message(pc, MT_N1_REL, PROTO_DIS_N1);
+ L3AddTimer(&pc->timer, T308, CC_T308_2);
+ newl3state(pc, 19);
+}
+
+static void
+l3_1tr6_t308_2(struct l3_process *pc, u_char pr, void *arg)
+{
+ L3DelTimer(&pc->timer);
+ pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc);
+ release_l3_process(pc);
+}
+
+static void
+l3_1tr6_dl_reset(struct l3_process *pc, u_char pr, void *arg)
+{
+ pc->para.cause = CAUSE_LocalProcErr;
+ l3_1tr6_disconnect_req(pc, pr, NULL);
+ pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+}
+
+static void
+l3_1tr6_dl_release(struct l3_process *pc, u_char pr, void *arg)
+{
+ newl3state(pc, 0);
+ pc->para.cause = 0x1b; /* Destination out of order */
+ pc->para.loc = 0;
+ pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+ release_l3_process(pc);
+}
+
+/* *INDENT-OFF* */
+static struct stateentry downstl[] =
+{
+ {SBIT(0),
+ CC_SETUP | REQUEST, l3_1tr6_setup_req},
+ {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(6) | SBIT(7) | SBIT(8) |
+ SBIT(10),
+ CC_DISCONNECT | REQUEST, l3_1tr6_disconnect_req},
+ {SBIT(12),
+ CC_RELEASE | REQUEST, l3_1tr6_release_req},
+ {SBIT(6),
+ CC_IGNORE | REQUEST, l3_1tr6_reset},
+ {SBIT(6),
+ CC_REJECT | REQUEST, l3_1tr6_disconnect_req},
+ {SBIT(6),
+ CC_ALERTING | REQUEST, l3_1tr6_alert_req},
+ {SBIT(6) | SBIT(7),
+ CC_SETUP | RESPONSE, l3_1tr6_setup_rsp},
+ {SBIT(1),
+ CC_T303, l3_1tr6_t303},
+ {SBIT(2),
+ CC_T304, l3_1tr6_t304},
+ {SBIT(3),
+ CC_T310, l3_1tr6_t310},
+ {SBIT(8),
+ CC_T313, l3_1tr6_t313},
+ {SBIT(11),
+ CC_T305, l3_1tr6_t305},
+ {SBIT(19),
+ CC_T308_1, l3_1tr6_t308_1},
+ {SBIT(19),
+ CC_T308_2, l3_1tr6_t308_2},
+};
+
+#define DOWNSTL_LEN \
+ (sizeof(downstl) / sizeof(struct stateentry))
+
+static struct stateentry datastln1[] =
+{
+ {SBIT(0),
+ MT_N1_INVALID, l3_1tr6_invalid},
+ {SBIT(0),
+ MT_N1_SETUP, l3_1tr6_setup},
+ {SBIT(1),
+ MT_N1_SETUP_ACK, l3_1tr6_setup_ack},
+ {SBIT(1) | SBIT(2),
+ MT_N1_CALL_SENT, l3_1tr6_call_sent},
+ {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) | SBIT(10),
+ MT_N1_DISC, l3_1tr6_disc},
+ {SBIT(2) | SBIT(3) | SBIT(4),
+ MT_N1_ALERT, l3_1tr6_alert},
+ {SBIT(2) | SBIT(3) | SBIT(4),
+ MT_N1_CONN, l3_1tr6_connect},
+ {SBIT(2),
+ MT_N1_INFO, l3_1tr6_info_s2},
+ {SBIT(8),
+ MT_N1_CONN_ACK, l3_1tr6_connect_ack},
+ {SBIT(10),
+ MT_N1_INFO, l3_1tr6_info},
+ {SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) |
+ SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17),
+ MT_N1_REL, l3_1tr6_rel},
+ {SBIT(19),
+ MT_N1_REL, l3_1tr6_rel_ack},
+ {SBIT(0) | SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4) | SBIT(7) | SBIT(8) |
+ SBIT(10) | SBIT(11) | SBIT(12) | SBIT(15) | SBIT(17),
+ MT_N1_REL_ACK, l3_1tr6_invalid},
+ {SBIT(19),
+ MT_N1_REL_ACK, l3_1tr6_rel_ack}
+};
+
+#define DATASTLN1_LEN \
+ (sizeof(datastln1) / sizeof(struct stateentry))
+
+static struct stateentry manstatelist[] =
+{
+ {SBIT(2),
+ DL_ESTABLISH | INDICATION, l3_1tr6_dl_reset},
+ {ALL_STATES,
+ DL_RELEASE | INDICATION, l3_1tr6_dl_release},
+};
+
+#define MANSLLEN \
+ (sizeof(manstatelist) / sizeof(struct stateentry))
+/* *INDENT-ON* */
+
+static void
+up1tr6(struct PStack *st, int pr, void *arg)
+{
+ int i, mt, cr;
+ struct l3_process *proc;
+ struct sk_buff *skb = arg;
+ char tmp[80];
+
+ switch (pr) {
+ case (DL_DATA | INDICATION):
+ case (DL_UNIT_DATA | INDICATION):
+ break;
+ case (DL_ESTABLISH | CONFIRM):
+ case (DL_ESTABLISH | INDICATION):
+ case (DL_RELEASE | INDICATION):
+ case (DL_RELEASE | CONFIRM):
+ l3_msg(st, pr, arg);
+ return;
+ break;
+ }
+ if (skb->len < 4) {
+ if (st->l3.debug & L3_DEB_PROTERR) {
+ sprintf(tmp, "up1tr6 len only %d", skb->len);
+ l3_debug(st, tmp);
+ }
+ dev_kfree_skb(skb);
+ return;
+ }
+ if ((skb->data[0] & 0xfe) != PROTO_DIS_N0) {
+ if (st->l3.debug & L3_DEB_PROTERR) {
+ sprintf(tmp, "up1tr6%sunexpected discriminator %x message len %d",
+ (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+ skb->data[0], skb->len);
+ l3_debug(st, tmp);
+ }
+ dev_kfree_skb(skb);
+ return;
+ }
+ if (skb->data[1] != 1) {
+ if (st->l3.debug & L3_DEB_PROTERR) {
+ sprintf(tmp, "up1tr6 CR len not 1");
+ l3_debug(st, tmp);
+ }
+ dev_kfree_skb(skb);
+ return;
+ }
+ cr = skb->data[2];
+ mt = skb->data[3];
+ if (skb->data[0] == PROTO_DIS_N0) {
+ dev_kfree_skb(skb);
+ if (st->l3.debug & L3_DEB_STATE) {
+ sprintf(tmp, "up1tr6%s N0 mt %x unhandled",
+ (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ", mt);
+ l3_debug(st, tmp);
+ }
+ } else if (skb->data[0] == PROTO_DIS_N1) {
+ if (!(proc = getl3proc(st, cr))) {
+ if (mt == MT_N1_SETUP) {
+ if (cr < 128) {
+ if (!(proc = new_l3_process(st, cr))) {
+ if (st->l3.debug & L3_DEB_PROTERR) {
+ sprintf(tmp, "up1tr6 no roc mem");
+ l3_debug(st, tmp);
+ }
+ dev_kfree_skb(skb);
+ return;
+ }
+ } else {
+ dev_kfree_skb(skb);
+ return;
+ }
+ } else if ((mt == MT_N1_REL) || (mt == MT_N1_REL_ACK) ||
+ (mt == MT_N1_CANC_ACK) || (mt == MT_N1_CANC_REJ) ||
+ (mt == MT_N1_REG_ACK) || (mt == MT_N1_REG_REJ) ||
+ (mt == MT_N1_SUSP_ACK) || (mt == MT_N1_RES_REJ) ||
+ (mt == MT_N1_INFO)) {
+ dev_kfree_skb(skb);
+ return;
+ } else {
+ if (!(proc = new_l3_process(st, cr))) {
+ if (st->l3.debug & L3_DEB_PROTERR) {
+ sprintf(tmp, "up1tr6 no roc mem");
+ l3_debug(st, tmp);
+ }
+ dev_kfree_skb(skb);
+ return;
+ }
+ mt = MT_N1_INVALID;
+ }
+ }
+ for (i = 0; i < DATASTLN1_LEN; i++)
+ if ((mt == datastln1[i].primitive) &&
+ ((1 << proc->state) & datastln1[i].state))
+ break;
+ if (i == DATASTLN1_LEN) {
+ dev_kfree_skb(skb);
+ if (st->l3.debug & L3_DEB_STATE) {
+ sprintf(tmp, "up1tr6%sstate %d mt %x unhandled",
+ (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+ proc->state, mt);
+ l3_debug(st, tmp);
+ }
+ return;
+ } else {
+ if (st->l3.debug & L3_DEB_STATE) {
+ sprintf(tmp, "up1tr6%sstate %d mt %x",
+ (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+ proc->state, mt);
+ l3_debug(st, tmp);
+ }
+ datastln1[i].rout(proc, pr, skb);
+ }
+ }
+}
+
+static void
+down1tr6(struct PStack *st, int pr, void *arg)
+{
+ int i, cr;
+ struct l3_process *proc;
+ struct Channel *chan;
+ char tmp[80];
+
+ if ((DL_ESTABLISH | REQUEST)== pr) {
+ l3_msg(st, pr, NULL);
+ return;
+ } else if ((CC_SETUP | REQUEST) == pr) {
+ chan = arg;
+ cr = newcallref();
+ cr |= 0x80;
+ if (!(proc = new_l3_process(st, cr))) {
+ return;
+ } else {
+ proc->chan = chan;
+ chan->proc = proc;
+ memcpy(&proc->para.setup, &chan->setup, sizeof(setup_parm));
+ proc->callref = cr;
+ }
+ } else {
+ proc = arg;
+ }
+
+ for (i = 0; i < DOWNSTL_LEN; i++)
+ if ((pr == downstl[i].primitive) &&
+ ((1 << proc->state) & downstl[i].state))
+ break;
+ if (i == DOWNSTL_LEN) {
+ if (st->l3.debug & L3_DEB_STATE) {
+ sprintf(tmp, "down1tr6 state %d prim %d unhandled",
+ proc->state, pr);
+ l3_debug(st, tmp);
+ }
+ } else {
+ if (st->l3.debug & L3_DEB_STATE) {
+ sprintf(tmp, "down1tr6 state %d prim %d",
+ proc->state, pr);
+ l3_debug(st, tmp);
+ }
+ downstl[i].rout(proc, pr, arg);
+ }
+}
+
+static void
+man1tr6(struct PStack *st, int pr, void *arg)
+{
+ int i;
+ struct l3_process *proc = arg;
+
+ if (!proc) {
+ printk(KERN_ERR "HiSax man1tr6 without proc pr=%04x\n", pr);
+ return;
+ }
+ for (i = 0; i < MANSLLEN; i++)
+ if ((pr == manstatelist[i].primitive) &&
+ ((1 << proc->state) & manstatelist[i].state))
+ break;
+ if (i == MANSLLEN) {
+ if (st->l3.debug & L3_DEB_STATE) {
+ l3_debug(st, "cr %d man1tr6 state %d prim %d unhandled",
+ proc->callref & 0x7f, proc->state, pr);
+ }
+ } else {
+ if (st->l3.debug & L3_DEB_STATE) {
+ l3_debug(st, "cr %d man1tr6 state %d prim %d",
+ proc->callref & 0x7f, proc->state, pr);
+ }
+ manstatelist[i].rout(proc, pr, arg);
+ }
+}
+
+void
+setstack_1tr6(struct PStack *st)
+{
+ char tmp[64];
+
+ st->lli.l4l3 = down1tr6;
+ st->l2.l2l3 = up1tr6;
+ st->l3.l3ml3 = man1tr6;
+ st->l3.N303 = 0;
+
+ strcpy(tmp, l3_1tr6_revision);
+ printk(KERN_INFO "HiSax: 1TR6 Rev. %s\n", HiSax_getrev(tmp));
+}
diff --git a/drivers/isdn/hisax/l3_1tr6.h b/drivers/isdn/hisax/l3_1tr6.h
new file mode 100644
index 0000000..43215c0
--- /dev/null
+++ b/drivers/isdn/hisax/l3_1tr6.h
@@ -0,0 +1,164 @@
+/* $Id: l3_1tr6.h,v 2.2.6.2 2001/09/23 22:24:49 kai Exp $
+ *
+ * German 1TR6 D-channel protocol defines
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef l3_1tr6
+#define l3_1tr6
+
+#define PROTO_DIS_N0 0x40
+#define PROTO_DIS_N1 0x41
+
+/*
+ * MsgType N0
+ */
+#define MT_N0_REG_IND 0x61
+#define MT_N0_CANC_IND 0x62
+#define MT_N0_FAC_STA 0x63
+#define MT_N0_STA_ACK 0x64
+#define MT_N0_STA_REJ 0x65
+#define MT_N0_FAC_INF 0x66
+#define MT_N0_INF_ACK 0x67
+#define MT_N0_INF_REJ 0x68
+#define MT_N0_CLOSE 0x75
+#define MT_N0_CLO_ACK 0x77
+
+/*
+ * MsgType N1
+ */
+
+#define MT_N1_ESC 0x00
+#define MT_N1_ALERT 0x01
+#define MT_N1_CALL_SENT 0x02
+#define MT_N1_CONN 0x07
+#define MT_N1_CONN_ACK 0x0F
+#define MT_N1_SETUP 0x05
+#define MT_N1_SETUP_ACK 0x0D
+#define MT_N1_RES 0x26
+#define MT_N1_RES_ACK 0x2E
+#define MT_N1_RES_REJ 0x22
+#define MT_N1_SUSP 0x25
+#define MT_N1_SUSP_ACK 0x2D
+#define MT_N1_SUSP_REJ 0x21
+#define MT_N1_USER_INFO 0x20
+#define MT_N1_DET 0x40
+#define MT_N1_DISC 0x45
+#define MT_N1_REL 0x4D
+#define MT_N1_REL_ACK 0x5A
+#define MT_N1_CANC_ACK 0x6E
+#define MT_N1_CANC_REJ 0x67
+#define MT_N1_CON_CON 0x69
+#define MT_N1_FAC 0x60
+#define MT_N1_FAC_ACK 0x68
+#define MT_N1_FAC_CAN 0x66
+#define MT_N1_FAC_REG 0x64
+#define MT_N1_FAC_REJ 0x65
+#define MT_N1_INFO 0x6D
+#define MT_N1_REG_ACK 0x6C
+#define MT_N1_REG_REJ 0x6F
+#define MT_N1_STAT 0x63
+#define MT_N1_INVALID 0
+
+/*
+ * W Elemente
+ */
+
+#define WE_Shift_F0 0x90
+#define WE_Shift_F6 0x96
+#define WE_Shift_OF0 0x98
+#define WE_Shift_OF6 0x9E
+
+#define WE0_cause 0x08
+#define WE0_connAddr 0x0C
+#define WE0_callID 0x10
+#define WE0_chanID 0x18
+#define WE0_netSpecFac 0x20
+#define WE0_display 0x28
+#define WE0_keypad 0x2C
+#define WE0_origAddr 0x6C
+#define WE0_destAddr 0x70
+#define WE0_userInfo 0x7E
+
+#define WE0_moreData 0xA0
+#define WE0_congestLevel 0xB0
+
+#define WE6_serviceInd 0x01
+#define WE6_chargingInfo 0x02
+#define WE6_date 0x03
+#define WE6_facSelect 0x05
+#define WE6_facStatus 0x06
+#define WE6_statusCalled 0x07
+#define WE6_addTransAttr 0x08
+
+/*
+ * FacCodes
+ */
+#define FAC_Sperre 0x01
+#define FAC_Sperre_All 0x02
+#define FAC_Sperre_Fern 0x03
+#define FAC_Sperre_Intl 0x04
+#define FAC_Sperre_Interk 0x05
+
+#define FAC_Forward1 0x02
+#define FAC_Forward2 0x03
+#define FAC_Konferenz 0x06
+#define FAC_GrabBchan 0x0F
+#define FAC_Reactivate 0x10
+#define FAC_Konferenz3 0x11
+#define FAC_Dienstwechsel1 0x12
+#define FAC_Dienstwechsel2 0x13
+#define FAC_NummernIdent 0x14
+#define FAC_GBG 0x15
+#define FAC_DisplayUebergeben 0x17
+#define FAC_DisplayUmgeleitet 0x1A
+#define FAC_Unterdruecke 0x1B
+#define FAC_Deactivate 0x1E
+#define FAC_Activate 0x1D
+#define FAC_SPV 0x1F
+#define FAC_Rueckwechsel 0x23
+#define FAC_Umleitung 0x24
+
+/*
+ * Cause codes
+ */
+#define CAUSE_InvCRef 0x01
+#define CAUSE_BearerNotImpl 0x03
+#define CAUSE_CIDunknown 0x07
+#define CAUSE_CIDinUse 0x08
+#define CAUSE_NoChans 0x0A
+#define CAUSE_FacNotImpl 0x10
+#define CAUSE_FacNotSubscr 0x11
+#define CAUSE_OutgoingBarred 0x20
+#define CAUSE_UserAccessBusy 0x21
+#define CAUSE_NegativeGBG 0x22
+#define CAUSE_UnknownGBG 0x23
+#define CAUSE_NoSPVknown 0x25
+#define CAUSE_DestNotObtain 0x35
+#define CAUSE_NumberChanged 0x38
+#define CAUSE_OutOfOrder 0x39
+#define CAUSE_NoUserResponse 0x3A
+#define CAUSE_UserBusy 0x3B
+#define CAUSE_IncomingBarred 0x3D
+#define CAUSE_CallRejected 0x3E
+#define CAUSE_NetworkCongestion 0x59
+#define CAUSE_RemoteUser 0x5A
+#define CAUSE_LocalProcErr 0x70
+#define CAUSE_RemoteProcErr 0x71
+#define CAUSE_RemoteUserSuspend 0x72
+#define CAUSE_RemoteUserResumed 0x73
+#define CAUSE_UserInfoDiscarded 0x7F
+
+#define T303 4000
+#define T304 20000
+#define T305 4000
+#define T308 4000
+#define T310 120000
+#define T313 4000
+#define T318 4000
+#define T319 4000
+
+#endif
diff --git a/drivers/isdn/hisax/l3dss1.c b/drivers/isdn/hisax/l3dss1.c
new file mode 100644
index 0000000..99feae8
--- /dev/null
+++ b/drivers/isdn/hisax/l3dss1.c
@@ -0,0 +1,3237 @@
+/* $Id: l3dss1.c,v 2.32.2.3 2004/01/13 14:31:25 keil Exp $
+ *
+ * EURO/DSS1 D-channel protocol
+ *
+ * German 1TR6 D-channel protocol
+ *
+ * Author Karsten Keil
+ * based on the teles driver from Jan den Ouden
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ * Thanks to Jan den Ouden
+ * Fritz Elfert
+ *
+ */
+
+#include "hisax.h"
+#include "isdnl3.h"
+#include "l3dss1.h"
+#include <linux/ctype.h>
+
+extern char *HiSax_getrev(const char *revision);
+static const char *dss1_revision = "$Revision: 2.32.2.3 $";
+
+#define EXT_BEARER_CAPS 1
+
+#define MsgHead(ptr, cref, mty) \
+ *ptr++ = 0x8; \
+ if (cref == -1) { \
+ *ptr++ = 0x0; \
+ } else { \
+ *ptr++ = 0x1; \
+ *ptr++ = cref^0x80; \
+ } \
+ *ptr++ = mty
+
+
+/**********************************************/
+/* get a new invoke id for remote operations. */
+/* Only a return value != 0 is valid */
+/**********************************************/
+static unsigned char new_invoke_id(struct PStack *p)
+{
+ unsigned char retval;
+ int i;
+
+ i = 32; /* maximum search depth */
+
+ retval = p->prot.dss1.last_invoke_id + 1; /* try new id */
+ while ((i) && (p->prot.dss1.invoke_used[retval >> 3] == 0xFF)) {
+ p->prot.dss1.last_invoke_id = (retval & 0xF8) + 8;
+ i--;
+ }
+ if (i) {
+ while (p->prot.dss1.invoke_used[retval >> 3] & (1 << (retval & 7)))
+ retval++;
+ } else
+ retval = 0;
+ p->prot.dss1.last_invoke_id = retval;
+ p->prot.dss1.invoke_used[retval >> 3] |= (1 << (retval & 7));
+ return(retval);
+} /* new_invoke_id */
+
+/*************************/
+/* free a used invoke id */
+/*************************/
+static void free_invoke_id(struct PStack *p, unsigned char id)
+{
+
+ if (!id) return; /* 0 = invalid value */
+
+ p->prot.dss1.invoke_used[id >> 3] &= ~(1 << (id & 7));
+} /* free_invoke_id */
+
+
+/**********************************************************/
+/* create a new l3 process and fill in dss1 specific data */
+/**********************************************************/
+static struct l3_process
+*dss1_new_l3_process(struct PStack *st, int cr)
+{ struct l3_process *proc;
+
+ if (!(proc = new_l3_process(st, cr)))
+ return(NULL);
+
+ proc->prot.dss1.invoke_id = 0;
+ proc->prot.dss1.remote_operation = 0;
+ proc->prot.dss1.uus1_data[0] = '\0';
+
+ return(proc);
+} /* dss1_new_l3_process */
+
+/************************************************/
+/* free a l3 process and all dss1 specific data */
+/************************************************/
+static void
+dss1_release_l3_process(struct l3_process *p)
+{
+ free_invoke_id(p->st,p->prot.dss1.invoke_id);
+ release_l3_process(p);
+} /* dss1_release_l3_process */
+
+/********************************************************/
+/* search a process with invoke id id and dummy callref */
+/********************************************************/
+static struct l3_process *
+l3dss1_search_dummy_proc(struct PStack *st, int id)
+{ struct l3_process *pc = st->l3.proc; /* start of processes */
+
+ if (!id) return(NULL);
+
+ while (pc)
+ { if ((pc->callref == -1) && (pc->prot.dss1.invoke_id == id))
+ return(pc);
+ pc = pc->next;
+ }
+ return(NULL);
+} /* l3dss1_search_dummy_proc */
+
+/*******************************************************************/
+/* called when a facility message with a dummy callref is received */
+/* and a return result is delivered. id specifies the invoke id. */
+/*******************************************************************/
+static void
+l3dss1_dummy_return_result(struct PStack *st, int id, u_char *p, u_char nlen)
+{ isdn_ctrl ic;
+ struct IsdnCardState *cs;
+ struct l3_process *pc = NULL;
+
+ if ((pc = l3dss1_search_dummy_proc(st, id)))
+ { L3DelTimer(&pc->timer); /* remove timer */
+
+ cs = pc->st->l1.hardware;
+ ic.driver = cs->myid;
+ ic.command = ISDN_STAT_PROT;
+ ic.arg = DSS1_STAT_INVOKE_RES;
+ ic.parm.dss1_io.hl_id = pc->prot.dss1.invoke_id;
+ ic.parm.dss1_io.ll_id = pc->prot.dss1.ll_id;
+ ic.parm.dss1_io.proc = pc->prot.dss1.proc;
+ ic.parm.dss1_io.timeout= 0;
+ ic.parm.dss1_io.datalen = nlen;
+ ic.parm.dss1_io.data = p;
+ free_invoke_id(pc->st, pc->prot.dss1.invoke_id);
+ pc->prot.dss1.invoke_id = 0; /* reset id */
+
+ cs->iif.statcallb(&ic);
+ dss1_release_l3_process(pc);
+ }
+ else
+ l3_debug(st, "dummy return result id=0x%x result len=%d",id,nlen);
+} /* l3dss1_dummy_return_result */
+
+/*******************************************************************/
+/* called when a facility message with a dummy callref is received */
+/* and a return error is delivered. id specifies the invoke id. */
+/*******************************************************************/
+static void
+l3dss1_dummy_error_return(struct PStack *st, int id, ulong error)
+{ isdn_ctrl ic;
+ struct IsdnCardState *cs;
+ struct l3_process *pc = NULL;
+
+ if ((pc = l3dss1_search_dummy_proc(st, id)))
+ { L3DelTimer(&pc->timer); /* remove timer */
+
+ cs = pc->st->l1.hardware;
+ ic.driver = cs->myid;
+ ic.command = ISDN_STAT_PROT;
+ ic.arg = DSS1_STAT_INVOKE_ERR;
+ ic.parm.dss1_io.hl_id = pc->prot.dss1.invoke_id;
+ ic.parm.dss1_io.ll_id = pc->prot.dss1.ll_id;
+ ic.parm.dss1_io.proc = pc->prot.dss1.proc;
+ ic.parm.dss1_io.timeout= error;
+ ic.parm.dss1_io.datalen = 0;
+ ic.parm.dss1_io.data = NULL;
+ free_invoke_id(pc->st, pc->prot.dss1.invoke_id);
+ pc->prot.dss1.invoke_id = 0; /* reset id */
+
+ cs->iif.statcallb(&ic);
+ dss1_release_l3_process(pc);
+ }
+ else
+ l3_debug(st, "dummy return error id=0x%x error=0x%lx",id,error);
+} /* l3dss1_error_return */
+
+/*******************************************************************/
+/* called when a facility message with a dummy callref is received */
+/* and a invoke is delivered. id specifies the invoke id. */
+/*******************************************************************/
+static void
+l3dss1_dummy_invoke(struct PStack *st, int cr, int id,
+ int ident, u_char *p, u_char nlen)
+{ isdn_ctrl ic;
+ struct IsdnCardState *cs;
+
+ l3_debug(st, "dummy invoke %s id=0x%x ident=0x%x datalen=%d",
+ (cr == -1) ? "local" : "broadcast",id,ident,nlen);
+ if (cr >= -1) return; /* ignore local data */
+
+ cs = st->l1.hardware;
+ ic.driver = cs->myid;
+ ic.command = ISDN_STAT_PROT;
+ ic.arg = DSS1_STAT_INVOKE_BRD;
+ ic.parm.dss1_io.hl_id = id;
+ ic.parm.dss1_io.ll_id = 0;
+ ic.parm.dss1_io.proc = ident;
+ ic.parm.dss1_io.timeout= 0;
+ ic.parm.dss1_io.datalen = nlen;
+ ic.parm.dss1_io.data = p;
+
+ cs->iif.statcallb(&ic);
+} /* l3dss1_dummy_invoke */
+
+static void
+l3dss1_parse_facility(struct PStack *st, struct l3_process *pc,
+ int cr, u_char * p)
+{
+ int qd_len = 0;
+ unsigned char nlen = 0, ilen, cp_tag;
+ int ident, id;
+ ulong err_ret;
+
+ if (pc)
+ st = pc->st; /* valid Stack */
+ else
+ if ((!st) || (cr >= 0)) return; /* neither pc nor st specified */
+
+ p++;
+ qd_len = *p++;
+ if (qd_len == 0) {
+ l3_debug(st, "qd_len == 0");
+ return;
+ }
+ if ((*p & 0x1F) != 0x11) { /* Service discriminator, supplementary service */
+ l3_debug(st, "supplementary service != 0x11");
+ return;
+ }
+ while (qd_len > 0 && !(*p & 0x80)) { /* extension ? */
+ p++;
+ qd_len--;
+ }
+ if (qd_len < 2) {
+ l3_debug(st, "qd_len < 2");
+ return;
+ }
+ p++;
+ qd_len--;
+ if ((*p & 0xE0) != 0xA0) { /* class and form */
+ l3_debug(st, "class and form != 0xA0");
+ return;
+ }
+
+ cp_tag = *p & 0x1F; /* remember tag value */
+
+ p++;
+ qd_len--;
+ if (qd_len < 1)
+ { l3_debug(st, "qd_len < 1");
+ return;
+ }
+ if (*p & 0x80)
+ { /* length format indefinite or limited */
+ nlen = *p++ & 0x7F; /* number of len bytes or indefinite */
+ if ((qd_len-- < ((!nlen) ? 3 : (1 + nlen))) ||
+ (nlen > 1))
+ { l3_debug(st, "length format error or not implemented");
+ return;
+ }
+ if (nlen == 1)
+ { nlen = *p++; /* complete length */
+ qd_len--;
+ }
+ else
+ { qd_len -= 2; /* trailing null bytes */
+ if ((*(p+qd_len)) || (*(p+qd_len+1)))
+ { l3_debug(st,"length format indefinite error");
+ return;
+ }
+ nlen = qd_len;
+ }
+ }
+ else
+ { nlen = *p++;
+ qd_len--;
+ }
+ if (qd_len < nlen)
+ { l3_debug(st, "qd_len < nlen");
+ return;
+ }
+ qd_len -= nlen;
+
+ if (nlen < 2)
+ { l3_debug(st, "nlen < 2");
+ return;
+ }
+ if (*p != 0x02)
+ { /* invoke identifier tag */
+ l3_debug(st, "invoke identifier tag !=0x02");
+ return;
+ }
+ p++;
+ nlen--;
+ if (*p & 0x80)
+ { /* length format */
+ l3_debug(st, "invoke id length format 2");
+ return;
+ }
+ ilen = *p++;
+ nlen--;
+ if (ilen > nlen || ilen == 0)
+ { l3_debug(st, "ilen > nlen || ilen == 0");
+ return;
+ }
+ nlen -= ilen;
+ id = 0;
+ while (ilen > 0)
+ { id = (id << 8) | (*p++ & 0xFF); /* invoke identifier */
+ ilen--;
+ }
+
+ switch (cp_tag) { /* component tag */
+ case 1: /* invoke */
+ if (nlen < 2) {
+ l3_debug(st, "nlen < 2 22");
+ return;
+ }
+ if (*p != 0x02) { /* operation value */
+ l3_debug(st, "operation value !=0x02");
+ return;
+ }
+ p++;
+ nlen--;
+ ilen = *p++;
+ nlen--;
+ if (ilen > nlen || ilen == 0) {
+ l3_debug(st, "ilen > nlen || ilen == 0 22");
+ return;
+ }
+ nlen -= ilen;
+ ident = 0;
+ while (ilen > 0) {
+ ident = (ident << 8) | (*p++ & 0xFF);
+ ilen--;
+ }
+
+ if (!pc)
+ { l3dss1_dummy_invoke(st, cr, id, ident, p, nlen);
+ return;
+ }
+#ifdef HISAX_DE_AOC
+ {
+
+#define FOO1(s,a,b) \
+ while(nlen > 1) { \
+ int ilen = p[1]; \
+ if(nlen < ilen+2) { \
+ l3_debug(st, "FOO1 nlen < ilen+2"); \
+ return; \
+ } \
+ nlen -= ilen+2; \
+ if((*p & 0xFF) == (a)) { \
+ int nlen = ilen; \
+ p += 2; \
+ b; \
+ } else { \
+ p += ilen+2; \
+ } \
+ }
+
+ switch (ident) {
+ case 0x22: /* during */
+ FOO1("1A", 0x30, FOO1("1C", 0xA1, FOO1("1D", 0x30, FOO1("1E", 0x02, ( {
+ ident = 0;
+ nlen = (nlen)?nlen:0; /* Make gcc happy */
+ while (ilen > 0) {
+ ident = (ident << 8) | *p++;
+ ilen--;
+ }
+ if (ident > pc->para.chargeinfo) {
+ pc->para.chargeinfo = ident;
+ st->l3.l3l4(st, CC_CHARGE | INDICATION, pc);
+ }
+ if (st->l3.debug & L3_DEB_CHARGE) {
+ if (*(p + 2) == 0) {
+ l3_debug(st, "charging info during %d", pc->para.chargeinfo);
+ }
+ else {
+ l3_debug(st, "charging info final %d", pc->para.chargeinfo);
+ }
+ }
+ }
+ )))))
+ break;
+ case 0x24: /* final */
+ FOO1("2A", 0x30, FOO1("2B", 0x30, FOO1("2C", 0xA1, FOO1("2D", 0x30, FOO1("2E", 0x02, ( {
+ ident = 0;
+ nlen = (nlen)?nlen:0; /* Make gcc happy */
+ while (ilen > 0) {
+ ident = (ident << 8) | *p++;
+ ilen--;
+ }
+ if (ident > pc->para.chargeinfo) {
+ pc->para.chargeinfo = ident;
+ st->l3.l3l4(st, CC_CHARGE | INDICATION, pc);
+ }
+ if (st->l3.debug & L3_DEB_CHARGE) {
+ l3_debug(st, "charging info final %d", pc->para.chargeinfo);
+ }
+ }
+ ))))))
+ break;
+ default:
+ l3_debug(st, "invoke break invalid ident %02x",ident);
+ break;
+ }
+#undef FOO1
+
+ }
+#else /* not HISAX_DE_AOC */
+ l3_debug(st, "invoke break");
+#endif /* not HISAX_DE_AOC */
+ break;
+ case 2: /* return result */
+ /* if no process available handle separately */
+ if (!pc)
+ { if (cr == -1)
+ l3dss1_dummy_return_result(st, id, p, nlen);
+ return;
+ }
+ if ((pc->prot.dss1.invoke_id) && (pc->prot.dss1.invoke_id == id))
+ { /* Diversion successful */
+ free_invoke_id(st,pc->prot.dss1.invoke_id);
+ pc->prot.dss1.remote_result = 0; /* success */
+ pc->prot.dss1.invoke_id = 0;
+ pc->redir_result = pc->prot.dss1.remote_result;
+ st->l3.l3l4(st, CC_REDIR | INDICATION, pc); } /* Diversion successful */
+ else
+ l3_debug(st,"return error unknown identifier");
+ break;
+ case 3: /* return error */
+ err_ret = 0;
+ if (nlen < 2)
+ { l3_debug(st, "return error nlen < 2");
+ return;
+ }
+ if (*p != 0x02)
+ { /* result tag */
+ l3_debug(st, "invoke error tag !=0x02");
+ return;
+ }
+ p++;
+ nlen--;
+ if (*p > 4)
+ { /* length format */
+ l3_debug(st, "invoke return errlen > 4 ");
+ return;
+ }
+ ilen = *p++;
+ nlen--;
+ if (ilen > nlen || ilen == 0)
+ { l3_debug(st, "error return ilen > nlen || ilen == 0");
+ return;
+ }
+ nlen -= ilen;
+ while (ilen > 0)
+ { err_ret = (err_ret << 8) | (*p++ & 0xFF); /* error value */
+ ilen--;
+ }
+ /* if no process available handle separately */
+ if (!pc)
+ { if (cr == -1)
+ l3dss1_dummy_error_return(st, id, err_ret);
+ return;
+ }
+ if ((pc->prot.dss1.invoke_id) && (pc->prot.dss1.invoke_id == id))
+ { /* Deflection error */
+ free_invoke_id(st,pc->prot.dss1.invoke_id);
+ pc->prot.dss1.remote_result = err_ret; /* result */
+ pc->prot.dss1.invoke_id = 0;
+ pc->redir_result = pc->prot.dss1.remote_result;
+ st->l3.l3l4(st, CC_REDIR | INDICATION, pc);
+ } /* Deflection error */
+ else
+ l3_debug(st,"return result unknown identifier");
+ break;
+ default:
+ l3_debug(st, "facility default break tag=0x%02x",cp_tag);
+ break;
+ }
+}
+
+static void
+l3dss1_message(struct l3_process *pc, u_char mt)
+{
+ struct sk_buff *skb;
+ u_char *p;
+
+ if (!(skb = l3_alloc_skb(4)))
+ return;
+ p = skb_put(skb, 4);
+ MsgHead(p, pc->callref, mt);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3dss1_message_cause(struct l3_process *pc, u_char mt, u_char cause)
+{
+ struct sk_buff *skb;
+ u_char tmp[16];
+ u_char *p = tmp;
+ int l;
+
+ MsgHead(p, pc->callref, mt);
+ *p++ = IE_CAUSE;
+ *p++ = 0x2;
+ *p++ = 0x80;
+ *p++ = cause | 0x80;
+
+ l = p - tmp;
+ if (!(skb = l3_alloc_skb(l)))
+ return;
+ memcpy(skb_put(skb, l), tmp, l);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3dss1_status_send(struct l3_process *pc, u_char pr, void *arg)
+{
+ u_char tmp[16];
+ u_char *p = tmp;
+ int l;
+ struct sk_buff *skb;
+
+ MsgHead(p, pc->callref, MT_STATUS);
+
+ *p++ = IE_CAUSE;
+ *p++ = 0x2;
+ *p++ = 0x80;
+ *p++ = pc->para.cause | 0x80;
+
+ *p++ = IE_CALL_STATE;
+ *p++ = 0x1;
+ *p++ = pc->state & 0x3f;
+
+ l = p - tmp;
+ if (!(skb = l3_alloc_skb(l)))
+ return;
+ memcpy(skb_put(skb, l), tmp, l);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3dss1_msg_without_setup(struct l3_process *pc, u_char pr, void *arg)
+{
+ /* 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
+ */
+ u_char tmp[16];
+ u_char *p = tmp;
+ int l;
+ struct sk_buff *skb;
+
+ switch (pc->para.cause) {
+ case 81: /* invalid callreference */
+ case 88: /* incomp destination */
+ case 96: /* mandory IE missing */
+ case 100: /* invalid IE contents */
+ case 101: /* incompatible Callstate */
+ MsgHead(p, pc->callref, MT_RELEASE_COMPLETE);
+ *p++ = IE_CAUSE;
+ *p++ = 0x2;
+ *p++ = 0x80;
+ *p++ = pc->para.cause | 0x80;
+ break;
+ default:
+ printk(KERN_ERR "HiSax l3dss1_msg_without_setup wrong cause %d\n",
+ pc->para.cause);
+ return;
+ }
+ l = p - tmp;
+ if (!(skb = l3_alloc_skb(l)))
+ return;
+ memcpy(skb_put(skb, l), tmp, l);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+ dss1_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_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_DISPLAY, IE_FACILITY, -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(struct l3_process *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(struct l3_process *pc, struct sk_buff *skb, int *checklist)
+{
+ int *cl = checklist;
+ u_char mt;
+ u_char *p, ie;
+ int l, newpos, oldpos;
+ int err_seq = 0, err_len = 0, err_compr = 0, err_ureg = 0;
+ u_char codeset = 0;
+ u_char old_codeset = 0;
+ u_char codelock = 1;
+
+ p = skb->data;
+ /* skip cr */
+ p++;
+ l = (*p++) & 0xf;
+ p += l;
+ mt = *p++;
+ oldpos = 0;
+ while ((p - skb->data) < skb->len) {
+ if ((*p & 0xf0) == 0x90) { /* shift codeset */
+ old_codeset = codeset;
+ codeset = *p & 7;
+ if (*p & 0x08)
+ codelock = 0;
+ else
+ codelock = 1;
+ if (pc->debug & L3_DEB_CHECK)
+ l3_debug(pc->st, "check IE shift%scodeset %d->%d",
+ codelock ? " locking ": " ", old_codeset, codeset);
+ p++;
+ continue;
+ }
+ if (!codeset) { /* only codeset 0 */
+ if ((newpos = ie_in_set(pc, *p, cl))) {
+ if (newpos > 0) {
+ if (newpos < oldpos)
+ err_seq++;
+ else
+ oldpos = newpos;
+ }
+ } else {
+ if (ie_in_set(pc, *p, comp_required))
+ err_compr++;
+ else
+ err_ureg++;
+ }
+ }
+ ie = *p++;
+ if (ie & 0x80) {
+ l = 1;
+ } else {
+ l = *p++;
+ p += l;
+ l += 2;
+ }
+ if (!codeset && (l > getmax_ie_len(ie)))
+ err_len++;
+ if (!codelock) {
+ if (pc->debug & L3_DEB_CHECK)
+ l3_debug(pc->st, "check IE shift back codeset %d->%d",
+ codeset, old_codeset);
+ codeset = old_codeset;
+ codelock = 1;
+ }
+ }
+ if (err_compr | err_ureg | err_len | err_seq) {
+ if (pc->debug & L3_DEB_CHECK)
+ l3_debug(pc->st, "check IE MT(%x) %d/%d/%d/%d",
+ mt, 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(struct l3_process *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->debug & L3_DEB_CHECK)
+ l3_debug(pc->st, "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->debug & (L3_DEB_CHECK | L3_DEB_WARN))
+ l3_debug(pc->st, "l3dss1_check_messagetype_validity mt(%x) fail", mt);
+ pc->para.cause = 97;
+ l3dss1_status_send(pc, 0, NULL);
+ return(1);
+ }
+ return(0);
+}
+
+static void
+l3dss1_std_ie_err(struct l3_process *pc, int ret) {
+
+ if (pc->debug & L3_DEB_CHECK)
+ l3_debug(pc->st, "check_infoelements ret %d", ret);
+ switch(ret) {
+ case 0:
+ break;
+ case ERR_IE_COMPREHENSION:
+ pc->para.cause = 96;
+ l3dss1_status_send(pc, 0, NULL);
+ break;
+ case ERR_IE_UNRECOGNIZED:
+ pc->para.cause = 99;
+ l3dss1_status_send(pc, 0, NULL);
+ break;
+ case ERR_IE_LENGTH:
+ pc->para.cause = 100;
+ l3dss1_status_send(pc, 0, NULL);
+ break;
+ case ERR_IE_SEQUENCE:
+ default:
+ break;
+ }
+}
+
+static int
+l3dss1_get_channel_id(struct l3_process *pc, struct sk_buff *skb) {
+ u_char *p;
+
+ p = skb->data;
+ if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) {
+ p++;
+ if (*p != 1) { /* len for BRI = 1 */
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "wrong chid len %d", *p);
+ return (-2);
+ }
+ p++;
+ if (*p & 0x60) { /* only base rate interface */
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "wrong chid %x", *p);
+ return (-3);
+ }
+ return(*p & 0x3);
+ } else
+ return(-1);
+}
+
+static int
+l3dss1_get_cause(struct l3_process *pc, struct sk_buff *skb) {
+ u_char l, i=0;
+ u_char *p;
+
+ p = skb->data;
+ pc->para.cause = 31;
+ pc->para.loc = 0;
+ if ((p = findie(p, skb->len, IE_CAUSE, 0))) {
+ p++;
+ l = *p++;
+ if (l>30)
+ return(1);
+ if (l) {
+ pc->para.loc = *p++;
+ l--;
+ } else {
+ return(2);
+ }
+ if (l && !(pc->para.loc & 0x80)) {
+ l--;
+ p++; /* skip recommendation */
+ }
+ if (l) {
+ pc->para.cause = *p++;
+ l--;
+ if (!(pc->para.cause & 0x80))
+ return(3);
+ } else
+ return(4);
+ while (l && (i<6)) {
+ pc->para.diag[i++] = *p++;
+ l--;
+ }
+ } else
+ return(-1);
+ return(0);
+}
+
+static void
+l3dss1_msg_with_uus(struct l3_process *pc, u_char cmd)
+{
+ struct sk_buff *skb;
+ u_char tmp[16+40];
+ u_char *p = tmp;
+ int l;
+
+ MsgHead(p, pc->callref, cmd);
+
+ if (pc->prot.dss1.uus1_data[0])
+ { *p++ = IE_USER_USER; /* UUS info element */
+ *p++ = strlen(pc->prot.dss1.uus1_data) + 1;
+ *p++ = 0x04; /* IA5 chars */
+ strcpy(p,pc->prot.dss1.uus1_data);
+ p += strlen(pc->prot.dss1.uus1_data);
+ pc->prot.dss1.uus1_data[0] = '\0';
+ }
+
+ l = p - tmp;
+ if (!(skb = l3_alloc_skb(l)))
+ return;
+ memcpy(skb_put(skb, l), tmp, l);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+} /* l3dss1_msg_with_uus */
+
+static void
+l3dss1_release_req(struct l3_process *pc, u_char pr, void *arg)
+{
+ StopAllL3Timer(pc);
+ newl3state(pc, 19);
+ if (!pc->prot.dss1.uus1_data[0])
+ l3dss1_message(pc, MT_RELEASE);
+ else
+ l3dss1_msg_with_uus(pc, MT_RELEASE);
+ L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3dss1_release_cmpl(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ int ret;
+
+ if ((ret = l3dss1_get_cause(pc, skb))>0) {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "RELCMPL get_cause ret(%d)",ret);
+ } else if (ret < 0)
+ pc->para.cause = NO_CAUSE;
+ StopAllL3Timer(pc);
+ newl3state(pc, 0);
+ pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc);
+ dss1_release_l3_process(pc);
+}
+
+#ifdef EXT_BEARER_CAPS
+
+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)
+{
+ info &= 0x7f;
+ switch (info) {
+ 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(struct sk_buff *skb)
+{
+ u_char *p; //, *pend=skb->data + skb->len;
+
+ if ((p = findie(skb->data, skb->len, 0x7c, 0))) {
+ 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;
+}
+
+#endif
+
+
+static void
+l3dss1_setup_req(struct l3_process *pc, u_char pr,
+ void *arg)
+{
+ struct sk_buff *skb;
+ u_char tmp[128];
+ u_char *p = tmp;
+ u_char channel = 0;
+
+ u_char send_keypad;
+ u_char screen = 0x80;
+ u_char *teln;
+ u_char *msn;
+ u_char *sub;
+ u_char *sp;
+ int l;
+
+ MsgHead(p, pc->callref, MT_SETUP);
+
+ teln = pc->para.setup.phone;
+#ifndef CONFIG_HISAX_NO_KEYPAD
+ send_keypad = (strchr(teln,'*') || strchr(teln,'#')) ? 1 : 0;
+#else
+ send_keypad = 0;
+#endif
+#ifndef CONFIG_HISAX_NO_SENDCOMPLETE
+ if (!send_keypad)
+ *p++ = 0xa1; /* complete indicator */
+#endif
+ /*
+ * Set Bearer Capability, Map info from 1TR6-convention to EDSS1
+ */
+ switch (pc->para.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 Std. CCITT, unrestr. dig. Inform. */
+ *p++ = 0x90; /* Circuit-Mode 64kbps */
+ break;
+ }
+
+ if (send_keypad) {
+ *p++ = IE_KEYPAD;
+ *p++ = strlen(teln);
+ while (*teln)
+ *p++ = (*teln++) & 0x7F;
+ }
+
+ /*
+ * What about info2? Mapping to High-Layer-Compatibility?
+ */
+ if ((*teln) && (!send_keypad)) {
+ /* parse number for special things */
+ if (!isdigit(*teln)) {
+ switch (0x5f & *teln) {
+ case 'C':
+ channel = 0x08;
+ case 'P':
+ channel |= 0x80;
+ teln++;
+ if (*teln == '1')
+ channel |= 0x01;
+ else
+ channel |= 0x02;
+ break;
+ case 'R':
+ screen = 0xA0;
+ break;
+ case 'D':
+ screen = 0x80;
+ break;
+
+ default:
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "Wrong MSN Code");
+ break;
+ }
+ teln++;
+ }
+ }
+ if (channel) {
+ *p++ = IE_CHANNEL_ID;
+ *p++ = 1;
+ *p++ = channel;
+ }
+ msn = pc->para.setup.eazmsn;
+ sub = NULL;
+ sp = msn;
+ while (*sp) {
+ if ('.' == *sp) {
+ sub = sp;
+ *sp = 0;
+ } else
+ sp++;
+ }
+ if (*msn) {
+ *p++ = IE_CALLING_PN;
+ *p++ = strlen(msn) + (screen ? 2 : 1);
+ /* Classify as AnyPref. */
+ if (screen) {
+ *p++ = 0x01; /* Ext = '0'B, Type = '000'B, Plan = '0001'B. */
+ *p++ = screen;
+ } else
+ *p++ = 0x81; /* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
+ while (*msn)
+ *p++ = *msn++ & 0x7f;
+ }
+ if (sub) {
+ *sub++ = '.';
+ *p++ = IE_CALLING_SUB;
+ *p++ = strlen(sub) + 2;
+ *p++ = 0x80; /* NSAP coded */
+ *p++ = 0x50; /* local IDI format */
+ while (*sub)
+ *p++ = *sub++ & 0x7f;
+ }
+ sub = NULL;
+ sp = teln;
+ while (*sp) {
+ if ('.' == *sp) {
+ sub = sp;
+ *sp = 0;
+ } else
+ sp++;
+ }
+
+ if (!send_keypad) {
+ *p++ = IE_CALLED_PN;
+ *p++ = strlen(teln) + 1;
+ /* Classify as AnyPref. */
+ *p++ = 0x81; /* Ext = '1'B, Type = '000'B, Plan = '0001'B. */
+ while (*teln)
+ *p++ = *teln++ & 0x7f;
+
+ if (sub) {
+ *sub++ = '.';
+ *p++ = IE_CALLED_SUB;
+ *p++ = strlen(sub) + 2;
+ *p++ = 0x80; /* NSAP coded */
+ *p++ = 0x50; /* local IDI format */
+ while (*sub)
+ *p++ = *sub++ & 0x7f;
+ }
+ }
+#ifdef EXT_BEARER_CAPS
+ if ((pc->para.setup.si2 >= 160) && (pc->para.setup.si2 <= 175)) { // sync. Bitratenadaption, V.110/X.30
+
+ *p++ = IE_LLC;
+ *p++ = 0x04;
+ *p++ = 0x88;
+ *p++ = 0x90;
+ *p++ = 0x21;
+ *p++ = EncodeSyncParams(pc->para.setup.si2 - 160, 0x80);
+ } else if ((pc->para.setup.si2 >= 176) && (pc->para.setup.si2 <= 191)) { // sync. Bitratenadaption, V.120
+
+ *p++ = IE_LLC;
+ *p++ = 0x05;
+ *p++ = 0x88;
+ *p++ = 0x90;
+ *p++ = 0x28;
+ *p++ = EncodeSyncParams(pc->para.setup.si2 - 176, 0);
+ *p++ = 0x82;
+ } else if (pc->para.setup.si2 >= 192) { // async. Bitratenadaption, V.110/X.30
+
+ *p++ = IE_LLC;
+ *p++ = 0x06;
+ *p++ = 0x88;
+ *p++ = 0x90;
+ *p++ = 0x21;
+ p = EncodeASyncParams(p, pc->para.setup.si2 - 192);
+#ifndef CONFIG_HISAX_NO_LLC
+ } else {
+ switch (pc->para.setup.si1) {
+ case 1: /* Telephony */
+ *p++ = IE_LLC;
+ *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_LLC;
+ *p++ = 0x2; /* Length */
+ *p++ = 0x88; /* Coding Std. CCITT, unrestr. dig. Inform. */
+ *p++ = 0x90; /* Circuit-Mode 64kbps */
+ break;
+ }
+#endif
+ }
+#endif
+ l = p - tmp;
+ if (!(skb = l3_alloc_skb(l)))
+ return;
+ memcpy(skb_put(skb, l), tmp, l);
+ L3DelTimer(&pc->timer);
+ L3AddTimer(&pc->timer, T303, CC_T303);
+ newl3state(pc, 1);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3dss1_call_proc(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ int id, ret;
+
+ if ((id = l3dss1_get_channel_id(pc, skb)) >= 0) {
+ if ((0 == id) || ((3 == id) && (0x10 == pc->para.moderate))) {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "setup answer with wrong chid %x", id);
+ pc->para.cause = 100;
+ l3dss1_status_send(pc, pr, NULL);
+ return;
+ }
+ pc->para.bchannel = id;
+ } else if (1 == pc->state) {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "setup answer wrong chid (ret %d)", id);
+ if (id == -1)
+ pc->para.cause = 96;
+ else
+ pc->para.cause = 100;
+ l3dss1_status_send(pc, pr, NULL);
+ 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);
+ return;
+ }
+ L3DelTimer(&pc->timer);
+ 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);
+ pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc);
+}
+
+static void
+l3dss1_setup_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ int id, ret;
+
+ if ((id = l3dss1_get_channel_id(pc, skb)) >= 0) {
+ if ((0 == id) || ((3 == id) && (0x10 == pc->para.moderate))) {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "setup answer with wrong chid %x", id);
+ pc->para.cause = 100;
+ l3dss1_status_send(pc, pr, NULL);
+ return;
+ }
+ pc->para.bchannel = id;
+ } else {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "setup answer wrong chid (ret %d)", id);
+ if (id == -1)
+ pc->para.cause = 96;
+ else
+ pc->para.cause = 100;
+ l3dss1_status_send(pc, pr, NULL);
+ 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);
+ return;
+ }
+ L3DelTimer(&pc->timer);
+ 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);
+ pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
+}
+
+static void
+l3dss1_disconnect(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ u_char *p;
+ int ret;
+ u_char cause = 0;
+
+ StopAllL3Timer(pc);
+ if ((ret = l3dss1_get_cause(pc, skb))) {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "DISC get_cause ret(%d)", ret);
+ if (ret < 0)
+ cause = 96;
+ else if (ret > 0)
+ cause = 100;
+ }
+ if ((p = findie(skb->data, skb->len, IE_FACILITY, 0)))
+ l3dss1_parse_facility(pc->st, pc, pc->callref, p);
+ ret = check_infoelements(pc, skb, ie_DISCONNECT);
+ if (ERR_IE_COMPREHENSION == ret)
+ cause = 96;
+ else if ((!cause) && (ERR_IE_UNRECOGNIZED == ret))
+ cause = 99;
+ ret = pc->state;
+ newl3state(pc, 12);
+ if (cause)
+ newl3state(pc, 19);
+ if (11 != ret)
+ pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc);
+ else if (!cause)
+ l3dss1_release_req(pc, pr, NULL);
+ if (cause) {
+ l3dss1_message_cause(pc, MT_RELEASE, cause);
+ L3AddTimer(&pc->timer, T308, CC_T308_1);
+ }
+}
+
+static void
+l3dss1_connect(struct l3_process *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);
+ return;
+ }
+ L3DelTimer(&pc->timer); /* T310 */
+ newl3state(pc, 10);
+ pc->para.chargeinfo = 0;
+ /* here should inserted COLP handling KKe */
+ if (ret)
+ l3dss1_std_ie_err(pc, ret);
+ pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc);
+}
+
+static void
+l3dss1_alerting(struct l3_process *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);
+ return;
+ }
+ L3DelTimer(&pc->timer); /* T304 */
+ newl3state(pc, 4);
+ if (ret)
+ l3dss1_std_ie_err(pc, ret);
+ pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc);
+}
+
+static void
+l3dss1_setup(struct l3_process *pc, u_char pr, void *arg)
+{
+ u_char *p;
+ int bcfound = 0;
+ char tmp[80];
+ struct sk_buff *skb = arg;
+ int id;
+ int err = 0;
+
+ /*
+ * Bearer Capabilities
+ */
+ p = skb->data;
+ /* only the first occurence 'll be detected ! */
+ if ((p = findie(p, skb->len, 0x04, 0))) {
+ if ((p[1] < 2) || (p[1] > 11))
+ err = 1;
+ else {
+ pc->para.setup.si2 = 0;
+ switch (p[2] & 0x7f) {
+ case 0x00: /* Speech */
+ case 0x10: /* 3.1 Khz audio */
+ pc->para.setup.si1 = 1;
+ break;
+ case 0x08: /* Unrestricted digital information */
+ pc->para.setup.si1 = 7;
+/* JIM, 05.11.97 I wanna set service indicator 2 */
+#ifdef EXT_BEARER_CAPS
+ pc->para.setup.si2 = DecodeSI2(skb);
+#endif
+ break;
+ case 0x09: /* Restricted digital information */
+ pc->para.setup.si1 = 2;
+ break;
+ case 0x11:
+ /* Unrestr. digital information with
+ * tones/announcements ( or 7 kHz audio
+ */
+ pc->para.setup.si1 = 3;
+ break;
+ case 0x18: /* Video */
+ pc->para.setup.si1 = 4;
+ break;
+ default:
+ err = 2;
+ break;
+ }
+ switch (p[3] & 0x7f) {
+ case 0x40: /* packed mode */
+ pc->para.setup.si1 = 8;
+ break;
+ case 0x10: /* 64 kbit */
+ case 0x11: /* 2*64 kbit */
+ case 0x13: /* 384 kbit */
+ case 0x15: /* 1536 kbit */
+ case 0x17: /* 1920 kbit */
+ pc->para.moderate = p[3] & 0x7f;
+ break;
+ default:
+ err = 3;
+ break;
+ }
+ }
+ if (pc->debug & L3_DEB_SI)
+ l3_debug(pc->st, "SI=%d, AI=%d",
+ pc->para.setup.si1, pc->para.setup.si2);
+ if (err) {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "setup with wrong bearer(l=%d:%x,%x)",
+ p[1], p[2], p[3]);
+ pc->para.cause = 100;
+ l3dss1_msg_without_setup(pc, pr, NULL);
+ return;
+ }
+ } else {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "setup without bearer capabilities");
+ /* ETS 300-104 1.3.3 */
+ pc->para.cause = 96;
+ l3dss1_msg_without_setup(pc, pr, NULL);
+ return;
+ }
+ /*
+ * Channel Identification
+ */
+ if ((id = l3dss1_get_channel_id(pc, skb)) >= 0) {
+ if ((pc->para.bchannel = id)) {
+ if ((3 == id) && (0x10 == pc->para.moderate)) {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "setup with wrong chid %x",
+ id);
+ pc->para.cause = 100;
+ l3dss1_msg_without_setup(pc, pr, NULL);
+ return;
+ }
+ bcfound++;
+ } else
+ { if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "setup without bchannel, call waiting");
+ bcfound++;
+ }
+ } else {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "setup with wrong chid ret %d", id);
+ if (id == -1)
+ pc->para.cause = 96;
+ else
+ pc->para.cause = 100;
+ l3dss1_msg_without_setup(pc, pr, NULL);
+ return;
+ }
+ /* Now we are on none mandatory IEs */
+ err = check_infoelements(pc, skb, ie_SETUP);
+ if (ERR_IE_COMPREHENSION == err) {
+ pc->para.cause = 96;
+ l3dss1_msg_without_setup(pc, pr, NULL);
+ return;
+ }
+ p = skb->data;
+ if ((p = findie(p, skb->len, 0x70, 0)))
+ iecpy(pc->para.setup.eazmsn, p, 1);
+ else
+ pc->para.setup.eazmsn[0] = 0;
+
+ p = skb->data;
+ if ((p = findie(p, skb->len, 0x71, 0))) {
+ /* Called party subaddress */
+ if ((p[1] >= 2) && (p[2] == 0x80) && (p[3] == 0x50)) {
+ tmp[0] = '.';
+ iecpy(&tmp[1], p, 2);
+ strcat(pc->para.setup.eazmsn, tmp);
+ } else if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "wrong called subaddress");
+ }
+ p = skb->data;
+ if ((p = findie(p, skb->len, 0x6c, 0))) {
+ pc->para.setup.plan = p[2];
+ if (p[2] & 0x80) {
+ iecpy(pc->para.setup.phone, p, 1);
+ pc->para.setup.screen = 0;
+ } else {
+ iecpy(pc->para.setup.phone, p, 2);
+ pc->para.setup.screen = p[3];
+ }
+ } else {
+ pc->para.setup.phone[0] = 0;
+ pc->para.setup.plan = 0;
+ pc->para.setup.screen = 0;
+ }
+ p = skb->data;
+ if ((p = findie(p, skb->len, 0x6d, 0))) {
+ /* Calling party subaddress */
+ if ((p[1] >= 2) && (p[2] == 0x80) && (p[3] == 0x50)) {
+ tmp[0] = '.';
+ iecpy(&tmp[1], p, 2);
+ strcat(pc->para.setup.phone, tmp);
+ } else if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "wrong calling subaddress");
+ }
+ newl3state(pc, 6);
+ if (err) /* STATUS for none mandatory IE errors after actions are taken */
+ l3dss1_std_ie_err(pc, err);
+ pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc);
+}
+
+static void
+l3dss1_reset(struct l3_process *pc, u_char pr, void *arg)
+{
+ dss1_release_l3_process(pc);
+}
+
+static void
+l3dss1_disconnect_req(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb;
+ u_char tmp[16+40];
+ u_char *p = tmp;
+ int l;
+ u_char cause = 16;
+
+ if (pc->para.cause != NO_CAUSE)
+ cause = pc->para.cause;
+
+ StopAllL3Timer(pc);
+
+ MsgHead(p, pc->callref, MT_DISCONNECT);
+
+ *p++ = IE_CAUSE;
+ *p++ = 0x2;
+ *p++ = 0x80;
+ *p++ = cause | 0x80;
+
+ if (pc->prot.dss1.uus1_data[0])
+ { *p++ = IE_USER_USER; /* UUS info element */
+ *p++ = strlen(pc->prot.dss1.uus1_data) + 1;
+ *p++ = 0x04; /* IA5 chars */
+ strcpy(p,pc->prot.dss1.uus1_data);
+ p += strlen(pc->prot.dss1.uus1_data);
+ pc->prot.dss1.uus1_data[0] = '\0';
+ }
+
+ l = p - tmp;
+ if (!(skb = l3_alloc_skb(l)))
+ return;
+ memcpy(skb_put(skb, l), tmp, l);
+ newl3state(pc, 11);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+ L3AddTimer(&pc->timer, T305, CC_T305);
+}
+
+static void
+l3dss1_setup_rsp(struct l3_process *pc, u_char pr,
+ void *arg)
+{
+ if (!pc->para.bchannel)
+ { if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "D-chan connect for waiting call");
+ l3dss1_disconnect_req(pc, pr, arg);
+ return;
+ }
+ newl3state(pc, 8);
+ l3dss1_message(pc, MT_CONNECT);
+ L3DelTimer(&pc->timer);
+ L3AddTimer(&pc->timer, T313, CC_T313);
+}
+
+static void
+l3dss1_connect_ack(struct l3_process *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);
+ return;
+ }
+ newl3state(pc, 10);
+ L3DelTimer(&pc->timer);
+ if (ret)
+ l3dss1_std_ie_err(pc, ret);
+ pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc);
+}
+
+static void
+l3dss1_reject_req(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb;
+ u_char tmp[16];
+ u_char *p = tmp;
+ int l;
+ u_char cause = 21;
+
+ if (pc->para.cause != NO_CAUSE)
+ cause = pc->para.cause;
+
+ MsgHead(p, pc->callref, MT_RELEASE_COMPLETE);
+
+ *p++ = IE_CAUSE;
+ *p++ = 0x2;
+ *p++ = 0x80;
+ *p++ = cause | 0x80;
+
+ l = p - tmp;
+ if (!(skb = l3_alloc_skb(l)))
+ return;
+ memcpy(skb_put(skb, l), tmp, l);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+ pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+ newl3state(pc, 0);
+ dss1_release_l3_process(pc);
+}
+
+static void
+l3dss1_release(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ u_char *p;
+ int ret, cause=0;
+
+ StopAllL3Timer(pc);
+ if ((ret = l3dss1_get_cause(pc, skb))>0) {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "REL get_cause ret(%d)", ret);
+ } else if (ret<0)
+ pc->para.cause = NO_CAUSE;
+ if ((p = findie(skb->data, skb->len, IE_FACILITY, 0))) {
+ l3dss1_parse_facility(pc->st, pc, pc->callref, p);
+ }
+ if ((ret<0) && (pc->state != 11))
+ cause = 96;
+ else if (ret>0)
+ cause = 100;
+ ret = check_infoelements(pc, skb, ie_RELEASE);
+ if (ERR_IE_COMPREHENSION == ret)
+ cause = 96;
+ else if ((ERR_IE_UNRECOGNIZED == ret) && (!cause))
+ cause = 99;
+ if (cause)
+ l3dss1_message_cause(pc, MT_RELEASE_COMPLETE, cause);
+ else
+ l3dss1_message(pc, MT_RELEASE_COMPLETE);
+ pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+ newl3state(pc, 0);
+ dss1_release_l3_process(pc);
+}
+
+static void
+l3dss1_alert_req(struct l3_process *pc, u_char pr,
+ void *arg)
+{
+ newl3state(pc, 7);
+ if (!pc->prot.dss1.uus1_data[0])
+ l3dss1_message(pc, MT_ALERTING);
+ else
+ l3dss1_msg_with_uus(pc, MT_ALERTING);
+}
+
+static void
+l3dss1_proceed_req(struct l3_process *pc, u_char pr,
+ void *arg)
+{
+ newl3state(pc, 9);
+ l3dss1_message(pc, MT_CALL_PROCEEDING);
+ pc->st->l3.l3l4(pc->st, CC_PROCEED_SEND | INDICATION, pc);
+}
+
+static void
+l3dss1_setup_ack_req(struct l3_process *pc, u_char pr,
+ void *arg)
+{
+ newl3state(pc, 25);
+ L3DelTimer(&pc->timer);
+ L3AddTimer(&pc->timer, T302, CC_T302);
+ l3dss1_message(pc, MT_SETUP_ACKNOWLEDGE);
+}
+
+/********************************************/
+/* deliver a incoming display message to HL */
+/********************************************/
+static void
+l3dss1_deliver_display(struct l3_process *pc, int pr, u_char *infp)
+{ u_char len;
+ isdn_ctrl ic;
+ struct IsdnCardState *cs;
+ char *p;
+
+ if (*infp++ != IE_DISPLAY) return;
+ if ((len = *infp++) > 80) return; /* total length <= 82 */
+ if (!pc->chan) return;
+
+ p = ic.parm.display;
+ while (len--)
+ *p++ = *infp++;
+ *p = '\0';
+ ic.command = ISDN_STAT_DISPLAY;
+ cs = pc->st->l1.hardware;
+ ic.driver = cs->myid;
+ ic.arg = pc->chan->chan;
+ cs->iif.statcallb(&ic);
+} /* l3dss1_deliver_display */
+
+
+static void
+l3dss1_progress(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ int err = 0;
+ u_char *p;
+
+ if ((p = findie(skb->data, skb->len, IE_PROGRESS, 0))) {
+ if (p[1] != 2) {
+ err = 1;
+ pc->para.cause = 100;
+ } else if (!(p[2] & 0x70)) {
+ switch (p[2]) {
+ case 0x80:
+ case 0x81:
+ case 0x82:
+ case 0x84:
+ case 0x85:
+ case 0x87:
+ case 0x8a:
+ switch (p[3]) {
+ case 0x81:
+ case 0x82:
+ case 0x83:
+ case 0x84:
+ case 0x88:
+ break;
+ default:
+ err = 2;
+ pc->para.cause = 100;
+ break;
+ }
+ break;
+ default:
+ err = 3;
+ pc->para.cause = 100;
+ break;
+ }
+ }
+ } else {
+ pc->para.cause = 96;
+ err = 4;
+ }
+ if (err) {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "progress error %d", err);
+ l3dss1_status_send(pc, pr, NULL);
+ 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)
+ pc->st->l3.l3l4(pc->st, CC_PROGRESS | INDICATION, pc);
+}
+
+static void
+l3dss1_notify(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ int err = 0;
+ u_char *p;
+
+ if ((p = findie(skb->data, skb->len, IE_NOTIFY, 0))) {
+ if (p[1] != 1) {
+ err = 1;
+ pc->para.cause = 100;
+ } else {
+ switch (p[2]) {
+ case 0x80:
+ case 0x81:
+ case 0x82:
+ break;
+ default:
+ pc->para.cause = 100;
+ err = 2;
+ break;
+ }
+ }
+ } else {
+ pc->para.cause = 96;
+ err = 3;
+ }
+ if (err) {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "notify error %d", err);
+ l3dss1_status_send(pc, pr, NULL);
+ 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)
+ pc->st->l3.l3l4(pc->st, CC_NOTIFY | INDICATION, pc);
+}
+
+static void
+l3dss1_status_enq(struct l3_process *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);
+ pc->para.cause = 30; /* response to STATUS_ENQUIRY */
+ l3dss1_status_send(pc, pr, NULL);
+}
+
+static void
+l3dss1_information(struct l3_process *pc, u_char pr, void *arg)
+{
+ int ret;
+ struct sk_buff *skb = arg;
+ u_char *p;
+ char tmp[32];
+
+ ret = check_infoelements(pc, skb, ie_INFORMATION);
+ if (ret)
+ l3dss1_std_ie_err(pc, ret);
+ if (pc->state == 25) { /* overlap receiving */
+ L3DelTimer(&pc->timer);
+ p = skb->data;
+ if ((p = findie(p, skb->len, 0x70, 0))) {
+ iecpy(tmp, p, 1);
+ strcat(pc->para.setup.eazmsn, tmp);
+ pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
+ }
+ L3AddTimer(&pc->timer, T302, CC_T302);
+ }
+}
+
+/******************************/
+/* handle deflection requests */
+/******************************/
+static void l3dss1_redir_req(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb;
+ u_char tmp[128];
+ u_char *p = tmp;
+ u_char *subp;
+ u_char len_phone = 0;
+ u_char len_sub = 0;
+ int l;
+
+
+ strcpy(pc->prot.dss1.uus1_data,pc->chan->setup.eazmsn); /* copy uus element if available */
+ if (!pc->chan->setup.phone[0])
+ { pc->para.cause = -1;
+ l3dss1_disconnect_req(pc,pr,arg); /* disconnect immediately */
+ return;
+ } /* only uus */
+
+ if (pc->prot.dss1.invoke_id)
+ free_invoke_id(pc->st,pc->prot.dss1.invoke_id);
+
+ if (!(pc->prot.dss1.invoke_id = new_invoke_id(pc->st)))
+ return;
+
+ MsgHead(p, pc->callref, MT_FACILITY);
+
+ for (subp = pc->chan->setup.phone; (*subp) && (*subp != '.'); subp++) len_phone++; /* len of phone number */
+ if (*subp++ == '.') len_sub = strlen(subp) + 2; /* length including info subaddress element */
+
+ *p++ = 0x1c; /* Facility info element */
+ *p++ = len_phone + len_sub + 2 + 2 + 8 + 3 + 3; /* length of element */
+ *p++ = 0x91; /* remote operations protocol */
+ *p++ = 0xa1; /* invoke component */
+
+ *p++ = len_phone + len_sub + 2 + 2 + 8 + 3; /* length of data */
+ *p++ = 0x02; /* invoke id tag, integer */
+ *p++ = 0x01; /* length */
+ *p++ = pc->prot.dss1.invoke_id; /* invoke id */
+ *p++ = 0x02; /* operation value tag, integer */
+ *p++ = 0x01; /* length */
+ *p++ = 0x0D; /* Call Deflect */
+
+ *p++ = 0x30; /* sequence phone number */
+ *p++ = len_phone + 2 + 2 + 3 + len_sub; /* length */
+
+ *p++ = 0x30; /* Deflected to UserNumber */
+ *p++ = len_phone+2+len_sub; /* length */
+ *p++ = 0x80; /* NumberDigits */
+ *p++ = len_phone; /* length */
+ for (l = 0; l < len_phone; l++)
+ *p++ = pc->chan->setup.phone[l];
+
+ if (len_sub)
+ { *p++ = 0x04; /* called party subaddress */
+ *p++ = len_sub - 2;
+ while (*subp) *p++ = *subp++;
+ }
+
+ *p++ = 0x01; /* screening identifier */
+ *p++ = 0x01;
+ *p++ = pc->chan->setup.screen;
+
+ l = p - tmp;
+ if (!(skb = l3_alloc_skb(l))) return;
+ memcpy(skb_put(skb, l), tmp, l);
+
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+} /* l3dss1_redir_req */
+
+/********************************************/
+/* handle deflection request in early state */
+/********************************************/
+static void l3dss1_redir_req_early(struct l3_process *pc, u_char pr, void *arg)
+{
+ l3dss1_proceed_req(pc,pr,arg);
+ l3dss1_redir_req(pc,pr,arg);
+} /* l3dss1_redir_req_early */
+
+/***********************************************/
+/* handle special commands for this protocol. */
+/* Examples are call independant services like */
+/* remote operations with dummy callref. */
+/***********************************************/
+static int l3dss1_cmd_global(struct PStack *st, isdn_ctrl *ic)
+{ u_char id;
+ u_char temp[265];
+ u_char *p = temp;
+ int i, l, proc_len;
+ struct sk_buff *skb;
+ struct l3_process *pc = NULL;
+
+ switch (ic->arg)
+ { case DSS1_CMD_INVOKE:
+ if (ic->parm.dss1_io.datalen < 0) return(-2); /* invalid parameter */
+
+ for (proc_len = 1, i = ic->parm.dss1_io.proc >> 8; i; i++)
+ i = i >> 8; /* add one byte */
+ l = ic->parm.dss1_io.datalen + proc_len + 8; /* length excluding ie header */
+ if (l > 255)
+ return(-2); /* too long */
+
+ if (!(id = new_invoke_id(st)))
+ return(0); /* first get a invoke id -> return if no available */
+
+ i = -1;
+ MsgHead(p, i, MT_FACILITY); /* build message head */
+ *p++ = 0x1C; /* Facility IE */
+ *p++ = l; /* length of ie */
+ *p++ = 0x91; /* remote operations */
+ *p++ = 0xA1; /* invoke */
+ *p++ = l - 3; /* length of invoke */
+ *p++ = 0x02; /* invoke id tag */
+ *p++ = 0x01; /* length is 1 */
+ *p++ = id; /* invoke id */
+ *p++ = 0x02; /* operation */
+ *p++ = proc_len; /* length of operation */
+
+ for (i = proc_len; i; i--)
+ *p++ = (ic->parm.dss1_io.proc >> (i-1)) & 0xFF;
+ memcpy(p, ic->parm.dss1_io.data, ic->parm.dss1_io.datalen); /* copy data */
+ l = (p - temp) + ic->parm.dss1_io.datalen; /* total length */
+
+ if (ic->parm.dss1_io.timeout > 0)
+ if (!(pc = dss1_new_l3_process(st, -1)))
+ { free_invoke_id(st, id);
+ return(-2);
+ }
+ pc->prot.dss1.ll_id = ic->parm.dss1_io.ll_id; /* remember id */
+ pc->prot.dss1.proc = ic->parm.dss1_io.proc; /* and procedure */
+
+ if (!(skb = l3_alloc_skb(l)))
+ { free_invoke_id(st, id);
+ if (pc) dss1_release_l3_process(pc);
+ return(-2);
+ }
+ memcpy(skb_put(skb, l), temp, l);
+
+ if (pc)
+ { pc->prot.dss1.invoke_id = id; /* remember id */
+ L3AddTimer(&pc->timer, ic->parm.dss1_io.timeout, CC_TDSS1_IO | REQUEST);
+ }
+
+ l3_msg(st, DL_DATA | REQUEST, skb);
+ ic->parm.dss1_io.hl_id = id; /* return id */
+ return(0);
+
+ case DSS1_CMD_INVOKE_ABORT:
+ if ((pc = l3dss1_search_dummy_proc(st, ic->parm.dss1_io.hl_id)))
+ { L3DelTimer(&pc->timer); /* remove timer */
+ dss1_release_l3_process(pc);
+ return(0);
+ }
+ else
+ { l3_debug(st, "l3dss1_cmd_global abort unknown id");
+ return(-2);
+ }
+ break;
+
+ default:
+ l3_debug(st, "l3dss1_cmd_global unknown cmd 0x%lx", ic->arg);
+ return(-1);
+ } /* switch ic-> arg */
+ return(-1);
+} /* l3dss1_cmd_global */
+
+static void
+l3dss1_io_timer(struct l3_process *pc)
+{ isdn_ctrl ic;
+ struct IsdnCardState *cs = pc->st->l1.hardware;
+
+ L3DelTimer(&pc->timer); /* remove timer */
+
+ ic.driver = cs->myid;
+ ic.command = ISDN_STAT_PROT;
+ ic.arg = DSS1_STAT_INVOKE_ERR;
+ ic.parm.dss1_io.hl_id = pc->prot.dss1.invoke_id;
+ ic.parm.dss1_io.ll_id = pc->prot.dss1.ll_id;
+ ic.parm.dss1_io.proc = pc->prot.dss1.proc;
+ ic.parm.dss1_io.timeout= -1;
+ ic.parm.dss1_io.datalen = 0;
+ ic.parm.dss1_io.data = NULL;
+ free_invoke_id(pc->st, pc->prot.dss1.invoke_id);
+ pc->prot.dss1.invoke_id = 0; /* reset id */
+
+ cs->iif.statcallb(&ic);
+
+ dss1_release_l3_process(pc);
+} /* l3dss1_io_timer */
+
+static void
+l3dss1_release_ind(struct l3_process *pc, u_char pr, void *arg)
+{
+ u_char *p;
+ struct sk_buff *skb = arg;
+ int callState = 0;
+ p = skb->data;
+
+ if ((p = findie(p, skb->len, IE_CALL_STATE, 0))) {
+ 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
+ */
+ pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+ newl3state(pc, 0);
+ dss1_release_l3_process(pc);
+ } else {
+ pc->st->l3.l3l4(pc->st, CC_IGNORE | INDICATION, pc);
+ }
+}
+
+static void
+l3dss1_dummy(struct l3_process *pc, u_char pr, void *arg)
+{
+}
+
+static void
+l3dss1_t302(struct l3_process *pc, u_char pr, void *arg)
+{
+ L3DelTimer(&pc->timer);
+ pc->para.loc = 0;
+ pc->para.cause = 28; /* invalid number */
+ l3dss1_disconnect_req(pc, pr, NULL);
+ pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+}
+
+static void
+l3dss1_t303(struct l3_process *pc, u_char pr, void *arg)
+{
+ if (pc->N303 > 0) {
+ pc->N303--;
+ L3DelTimer(&pc->timer);
+ l3dss1_setup_req(pc, pr, arg);
+ } else {
+ L3DelTimer(&pc->timer);
+ l3dss1_message_cause(pc, MT_RELEASE_COMPLETE, 102);
+ pc->st->l3.l3l4(pc->st, CC_NOSETUP_RSP, pc);
+ dss1_release_l3_process(pc);
+ }
+}
+
+static void
+l3dss1_t304(struct l3_process *pc, u_char pr, void *arg)
+{
+ L3DelTimer(&pc->timer);
+ pc->para.loc = 0;
+ pc->para.cause = 102;
+ l3dss1_disconnect_req(pc, pr, NULL);
+ pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+
+}
+
+static void
+l3dss1_t305(struct l3_process *pc, u_char pr, void *arg)
+{
+ u_char tmp[16];
+ u_char *p = tmp;
+ int l;
+ struct sk_buff *skb;
+ u_char cause = 16;
+
+ L3DelTimer(&pc->timer);
+ if (pc->para.cause != NO_CAUSE)
+ cause = pc->para.cause;
+
+ MsgHead(p, pc->callref, MT_RELEASE);
+
+ *p++ = IE_CAUSE;
+ *p++ = 0x2;
+ *p++ = 0x80;
+ *p++ = cause | 0x80;
+
+ l = p - tmp;
+ if (!(skb = l3_alloc_skb(l)))
+ return;
+ memcpy(skb_put(skb, l), tmp, l);
+ newl3state(pc, 19);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+ L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3dss1_t310(struct l3_process *pc, u_char pr, void *arg)
+{
+ L3DelTimer(&pc->timer);
+ pc->para.loc = 0;
+ pc->para.cause = 102;
+ l3dss1_disconnect_req(pc, pr, NULL);
+ pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+}
+
+static void
+l3dss1_t313(struct l3_process *pc, u_char pr, void *arg)
+{
+ L3DelTimer(&pc->timer);
+ pc->para.loc = 0;
+ pc->para.cause = 102;
+ l3dss1_disconnect_req(pc, pr, NULL);
+ pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc);
+}
+
+static void
+l3dss1_t308_1(struct l3_process *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(struct l3_process *pc, u_char pr, void *arg)
+{
+ L3DelTimer(&pc->timer);
+ pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc);
+ dss1_release_l3_process(pc);
+}
+
+static void
+l3dss1_t318(struct l3_process *pc, u_char pr, void *arg)
+{
+ L3DelTimer(&pc->timer);
+ pc->para.cause = 102; /* Timer expiry */
+ pc->para.loc = 0; /* local */
+ pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc);
+ newl3state(pc, 19);
+ l3dss1_message(pc, MT_RELEASE);
+ L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3dss1_t319(struct l3_process *pc, u_char pr, void *arg)
+{
+ L3DelTimer(&pc->timer);
+ pc->para.cause = 102; /* Timer expiry */
+ pc->para.loc = 0; /* local */
+ pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc);
+ newl3state(pc, 10);
+}
+
+static void
+l3dss1_restart(struct l3_process *pc, u_char pr, void *arg)
+{
+ L3DelTimer(&pc->timer);
+ pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+ dss1_release_l3_process(pc);
+}
+
+static void
+l3dss1_status(struct l3_process *pc, u_char pr, void *arg)
+{
+ u_char *p;
+ struct sk_buff *skb = arg;
+ int ret;
+ u_char cause = 0, callState = 0;
+
+ if ((ret = l3dss1_get_cause(pc, skb))) {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "STATUS get_cause ret(%d)",ret);
+ if (ret < 0)
+ cause = 96;
+ else if (ret > 0)
+ cause = 100;
+ }
+ if ((p = findie(skb->data, skb->len, IE_CALL_STATE, 0))) {
+ p++;
+ if (1 == *p++) {
+ callState = *p;
+ if (!ie_in_set(pc, *p, l3_valid_states))
+ cause = 100;
+ } else
+ cause = 100;
+ } else
+ cause = 96;
+ if (!cause) { /* no error before */
+ ret = check_infoelements(pc, skb, ie_STATUS);
+ if (ERR_IE_COMPREHENSION == ret)
+ cause = 96;
+ else if (ERR_IE_UNRECOGNIZED == ret)
+ cause = 99;
+ }
+ if (cause) {
+ u_char tmp;
+
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "STATUS error(%d/%d)",ret,cause);
+ tmp = pc->para.cause;
+ pc->para.cause = cause;
+ l3dss1_status_send(pc, 0, NULL);
+ if (cause == 99)
+ pc->para.cause = tmp;
+ else
+ return;
+ }
+ cause = pc->para.cause;
+ if (((cause & 0x7f) == 111) && (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
+ */
+ pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+ newl3state(pc, 0);
+ dss1_release_l3_process(pc);
+ }
+}
+
+static void
+l3dss1_facility(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ int ret;
+
+ ret = check_infoelements(pc, skb, ie_FACILITY);
+ l3dss1_std_ie_err(pc, ret);
+ {
+ u_char *p;
+ if ((p = findie(skb->data, skb->len, IE_FACILITY, 0)))
+ l3dss1_parse_facility(pc->st, pc, pc->callref, p);
+ }
+}
+
+static void
+l3dss1_suspend_req(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb;
+ u_char tmp[32];
+ u_char *p = tmp;
+ u_char i, l;
+ u_char *msg = pc->chan->setup.phone;
+
+ MsgHead(p, pc->callref, MT_SUSPEND);
+ l = *msg++;
+ if (l && (l <= 10)) { /* Max length 10 octets */
+ *p++ = IE_CALL_ID;
+ *p++ = l;
+ for (i = 0; i < l; i++)
+ *p++ = *msg++;
+ } else if (l) {
+ l3_debug(pc->st, "SUS wrong CALL_ID len %d", l);
+ return;
+ }
+ l = p - tmp;
+ if (!(skb = l3_alloc_skb(l)))
+ return;
+ memcpy(skb_put(skb, l), tmp, l);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+ newl3state(pc, 15);
+ L3AddTimer(&pc->timer, T319, CC_T319);
+}
+
+static void
+l3dss1_suspend_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ int ret;
+
+ L3DelTimer(&pc->timer);
+ newl3state(pc, 0);
+ pc->para.cause = NO_CAUSE;
+ pc->st->l3.l3l4(pc->st, CC_SUSPEND | CONFIRM, pc);
+ /* We don't handle suspend_ack for IE errors now */
+ if ((ret = check_infoelements(pc, skb, ie_SUSPEND_ACKNOWLEDGE)))
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "SUSPACK check ie(%d)",ret);
+ dss1_release_l3_process(pc);
+}
+
+static void
+l3dss1_suspend_rej(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ int ret;
+
+ if ((ret = l3dss1_get_cause(pc, skb))) {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "SUSP_REJ get_cause ret(%d)",ret);
+ if (ret < 0)
+ pc->para.cause = 96;
+ else
+ pc->para.cause = 100;
+ l3dss1_status_send(pc, pr, NULL);
+ return;
+ }
+ ret = check_infoelements(pc, skb, ie_SUSPEND_REJECT);
+ if (ERR_IE_COMPREHENSION == ret) {
+ l3dss1_std_ie_err(pc, ret);
+ return;
+ }
+ L3DelTimer(&pc->timer);
+ pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc);
+ 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_req(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb;
+ u_char tmp[32];
+ u_char *p = tmp;
+ u_char i, l;
+ u_char *msg = pc->para.setup.phone;
+
+ MsgHead(p, pc->callref, MT_RESUME);
+
+ l = *msg++;
+ if (l && (l <= 10)) { /* Max length 10 octets */
+ *p++ = IE_CALL_ID;
+ *p++ = l;
+ for (i = 0; i < l; i++)
+ *p++ = *msg++;
+ } else if (l) {
+ l3_debug(pc->st, "RES wrong CALL_ID len %d", l);
+ return;
+ }
+ l = p - tmp;
+ if (!(skb = l3_alloc_skb(l)))
+ return;
+ memcpy(skb_put(skb, l), tmp, l);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+ newl3state(pc, 17);
+ L3AddTimer(&pc->timer, T318, CC_T318);
+}
+
+static void
+l3dss1_resume_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ int id, ret;
+
+ if ((id = l3dss1_get_channel_id(pc, skb)) > 0) {
+ if ((0 == id) || ((3 == id) && (0x10 == pc->para.moderate))) {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "resume ack with wrong chid %x", id);
+ pc->para.cause = 100;
+ l3dss1_status_send(pc, pr, NULL);
+ return;
+ }
+ pc->para.bchannel = id;
+ } else if (1 == pc->state) {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "resume ack without chid (ret %d)", id);
+ pc->para.cause = 96;
+ l3dss1_status_send(pc, pr, NULL);
+ return;
+ }
+ ret = check_infoelements(pc, skb, ie_RESUME_ACKNOWLEDGE);
+ if (ERR_IE_COMPREHENSION == ret) {
+ l3dss1_std_ie_err(pc, ret);
+ return;
+ }
+ L3DelTimer(&pc->timer);
+ pc->st->l3.l3l4(pc->st, CC_RESUME | CONFIRM, pc);
+ 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(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ int ret;
+
+ if ((ret = l3dss1_get_cause(pc, skb))) {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "RES_REJ get_cause ret(%d)",ret);
+ if (ret < 0)
+ pc->para.cause = 96;
+ else
+ pc->para.cause = 100;
+ l3dss1_status_send(pc, pr, NULL);
+ return;
+ }
+ ret = check_infoelements(pc, skb, ie_RESUME_REJECT);
+ if (ERR_IE_COMPREHENSION == ret) {
+ l3dss1_std_ie_err(pc, ret);
+ return;
+ }
+ L3DelTimer(&pc->timer);
+ pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc);
+ newl3state(pc, 0);
+ if (ret) /* STATUS for none mandatory IE errors after actions are taken */
+ l3dss1_std_ie_err(pc, ret);
+ dss1_release_l3_process(pc);
+}
+
+static void
+l3dss1_global_restart(struct l3_process *pc, u_char pr, void *arg)
+{
+ u_char tmp[32];
+ u_char *p;
+ u_char ri, ch = 0, chan = 0;
+ int l;
+ struct sk_buff *skb = arg;
+ struct l3_process *up;
+
+ newl3state(pc, 2);
+ L3DelTimer(&pc->timer);
+ p = skb->data;
+ if ((p = findie(p, skb->len, IE_RESTART_IND, 0))) {
+ ri = p[2];
+ l3_debug(pc->st, "Restart %x", ri);
+ } else {
+ l3_debug(pc->st, "Restart without restart IE");
+ ri = 0x86;
+ }
+ p = skb->data;
+ if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) {
+ chan = p[2] & 3;
+ ch = p[2];
+ if (pc->st->l3.debug)
+ l3_debug(pc->st, "Restart for channel %d", chan);
+ }
+ newl3state(pc, 2);
+ up = pc->st->l3.proc;
+ while (up) {
+ if ((ri & 7) == 7)
+ up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up);
+ else if (up->para.bchannel == chan)
+ up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up);
+ up = up->next;
+ }
+ p = tmp;
+ MsgHead(p, pc->callref, MT_RESTART_ACKNOWLEDGE);
+ if (chan) {
+ *p++ = IE_CHANNEL_ID;
+ *p++ = 1;
+ *p++ = ch | 0x80;
+ }
+ *p++ = 0x79; /* RESTART Ind */
+ *p++ = 1;
+ *p++ = ri;
+ l = p - tmp;
+ if (!(skb = l3_alloc_skb(l)))
+ return;
+ memcpy(skb_put(skb, l), tmp, l);
+ newl3state(pc, 0);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3dss1_dl_reset(struct l3_process *pc, u_char pr, void *arg)
+{
+ pc->para.cause = 0x29; /* Temporary failure */
+ pc->para.loc = 0;
+ l3dss1_disconnect_req(pc, pr, NULL);
+ pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+}
+
+static void
+l3dss1_dl_release(struct l3_process *pc, u_char pr, void *arg)
+{
+ newl3state(pc, 0);
+ pc->para.cause = 0x1b; /* Destination out of order */
+ pc->para.loc = 0;
+ pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+ release_l3_process(pc);
+}
+
+static void
+l3dss1_dl_reestablish(struct l3_process *pc, u_char pr, void *arg)
+{
+ L3DelTimer(&pc->timer);
+ L3AddTimer(&pc->timer, T309, CC_T309);
+ l3_msg(pc->st, DL_ESTABLISH | REQUEST, NULL);
+}
+
+static void
+l3dss1_dl_reest_status(struct l3_process *pc, u_char pr, void *arg)
+{
+ L3DelTimer(&pc->timer);
+
+ pc->para.cause = 0x1F; /* normal, unspecified */
+ l3dss1_status_send(pc, 0, NULL);
+}
+
+/* *INDENT-OFF* */
+static struct stateentry downstatelist[] =
+{
+ {SBIT(0),
+ CC_SETUP | REQUEST, l3dss1_setup_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(12),
+ CC_RELEASE | REQUEST, l3dss1_release_req},
+ {ALL_STATES,
+ CC_RESTART | REQUEST, l3dss1_restart},
+ {SBIT(6) | SBIT(25),
+ CC_IGNORE | REQUEST, l3dss1_reset},
+ {SBIT(6) | SBIT(25),
+ CC_REJECT | REQUEST, l3dss1_reject_req},
+ {SBIT(6) | SBIT(25),
+ CC_PROCEED_SEND | REQUEST, l3dss1_proceed_req},
+ {SBIT(6),
+ CC_MORE_INFO | REQUEST, l3dss1_setup_ack_req},
+ {SBIT(25),
+ CC_MORE_INFO | REQUEST, l3dss1_dummy},
+ {SBIT(6) | SBIT(9) | SBIT(25),
+ CC_ALERTING | REQUEST, l3dss1_alert_req},
+ {SBIT(6) | SBIT(7) | SBIT(9) | SBIT(25),
+ CC_SETUP | RESPONSE, l3dss1_setup_rsp},
+ {SBIT(10),
+ CC_SUSPEND | REQUEST, l3dss1_suspend_req},
+ {SBIT(7) | SBIT(9) | SBIT(25),
+ CC_REDIR | REQUEST, l3dss1_redir_req},
+ {SBIT(6),
+ CC_REDIR | REQUEST, l3dss1_redir_req_early},
+ {SBIT(9) | SBIT(25),
+ CC_DISCONNECT | REQUEST, l3dss1_disconnect_req},
+ {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},
+};
+
+#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},
+};
+
+#define MANSLLEN \
+ (sizeof(manstatelist) / sizeof(struct stateentry))
+/* *INDENT-ON* */
+
+
+static void
+global_handler(struct PStack *st, int mt, struct sk_buff *skb)
+{
+ u_char tmp[16];
+ u_char *p = tmp;
+ int l;
+ int i;
+ struct l3_process *proc = st->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 (st->l3.debug & L3_DEB_STATE) {
+ l3_debug(st, "dss1 global state %d mt %x unhandled",
+ proc->state, mt);
+ }
+ MsgHead(p, proc->callref, MT_STATUS);
+ *p++ = IE_CAUSE;
+ *p++ = 0x2;
+ *p++ = 0x80;
+ *p++ = 81 |0x80; /* invalid cr */
+ *p++ = 0x14; /* CallState */
+ *p++ = 0x1;
+ *p++ = proc->state & 0x3f;
+ l = p - tmp;
+ if (!(skb = l3_alloc_skb(l)))
+ return;
+ memcpy(skb_put(skb, l), tmp, l);
+ l3_msg(proc->st, DL_DATA | REQUEST, skb);
+ } else {
+ if (st->l3.debug & L3_DEB_STATE) {
+ l3_debug(st, "dss1 global %d mt %x",
+ proc->state, mt);
+ }
+ globalmes_list[i].rout(proc, mt, skb);
+ }
+}
+
+static void
+dss1up(struct PStack *st, int pr, void *arg)
+{
+ int i, mt, cr, cause, callState;
+ char *ptr;
+ u_char *p;
+ struct sk_buff *skb = arg;
+ struct l3_process *proc;
+
+ switch (pr) {
+ case (DL_DATA | INDICATION):
+ case (DL_UNIT_DATA | INDICATION):
+ break;
+ case (DL_ESTABLISH | CONFIRM):
+ case (DL_ESTABLISH | INDICATION):
+ case (DL_RELEASE | INDICATION):
+ case (DL_RELEASE | CONFIRM):
+ l3_msg(st, pr, arg);
+ return;
+ break;
+ default:
+ printk(KERN_ERR "HiSax dss1up unknown pr=%04x\n", pr);
+ return;
+ }
+ if (skb->len < 3) {
+ l3_debug(st, "dss1up frame too short(%d)", skb->len);
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ if (skb->data[0] != PROTO_DIS_EURO) {
+ if (st->l3.debug & L3_DEB_PROTERR) {
+ l3_debug(st, "dss1up%sunexpected discriminator %x message len %d",
+ (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+ skb->data[0], skb->len);
+ }
+ dev_kfree_skb(skb);
+ return;
+ }
+ cr = getcallref(skb->data);
+ if (skb->len < ((skb->data[1] & 0x0f) + 3)) {
+ l3_debug(st, "dss1up frame too short(%d)", skb->len);
+ dev_kfree_skb(skb);
+ return;
+ }
+ mt = skb->data[skb->data[1] + 2];
+ if (st->l3.debug & L3_DEB_STATE)
+ l3_debug(st, "dss1up cr %d", cr);
+ if (cr == -2) { /* wrong Callref */
+ if (st->l3.debug & L3_DEB_WARN)
+ l3_debug(st, "dss1up wrong Callref");
+ dev_kfree_skb(skb);
+ return;
+ } else if (cr == -1) { /* Dummy Callref */
+ if (mt == MT_FACILITY)
+ if ((p = findie(skb->data, skb->len, IE_FACILITY, 0))) {
+ l3dss1_parse_facility(st, NULL,
+ (pr == (DL_DATA | INDICATION)) ? -1 : -2, p);
+ dev_kfree_skb(skb);
+ return;
+ }
+ if (st->l3.debug & L3_DEB_WARN)
+ l3_debug(st, "dss1up dummy Callref (no facility msg or ie)");
+ dev_kfree_skb(skb);
+ return;
+ } else if ((((skb->data[1] & 0x0f) == 1) && (0==(cr & 0x7f))) ||
+ (((skb->data[1] & 0x0f) == 2) && (0==(cr & 0x7fff)))) { /* Global CallRef */
+ if (st->l3.debug & L3_DEB_STATE)
+ l3_debug(st, "dss1up Global CallRef");
+ global_handler(st, mt, skb);
+ dev_kfree_skb(skb);
+ return;
+ } else if (!(proc = getl3proc(st, cr))) {
+ /* No transaction process exist, that means no call with
+ * this callreference is active
+ */
+ if (mt == MT_SETUP) {
+ /* Setup creates a new transaction process */
+ if (skb->data[2] & 0x80) {
+ /* Setup with wrong CREF flag */
+ if (st->l3.debug & L3_DEB_STATE)
+ l3_debug(st, "dss1up wrong CRef flag");
+ dev_kfree_skb(skb);
+ return;
+ }
+ if (!(proc = dss1_new_l3_process(st, cr))) {
+ /* 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;
+ }
+ } else if (mt == MT_STATUS) {
+ cause = 0;
+ if ((ptr = findie(skb->data, skb->len, IE_CAUSE, 0)) != NULL) {
+ ptr++;
+ if (*ptr++ == 2)
+ ptr++;
+ cause = *ptr & 0x7f;
+ }
+ callState = 0;
+ if ((ptr = findie(skb->data, skb->len, IE_CALL_STATE, 0)) != NULL) {
+ ptr++;
+ if (*ptr++ == 2)
+ ptr++;
+ callState = *ptr;
+ }
+ /* 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 = dss1_new_l3_process(st, cr))) {
+ proc->para.cause = 101;
+ l3dss1_msg_without_setup(proc, 0, NULL);
+ }
+ }
+ dev_kfree_skb(skb);
+ return;
+ } else if (mt == MT_RELEASE_COMPLETE) {
+ dev_kfree_skb(skb);
+ return;
+ } 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 */
+ dev_kfree_skb(skb);
+ if ((proc = dss1_new_l3_process(st, cr))) {
+ proc->para.cause = 81;
+ l3dss1_msg_without_setup(proc, 0, NULL);
+ }
+ return;
+ }
+ }
+ if (l3dss1_check_messagetype_validity(proc, mt, skb)) {
+ dev_kfree_skb(skb);
+ return;
+ }
+ if ((p = findie(skb->data, skb->len, IE_DISPLAY, 0)) != NULL)
+ l3dss1_deliver_display(proc, pr, p); /* Display IE included */
+ for (i = 0; i < DATASLLEN; i++)
+ if ((mt == datastatelist[i].primitive) &&
+ ((1 << proc->state) & datastatelist[i].state))
+ break;
+ if (i == DATASLLEN) {
+ if (st->l3.debug & L3_DEB_STATE) {
+ l3_debug(st, "dss1up%sstate %d mt %#x unhandled",
+ (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+ proc->state, mt);
+ }
+ if ((MT_RELEASE_COMPLETE != mt) && (MT_RELEASE != mt)) {
+ proc->para.cause = 101;
+ l3dss1_status_send(proc, pr, skb);
+ }
+ } else {
+ if (st->l3.debug & L3_DEB_STATE) {
+ l3_debug(st, "dss1up%sstate %d mt %x",
+ (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+ proc->state, mt);
+ }
+ datastatelist[i].rout(proc, pr, skb);
+ }
+ dev_kfree_skb(skb);
+ return;
+}
+
+static void
+dss1down(struct PStack *st, int pr, void *arg)
+{
+ int i, cr;
+ struct l3_process *proc;
+ struct Channel *chan;
+
+ if ((DL_ESTABLISH | REQUEST) == pr) {
+ l3_msg(st, pr, NULL);
+ return;
+ } else if (((CC_SETUP | REQUEST) == pr) || ((CC_RESUME | REQUEST) == pr)) {
+ chan = arg;
+ cr = newcallref();
+ cr |= 0x80;
+ if ((proc = dss1_new_l3_process(st, cr))) {
+ proc->chan = chan;
+ chan->proc = proc;
+ memcpy(&proc->para.setup, &chan->setup, sizeof(setup_parm));
+ proc->callref = cr;
+ }
+ } else {
+ proc = arg;
+ }
+ if (!proc) {
+ printk(KERN_ERR "HiSax dss1down without proc pr=%04x\n", pr);
+ return;
+ }
+
+ if ( pr == (CC_TDSS1_IO | REQUEST)) {
+ l3dss1_io_timer(proc); /* timer expires */
+ return;
+ }
+
+ for (i = 0; i < DOWNSLLEN; i++)
+ if ((pr == downstatelist[i].primitive) &&
+ ((1 << proc->state) & downstatelist[i].state))
+ break;
+ if (i == DOWNSLLEN) {
+ if (st->l3.debug & L3_DEB_STATE) {
+ l3_debug(st, "dss1down state %d prim %#x unhandled",
+ proc->state, pr);
+ }
+ } else {
+ if (st->l3.debug & L3_DEB_STATE) {
+ l3_debug(st, "dss1down state %d prim %#x",
+ proc->state, pr);
+ }
+ downstatelist[i].rout(proc, pr, arg);
+ }
+}
+
+static void
+dss1man(struct PStack *st, int pr, void *arg)
+{
+ int i;
+ struct l3_process *proc = arg;
+
+ if (!proc) {
+ printk(KERN_ERR "HiSax dss1man without proc pr=%04x\n", pr);
+ return;
+ }
+ for (i = 0; i < MANSLLEN; i++)
+ if ((pr == manstatelist[i].primitive) &&
+ ((1 << proc->state) & manstatelist[i].state))
+ break;
+ if (i == MANSLLEN) {
+ if (st->l3.debug & L3_DEB_STATE) {
+ l3_debug(st, "cr %d dss1man state %d prim %#x unhandled",
+ proc->callref & 0x7f, proc->state, pr);
+ }
+ } else {
+ if (st->l3.debug & L3_DEB_STATE) {
+ l3_debug(st, "cr %d dss1man state %d prim %#x",
+ proc->callref & 0x7f, proc->state, pr);
+ }
+ manstatelist[i].rout(proc, pr, arg);
+ }
+}
+
+void
+setstack_dss1(struct PStack *st)
+{
+ char tmp[64];
+ int i;
+
+ st->lli.l4l3 = dss1down;
+ st->lli.l4l3_proto = l3dss1_cmd_global;
+ st->l2.l2l3 = dss1up;
+ st->l3.l3ml3 = dss1man;
+ st->l3.N303 = 1;
+ st->prot.dss1.last_invoke_id = 0;
+ st->prot.dss1.invoke_used[0] = 1; /* Bit 0 must always be set to 1 */
+ i = 1;
+ while (i < 32)
+ st->prot.dss1.invoke_used[i++] = 0;
+
+ if (!(st->l3.global = kmalloc(sizeof(struct l3_process), GFP_ATOMIC))) {
+ printk(KERN_ERR "HiSax can't get memory for dss1 global CR\n");
+ } else {
+ st->l3.global->state = 0;
+ st->l3.global->callref = 0;
+ st->l3.global->next = NULL;
+ st->l3.global->debug = L3_DEB_WARN;
+ st->l3.global->st = st;
+ st->l3.global->N303 = 1;
+ st->l3.global->prot.dss1.invoke_id = 0;
+
+ L3InitTimer(st->l3.global, &st->l3.global->timer);
+ }
+ strcpy(tmp, dss1_revision);
+ printk(KERN_INFO "HiSax: DSS1 Rev. %s\n", HiSax_getrev(tmp));
+}
diff --git a/drivers/isdn/hisax/l3dss1.h b/drivers/isdn/hisax/l3dss1.h
new file mode 100644
index 0000000..6da47f0
--- /dev/null
+++ b/drivers/isdn/hisax/l3dss1.h
@@ -0,0 +1,124 @@
+/* $Id: l3dss1.h,v 1.10.6.2 2001/09/23 22:24:50 kai Exp $
+ *
+ * DSS1 (Euro) D-channel protocol defines
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#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
+
+/*
+ * 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
+
+#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 */
+ u8 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 --git a/drivers/isdn/hisax/l3ni1.c b/drivers/isdn/hisax/l3ni1.c
new file mode 100644
index 0000000..f7041d5
--- /dev/null
+++ b/drivers/isdn/hisax/l3ni1.c
@@ -0,0 +1,3189 @@
+/* $Id: l3ni1.c,v 2.8.2.3 2004/01/13 14:31:25 keil Exp $
+ *
+ * NI1 D-channel protocol
+ *
+ * Author Matt Henderson & Guy Ellis
+ * Copyright by Traverse Technologies Pty Ltd, www.travers.com.au
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * 2000.6.6 Initial implementation of routines for US NI1
+ * Layer 3 protocol based on the EURO/DSS1 D-channel protocol
+ * driver written by Karsten Keil et al.
+ * NI-1 Hall of Fame - Thanks to....
+ * Ragnar Paulson - for some handy code fragments
+ * Will Scales - beta tester extraordinaire
+ * Brett Whittacre - beta tester and remote devel system in Vegas
+ *
+ */
+
+#include "hisax.h"
+#include "isdnl3.h"
+#include "l3ni1.h"
+#include <linux/ctype.h>
+
+extern char *HiSax_getrev(const char *revision);
+static const char *ni1_revision = "$Revision: 2.8.2.3 $";
+
+#define EXT_BEARER_CAPS 1
+
+#define MsgHead(ptr, cref, mty) \
+ *ptr++ = 0x8; \
+ if (cref == -1) { \
+ *ptr++ = 0x0; \
+ } else { \
+ *ptr++ = 0x1; \
+ *ptr++ = cref^0x80; \
+ } \
+ *ptr++ = mty
+
+
+/**********************************************/
+/* get a new invoke id for remote operations. */
+/* Only a return value != 0 is valid */
+/**********************************************/
+static unsigned char new_invoke_id(struct PStack *p)
+{
+ unsigned char retval;
+ int i;
+
+ i = 32; /* maximum search depth */
+
+ retval = p->prot.ni1.last_invoke_id + 1; /* try new id */
+ while ((i) && (p->prot.ni1.invoke_used[retval >> 3] == 0xFF)) {
+ p->prot.ni1.last_invoke_id = (retval & 0xF8) + 8;
+ i--;
+ }
+ if (i) {
+ while (p->prot.ni1.invoke_used[retval >> 3] & (1 << (retval & 7)))
+ retval++;
+ } else
+ retval = 0;
+ p->prot.ni1.last_invoke_id = retval;
+ p->prot.ni1.invoke_used[retval >> 3] |= (1 << (retval & 7));
+ return(retval);
+} /* new_invoke_id */
+
+/*************************/
+/* free a used invoke id */
+/*************************/
+static void free_invoke_id(struct PStack *p, unsigned char id)
+{
+
+ if (!id) return; /* 0 = invalid value */
+
+ p->prot.ni1.invoke_used[id >> 3] &= ~(1 << (id & 7));
+} /* free_invoke_id */
+
+
+/**********************************************************/
+/* create a new l3 process and fill in ni1 specific data */
+/**********************************************************/
+static struct l3_process
+*ni1_new_l3_process(struct PStack *st, int cr)
+{ struct l3_process *proc;
+
+ if (!(proc = new_l3_process(st, cr)))
+ return(NULL);
+
+ proc->prot.ni1.invoke_id = 0;
+ proc->prot.ni1.remote_operation = 0;
+ proc->prot.ni1.uus1_data[0] = '\0';
+
+ return(proc);
+} /* ni1_new_l3_process */
+
+/************************************************/
+/* free a l3 process and all ni1 specific data */
+/************************************************/
+static void
+ni1_release_l3_process(struct l3_process *p)
+{
+ free_invoke_id(p->st,p->prot.ni1.invoke_id);
+ release_l3_process(p);
+} /* ni1_release_l3_process */
+
+/********************************************************/
+/* search a process with invoke id id and dummy callref */
+/********************************************************/
+static struct l3_process *
+l3ni1_search_dummy_proc(struct PStack *st, int id)
+{ struct l3_process *pc = st->l3.proc; /* start of processes */
+
+ if (!id) return(NULL);
+
+ while (pc)
+ { if ((pc->callref == -1) && (pc->prot.ni1.invoke_id == id))
+ return(pc);
+ pc = pc->next;
+ }
+ return(NULL);
+} /* l3ni1_search_dummy_proc */
+
+/*******************************************************************/
+/* called when a facility message with a dummy callref is received */
+/* and a return result is delivered. id specifies the invoke id. */
+/*******************************************************************/
+static void
+l3ni1_dummy_return_result(struct PStack *st, int id, u_char *p, u_char nlen)
+{ isdn_ctrl ic;
+ struct IsdnCardState *cs;
+ struct l3_process *pc = NULL;
+
+ if ((pc = l3ni1_search_dummy_proc(st, id)))
+ { L3DelTimer(&pc->timer); /* remove timer */
+
+ cs = pc->st->l1.hardware;
+ ic.driver = cs->myid;
+ ic.command = ISDN_STAT_PROT;
+ ic.arg = NI1_STAT_INVOKE_RES;
+ ic.parm.ni1_io.hl_id = pc->prot.ni1.invoke_id;
+ ic.parm.ni1_io.ll_id = pc->prot.ni1.ll_id;
+ ic.parm.ni1_io.proc = pc->prot.ni1.proc;
+ ic.parm.ni1_io.timeout= 0;
+ ic.parm.ni1_io.datalen = nlen;
+ ic.parm.ni1_io.data = p;
+ free_invoke_id(pc->st, pc->prot.ni1.invoke_id);
+ pc->prot.ni1.invoke_id = 0; /* reset id */
+
+ cs->iif.statcallb(&ic);
+ ni1_release_l3_process(pc);
+ }
+ else
+ l3_debug(st, "dummy return result id=0x%x result len=%d",id,nlen);
+} /* l3ni1_dummy_return_result */
+
+/*******************************************************************/
+/* called when a facility message with a dummy callref is received */
+/* and a return error is delivered. id specifies the invoke id. */
+/*******************************************************************/
+static void
+l3ni1_dummy_error_return(struct PStack *st, int id, ulong error)
+{ isdn_ctrl ic;
+ struct IsdnCardState *cs;
+ struct l3_process *pc = NULL;
+
+ if ((pc = l3ni1_search_dummy_proc(st, id)))
+ { L3DelTimer(&pc->timer); /* remove timer */
+
+ cs = pc->st->l1.hardware;
+ ic.driver = cs->myid;
+ ic.command = ISDN_STAT_PROT;
+ ic.arg = NI1_STAT_INVOKE_ERR;
+ ic.parm.ni1_io.hl_id = pc->prot.ni1.invoke_id;
+ ic.parm.ni1_io.ll_id = pc->prot.ni1.ll_id;
+ ic.parm.ni1_io.proc = pc->prot.ni1.proc;
+ ic.parm.ni1_io.timeout= error;
+ ic.parm.ni1_io.datalen = 0;
+ ic.parm.ni1_io.data = NULL;
+ free_invoke_id(pc->st, pc->prot.ni1.invoke_id);
+ pc->prot.ni1.invoke_id = 0; /* reset id */
+
+ cs->iif.statcallb(&ic);
+ ni1_release_l3_process(pc);
+ }
+ else
+ l3_debug(st, "dummy return error id=0x%x error=0x%lx",id,error);
+} /* l3ni1_error_return */
+
+/*******************************************************************/
+/* called when a facility message with a dummy callref is received */
+/* and a invoke is delivered. id specifies the invoke id. */
+/*******************************************************************/
+static void
+l3ni1_dummy_invoke(struct PStack *st, int cr, int id,
+ int ident, u_char *p, u_char nlen)
+{ isdn_ctrl ic;
+ struct IsdnCardState *cs;
+
+ l3_debug(st, "dummy invoke %s id=0x%x ident=0x%x datalen=%d",
+ (cr == -1) ? "local" : "broadcast",id,ident,nlen);
+ if (cr >= -1) return; /* ignore local data */
+
+ cs = st->l1.hardware;
+ ic.driver = cs->myid;
+ ic.command = ISDN_STAT_PROT;
+ ic.arg = NI1_STAT_INVOKE_BRD;
+ ic.parm.ni1_io.hl_id = id;
+ ic.parm.ni1_io.ll_id = 0;
+ ic.parm.ni1_io.proc = ident;
+ ic.parm.ni1_io.timeout= 0;
+ ic.parm.ni1_io.datalen = nlen;
+ ic.parm.ni1_io.data = p;
+
+ cs->iif.statcallb(&ic);
+} /* l3ni1_dummy_invoke */
+
+static void
+l3ni1_parse_facility(struct PStack *st, struct l3_process *pc,
+ int cr, u_char * p)
+{
+ int qd_len = 0;
+ unsigned char nlen = 0, ilen, cp_tag;
+ int ident, id;
+ ulong err_ret;
+
+ if (pc)
+ st = pc->st; /* valid Stack */
+ else
+ if ((!st) || (cr >= 0)) return; /* neither pc nor st specified */
+
+ p++;
+ qd_len = *p++;
+ if (qd_len == 0) {
+ l3_debug(st, "qd_len == 0");
+ return;
+ }
+ if ((*p & 0x1F) != 0x11) { /* Service discriminator, supplementary service */
+ l3_debug(st, "supplementary service != 0x11");
+ return;
+ }
+ while (qd_len > 0 && !(*p & 0x80)) { /* extension ? */
+ p++;
+ qd_len--;
+ }
+ if (qd_len < 2) {
+ l3_debug(st, "qd_len < 2");
+ return;
+ }
+ p++;
+ qd_len--;
+ if ((*p & 0xE0) != 0xA0) { /* class and form */
+ l3_debug(st, "class and form != 0xA0");
+ return;
+ }
+
+ cp_tag = *p & 0x1F; /* remember tag value */
+
+ p++;
+ qd_len--;
+ if (qd_len < 1)
+ { l3_debug(st, "qd_len < 1");
+ return;
+ }
+ if (*p & 0x80)
+ { /* length format indefinite or limited */
+ nlen = *p++ & 0x7F; /* number of len bytes or indefinite */
+ if ((qd_len-- < ((!nlen) ? 3 : (1 + nlen))) ||
+ (nlen > 1))
+ { l3_debug(st, "length format error or not implemented");
+ return;
+ }
+ if (nlen == 1)
+ { nlen = *p++; /* complete length */
+ qd_len--;
+ }
+ else
+ { qd_len -= 2; /* trailing null bytes */
+ if ((*(p+qd_len)) || (*(p+qd_len+1)))
+ { l3_debug(st,"length format indefinite error");
+ return;
+ }
+ nlen = qd_len;
+ }
+ }
+ else
+ { nlen = *p++;
+ qd_len--;
+ }
+ if (qd_len < nlen)
+ { l3_debug(st, "qd_len < nlen");
+ return;
+ }
+ qd_len -= nlen;
+
+ if (nlen < 2)
+ { l3_debug(st, "nlen < 2");
+ return;
+ }
+ if (*p != 0x02)
+ { /* invoke identifier tag */
+ l3_debug(st, "invoke identifier tag !=0x02");
+ return;
+ }
+ p++;
+ nlen--;
+ if (*p & 0x80)
+ { /* length format */
+ l3_debug(st, "invoke id length format 2");
+ return;
+ }
+ ilen = *p++;
+ nlen--;
+ if (ilen > nlen || ilen == 0)
+ { l3_debug(st, "ilen > nlen || ilen == 0");
+ return;
+ }
+ nlen -= ilen;
+ id = 0;
+ while (ilen > 0)
+ { id = (id << 8) | (*p++ & 0xFF); /* invoke identifier */
+ ilen--;
+ }
+
+ switch (cp_tag) { /* component tag */
+ case 1: /* invoke */
+ if (nlen < 2) {
+ l3_debug(st, "nlen < 2 22");
+ return;
+ }
+ if (*p != 0x02) { /* operation value */
+ l3_debug(st, "operation value !=0x02");
+ return;
+ }
+ p++;
+ nlen--;
+ ilen = *p++;
+ nlen--;
+ if (ilen > nlen || ilen == 0) {
+ l3_debug(st, "ilen > nlen || ilen == 0 22");
+ return;
+ }
+ nlen -= ilen;
+ ident = 0;
+ while (ilen > 0) {
+ ident = (ident << 8) | (*p++ & 0xFF);
+ ilen--;
+ }
+
+ if (!pc)
+ {
+ l3ni1_dummy_invoke(st, cr, id, ident, p, nlen);
+ return;
+ }
+ l3_debug(st, "invoke break");
+ break;
+ case 2: /* return result */
+ /* if no process available handle separately */
+ if (!pc)
+ { if (cr == -1)
+ l3ni1_dummy_return_result(st, id, p, nlen);
+ return;
+ }
+ if ((pc->prot.ni1.invoke_id) && (pc->prot.ni1.invoke_id == id))
+ { /* Diversion successful */
+ free_invoke_id(st,pc->prot.ni1.invoke_id);
+ pc->prot.ni1.remote_result = 0; /* success */
+ pc->prot.ni1.invoke_id = 0;
+ pc->redir_result = pc->prot.ni1.remote_result;
+ st->l3.l3l4(st, CC_REDIR | INDICATION, pc); } /* Diversion successful */
+ else
+ l3_debug(st,"return error unknown identifier");
+ break;
+ case 3: /* return error */
+ err_ret = 0;
+ if (nlen < 2)
+ { l3_debug(st, "return error nlen < 2");
+ return;
+ }
+ if (*p != 0x02)
+ { /* result tag */
+ l3_debug(st, "invoke error tag !=0x02");
+ return;
+ }
+ p++;
+ nlen--;
+ if (*p > 4)
+ { /* length format */
+ l3_debug(st, "invoke return errlen > 4 ");
+ return;
+ }
+ ilen = *p++;
+ nlen--;
+ if (ilen > nlen || ilen == 0)
+ { l3_debug(st, "error return ilen > nlen || ilen == 0");
+ return;
+ }
+ nlen -= ilen;
+ while (ilen > 0)
+ { err_ret = (err_ret << 8) | (*p++ & 0xFF); /* error value */
+ ilen--;
+ }
+ /* if no process available handle separately */
+ if (!pc)
+ { if (cr == -1)
+ l3ni1_dummy_error_return(st, id, err_ret);
+ return;
+ }
+ if ((pc->prot.ni1.invoke_id) && (pc->prot.ni1.invoke_id == id))
+ { /* Deflection error */
+ free_invoke_id(st,pc->prot.ni1.invoke_id);
+ pc->prot.ni1.remote_result = err_ret; /* result */
+ pc->prot.ni1.invoke_id = 0;
+ pc->redir_result = pc->prot.ni1.remote_result;
+ st->l3.l3l4(st, CC_REDIR | INDICATION, pc);
+ } /* Deflection error */
+ else
+ l3_debug(st,"return result unknown identifier");
+ break;
+ default:
+ l3_debug(st, "facility default break tag=0x%02x",cp_tag);
+ break;
+ }
+}
+
+static void
+l3ni1_message(struct l3_process *pc, u_char mt)
+{
+ struct sk_buff *skb;
+ u_char *p;
+
+ if (!(skb = l3_alloc_skb(4)))
+ return;
+ p = skb_put(skb, 4);
+ MsgHead(p, pc->callref, mt);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3ni1_message_plus_chid(struct l3_process *pc, u_char mt)
+/* sends an l3 messages plus channel id - added GE 05/09/00 */
+{
+ struct sk_buff *skb;
+ u_char tmp[16];
+ u_char *p = tmp;
+ u_char chid;
+
+ chid = (u_char)(pc->para.bchannel & 0x03) | 0x88;
+ MsgHead(p, pc->callref, mt);
+ *p++ = IE_CHANNEL_ID;
+ *p++ = 0x01;
+ *p++ = chid;
+
+ if (!(skb = l3_alloc_skb(7)))
+ return;
+ memcpy(skb_put(skb, 7), tmp, 7);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3ni1_message_cause(struct l3_process *pc, u_char mt, u_char cause)
+{
+ struct sk_buff *skb;
+ u_char tmp[16];
+ u_char *p = tmp;
+ int l;
+
+ MsgHead(p, pc->callref, mt);
+ *p++ = IE_CAUSE;
+ *p++ = 0x2;
+ *p++ = 0x80;
+ *p++ = cause | 0x80;
+
+ l = p - tmp;
+ if (!(skb = l3_alloc_skb(l)))
+ return;
+ memcpy(skb_put(skb, l), tmp, l);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3ni1_status_send(struct l3_process *pc, u_char pr, void *arg)
+{
+ u_char tmp[16];
+ u_char *p = tmp;
+ int l;
+ struct sk_buff *skb;
+
+ MsgHead(p, pc->callref, MT_STATUS);
+
+ *p++ = IE_CAUSE;
+ *p++ = 0x2;
+ *p++ = 0x80;
+ *p++ = pc->para.cause | 0x80;
+
+ *p++ = IE_CALL_STATE;
+ *p++ = 0x1;
+ *p++ = pc->state & 0x3f;
+
+ l = p - tmp;
+ if (!(skb = l3_alloc_skb(l)))
+ return;
+ memcpy(skb_put(skb, l), tmp, l);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3ni1_msg_without_setup(struct l3_process *pc, u_char pr, void *arg)
+{
+ /* This routine is called if here was no SETUP made (checks in ni1up and in
+ * l3ni1_setup) and a RELEASE_COMPLETE have to be sent with an error code
+ * MT_STATUS_ENQUIRE in the NULL state is handled too
+ */
+ u_char tmp[16];
+ u_char *p = tmp;
+ int l;
+ struct sk_buff *skb;
+
+ switch (pc->para.cause) {
+ case 81: /* invalid callreference */
+ case 88: /* incomp destination */
+ case 96: /* mandory IE missing */
+ case 100: /* invalid IE contents */
+ case 101: /* incompatible Callstate */
+ MsgHead(p, pc->callref, MT_RELEASE_COMPLETE);
+ *p++ = IE_CAUSE;
+ *p++ = 0x2;
+ *p++ = 0x80;
+ *p++ = pc->para.cause | 0x80;
+ break;
+ default:
+ printk(KERN_ERR "HiSax l3ni1_msg_without_setup wrong cause %d\n",
+ pc->para.cause);
+ return;
+ }
+ l = p - tmp;
+ if (!(skb = l3_alloc_skb(l)))
+ return;
+ memcpy(skb_put(skb, l), tmp, l);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+ ni1_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_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_DISPLAY, IE_FACILITY, -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(struct l3_process *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(struct l3_process *pc, struct sk_buff *skb, int *checklist)
+{
+ int *cl = checklist;
+ u_char mt;
+ u_char *p, ie;
+ int l, newpos, oldpos;
+ int err_seq = 0, err_len = 0, err_compr = 0, err_ureg = 0;
+ u_char codeset = 0;
+ u_char old_codeset = 0;
+ u_char codelock = 1;
+
+ p = skb->data;
+ /* skip cr */
+ p++;
+ l = (*p++) & 0xf;
+ p += l;
+ mt = *p++;
+ oldpos = 0;
+ while ((p - skb->data) < skb->len) {
+ if ((*p & 0xf0) == 0x90) { /* shift codeset */
+ old_codeset = codeset;
+ codeset = *p & 7;
+ if (*p & 0x08)
+ codelock = 0;
+ else
+ codelock = 1;
+ if (pc->debug & L3_DEB_CHECK)
+ l3_debug(pc->st, "check IE shift%scodeset %d->%d",
+ codelock ? " locking ": " ", old_codeset, codeset);
+ p++;
+ continue;
+ }
+ if (!codeset) { /* only codeset 0 */
+ if ((newpos = ie_in_set(pc, *p, cl))) {
+ if (newpos > 0) {
+ if (newpos < oldpos)
+ err_seq++;
+ else
+ oldpos = newpos;
+ }
+ } else {
+ if (ie_in_set(pc, *p, comp_required))
+ err_compr++;
+ else
+ err_ureg++;
+ }
+ }
+ ie = *p++;
+ if (ie & 0x80) {
+ l = 1;
+ } else {
+ l = *p++;
+ p += l;
+ l += 2;
+ }
+ if (!codeset && (l > getmax_ie_len(ie)))
+ err_len++;
+ if (!codelock) {
+ if (pc->debug & L3_DEB_CHECK)
+ l3_debug(pc->st, "check IE shift back codeset %d->%d",
+ codeset, old_codeset);
+ codeset = old_codeset;
+ codelock = 1;
+ }
+ }
+ if (err_compr | err_ureg | err_len | err_seq) {
+ if (pc->debug & L3_DEB_CHECK)
+ l3_debug(pc->st, "check IE MT(%x) %d/%d/%d/%d",
+ mt, 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
+l3ni1_check_messagetype_validity(struct l3_process *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->debug & L3_DEB_CHECK)
+ l3_debug(pc->st, "l3ni1_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->debug & (L3_DEB_CHECK | L3_DEB_WARN))
+ l3_debug(pc->st, "l3ni1_check_messagetype_validity mt(%x) fail", mt);
+ pc->para.cause = 97;
+ l3ni1_status_send(pc, 0, NULL);
+ return(1);
+ }
+ return(0);
+}
+
+static void
+l3ni1_std_ie_err(struct l3_process *pc, int ret) {
+
+ if (pc->debug & L3_DEB_CHECK)
+ l3_debug(pc->st, "check_infoelements ret %d", ret);
+ switch(ret) {
+ case 0:
+ break;
+ case ERR_IE_COMPREHENSION:
+ pc->para.cause = 96;
+ l3ni1_status_send(pc, 0, NULL);
+ break;
+ case ERR_IE_UNRECOGNIZED:
+ pc->para.cause = 99;
+ l3ni1_status_send(pc, 0, NULL);
+ break;
+ case ERR_IE_LENGTH:
+ pc->para.cause = 100;
+ l3ni1_status_send(pc, 0, NULL);
+ break;
+ case ERR_IE_SEQUENCE:
+ default:
+ break;
+ }
+}
+
+static int
+l3ni1_get_channel_id(struct l3_process *pc, struct sk_buff *skb) {
+ u_char *p;
+
+ p = skb->data;
+ if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) {
+ p++;
+ if (*p != 1) { /* len for BRI = 1 */
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "wrong chid len %d", *p);
+ return (-2);
+ }
+ p++;
+ if (*p & 0x60) { /* only base rate interface */
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "wrong chid %x", *p);
+ return (-3);
+ }
+ return(*p & 0x3);
+ } else
+ return(-1);
+}
+
+static int
+l3ni1_get_cause(struct l3_process *pc, struct sk_buff *skb) {
+ u_char l, i=0;
+ u_char *p;
+
+ p = skb->data;
+ pc->para.cause = 31;
+ pc->para.loc = 0;
+ if ((p = findie(p, skb->len, IE_CAUSE, 0))) {
+ p++;
+ l = *p++;
+ if (l>30)
+ return(1);
+ if (l) {
+ pc->para.loc = *p++;
+ l--;
+ } else {
+ return(2);
+ }
+ if (l && !(pc->para.loc & 0x80)) {
+ l--;
+ p++; /* skip recommendation */
+ }
+ if (l) {
+ pc->para.cause = *p++;
+ l--;
+ if (!(pc->para.cause & 0x80))
+ return(3);
+ } else
+ return(4);
+ while (l && (i<6)) {
+ pc->para.diag[i++] = *p++;
+ l--;
+ }
+ } else
+ return(-1);
+ return(0);
+}
+
+static void
+l3ni1_msg_with_uus(struct l3_process *pc, u_char cmd)
+{
+ struct sk_buff *skb;
+ u_char tmp[16+40];
+ u_char *p = tmp;
+ int l;
+
+ MsgHead(p, pc->callref, cmd);
+
+ if (pc->prot.ni1.uus1_data[0])
+ { *p++ = IE_USER_USER; /* UUS info element */
+ *p++ = strlen(pc->prot.ni1.uus1_data) + 1;
+ *p++ = 0x04; /* IA5 chars */
+ strcpy(p,pc->prot.ni1.uus1_data);
+ p += strlen(pc->prot.ni1.uus1_data);
+ pc->prot.ni1.uus1_data[0] = '\0';
+ }
+
+ l = p - tmp;
+ if (!(skb = l3_alloc_skb(l)))
+ return;
+ memcpy(skb_put(skb, l), tmp, l);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+} /* l3ni1_msg_with_uus */
+
+static void
+l3ni1_release_req(struct l3_process *pc, u_char pr, void *arg)
+{
+ StopAllL3Timer(pc);
+ newl3state(pc, 19);
+ if (!pc->prot.ni1.uus1_data[0])
+ l3ni1_message(pc, MT_RELEASE);
+ else
+ l3ni1_msg_with_uus(pc, MT_RELEASE);
+ L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3ni1_release_cmpl(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ int ret;
+
+ if ((ret = l3ni1_get_cause(pc, skb))>0) {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "RELCMPL get_cause ret(%d)",ret);
+ } else if (ret < 0)
+ pc->para.cause = NO_CAUSE;
+ StopAllL3Timer(pc);
+ newl3state(pc, 0);
+ pc->st->l3.l3l4(pc->st, CC_RELEASE | CONFIRM, pc);
+ ni1_release_l3_process(pc);
+}
+
+#if EXT_BEARER_CAPS
+
+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)
+{
+ info &= 0x7f;
+ switch (info) {
+ 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(struct sk_buff *skb)
+{
+ u_char *p; //, *pend=skb->data + skb->len;
+
+ if ((p = findie(skb->data, skb->len, 0x7c, 0))) {
+ 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;
+}
+
+#endif
+
+
+static void
+l3ni1_setup_req(struct l3_process *pc, u_char pr,
+ void *arg)
+{
+ struct sk_buff *skb;
+ u_char tmp[128];
+ u_char *p = tmp;
+
+ u_char *teln;
+ u_char *sub;
+ u_char *sp;
+ int l;
+
+ MsgHead(p, pc->callref, MT_SETUP);
+
+ teln = pc->para.setup.phone;
+
+ *p++ = 0xa1; /* complete indicator */
+ /*
+ * Set Bearer Capability, Map info from 1TR6-convention to NI1
+ */
+ switch (pc->para.setup.si1) {
+ case 1: /* Telephony */
+ *p++ = IE_BEARER;
+ *p++ = 0x3; /* Length */
+ *p++ = 0x90; /* 3.1khz Audio */
+ *p++ = 0x90; /* Circuit-Mode 64kbps */
+ *p++ = 0xa2; /* u-Law Audio */
+ break;
+ case 5: /* Datatransmission 64k, BTX */
+ case 7: /* Datatransmission 64k */
+ default:
+ *p++ = IE_BEARER;
+ *p++ = 0x2; /* Length */
+ *p++ = 0x88; /* Coding Std. CCITT, unrestr. dig. Inform. */
+ *p++ = 0x90; /* Circuit-Mode 64kbps */
+ break;
+ }
+
+ sub = NULL;
+ sp = teln;
+ while (*sp) {
+ if ('.' == *sp) {
+ sub = sp;
+ *sp = 0;
+ } else
+ sp++;
+ }
+
+ *p++ = IE_KEYPAD;
+ *p++ = strlen(teln);
+ while (*teln)
+ *p++ = (*teln++) & 0x7F;
+
+ if (sub)
+ *sub++ = '.';
+
+#if EXT_BEARER_CAPS
+ if ((pc->para.setup.si2 >= 160) && (pc->para.setup.si2 <= 175)) { // sync. Bitratenadaption, V.110/X.30
+
+ *p++ = IE_LLC;
+ *p++ = 0x04;
+ *p++ = 0x88;
+ *p++ = 0x90;
+ *p++ = 0x21;
+ *p++ = EncodeSyncParams(pc->para.setup.si2 - 160, 0x80);
+ } else if ((pc->para.setup.si2 >= 176) && (pc->para.setup.si2 <= 191)) { // sync. Bitratenadaption, V.120
+
+ *p++ = IE_LLC;
+ *p++ = 0x05;
+ *p++ = 0x88;
+ *p++ = 0x90;
+ *p++ = 0x28;
+ *p++ = EncodeSyncParams(pc->para.setup.si2 - 176, 0);
+ *p++ = 0x82;
+ } else if (pc->para.setup.si2 >= 192) { // async. Bitratenadaption, V.110/X.30
+
+ *p++ = IE_LLC;
+ *p++ = 0x06;
+ *p++ = 0x88;
+ *p++ = 0x90;
+ *p++ = 0x21;
+ p = EncodeASyncParams(p, pc->para.setup.si2 - 192);
+ } else {
+ switch (pc->para.setup.si1) {
+ case 1: /* Telephony */
+ *p++ = IE_LLC;
+ *p++ = 0x3; /* Length */
+ *p++ = 0x90; /* Coding Std. CCITT, 3.1 kHz audio */
+ *p++ = 0x90; /* Circuit-Mode 64kbps */
+ *p++ = 0xa2; /* u-Law Audio */
+ break;
+ case 5: /* Datatransmission 64k, BTX */
+ case 7: /* Datatransmission 64k */
+ default:
+ *p++ = IE_LLC;
+ *p++ = 0x2; /* Length */
+ *p++ = 0x88; /* Coding Std. CCITT, unrestr. dig. Inform. */
+ *p++ = 0x90; /* Circuit-Mode 64kbps */
+ break;
+ }
+ }
+#endif
+ l = p - tmp;
+ if (!(skb = l3_alloc_skb(l)))
+{
+ return;
+}
+ memcpy(skb_put(skb, l), tmp, l);
+ L3DelTimer(&pc->timer);
+ L3AddTimer(&pc->timer, T303, CC_T303);
+ newl3state(pc, 1);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3ni1_call_proc(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ int id, ret;
+
+ if ((id = l3ni1_get_channel_id(pc, skb)) >= 0) {
+ if ((0 == id) || ((3 == id) && (0x10 == pc->para.moderate))) {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "setup answer with wrong chid %x", id);
+ pc->para.cause = 100;
+ l3ni1_status_send(pc, pr, NULL);
+ return;
+ }
+ pc->para.bchannel = id;
+ } else if (1 == pc->state) {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "setup answer wrong chid (ret %d)", id);
+ if (id == -1)
+ pc->para.cause = 96;
+ else
+ pc->para.cause = 100;
+ l3ni1_status_send(pc, pr, NULL);
+ return;
+ }
+ /* Now we are on none mandatory IEs */
+ ret = check_infoelements(pc, skb, ie_CALL_PROCEEDING);
+ if (ERR_IE_COMPREHENSION == ret) {
+ l3ni1_std_ie_err(pc, ret);
+ return;
+ }
+ L3DelTimer(&pc->timer);
+ newl3state(pc, 3);
+ L3AddTimer(&pc->timer, T310, CC_T310);
+ if (ret) /* STATUS for none mandatory IE errors after actions are taken */
+ l3ni1_std_ie_err(pc, ret);
+ pc->st->l3.l3l4(pc->st, CC_PROCEEDING | INDICATION, pc);
+}
+
+static void
+l3ni1_setup_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ int id, ret;
+
+ if ((id = l3ni1_get_channel_id(pc, skb)) >= 0) {
+ if ((0 == id) || ((3 == id) && (0x10 == pc->para.moderate))) {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "setup answer with wrong chid %x", id);
+ pc->para.cause = 100;
+ l3ni1_status_send(pc, pr, NULL);
+ return;
+ }
+ pc->para.bchannel = id;
+ } else {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "setup answer wrong chid (ret %d)", id);
+ if (id == -1)
+ pc->para.cause = 96;
+ else
+ pc->para.cause = 100;
+ l3ni1_status_send(pc, pr, NULL);
+ return;
+ }
+ /* Now we are on none mandatory IEs */
+ ret = check_infoelements(pc, skb, ie_SETUP_ACKNOWLEDGE);
+ if (ERR_IE_COMPREHENSION == ret) {
+ l3ni1_std_ie_err(pc, ret);
+ return;
+ }
+ L3DelTimer(&pc->timer);
+ newl3state(pc, 2);
+ L3AddTimer(&pc->timer, T304, CC_T304);
+ if (ret) /* STATUS for none mandatory IE errors after actions are taken */
+ l3ni1_std_ie_err(pc, ret);
+ pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
+}
+
+static void
+l3ni1_disconnect(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ u_char *p;
+ int ret;
+ u_char cause = 0;
+
+ StopAllL3Timer(pc);
+ if ((ret = l3ni1_get_cause(pc, skb))) {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "DISC get_cause ret(%d)", ret);
+ if (ret < 0)
+ cause = 96;
+ else if (ret > 0)
+ cause = 100;
+ }
+ if ((p = findie(skb->data, skb->len, IE_FACILITY, 0)))
+ l3ni1_parse_facility(pc->st, pc, pc->callref, p);
+ ret = check_infoelements(pc, skb, ie_DISCONNECT);
+ if (ERR_IE_COMPREHENSION == ret)
+ cause = 96;
+ else if ((!cause) && (ERR_IE_UNRECOGNIZED == ret))
+ cause = 99;
+ ret = pc->state;
+ newl3state(pc, 12);
+ if (cause)
+ newl3state(pc, 19);
+ if (11 != ret)
+ pc->st->l3.l3l4(pc->st, CC_DISCONNECT | INDICATION, pc);
+ else if (!cause)
+ l3ni1_release_req(pc, pr, NULL);
+ if (cause) {
+ l3ni1_message_cause(pc, MT_RELEASE, cause);
+ L3AddTimer(&pc->timer, T308, CC_T308_1);
+ }
+}
+
+static void
+l3ni1_connect(struct l3_process *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) {
+ l3ni1_std_ie_err(pc, ret);
+ return;
+ }
+ L3DelTimer(&pc->timer); /* T310 */
+ newl3state(pc, 10);
+ pc->para.chargeinfo = 0;
+ /* here should inserted COLP handling KKe */
+ if (ret)
+ l3ni1_std_ie_err(pc, ret);
+ pc->st->l3.l3l4(pc->st, CC_SETUP | CONFIRM, pc);
+}
+
+static void
+l3ni1_alerting(struct l3_process *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) {
+ l3ni1_std_ie_err(pc, ret);
+ return;
+ }
+ L3DelTimer(&pc->timer); /* T304 */
+ newl3state(pc, 4);
+ if (ret)
+ l3ni1_std_ie_err(pc, ret);
+ pc->st->l3.l3l4(pc->st, CC_ALERTING | INDICATION, pc);
+}
+
+static void
+l3ni1_setup(struct l3_process *pc, u_char pr, void *arg)
+{
+ u_char *p;
+ int bcfound = 0;
+ char tmp[80];
+ struct sk_buff *skb = arg;
+ int id;
+ int err = 0;
+
+ /*
+ * Bearer Capabilities
+ */
+ p = skb->data;
+ /* only the first occurence 'll be detected ! */
+ if ((p = findie(p, skb->len, 0x04, 0))) {
+ if ((p[1] < 2) || (p[1] > 11))
+ err = 1;
+ else {
+ pc->para.setup.si2 = 0;
+ switch (p[2] & 0x7f) {
+ case 0x00: /* Speech */
+ case 0x10: /* 3.1 Khz audio */
+ pc->para.setup.si1 = 1;
+ break;
+ case 0x08: /* Unrestricted digital information */
+ pc->para.setup.si1 = 7;
+/* JIM, 05.11.97 I wanna set service indicator 2 */
+#if EXT_BEARER_CAPS
+ pc->para.setup.si2 = DecodeSI2(skb);
+#endif
+ break;
+ case 0x09: /* Restricted digital information */
+ pc->para.setup.si1 = 2;
+ break;
+ case 0x11:
+ /* Unrestr. digital information with
+ * tones/announcements ( or 7 kHz audio
+ */
+ pc->para.setup.si1 = 3;
+ break;
+ case 0x18: /* Video */
+ pc->para.setup.si1 = 4;
+ break;
+ default:
+ err = 2;
+ break;
+ }
+ switch (p[3] & 0x7f) {
+ case 0x40: /* packed mode */
+ pc->para.setup.si1 = 8;
+ break;
+ case 0x10: /* 64 kbit */
+ case 0x11: /* 2*64 kbit */
+ case 0x13: /* 384 kbit */
+ case 0x15: /* 1536 kbit */
+ case 0x17: /* 1920 kbit */
+ pc->para.moderate = p[3] & 0x7f;
+ break;
+ default:
+ err = 3;
+ break;
+ }
+ }
+ if (pc->debug & L3_DEB_SI)
+ l3_debug(pc->st, "SI=%d, AI=%d",
+ pc->para.setup.si1, pc->para.setup.si2);
+ if (err) {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "setup with wrong bearer(l=%d:%x,%x)",
+ p[1], p[2], p[3]);
+ pc->para.cause = 100;
+ l3ni1_msg_without_setup(pc, pr, NULL);
+ return;
+ }
+ } else {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "setup without bearer capabilities");
+ /* ETS 300-104 1.3.3 */
+ pc->para.cause = 96;
+ l3ni1_msg_without_setup(pc, pr, NULL);
+ return;
+ }
+ /*
+ * Channel Identification
+ */
+ if ((id = l3ni1_get_channel_id(pc, skb)) >= 0) {
+ if ((pc->para.bchannel = id)) {
+ if ((3 == id) && (0x10 == pc->para.moderate)) {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "setup with wrong chid %x",
+ id);
+ pc->para.cause = 100;
+ l3ni1_msg_without_setup(pc, pr, NULL);
+ return;
+ }
+ bcfound++;
+ } else
+ { if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "setup without bchannel, call waiting");
+ bcfound++;
+ }
+ } else {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "setup with wrong chid ret %d", id);
+ if (id == -1)
+ pc->para.cause = 96;
+ else
+ pc->para.cause = 100;
+ l3ni1_msg_without_setup(pc, pr, NULL);
+ return;
+ }
+ /* Now we are on none mandatory IEs */
+ err = check_infoelements(pc, skb, ie_SETUP);
+ if (ERR_IE_COMPREHENSION == err) {
+ pc->para.cause = 96;
+ l3ni1_msg_without_setup(pc, pr, NULL);
+ return;
+ }
+ p = skb->data;
+ if ((p = findie(p, skb->len, 0x70, 0)))
+ iecpy(pc->para.setup.eazmsn, p, 1);
+ else
+ pc->para.setup.eazmsn[0] = 0;
+
+ p = skb->data;
+ if ((p = findie(p, skb->len, 0x71, 0))) {
+ /* Called party subaddress */
+ if ((p[1] >= 2) && (p[2] == 0x80) && (p[3] == 0x50)) {
+ tmp[0] = '.';
+ iecpy(&tmp[1], p, 2);
+ strcat(pc->para.setup.eazmsn, tmp);
+ } else if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "wrong called subaddress");
+ }
+ p = skb->data;
+ if ((p = findie(p, skb->len, 0x6c, 0))) {
+ pc->para.setup.plan = p[2];
+ if (p[2] & 0x80) {
+ iecpy(pc->para.setup.phone, p, 1);
+ pc->para.setup.screen = 0;
+ } else {
+ iecpy(pc->para.setup.phone, p, 2);
+ pc->para.setup.screen = p[3];
+ }
+ } else {
+ pc->para.setup.phone[0] = 0;
+ pc->para.setup.plan = 0;
+ pc->para.setup.screen = 0;
+ }
+ p = skb->data;
+ if ((p = findie(p, skb->len, 0x6d, 0))) {
+ /* Calling party subaddress */
+ if ((p[1] >= 2) && (p[2] == 0x80) && (p[3] == 0x50)) {
+ tmp[0] = '.';
+ iecpy(&tmp[1], p, 2);
+ strcat(pc->para.setup.phone, tmp);
+ } else if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "wrong calling subaddress");
+ }
+ newl3state(pc, 6);
+ if (err) /* STATUS for none mandatory IE errors after actions are taken */
+ l3ni1_std_ie_err(pc, err);
+ pc->st->l3.l3l4(pc->st, CC_SETUP | INDICATION, pc);
+}
+
+static void
+l3ni1_reset(struct l3_process *pc, u_char pr, void *arg)
+{
+ ni1_release_l3_process(pc);
+}
+
+static void
+l3ni1_disconnect_req(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb;
+ u_char tmp[16+40];
+ u_char *p = tmp;
+ int l;
+ u_char cause = 16;
+
+ if (pc->para.cause != NO_CAUSE)
+ cause = pc->para.cause;
+
+ StopAllL3Timer(pc);
+
+ MsgHead(p, pc->callref, MT_DISCONNECT);
+
+ *p++ = IE_CAUSE;
+ *p++ = 0x2;
+ *p++ = 0x80;
+ *p++ = cause | 0x80;
+
+ if (pc->prot.ni1.uus1_data[0])
+ { *p++ = IE_USER_USER; /* UUS info element */
+ *p++ = strlen(pc->prot.ni1.uus1_data) + 1;
+ *p++ = 0x04; /* IA5 chars */
+ strcpy(p,pc->prot.ni1.uus1_data);
+ p += strlen(pc->prot.ni1.uus1_data);
+ pc->prot.ni1.uus1_data[0] = '\0';
+ }
+
+ l = p - tmp;
+ if (!(skb = l3_alloc_skb(l)))
+ return;
+ memcpy(skb_put(skb, l), tmp, l);
+ newl3state(pc, 11);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+ L3AddTimer(&pc->timer, T305, CC_T305);
+}
+
+static void
+l3ni1_setup_rsp(struct l3_process *pc, u_char pr,
+ void *arg)
+{
+ if (!pc->para.bchannel)
+ { if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "D-chan connect for waiting call");
+ l3ni1_disconnect_req(pc, pr, arg);
+ return;
+ }
+ newl3state(pc, 8);
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "D-chan connect for waiting call");
+ l3ni1_message_plus_chid(pc, MT_CONNECT); /* GE 05/09/00 */
+ L3DelTimer(&pc->timer);
+ L3AddTimer(&pc->timer, T313, CC_T313);
+}
+
+static void
+l3ni1_connect_ack(struct l3_process *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) {
+ l3ni1_std_ie_err(pc, ret);
+ return;
+ }
+ newl3state(pc, 10);
+ L3DelTimer(&pc->timer);
+ if (ret)
+ l3ni1_std_ie_err(pc, ret);
+ pc->st->l3.l3l4(pc->st, CC_SETUP_COMPL | INDICATION, pc);
+}
+
+static void
+l3ni1_reject_req(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb;
+ u_char tmp[16];
+ u_char *p = tmp;
+ int l;
+ u_char cause = 21;
+
+ if (pc->para.cause != NO_CAUSE)
+ cause = pc->para.cause;
+
+ MsgHead(p, pc->callref, MT_RELEASE_COMPLETE);
+
+ *p++ = IE_CAUSE;
+ *p++ = 0x2;
+ *p++ = 0x80;
+ *p++ = cause | 0x80;
+
+ l = p - tmp;
+ if (!(skb = l3_alloc_skb(l)))
+ return;
+ memcpy(skb_put(skb, l), tmp, l);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+ pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+ newl3state(pc, 0);
+ ni1_release_l3_process(pc);
+}
+
+static void
+l3ni1_release(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ u_char *p;
+ int ret, cause=0;
+
+ StopAllL3Timer(pc);
+ if ((ret = l3ni1_get_cause(pc, skb))>0) {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "REL get_cause ret(%d)", ret);
+ } else if (ret<0)
+ pc->para.cause = NO_CAUSE;
+ if ((p = findie(skb->data, skb->len, IE_FACILITY, 0))) {
+ l3ni1_parse_facility(pc->st, pc, pc->callref, p);
+ }
+ if ((ret<0) && (pc->state != 11))
+ cause = 96;
+ else if (ret>0)
+ cause = 100;
+ ret = check_infoelements(pc, skb, ie_RELEASE);
+ if (ERR_IE_COMPREHENSION == ret)
+ cause = 96;
+ else if ((ERR_IE_UNRECOGNIZED == ret) && (!cause))
+ cause = 99;
+ if (cause)
+ l3ni1_message_cause(pc, MT_RELEASE_COMPLETE, cause);
+ else
+ l3ni1_message(pc, MT_RELEASE_COMPLETE);
+ pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+ newl3state(pc, 0);
+ ni1_release_l3_process(pc);
+}
+
+static void
+l3ni1_alert_req(struct l3_process *pc, u_char pr,
+ void *arg)
+{
+ newl3state(pc, 7);
+ if (!pc->prot.ni1.uus1_data[0])
+ l3ni1_message(pc, MT_ALERTING);
+ else
+ l3ni1_msg_with_uus(pc, MT_ALERTING);
+}
+
+static void
+l3ni1_proceed_req(struct l3_process *pc, u_char pr,
+ void *arg)
+{
+ newl3state(pc, 9);
+ l3ni1_message(pc, MT_CALL_PROCEEDING);
+ pc->st->l3.l3l4(pc->st, CC_PROCEED_SEND | INDICATION, pc);
+}
+
+static void
+l3ni1_setup_ack_req(struct l3_process *pc, u_char pr,
+ void *arg)
+{
+ newl3state(pc, 25);
+ L3DelTimer(&pc->timer);
+ L3AddTimer(&pc->timer, T302, CC_T302);
+ l3ni1_message(pc, MT_SETUP_ACKNOWLEDGE);
+}
+
+/********************************************/
+/* deliver a incoming display message to HL */
+/********************************************/
+static void
+l3ni1_deliver_display(struct l3_process *pc, int pr, u_char *infp)
+{ u_char len;
+ isdn_ctrl ic;
+ struct IsdnCardState *cs;
+ char *p;
+
+ if (*infp++ != IE_DISPLAY) return;
+ if ((len = *infp++) > 80) return; /* total length <= 82 */
+ if (!pc->chan) return;
+
+ p = ic.parm.display;
+ while (len--)
+ *p++ = *infp++;
+ *p = '\0';
+ ic.command = ISDN_STAT_DISPLAY;
+ cs = pc->st->l1.hardware;
+ ic.driver = cs->myid;
+ ic.arg = pc->chan->chan;
+ cs->iif.statcallb(&ic);
+} /* l3ni1_deliver_display */
+
+
+static void
+l3ni1_progress(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ int err = 0;
+ u_char *p;
+
+ if ((p = findie(skb->data, skb->len, IE_PROGRESS, 0))) {
+ if (p[1] != 2) {
+ err = 1;
+ pc->para.cause = 100;
+ } else if (!(p[2] & 0x70)) {
+ switch (p[2]) {
+ case 0x80:
+ case 0x81:
+ case 0x82:
+ case 0x84:
+ case 0x85:
+ case 0x87:
+ case 0x8a:
+ switch (p[3]) {
+ case 0x81:
+ case 0x82:
+ case 0x83:
+ case 0x84:
+ case 0x88:
+ break;
+ default:
+ err = 2;
+ pc->para.cause = 100;
+ break;
+ }
+ break;
+ default:
+ err = 3;
+ pc->para.cause = 100;
+ break;
+ }
+ }
+ } else {
+ pc->para.cause = 96;
+ err = 4;
+ }
+ if (err) {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "progress error %d", err);
+ l3ni1_status_send(pc, pr, NULL);
+ return;
+ }
+ /* Now we are on none mandatory IEs */
+ err = check_infoelements(pc, skb, ie_PROGRESS);
+ if (err)
+ l3ni1_std_ie_err(pc, err);
+ if (ERR_IE_COMPREHENSION != err)
+ pc->st->l3.l3l4(pc->st, CC_PROGRESS | INDICATION, pc);
+}
+
+static void
+l3ni1_notify(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ int err = 0;
+ u_char *p;
+
+ if ((p = findie(skb->data, skb->len, IE_NOTIFY, 0))) {
+ if (p[1] != 1) {
+ err = 1;
+ pc->para.cause = 100;
+ } else {
+ switch (p[2]) {
+ case 0x80:
+ case 0x81:
+ case 0x82:
+ break;
+ default:
+ pc->para.cause = 100;
+ err = 2;
+ break;
+ }
+ }
+ } else {
+ pc->para.cause = 96;
+ err = 3;
+ }
+ if (err) {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "notify error %d", err);
+ l3ni1_status_send(pc, pr, NULL);
+ return;
+ }
+ /* Now we are on none mandatory IEs */
+ err = check_infoelements(pc, skb, ie_NOTIFY);
+ if (err)
+ l3ni1_std_ie_err(pc, err);
+ if (ERR_IE_COMPREHENSION != err)
+ pc->st->l3.l3l4(pc->st, CC_NOTIFY | INDICATION, pc);
+}
+
+static void
+l3ni1_status_enq(struct l3_process *pc, u_char pr, void *arg)
+{
+ int ret;
+ struct sk_buff *skb = arg;
+
+ ret = check_infoelements(pc, skb, ie_STATUS_ENQUIRY);
+ l3ni1_std_ie_err(pc, ret);
+ pc->para.cause = 30; /* response to STATUS_ENQUIRY */
+ l3ni1_status_send(pc, pr, NULL);
+}
+
+static void
+l3ni1_information(struct l3_process *pc, u_char pr, void *arg)
+{
+ int ret;
+ struct sk_buff *skb = arg;
+ u_char *p;
+ char tmp[32];
+
+ ret = check_infoelements(pc, skb, ie_INFORMATION);
+ if (ret)
+ l3ni1_std_ie_err(pc, ret);
+ if (pc->state == 25) { /* overlap receiving */
+ L3DelTimer(&pc->timer);
+ p = skb->data;
+ if ((p = findie(p, skb->len, 0x70, 0))) {
+ iecpy(tmp, p, 1);
+ strcat(pc->para.setup.eazmsn, tmp);
+ pc->st->l3.l3l4(pc->st, CC_MORE_INFO | INDICATION, pc);
+ }
+ L3AddTimer(&pc->timer, T302, CC_T302);
+ }
+}
+
+/******************************/
+/* handle deflection requests */
+/******************************/
+static void l3ni1_redir_req(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb;
+ u_char tmp[128];
+ u_char *p = tmp;
+ u_char *subp;
+ u_char len_phone = 0;
+ u_char len_sub = 0;
+ int l;
+
+
+ strcpy(pc->prot.ni1.uus1_data,pc->chan->setup.eazmsn); /* copy uus element if available */
+ if (!pc->chan->setup.phone[0])
+ { pc->para.cause = -1;
+ l3ni1_disconnect_req(pc,pr,arg); /* disconnect immediately */
+ return;
+ } /* only uus */
+
+ if (pc->prot.ni1.invoke_id)
+ free_invoke_id(pc->st,pc->prot.ni1.invoke_id);
+
+ if (!(pc->prot.ni1.invoke_id = new_invoke_id(pc->st)))
+ return;
+
+ MsgHead(p, pc->callref, MT_FACILITY);
+
+ for (subp = pc->chan->setup.phone; (*subp) && (*subp != '.'); subp++) len_phone++; /* len of phone number */
+ if (*subp++ == '.') len_sub = strlen(subp) + 2; /* length including info subaddress element */
+
+ *p++ = 0x1c; /* Facility info element */
+ *p++ = len_phone + len_sub + 2 + 2 + 8 + 3 + 3; /* length of element */
+ *p++ = 0x91; /* remote operations protocol */
+ *p++ = 0xa1; /* invoke component */
+
+ *p++ = len_phone + len_sub + 2 + 2 + 8 + 3; /* length of data */
+ *p++ = 0x02; /* invoke id tag, integer */
+ *p++ = 0x01; /* length */
+ *p++ = pc->prot.ni1.invoke_id; /* invoke id */
+ *p++ = 0x02; /* operation value tag, integer */
+ *p++ = 0x01; /* length */
+ *p++ = 0x0D; /* Call Deflect */
+
+ *p++ = 0x30; /* sequence phone number */
+ *p++ = len_phone + 2 + 2 + 3 + len_sub; /* length */
+
+ *p++ = 0x30; /* Deflected to UserNumber */
+ *p++ = len_phone+2+len_sub; /* length */
+ *p++ = 0x80; /* NumberDigits */
+ *p++ = len_phone; /* length */
+ for (l = 0; l < len_phone; l++)
+ *p++ = pc->chan->setup.phone[l];
+
+ if (len_sub)
+ { *p++ = 0x04; /* called party subaddress */
+ *p++ = len_sub - 2;
+ while (*subp) *p++ = *subp++;
+ }
+
+ *p++ = 0x01; /* screening identifier */
+ *p++ = 0x01;
+ *p++ = pc->chan->setup.screen;
+
+ l = p - tmp;
+ if (!(skb = l3_alloc_skb(l))) return;
+ memcpy(skb_put(skb, l), tmp, l);
+
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+} /* l3ni1_redir_req */
+
+/********************************************/
+/* handle deflection request in early state */
+/********************************************/
+static void l3ni1_redir_req_early(struct l3_process *pc, u_char pr, void *arg)
+{
+ l3ni1_proceed_req(pc,pr,arg);
+ l3ni1_redir_req(pc,pr,arg);
+} /* l3ni1_redir_req_early */
+
+/***********************************************/
+/* handle special commands for this protocol. */
+/* Examples are call independant services like */
+/* remote operations with dummy callref. */
+/***********************************************/
+static int l3ni1_cmd_global(struct PStack *st, isdn_ctrl *ic)
+{ u_char id;
+ u_char temp[265];
+ u_char *p = temp;
+ int i, l, proc_len;
+ struct sk_buff *skb;
+ struct l3_process *pc = NULL;
+
+ switch (ic->arg)
+ { case NI1_CMD_INVOKE:
+ if (ic->parm.ni1_io.datalen < 0) return(-2); /* invalid parameter */
+
+ for (proc_len = 1, i = ic->parm.ni1_io.proc >> 8; i; i++)
+ i = i >> 8; /* add one byte */
+ l = ic->parm.ni1_io.datalen + proc_len + 8; /* length excluding ie header */
+ if (l > 255)
+ return(-2); /* too long */
+
+ if (!(id = new_invoke_id(st)))
+ return(0); /* first get a invoke id -> return if no available */
+
+ i = -1;
+ MsgHead(p, i, MT_FACILITY); /* build message head */
+ *p++ = 0x1C; /* Facility IE */
+ *p++ = l; /* length of ie */
+ *p++ = 0x91; /* remote operations */
+ *p++ = 0xA1; /* invoke */
+ *p++ = l - 3; /* length of invoke */
+ *p++ = 0x02; /* invoke id tag */
+ *p++ = 0x01; /* length is 1 */
+ *p++ = id; /* invoke id */
+ *p++ = 0x02; /* operation */
+ *p++ = proc_len; /* length of operation */
+
+ for (i = proc_len; i; i--)
+ *p++ = (ic->parm.ni1_io.proc >> (i-1)) & 0xFF;
+ memcpy(p, ic->parm.ni1_io.data, ic->parm.ni1_io.datalen); /* copy data */
+ l = (p - temp) + ic->parm.ni1_io.datalen; /* total length */
+
+ if (ic->parm.ni1_io.timeout > 0)
+ if (!(pc = ni1_new_l3_process(st, -1)))
+ { free_invoke_id(st, id);
+ return(-2);
+ }
+ pc->prot.ni1.ll_id = ic->parm.ni1_io.ll_id; /* remember id */
+ pc->prot.ni1.proc = ic->parm.ni1_io.proc; /* and procedure */
+
+ if (!(skb = l3_alloc_skb(l)))
+ { free_invoke_id(st, id);
+ if (pc) ni1_release_l3_process(pc);
+ return(-2);
+ }
+ memcpy(skb_put(skb, l), temp, l);
+
+ if (pc)
+ { pc->prot.ni1.invoke_id = id; /* remember id */
+ L3AddTimer(&pc->timer, ic->parm.ni1_io.timeout, CC_TNI1_IO | REQUEST);
+ }
+
+ l3_msg(st, DL_DATA | REQUEST, skb);
+ ic->parm.ni1_io.hl_id = id; /* return id */
+ return(0);
+
+ case NI1_CMD_INVOKE_ABORT:
+ if ((pc = l3ni1_search_dummy_proc(st, ic->parm.ni1_io.hl_id)))
+ { L3DelTimer(&pc->timer); /* remove timer */
+ ni1_release_l3_process(pc);
+ return(0);
+ }
+ else
+ { l3_debug(st, "l3ni1_cmd_global abort unknown id");
+ return(-2);
+ }
+ break;
+
+ default:
+ l3_debug(st, "l3ni1_cmd_global unknown cmd 0x%lx", ic->arg);
+ return(-1);
+ } /* switch ic-> arg */
+ return(-1);
+} /* l3ni1_cmd_global */
+
+static void
+l3ni1_io_timer(struct l3_process *pc)
+{ isdn_ctrl ic;
+ struct IsdnCardState *cs = pc->st->l1.hardware;
+
+ L3DelTimer(&pc->timer); /* remove timer */
+
+ ic.driver = cs->myid;
+ ic.command = ISDN_STAT_PROT;
+ ic.arg = NI1_STAT_INVOKE_ERR;
+ ic.parm.ni1_io.hl_id = pc->prot.ni1.invoke_id;
+ ic.parm.ni1_io.ll_id = pc->prot.ni1.ll_id;
+ ic.parm.ni1_io.proc = pc->prot.ni1.proc;
+ ic.parm.ni1_io.timeout= -1;
+ ic.parm.ni1_io.datalen = 0;
+ ic.parm.ni1_io.data = NULL;
+ free_invoke_id(pc->st, pc->prot.ni1.invoke_id);
+ pc->prot.ni1.invoke_id = 0; /* reset id */
+
+ cs->iif.statcallb(&ic);
+
+ ni1_release_l3_process(pc);
+} /* l3ni1_io_timer */
+
+static void
+l3ni1_release_ind(struct l3_process *pc, u_char pr, void *arg)
+{
+ u_char *p;
+ struct sk_buff *skb = arg;
+ int callState = 0;
+ p = skb->data;
+
+ if ((p = findie(p, skb->len, IE_CALL_STATE, 0))) {
+ 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
+ */
+ pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+ newl3state(pc, 0);
+ ni1_release_l3_process(pc);
+ } else {
+ pc->st->l3.l3l4(pc->st, CC_IGNORE | INDICATION, pc);
+ }
+}
+
+static void
+l3ni1_dummy(struct l3_process *pc, u_char pr, void *arg)
+{
+}
+
+static void
+l3ni1_t302(struct l3_process *pc, u_char pr, void *arg)
+{
+ L3DelTimer(&pc->timer);
+ pc->para.loc = 0;
+ pc->para.cause = 28; /* invalid number */
+ l3ni1_disconnect_req(pc, pr, NULL);
+ pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+}
+
+static void
+l3ni1_t303(struct l3_process *pc, u_char pr, void *arg)
+{
+ if (pc->N303 > 0) {
+ pc->N303--;
+ L3DelTimer(&pc->timer);
+ l3ni1_setup_req(pc, pr, arg);
+ } else {
+ L3DelTimer(&pc->timer);
+ l3ni1_message_cause(pc, MT_RELEASE_COMPLETE, 102);
+ pc->st->l3.l3l4(pc->st, CC_NOSETUP_RSP, pc);
+ ni1_release_l3_process(pc);
+ }
+}
+
+static void
+l3ni1_t304(struct l3_process *pc, u_char pr, void *arg)
+{
+ L3DelTimer(&pc->timer);
+ pc->para.loc = 0;
+ pc->para.cause = 102;
+ l3ni1_disconnect_req(pc, pr, NULL);
+ pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+
+}
+
+static void
+l3ni1_t305(struct l3_process *pc, u_char pr, void *arg)
+{
+ u_char tmp[16];
+ u_char *p = tmp;
+ int l;
+ struct sk_buff *skb;
+ u_char cause = 16;
+
+ L3DelTimer(&pc->timer);
+ if (pc->para.cause != NO_CAUSE)
+ cause = pc->para.cause;
+
+ MsgHead(p, pc->callref, MT_RELEASE);
+
+ *p++ = IE_CAUSE;
+ *p++ = 0x2;
+ *p++ = 0x80;
+ *p++ = cause | 0x80;
+
+ l = p - tmp;
+ if (!(skb = l3_alloc_skb(l)))
+ return;
+ memcpy(skb_put(skb, l), tmp, l);
+ newl3state(pc, 19);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+ L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3ni1_t310(struct l3_process *pc, u_char pr, void *arg)
+{
+ L3DelTimer(&pc->timer);
+ pc->para.loc = 0;
+ pc->para.cause = 102;
+ l3ni1_disconnect_req(pc, pr, NULL);
+ pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+}
+
+static void
+l3ni1_t313(struct l3_process *pc, u_char pr, void *arg)
+{
+ L3DelTimer(&pc->timer);
+ pc->para.loc = 0;
+ pc->para.cause = 102;
+ l3ni1_disconnect_req(pc, pr, NULL);
+ pc->st->l3.l3l4(pc->st, CC_CONNECT_ERR, pc);
+}
+
+static void
+l3ni1_t308_1(struct l3_process *pc, u_char pr, void *arg)
+{
+ newl3state(pc, 19);
+ L3DelTimer(&pc->timer);
+ l3ni1_message(pc, MT_RELEASE);
+ L3AddTimer(&pc->timer, T308, CC_T308_2);
+}
+
+static void
+l3ni1_t308_2(struct l3_process *pc, u_char pr, void *arg)
+{
+ L3DelTimer(&pc->timer);
+ pc->st->l3.l3l4(pc->st, CC_RELEASE_ERR, pc);
+ ni1_release_l3_process(pc);
+}
+
+static void
+l3ni1_t318(struct l3_process *pc, u_char pr, void *arg)
+{
+ L3DelTimer(&pc->timer);
+ pc->para.cause = 102; /* Timer expiry */
+ pc->para.loc = 0; /* local */
+ pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc);
+ newl3state(pc, 19);
+ l3ni1_message(pc, MT_RELEASE);
+ L3AddTimer(&pc->timer, T308, CC_T308_1);
+}
+
+static void
+l3ni1_t319(struct l3_process *pc, u_char pr, void *arg)
+{
+ L3DelTimer(&pc->timer);
+ pc->para.cause = 102; /* Timer expiry */
+ pc->para.loc = 0; /* local */
+ pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc);
+ newl3state(pc, 10);
+}
+
+static void
+l3ni1_restart(struct l3_process *pc, u_char pr, void *arg)
+{
+ L3DelTimer(&pc->timer);
+ pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+ ni1_release_l3_process(pc);
+}
+
+static void
+l3ni1_status(struct l3_process *pc, u_char pr, void *arg)
+{
+ u_char *p;
+ struct sk_buff *skb = arg;
+ int ret;
+ u_char cause = 0, callState = 0;
+
+ if ((ret = l3ni1_get_cause(pc, skb))) {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "STATUS get_cause ret(%d)",ret);
+ if (ret < 0)
+ cause = 96;
+ else if (ret > 0)
+ cause = 100;
+ }
+ if ((p = findie(skb->data, skb->len, IE_CALL_STATE, 0))) {
+ p++;
+ if (1 == *p++) {
+ callState = *p;
+ if (!ie_in_set(pc, *p, l3_valid_states))
+ cause = 100;
+ } else
+ cause = 100;
+ } else
+ cause = 96;
+ if (!cause) { /* no error before */
+ ret = check_infoelements(pc, skb, ie_STATUS);
+ if (ERR_IE_COMPREHENSION == ret)
+ cause = 96;
+ else if (ERR_IE_UNRECOGNIZED == ret)
+ cause = 99;
+ }
+ if (cause) {
+ u_char tmp;
+
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "STATUS error(%d/%d)",ret,cause);
+ tmp = pc->para.cause;
+ pc->para.cause = cause;
+ l3ni1_status_send(pc, 0, NULL);
+ if (cause == 99)
+ pc->para.cause = tmp;
+ else
+ return;
+ }
+ cause = pc->para.cause;
+ if (((cause & 0x7f) == 111) && (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
+ */
+ pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+ newl3state(pc, 0);
+ ni1_release_l3_process(pc);
+ }
+}
+
+static void
+l3ni1_facility(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ int ret;
+
+ ret = check_infoelements(pc, skb, ie_FACILITY);
+ l3ni1_std_ie_err(pc, ret);
+ {
+ u_char *p;
+ if ((p = findie(skb->data, skb->len, IE_FACILITY, 0)))
+ l3ni1_parse_facility(pc->st, pc, pc->callref, p);
+ }
+}
+
+static void
+l3ni1_suspend_req(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb;
+ u_char tmp[32];
+ u_char *p = tmp;
+ u_char i, l;
+ u_char *msg = pc->chan->setup.phone;
+
+ MsgHead(p, pc->callref, MT_SUSPEND);
+ l = *msg++;
+ if (l && (l <= 10)) { /* Max length 10 octets */
+ *p++ = IE_CALL_ID;
+ *p++ = l;
+ for (i = 0; i < l; i++)
+ *p++ = *msg++;
+ } else if (l) {
+ l3_debug(pc->st, "SUS wrong CALL_ID len %d", l);
+ return;
+ }
+ l = p - tmp;
+ if (!(skb = l3_alloc_skb(l)))
+ return;
+ memcpy(skb_put(skb, l), tmp, l);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+ newl3state(pc, 15);
+ L3AddTimer(&pc->timer, T319, CC_T319);
+}
+
+static void
+l3ni1_suspend_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ int ret;
+
+ L3DelTimer(&pc->timer);
+ newl3state(pc, 0);
+ pc->para.cause = NO_CAUSE;
+ pc->st->l3.l3l4(pc->st, CC_SUSPEND | CONFIRM, pc);
+ /* We don't handle suspend_ack for IE errors now */
+ if ((ret = check_infoelements(pc, skb, ie_SUSPEND_ACKNOWLEDGE)))
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "SUSPACK check ie(%d)",ret);
+ ni1_release_l3_process(pc);
+}
+
+static void
+l3ni1_suspend_rej(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ int ret;
+
+ if ((ret = l3ni1_get_cause(pc, skb))) {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "SUSP_REJ get_cause ret(%d)",ret);
+ if (ret < 0)
+ pc->para.cause = 96;
+ else
+ pc->para.cause = 100;
+ l3ni1_status_send(pc, pr, NULL);
+ return;
+ }
+ ret = check_infoelements(pc, skb, ie_SUSPEND_REJECT);
+ if (ERR_IE_COMPREHENSION == ret) {
+ l3ni1_std_ie_err(pc, ret);
+ return;
+ }
+ L3DelTimer(&pc->timer);
+ pc->st->l3.l3l4(pc->st, CC_SUSPEND_ERR, pc);
+ newl3state(pc, 10);
+ if (ret) /* STATUS for none mandatory IE errors after actions are taken */
+ l3ni1_std_ie_err(pc, ret);
+}
+
+static void
+l3ni1_resume_req(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb;
+ u_char tmp[32];
+ u_char *p = tmp;
+ u_char i, l;
+ u_char *msg = pc->para.setup.phone;
+
+ MsgHead(p, pc->callref, MT_RESUME);
+
+ l = *msg++;
+ if (l && (l <= 10)) { /* Max length 10 octets */
+ *p++ = IE_CALL_ID;
+ *p++ = l;
+ for (i = 0; i < l; i++)
+ *p++ = *msg++;
+ } else if (l) {
+ l3_debug(pc->st, "RES wrong CALL_ID len %d", l);
+ return;
+ }
+ l = p - tmp;
+ if (!(skb = l3_alloc_skb(l)))
+ return;
+ memcpy(skb_put(skb, l), tmp, l);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+ newl3state(pc, 17);
+ L3AddTimer(&pc->timer, T318, CC_T318);
+}
+
+static void
+l3ni1_resume_ack(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ int id, ret;
+
+ if ((id = l3ni1_get_channel_id(pc, skb)) > 0) {
+ if ((0 == id) || ((3 == id) && (0x10 == pc->para.moderate))) {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "resume ack with wrong chid %x", id);
+ pc->para.cause = 100;
+ l3ni1_status_send(pc, pr, NULL);
+ return;
+ }
+ pc->para.bchannel = id;
+ } else if (1 == pc->state) {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "resume ack without chid (ret %d)", id);
+ pc->para.cause = 96;
+ l3ni1_status_send(pc, pr, NULL);
+ return;
+ }
+ ret = check_infoelements(pc, skb, ie_RESUME_ACKNOWLEDGE);
+ if (ERR_IE_COMPREHENSION == ret) {
+ l3ni1_std_ie_err(pc, ret);
+ return;
+ }
+ L3DelTimer(&pc->timer);
+ pc->st->l3.l3l4(pc->st, CC_RESUME | CONFIRM, pc);
+ newl3state(pc, 10);
+ if (ret) /* STATUS for none mandatory IE errors after actions are taken */
+ l3ni1_std_ie_err(pc, ret);
+}
+
+static void
+l3ni1_resume_rej(struct l3_process *pc, u_char pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ int ret;
+
+ if ((ret = l3ni1_get_cause(pc, skb))) {
+ if (pc->debug & L3_DEB_WARN)
+ l3_debug(pc->st, "RES_REJ get_cause ret(%d)",ret);
+ if (ret < 0)
+ pc->para.cause = 96;
+ else
+ pc->para.cause = 100;
+ l3ni1_status_send(pc, pr, NULL);
+ return;
+ }
+ ret = check_infoelements(pc, skb, ie_RESUME_REJECT);
+ if (ERR_IE_COMPREHENSION == ret) {
+ l3ni1_std_ie_err(pc, ret);
+ return;
+ }
+ L3DelTimer(&pc->timer);
+ pc->st->l3.l3l4(pc->st, CC_RESUME_ERR, pc);
+ newl3state(pc, 0);
+ if (ret) /* STATUS for none mandatory IE errors after actions are taken */
+ l3ni1_std_ie_err(pc, ret);
+ ni1_release_l3_process(pc);
+}
+
+static void
+l3ni1_global_restart(struct l3_process *pc, u_char pr, void *arg)
+{
+ u_char tmp[32];
+ u_char *p;
+ u_char ri, ch = 0, chan = 0;
+ int l;
+ struct sk_buff *skb = arg;
+ struct l3_process *up;
+
+ newl3state(pc, 2);
+ L3DelTimer(&pc->timer);
+ p = skb->data;
+ if ((p = findie(p, skb->len, IE_RESTART_IND, 0))) {
+ ri = p[2];
+ l3_debug(pc->st, "Restart %x", ri);
+ } else {
+ l3_debug(pc->st, "Restart without restart IE");
+ ri = 0x86;
+ }
+ p = skb->data;
+ if ((p = findie(p, skb->len, IE_CHANNEL_ID, 0))) {
+ chan = p[2] & 3;
+ ch = p[2];
+ if (pc->st->l3.debug)
+ l3_debug(pc->st, "Restart for channel %d", chan);
+ }
+ newl3state(pc, 2);
+ up = pc->st->l3.proc;
+ while (up) {
+ if ((ri & 7) == 7)
+ up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up);
+ else if (up->para.bchannel == chan)
+ up->st->lli.l4l3(up->st, CC_RESTART | REQUEST, up);
+
+ up = up->next;
+ }
+ p = tmp;
+ MsgHead(p, pc->callref, MT_RESTART_ACKNOWLEDGE);
+ if (chan) {
+ *p++ = IE_CHANNEL_ID;
+ *p++ = 1;
+ *p++ = ch | 0x80;
+ }
+ *p++ = 0x79; /* RESTART Ind */
+ *p++ = 1;
+ *p++ = ri;
+ l = p - tmp;
+ if (!(skb = l3_alloc_skb(l)))
+ return;
+ memcpy(skb_put(skb, l), tmp, l);
+ newl3state(pc, 0);
+ l3_msg(pc->st, DL_DATA | REQUEST, skb);
+}
+
+static void
+l3ni1_dl_reset(struct l3_process *pc, u_char pr, void *arg)
+{
+ pc->para.cause = 0x29; /* Temporary failure */
+ pc->para.loc = 0;
+ l3ni1_disconnect_req(pc, pr, NULL);
+ pc->st->l3.l3l4(pc->st, CC_SETUP_ERR, pc);
+}
+
+static void
+l3ni1_dl_release(struct l3_process *pc, u_char pr, void *arg)
+{
+ newl3state(pc, 0);
+ pc->para.cause = 0x1b; /* Destination out of order */
+ pc->para.loc = 0;
+ pc->st->l3.l3l4(pc->st, CC_RELEASE | INDICATION, pc);
+ release_l3_process(pc);
+}
+
+static void
+l3ni1_dl_reestablish(struct l3_process *pc, u_char pr, void *arg)
+{
+ L3DelTimer(&pc->timer);
+ L3AddTimer(&pc->timer, T309, CC_T309);
+ l3_msg(pc->st, DL_ESTABLISH | REQUEST, NULL);
+}
+
+static void
+l3ni1_dl_reest_status(struct l3_process *pc, u_char pr, void *arg)
+{
+ L3DelTimer(&pc->timer);
+
+ pc->para.cause = 0x1F; /* normal, unspecified */
+ l3ni1_status_send(pc, 0, NULL);
+}
+
+static void l3ni1_SendSpid( struct l3_process *pc, u_char pr, struct sk_buff *skb, int iNewState )
+{
+ u_char * p;
+ char * pSPID;
+ struct Channel * pChan = pc->st->lli.userdata;
+ int l;
+
+ if ( skb )
+ dev_kfree_skb( skb);
+
+ if ( !( pSPID = strchr( pChan->setup.eazmsn, ':' ) ) )
+ {
+ printk( KERN_ERR "SPID not supplied in EAZMSN %s\n", pChan->setup.eazmsn );
+ newl3state( pc, 0 );
+ pc->st->l3.l3l2( pc->st, DL_RELEASE | REQUEST, NULL );
+ return;
+ }
+
+ l = strlen( ++pSPID );
+ if ( !( skb = l3_alloc_skb( 5+l ) ) )
+ {
+ printk( KERN_ERR "HiSax can't get memory to send SPID\n" );
+ return;
+ }
+
+ p = skb_put( skb, 5 );
+ *p++ = PROTO_DIS_EURO;
+ *p++ = 0;
+ *p++ = MT_INFORMATION;
+ *p++ = IE_SPID;
+ *p++ = l;
+
+ memcpy( skb_put( skb, l ), pSPID, l );
+
+ newl3state( pc, iNewState );
+
+ L3DelTimer( &pc->timer );
+ L3AddTimer( &pc->timer, TSPID, CC_TSPID );
+
+ pc->st->l3.l3l2( pc->st, DL_DATA | REQUEST, skb );
+}
+
+static void l3ni1_spid_send( struct l3_process *pc, u_char pr, void *arg )
+{
+ l3ni1_SendSpid( pc, pr, arg, 20 );
+}
+
+static void l3ni1_spid_epid( struct l3_process *pc, u_char pr, void *arg )
+{
+ struct sk_buff *skb = arg;
+
+ if ( skb->data[ 1 ] == 0 )
+ if ( skb->data[ 3 ] == IE_ENDPOINT_ID )
+ {
+ L3DelTimer( &pc->timer );
+ newl3state( pc, 0 );
+ l3_msg( pc->st, DL_ESTABLISH | CONFIRM, NULL );
+ }
+ dev_kfree_skb( skb);
+}
+
+static void l3ni1_spid_tout( struct l3_process *pc, u_char pr, void *arg )
+{
+ if ( pc->state < 22 )
+ l3ni1_SendSpid( pc, pr, arg, pc->state+1 );
+ else
+ {
+ L3DelTimer( &pc->timer );
+ dev_kfree_skb( arg);
+
+ printk( KERN_ERR "SPID not accepted\n" );
+ newl3state( pc, 0 );
+ pc->st->l3.l3l2( pc->st, DL_RELEASE | REQUEST, NULL );
+ }
+}
+
+/* *INDENT-OFF* */
+static struct stateentry downstatelist[] =
+{
+ {SBIT(0),
+ CC_SETUP | REQUEST, l3ni1_setup_req},
+ {SBIT(0),
+ CC_RESUME | REQUEST, l3ni1_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, l3ni1_disconnect_req},
+ {SBIT(12),
+ CC_RELEASE | REQUEST, l3ni1_release_req},
+ {ALL_STATES,
+ CC_RESTART | REQUEST, l3ni1_restart},
+ {SBIT(6) | SBIT(25),
+ CC_IGNORE | REQUEST, l3ni1_reset},
+ {SBIT(6) | SBIT(25),
+ CC_REJECT | REQUEST, l3ni1_reject_req},
+ {SBIT(6) | SBIT(25),
+ CC_PROCEED_SEND | REQUEST, l3ni1_proceed_req},
+ {SBIT(6),
+ CC_MORE_INFO | REQUEST, l3ni1_setup_ack_req},
+ {SBIT(25),
+ CC_MORE_INFO | REQUEST, l3ni1_dummy},
+ {SBIT(6) | SBIT(9) | SBIT(25),
+ CC_ALERTING | REQUEST, l3ni1_alert_req},
+ {SBIT(6) | SBIT(7) | SBIT(9) | SBIT(25),
+ CC_SETUP | RESPONSE, l3ni1_setup_rsp},
+ {SBIT(10),
+ CC_SUSPEND | REQUEST, l3ni1_suspend_req},
+ {SBIT(7) | SBIT(9) | SBIT(25),
+ CC_REDIR | REQUEST, l3ni1_redir_req},
+ {SBIT(6),
+ CC_REDIR | REQUEST, l3ni1_redir_req_early},
+ {SBIT(9) | SBIT(25),
+ CC_DISCONNECT | REQUEST, l3ni1_disconnect_req},
+ {SBIT(25),
+ CC_T302, l3ni1_t302},
+ {SBIT(1),
+ CC_T303, l3ni1_t303},
+ {SBIT(2),
+ CC_T304, l3ni1_t304},
+ {SBIT(3),
+ CC_T310, l3ni1_t310},
+ {SBIT(8),
+ CC_T313, l3ni1_t313},
+ {SBIT(11),
+ CC_T305, l3ni1_t305},
+ {SBIT(15),
+ CC_T319, l3ni1_t319},
+ {SBIT(17),
+ CC_T318, l3ni1_t318},
+ {SBIT(19),
+ CC_T308_1, l3ni1_t308_1},
+ {SBIT(19),
+ CC_T308_2, l3ni1_t308_2},
+ {SBIT(10),
+ CC_T309, l3ni1_dl_release},
+ { SBIT( 20 ) | SBIT( 21 ) | SBIT( 22 ),
+ CC_TSPID, l3ni1_spid_tout },
+};
+
+#define DOWNSLLEN \
+ (sizeof(downstatelist) / sizeof(struct stateentry))
+
+static struct stateentry datastatelist[] =
+{
+ {ALL_STATES,
+ MT_STATUS_ENQUIRY, l3ni1_status_enq},
+ {ALL_STATES,
+ MT_FACILITY, l3ni1_facility},
+ {SBIT(19),
+ MT_STATUS, l3ni1_release_ind},
+ {ALL_STATES,
+ MT_STATUS, l3ni1_status},
+ {SBIT(0),
+ MT_SETUP, l3ni1_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, l3ni1_dummy},
+ {SBIT(1) | SBIT(2),
+ MT_CALL_PROCEEDING, l3ni1_call_proc},
+ {SBIT(1),
+ MT_SETUP_ACKNOWLEDGE, l3ni1_setup_ack},
+ {SBIT(2) | SBIT(3),
+ MT_ALERTING, l3ni1_alerting},
+ {SBIT(2) | SBIT(3),
+ MT_PROGRESS, l3ni1_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, l3ni1_information},
+ {SBIT(10) | SBIT(11) | SBIT(15),
+ MT_NOTIFY, l3ni1_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, l3ni1_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, l3ni1_release},
+ {SBIT(19), MT_RELEASE, l3ni1_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, l3ni1_disconnect},
+ {SBIT(19),
+ MT_DISCONNECT, l3ni1_dummy},
+ {SBIT(1) | SBIT(2) | SBIT(3) | SBIT(4),
+ MT_CONNECT, l3ni1_connect},
+ {SBIT(8),
+ MT_CONNECT_ACKNOWLEDGE, l3ni1_connect_ack},
+ {SBIT(15),
+ MT_SUSPEND_ACKNOWLEDGE, l3ni1_suspend_ack},
+ {SBIT(15),
+ MT_SUSPEND_REJECT, l3ni1_suspend_rej},
+ {SBIT(17),
+ MT_RESUME_ACKNOWLEDGE, l3ni1_resume_ack},
+ {SBIT(17),
+ MT_RESUME_REJECT, l3ni1_resume_rej},
+};
+
+#define DATASLLEN \
+ (sizeof(datastatelist) / sizeof(struct stateentry))
+
+static struct stateentry globalmes_list[] =
+{
+ {ALL_STATES,
+ MT_STATUS, l3ni1_status},
+ {SBIT(0),
+ MT_RESTART, l3ni1_global_restart},
+/* {SBIT(1),
+ MT_RESTART_ACKNOWLEDGE, l3ni1_restart_ack},
+*/
+ { SBIT( 0 ), MT_DL_ESTABLISHED, l3ni1_spid_send },
+ { SBIT( 20 ) | SBIT( 21 ) | SBIT( 22 ), MT_INFORMATION, l3ni1_spid_epid },
+};
+#define GLOBALM_LEN \
+ (sizeof(globalmes_list) / sizeof(struct stateentry))
+
+static struct stateentry manstatelist[] =
+{
+ {SBIT(2),
+ DL_ESTABLISH | INDICATION, l3ni1_dl_reset},
+ {SBIT(10),
+ DL_ESTABLISH | CONFIRM, l3ni1_dl_reest_status},
+ {SBIT(10),
+ DL_RELEASE | INDICATION, l3ni1_dl_reestablish},
+ {ALL_STATES,
+ DL_RELEASE | INDICATION, l3ni1_dl_release},
+};
+
+#define MANSLLEN \
+ (sizeof(manstatelist) / sizeof(struct stateentry))
+/* *INDENT-ON* */
+
+
+static void
+global_handler(struct PStack *st, int mt, struct sk_buff *skb)
+{
+ u_char tmp[16];
+ u_char *p = tmp;
+ int l;
+ int i;
+ struct l3_process *proc = st->l3.global;
+
+ if ( skb )
+ proc->callref = skb->data[2]; /* cr flag */
+ else
+ proc->callref = 0;
+ 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 (st->l3.debug & L3_DEB_STATE) {
+ l3_debug(st, "ni1 global state %d mt %x unhandled",
+ proc->state, mt);
+ }
+ MsgHead(p, proc->callref, MT_STATUS);
+ *p++ = IE_CAUSE;
+ *p++ = 0x2;
+ *p++ = 0x80;
+ *p++ = 81 |0x80; /* invalid cr */
+ *p++ = 0x14; /* CallState */
+ *p++ = 0x1;
+ *p++ = proc->state & 0x3f;
+ l = p - tmp;
+ if (!(skb = l3_alloc_skb(l)))
+ return;
+ memcpy(skb_put(skb, l), tmp, l);
+ l3_msg(proc->st, DL_DATA | REQUEST, skb);
+ } else {
+ if (st->l3.debug & L3_DEB_STATE) {
+ l3_debug(st, "ni1 global %d mt %x",
+ proc->state, mt);
+ }
+ globalmes_list[i].rout(proc, mt, skb);
+ }
+}
+
+static void
+ni1up(struct PStack *st, int pr, void *arg)
+{
+ int i, mt, cr, cause, callState;
+ char *ptr;
+ u_char *p;
+ struct sk_buff *skb = arg;
+ struct l3_process *proc;
+
+ switch (pr) {
+ case (DL_DATA | INDICATION):
+ case (DL_UNIT_DATA | INDICATION):
+ break;
+ case (DL_ESTABLISH | INDICATION):
+ case (DL_RELEASE | INDICATION):
+ case (DL_RELEASE | CONFIRM):
+ l3_msg(st, pr, arg);
+ return;
+ break;
+
+ case (DL_ESTABLISH | CONFIRM):
+ global_handler( st, MT_DL_ESTABLISHED, NULL );
+ return;
+
+ default:
+ printk(KERN_ERR "HiSax ni1up unknown pr=%04x\n", pr);
+ return;
+ }
+ if (skb->len < 3) {
+ l3_debug(st, "ni1up frame too short(%d)", skb->len);
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ if (skb->data[0] != PROTO_DIS_EURO) {
+ if (st->l3.debug & L3_DEB_PROTERR) {
+ l3_debug(st, "ni1up%sunexpected discriminator %x message len %d",
+ (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+ skb->data[0], skb->len);
+ }
+ dev_kfree_skb(skb);
+ return;
+ }
+ cr = getcallref(skb->data);
+ if (skb->len < ((skb->data[1] & 0x0f) + 3)) {
+ l3_debug(st, "ni1up frame too short(%d)", skb->len);
+ dev_kfree_skb(skb);
+ return;
+ }
+ mt = skb->data[skb->data[1] + 2];
+ if (st->l3.debug & L3_DEB_STATE)
+ l3_debug(st, "ni1up cr %d", cr);
+ if (cr == -2) { /* wrong Callref */
+ if (st->l3.debug & L3_DEB_WARN)
+ l3_debug(st, "ni1up wrong Callref");
+ dev_kfree_skb(skb);
+ return;
+ } else if (cr == -1) { /* Dummy Callref */
+ if (mt == MT_FACILITY)
+ {
+ if ((p = findie(skb->data, skb->len, IE_FACILITY, 0))) {
+ l3ni1_parse_facility(st, NULL,
+ (pr == (DL_DATA | INDICATION)) ? -1 : -2, p);
+ dev_kfree_skb(skb);
+ return;
+ }
+ }
+ else
+ {
+ global_handler(st, mt, skb);
+ return;
+ }
+
+ if (st->l3.debug & L3_DEB_WARN)
+ l3_debug(st, "ni1up dummy Callref (no facility msg or ie)");
+ dev_kfree_skb(skb);
+ return;
+ } else if ((((skb->data[1] & 0x0f) == 1) && (0==(cr & 0x7f))) ||
+ (((skb->data[1] & 0x0f) == 2) && (0==(cr & 0x7fff)))) { /* Global CallRef */
+ if (st->l3.debug & L3_DEB_STATE)
+ l3_debug(st, "ni1up Global CallRef");
+ global_handler(st, mt, skb);
+ dev_kfree_skb(skb);
+ return;
+ } else if (!(proc = getl3proc(st, cr))) {
+ /* No transaction process exist, that means no call with
+ * this callreference is active
+ */
+ if (mt == MT_SETUP) {
+ /* Setup creates a new transaction process */
+ if (skb->data[2] & 0x80) {
+ /* Setup with wrong CREF flag */
+ if (st->l3.debug & L3_DEB_STATE)
+ l3_debug(st, "ni1up wrong CRef flag");
+ dev_kfree_skb(skb);
+ return;
+ }
+ if (!(proc = ni1_new_l3_process(st, cr))) {
+ /* 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;
+ }
+ } else if (mt == MT_STATUS) {
+ cause = 0;
+ if ((ptr = findie(skb->data, skb->len, IE_CAUSE, 0)) != NULL) {
+ ptr++;
+ if (*ptr++ == 2)
+ ptr++;
+ cause = *ptr & 0x7f;
+ }
+ callState = 0;
+ if ((ptr = findie(skb->data, skb->len, IE_CALL_STATE, 0)) != NULL) {
+ ptr++;
+ if (*ptr++ == 2)
+ ptr++;
+ callState = *ptr;
+ }
+ /* 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 = ni1_new_l3_process(st, cr))) {
+ proc->para.cause = 101;
+ l3ni1_msg_without_setup(proc, 0, NULL);
+ }
+ }
+ dev_kfree_skb(skb);
+ return;
+ } else if (mt == MT_RELEASE_COMPLETE) {
+ dev_kfree_skb(skb);
+ return;
+ } 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 */
+ dev_kfree_skb(skb);
+ if ((proc = ni1_new_l3_process(st, cr))) {
+ proc->para.cause = 81;
+ l3ni1_msg_without_setup(proc, 0, NULL);
+ }
+ return;
+ }
+ }
+ if (l3ni1_check_messagetype_validity(proc, mt, skb)) {
+ dev_kfree_skb(skb);
+ return;
+ }
+ if ((p = findie(skb->data, skb->len, IE_DISPLAY, 0)) != NULL)
+ l3ni1_deliver_display(proc, pr, p); /* Display IE included */
+ for (i = 0; i < DATASLLEN; i++)
+ if ((mt == datastatelist[i].primitive) &&
+ ((1 << proc->state) & datastatelist[i].state))
+ break;
+ if (i == DATASLLEN) {
+ if (st->l3.debug & L3_DEB_STATE) {
+ l3_debug(st, "ni1up%sstate %d mt %#x unhandled",
+ (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+ proc->state, mt);
+ }
+ if ((MT_RELEASE_COMPLETE != mt) && (MT_RELEASE != mt)) {
+ proc->para.cause = 101;
+ l3ni1_status_send(proc, pr, skb);
+ }
+ } else {
+ if (st->l3.debug & L3_DEB_STATE) {
+ l3_debug(st, "ni1up%sstate %d mt %x",
+ (pr == (DL_DATA | INDICATION)) ? " " : "(broadcast) ",
+ proc->state, mt);
+ }
+ datastatelist[i].rout(proc, pr, skb);
+ }
+ dev_kfree_skb(skb);
+ return;
+}
+
+static void
+ni1down(struct PStack *st, int pr, void *arg)
+{
+ int i, cr;
+ struct l3_process *proc;
+ struct Channel *chan;
+
+ if ((DL_ESTABLISH | REQUEST) == pr) {
+ l3_msg(st, pr, NULL);
+ return;
+ } else if (((CC_SETUP | REQUEST) == pr) || ((CC_RESUME | REQUEST) == pr)) {
+ chan = arg;
+ cr = newcallref();
+ cr |= 0x80;
+ if ((proc = ni1_new_l3_process(st, cr))) {
+ proc->chan = chan;
+ chan->proc = proc;
+ memcpy(&proc->para.setup, &chan->setup, sizeof(setup_parm));
+ proc->callref = cr;
+ }
+ } else {
+ proc = arg;
+ }
+ if (!proc) {
+ printk(KERN_ERR "HiSax ni1down without proc pr=%04x\n", pr);
+ return;
+ }
+
+ if ( pr == (CC_TNI1_IO | REQUEST)) {
+ l3ni1_io_timer(proc); /* timer expires */
+ return;
+ }
+
+ for (i = 0; i < DOWNSLLEN; i++)
+ if ((pr == downstatelist[i].primitive) &&
+ ((1 << proc->state) & downstatelist[i].state))
+ break;
+ if (i == DOWNSLLEN) {
+ if (st->l3.debug & L3_DEB_STATE) {
+ l3_debug(st, "ni1down state %d prim %#x unhandled",
+ proc->state, pr);
+ }
+ } else {
+ if (st->l3.debug & L3_DEB_STATE) {
+ l3_debug(st, "ni1down state %d prim %#x",
+ proc->state, pr);
+ }
+ downstatelist[i].rout(proc, pr, arg);
+ }
+}
+
+static void
+ni1man(struct PStack *st, int pr, void *arg)
+{
+ int i;
+ struct l3_process *proc = arg;
+
+ if (!proc) {
+ printk(KERN_ERR "HiSax ni1man without proc pr=%04x\n", pr);
+ return;
+ }
+ for (i = 0; i < MANSLLEN; i++)
+ if ((pr == manstatelist[i].primitive) &&
+ ((1 << proc->state) & manstatelist[i].state))
+ break;
+ if (i == MANSLLEN) {
+ if (st->l3.debug & L3_DEB_STATE) {
+ l3_debug(st, "cr %d ni1man state %d prim %#x unhandled",
+ proc->callref & 0x7f, proc->state, pr);
+ }
+ } else {
+ if (st->l3.debug & L3_DEB_STATE) {
+ l3_debug(st, "cr %d ni1man state %d prim %#x",
+ proc->callref & 0x7f, proc->state, pr);
+ }
+ manstatelist[i].rout(proc, pr, arg);
+ }
+}
+
+void
+setstack_ni1(struct PStack *st)
+{
+ char tmp[64];
+ int i;
+
+ st->lli.l4l3 = ni1down;
+ st->lli.l4l3_proto = l3ni1_cmd_global;
+ st->l2.l2l3 = ni1up;
+ st->l3.l3ml3 = ni1man;
+ st->l3.N303 = 1;
+ st->prot.ni1.last_invoke_id = 0;
+ st->prot.ni1.invoke_used[0] = 1; /* Bit 0 must always be set to 1 */
+ i = 1;
+ while (i < 32)
+ st->prot.ni1.invoke_used[i++] = 0;
+
+ if (!(st->l3.global = kmalloc(sizeof(struct l3_process), GFP_ATOMIC))) {
+ printk(KERN_ERR "HiSax can't get memory for ni1 global CR\n");
+ } else {
+ st->l3.global->state = 0;
+ st->l3.global->callref = 0;
+ st->l3.global->next = NULL;
+ st->l3.global->debug = L3_DEB_WARN;
+ st->l3.global->st = st;
+ st->l3.global->N303 = 1;
+ st->l3.global->prot.ni1.invoke_id = 0;
+
+ L3InitTimer(st->l3.global, &st->l3.global->timer);
+ }
+ strcpy(tmp, ni1_revision);
+ printk(KERN_INFO "HiSax: National ISDN-1 Rev. %s\n", HiSax_getrev(tmp));
+}
diff --git a/drivers/isdn/hisax/l3ni1.h b/drivers/isdn/hisax/l3ni1.h
new file mode 100644
index 0000000..4066da2
--- /dev/null
+++ b/drivers/isdn/hisax/l3ni1.h
@@ -0,0 +1,136 @@
+/* $Id: l3ni1.h,v 2.3.6.2 2001/09/23 22:24:50 kai Exp $
+ *
+ * NI1 D-channel protocol
+ *
+ * Author Matt Henderson & Guy Ellis
+ * Copyright by Traverse Technologies Pty Ltd, www.travers.com.au
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * 2000.6.6 Initial implementation of routines for US NI1
+ * Layer 3 protocol based on the EURO/DSS1 D-channel protocol
+ * driver written by Karsten Keil et al. Thanks also for the
+ * code provided by Ragnar Paulson.
+ *
+ */
+
+#ifndef l3ni1_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 TSPID 5000 /* was 2000 - Guy Ellis */
+
+/*
+ * 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 MT_DL_ESTABLISHED 0xfe
+
+#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_SPID 0x3a
+#define IE_ENDPOINT_ID 0x3b
+#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
+
+#else /* only l3ni1_process */
+
+/* l3ni1 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 */
+ u8 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 */
+ } ni1_proc_priv;
+
+/* l3dni1 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 */
+ } ni1_stk_priv;
+
+#endif /* only l3dni1_process */
diff --git a/drivers/isdn/hisax/lmgr.c b/drivers/isdn/hisax/lmgr.c
new file mode 100644
index 0000000..d4f86d6
--- /dev/null
+++ b/drivers/isdn/hisax/lmgr.c
@@ -0,0 +1,50 @@
+/* $Id: lmgr.c,v 1.7.6.2 2001/09/23 22:24:50 kai Exp $
+ *
+ * Layermanagement module
+ *
+ * Author Karsten Keil
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include "hisax.h"
+
+static void
+error_handling_dchan(struct PStack *st, int Error)
+{
+ switch (Error) {
+ case 'C':
+ case 'D':
+ case 'G':
+ case 'H':
+ st->l2.l2tei(st, MDL_ERROR | REQUEST, NULL);
+ break;
+ }
+}
+
+static void
+hisax_manager(struct PStack *st, int pr, void *arg)
+{
+ long Code;
+
+ switch (pr) {
+ case (MDL_ERROR | INDICATION):
+ Code = (long) arg;
+ HiSax_putstatus(st->l1.hardware, "manager: MDL_ERROR",
+ " %c %s", (char)Code,
+ test_bit(FLG_LAPD, &st->l2.flag) ?
+ "D-channel" : "B-channel");
+ if (test_bit(FLG_LAPD, &st->l2.flag))
+ error_handling_dchan(st, Code);
+ break;
+ }
+}
+
+void
+setstack_manager(struct PStack *st)
+{
+ st->ma.layer = hisax_manager;
+}
diff --git a/drivers/isdn/hisax/mic.c b/drivers/isdn/hisax/mic.c
new file mode 100644
index 0000000..2539430
--- /dev/null
+++ b/drivers/isdn/hisax/mic.c
@@ -0,0 +1,236 @@
+/* $Id: mic.c,v 1.12.2.4 2004/01/13 23:48:39 keil Exp $
+ *
+ * low level stuff for mic cards
+ *
+ * Author Stephan von Krawczynski
+ * Copyright by Stephan von Krawczynski <skraw@ithnet.com>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+static const char *mic_revision = "$Revision: 1.12.2.4 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define MIC_ISAC 2
+#define MIC_HSCX 1
+#define MIC_ADR 7
+
+/* CARD_ADR (Write) */
+#define MIC_RESET 0x3 /* same as DOS driver */
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+ register u_char ret;
+
+ byteout(ale, off);
+ ret = bytein(adr);
+ return (ret);
+}
+
+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(struct IsdnCardState *cs, u_char offset)
+{
+ return (readreg(cs->hw.mic.adr, cs->hw.mic.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ writereg(cs->hw.mic.adr, cs->hw.mic.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ readfifo(cs->hw.mic.adr, cs->hw.mic.isac, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ writefifo(cs->hw.mic.adr, cs->hw.mic.isac, 0, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+ return (readreg(cs->hw.mic.adr,
+ cs->hw.mic.hscx, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+ writereg(cs->hw.mic.adr,
+ cs->hw.mic.hscx, offset + (hscx ? 0x40 : 0), value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.mic.adr, \
+ cs->hw.mic.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.mic.adr, \
+ cs->hw.mic.hscx, reg + (nr ? 0x40 : 0), data)
+
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.mic.adr, \
+ cs->hw.mic.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.mic.adr, \
+ cs->hw.mic.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+mic_interrupt(int intno, void *dev_id)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char val;
+ u_long flags;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ val = readreg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_ISTA + 0x40);
+ Start_HSCX:
+ if (val)
+ hscx_int_main(cs, val);
+ val = readreg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_ISTA);
+ Start_ISAC:
+ if (val)
+ isac_interrupt(cs, val);
+ val = readreg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_ISTA + 0x40);
+ if (val) {
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "HSCX IntStat after IntRoutine");
+ goto Start_HSCX;
+ }
+ val = readreg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_ISTA);
+ if (val) {
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ISAC IntStat after IntRoutine");
+ goto Start_ISAC;
+ }
+ writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK, 0xFF);
+ writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK + 0x40, 0xFF);
+ writereg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_MASK, 0xFF);
+ writereg(cs->hw.mic.adr, cs->hw.mic.isac, ISAC_MASK, 0x0);
+ writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK, 0x0);
+ writereg(cs->hw.mic.adr, cs->hw.mic.hscx, HSCX_MASK + 0x40, 0x0);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static void
+release_io_mic(struct IsdnCardState *cs)
+{
+ int bytecnt = 8;
+
+ if (cs->hw.mic.cfg_reg)
+ release_region(cs->hw.mic.cfg_reg, bytecnt);
+}
+
+static int
+mic_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ u_long flags;
+
+ switch (mt) {
+ case CARD_RESET:
+ return(0);
+ case CARD_RELEASE:
+ release_io_mic(cs);
+ return(0);
+ case CARD_INIT:
+ spin_lock_irqsave(&cs->lock, flags);
+ inithscx(cs); /* /RTSA := ISAC RST */
+ inithscxisac(cs, 3);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return(0);
+ case CARD_TEST:
+ return(0);
+ }
+ return(0);
+}
+
+int __devinit
+setup_mic(struct IsdnCard *card)
+{
+ int bytecnt;
+ struct IsdnCardState *cs = card->cs;
+ char tmp[64];
+
+ strcpy(tmp, mic_revision);
+ printk(KERN_INFO "HiSax: mic driver Rev. %s\n", HiSax_getrev(tmp));
+ if (cs->typ != ISDN_CTYPE_MIC)
+ return (0);
+
+ bytecnt = 8;
+ cs->hw.mic.cfg_reg = card->para[1];
+ cs->irq = card->para[0];
+ cs->hw.mic.adr = cs->hw.mic.cfg_reg + MIC_ADR;
+ cs->hw.mic.isac = cs->hw.mic.cfg_reg + MIC_ISAC;
+ cs->hw.mic.hscx = cs->hw.mic.cfg_reg + MIC_HSCX;
+
+ if (!request_region(cs->hw.mic.cfg_reg, bytecnt, "mic isdn")) {
+ printk(KERN_WARNING
+ "HiSax: ith mic config port %x-%x already in use\n",
+ cs->hw.mic.cfg_reg,
+ cs->hw.mic.cfg_reg + bytecnt);
+ return (0);
+ }
+ printk(KERN_INFO "mic: defined at 0x%x IRQ %d\n",
+ cs->hw.mic.cfg_reg, cs->irq);
+ setup_isac(cs);
+ cs->readisac = &ReadISAC;
+ cs->writeisac = &WriteISAC;
+ cs->readisacfifo = &ReadISACfifo;
+ cs->writeisacfifo = &WriteISACfifo;
+ cs->BC_Read_Reg = &ReadHSCX;
+ cs->BC_Write_Reg = &WriteHSCX;
+ cs->BC_Send_Data = &hscx_fill_fifo;
+ cs->cardmsg = &mic_card_msg;
+ cs->irq_func = &mic_interrupt;
+ ISACVersion(cs, "mic:");
+ if (HscxVersion(cs, "mic:")) {
+ printk(KERN_WARNING
+ "mic: wrong HSCX versions check IO address\n");
+ release_io_mic(cs);
+ return (0);
+ }
+ return (1);
+}
diff --git a/drivers/isdn/hisax/netjet.c b/drivers/isdn/hisax/netjet.c
new file mode 100644
index 0000000..02c6fba
--- /dev/null
+++ b/drivers/isdn/hisax/netjet.c
@@ -0,0 +1,981 @@
+/* $Id: netjet.c,v 1.29.2.4 2004/02/11 13:21:34 keil Exp $
+ *
+ * low level stuff for Traverse Technologie NETJet ISDN cards
+ *
+ * Author Karsten Keil
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to Traverse Technologies Australia for documents and information
+ *
+ * 16-Apr-2002 - led code added - Guy Ellis (guy@traverse.com.au)
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+#include <linux/ppp_defs.h>
+#include <asm/io.h>
+#include "netjet.h"
+
+/* Interface functions */
+
+u_char
+NETjet_ReadIC(struct IsdnCardState *cs, u_char offset)
+{
+ u_char ret;
+
+ cs->hw.njet.auxd &= 0xfc;
+ cs->hw.njet.auxd |= (offset>>4) & 3;
+ byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+ ret = bytein(cs->hw.njet.isac + ((offset & 0xf)<<2));
+ return(ret);
+}
+
+void
+NETjet_WriteIC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ cs->hw.njet.auxd &= 0xfc;
+ cs->hw.njet.auxd |= (offset>>4) & 3;
+ byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+ byteout(cs->hw.njet.isac + ((offset & 0xf)<<2), value);
+}
+
+void
+NETjet_ReadICfifo(struct IsdnCardState *cs, u_char *data, int size)
+{
+ cs->hw.njet.auxd &= 0xfc;
+ byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+ insb(cs->hw.njet.isac, data, size);
+}
+
+void
+NETjet_WriteICfifo(struct IsdnCardState *cs, u_char *data, int size)
+{
+ cs->hw.njet.auxd &= 0xfc;
+ byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+ outsb(cs->hw.njet.isac, data, size);
+}
+
+static void fill_mem(struct BCState *bcs, u_int *pos, u_int cnt, int chan, u_char fill)
+{
+ u_int mask=0x000000ff, val = 0, *p=pos;
+ u_int i;
+
+ val |= fill;
+ if (chan) {
+ val <<= 8;
+ mask <<= 8;
+ }
+ mask ^= 0xffffffff;
+ for (i=0; i<cnt; i++) {
+ *p &= mask;
+ *p++ |= val;
+ if (p > bcs->hw.tiger.s_end)
+ p = bcs->hw.tiger.send;
+ }
+}
+
+static void
+mode_tiger(struct BCState *bcs, int mode, int bc)
+{
+ struct IsdnCardState *cs = bcs->cs;
+ u_char led;
+
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "Tiger mode %d bchan %d/%d",
+ mode, bc, bcs->channel);
+ bcs->mode = mode;
+ bcs->channel = bc;
+ switch (mode) {
+ case (L1_MODE_NULL):
+ fill_mem(bcs, bcs->hw.tiger.send,
+ NETJET_DMA_TXSIZE, bc, 0xff);
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "Tiger stat rec %d/%d send %d",
+ bcs->hw.tiger.r_tot, bcs->hw.tiger.r_err,
+ bcs->hw.tiger.s_tot);
+ if ((cs->bcs[0].mode == L1_MODE_NULL) &&
+ (cs->bcs[1].mode == L1_MODE_NULL)) {
+ cs->hw.njet.dmactrl = 0;
+ byteout(cs->hw.njet.base + NETJET_DMACTRL,
+ cs->hw.njet.dmactrl);
+ byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0);
+ }
+ if (cs->typ == ISDN_CTYPE_NETJET_S)
+ {
+ // led off
+ led = bc & 0x01;
+ led = 0x01 << (6 + led); // convert to mask
+ led = ~led;
+ cs->hw.njet.auxd &= led;
+ byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+ }
+ break;
+ case (L1_MODE_TRANS):
+ break;
+ case (L1_MODE_HDLC_56K):
+ case (L1_MODE_HDLC):
+ fill_mem(bcs, bcs->hw.tiger.send,
+ NETJET_DMA_TXSIZE, bc, 0xff);
+ bcs->hw.tiger.r_state = HDLC_ZERO_SEARCH;
+ bcs->hw.tiger.r_tot = 0;
+ bcs->hw.tiger.r_bitcnt = 0;
+ bcs->hw.tiger.r_one = 0;
+ bcs->hw.tiger.r_err = 0;
+ bcs->hw.tiger.s_tot = 0;
+ if (! cs->hw.njet.dmactrl) {
+ fill_mem(bcs, bcs->hw.tiger.send,
+ NETJET_DMA_TXSIZE, !bc, 0xff);
+ cs->hw.njet.dmactrl = 1;
+ byteout(cs->hw.njet.base + NETJET_DMACTRL,
+ cs->hw.njet.dmactrl);
+ byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0x0f);
+ /* was 0x3f now 0x0f for TJ300 and TJ320 GE 13/07/00 */
+ }
+ bcs->hw.tiger.sendp = bcs->hw.tiger.send;
+ bcs->hw.tiger.free = NETJET_DMA_TXSIZE;
+ test_and_set_bit(BC_FLG_EMPTY, &bcs->Flag);
+ if (cs->typ == ISDN_CTYPE_NETJET_S)
+ {
+ // led on
+ led = bc & 0x01;
+ led = 0x01 << (6 + led); // convert to mask
+ cs->hw.njet.auxd |= led;
+ byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+ }
+ break;
+ }
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "tiger: set %x %x %x %x/%x pulse=%d",
+ bytein(cs->hw.njet.base + NETJET_DMACTRL),
+ bytein(cs->hw.njet.base + NETJET_IRQMASK0),
+ bytein(cs->hw.njet.base + NETJET_IRQSTAT0),
+ inl(cs->hw.njet.base + NETJET_DMA_READ_ADR),
+ inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR),
+ bytein(cs->hw.njet.base + NETJET_PULSE_CNT));
+}
+
+static void printframe(struct IsdnCardState *cs, u_char *buf, int count, char *s) {
+ char tmp[128];
+ char *t = tmp;
+ int i=count,j;
+ u_char *p = buf;
+
+ t += sprintf(t, "tiger %s(%4d)", s, count);
+ while (i>0) {
+ if (i>16)
+ j=16;
+ else
+ j=i;
+ QuickHex(t, p, j);
+ debugl1(cs, tmp);
+ p += j;
+ i -= j;
+ t = tmp;
+ t += sprintf(t, "tiger %s ", s);
+ }
+}
+
+// macro for 64k
+
+#define MAKE_RAW_BYTE for (j=0; j<8; j++) { \
+ bitcnt++;\
+ s_val >>= 1;\
+ if (val & 1) {\
+ s_one++;\
+ s_val |= 0x80;\
+ } else {\
+ s_one = 0;\
+ s_val &= 0x7f;\
+ }\
+ if (bitcnt==8) {\
+ bcs->hw.tiger.sendbuf[s_cnt++] = s_val;\
+ bitcnt = 0;\
+ }\
+ if (s_one == 5) {\
+ s_val >>= 1;\
+ s_val &= 0x7f;\
+ bitcnt++;\
+ s_one = 0;\
+ }\
+ if (bitcnt==8) {\
+ bcs->hw.tiger.sendbuf[s_cnt++] = s_val;\
+ bitcnt = 0;\
+ }\
+ val >>= 1;\
+ }
+
+static int make_raw_data(struct BCState *bcs) {
+// this make_raw is for 64k
+ register u_int i,s_cnt=0;
+ register u_char j;
+ register u_char val;
+ register u_char s_one = 0;
+ register u_char s_val = 0;
+ register u_char bitcnt = 0;
+ u_int fcs;
+
+ if (!bcs->tx_skb) {
+ debugl1(bcs->cs, "tiger make_raw: NULL skb");
+ return(1);
+ }
+ bcs->hw.tiger.sendbuf[s_cnt++] = HDLC_FLAG_VALUE;
+ fcs = PPP_INITFCS;
+ for (i=0; i<bcs->tx_skb->len; i++) {
+ val = bcs->tx_skb->data[i];
+ fcs = PPP_FCS (fcs, val);
+ MAKE_RAW_BYTE;
+ }
+ fcs ^= 0xffff;
+ val = fcs & 0xff;
+ MAKE_RAW_BYTE;
+ val = (fcs>>8) & 0xff;
+ MAKE_RAW_BYTE;
+ val = HDLC_FLAG_VALUE;
+ for (j=0; j<8; j++) {
+ bitcnt++;
+ s_val >>= 1;
+ if (val & 1)
+ s_val |= 0x80;
+ else
+ s_val &= 0x7f;
+ if (bitcnt==8) {
+ bcs->hw.tiger.sendbuf[s_cnt++] = s_val;
+ bitcnt = 0;
+ }
+ val >>= 1;
+ }
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs,"tiger make_raw: in %ld out %d.%d",
+ bcs->tx_skb->len, s_cnt, bitcnt);
+ if (bitcnt) {
+ while (8>bitcnt++) {
+ s_val >>= 1;
+ s_val |= 0x80;
+ }
+ bcs->hw.tiger.sendbuf[s_cnt++] = s_val;
+ bcs->hw.tiger.sendbuf[s_cnt++] = 0xff; // NJ<->NJ thoughput bug fix
+ }
+ bcs->hw.tiger.sendcnt = s_cnt;
+ bcs->tx_cnt -= bcs->tx_skb->len;
+ bcs->hw.tiger.sp = bcs->hw.tiger.sendbuf;
+ return(0);
+}
+
+// macro for 56k
+
+#define MAKE_RAW_BYTE_56K for (j=0; j<8; j++) { \
+ bitcnt++;\
+ s_val >>= 1;\
+ if (val & 1) {\
+ s_one++;\
+ s_val |= 0x80;\
+ } else {\
+ s_one = 0;\
+ s_val &= 0x7f;\
+ }\
+ if (bitcnt==7) {\
+ s_val >>= 1;\
+ s_val |= 0x80;\
+ bcs->hw.tiger.sendbuf[s_cnt++] = s_val;\
+ bitcnt = 0;\
+ }\
+ if (s_one == 5) {\
+ s_val >>= 1;\
+ s_val &= 0x7f;\
+ bitcnt++;\
+ s_one = 0;\
+ }\
+ if (bitcnt==7) {\
+ s_val >>= 1;\
+ s_val |= 0x80;\
+ bcs->hw.tiger.sendbuf[s_cnt++] = s_val;\
+ bitcnt = 0;\
+ }\
+ val >>= 1;\
+ }
+
+static int make_raw_data_56k(struct BCState *bcs) {
+// this make_raw is for 56k
+ register u_int i,s_cnt=0;
+ register u_char j;
+ register u_char val;
+ register u_char s_one = 0;
+ register u_char s_val = 0;
+ register u_char bitcnt = 0;
+ u_int fcs;
+
+ if (!bcs->tx_skb) {
+ debugl1(bcs->cs, "tiger make_raw_56k: NULL skb");
+ return(1);
+ }
+ val = HDLC_FLAG_VALUE;
+ for (j=0; j<8; j++) {
+ bitcnt++;
+ s_val >>= 1;
+ if (val & 1)
+ s_val |= 0x80;
+ else
+ s_val &= 0x7f;
+ if (bitcnt==7) {
+ s_val >>= 1;
+ s_val |= 0x80;
+ bcs->hw.tiger.sendbuf[s_cnt++] = s_val;
+ bitcnt = 0;
+ }
+ val >>= 1;
+ };
+ fcs = PPP_INITFCS;
+ for (i=0; i<bcs->tx_skb->len; i++) {
+ val = bcs->tx_skb->data[i];
+ fcs = PPP_FCS (fcs, val);
+ MAKE_RAW_BYTE_56K;
+ }
+ fcs ^= 0xffff;
+ val = fcs & 0xff;
+ MAKE_RAW_BYTE_56K;
+ val = (fcs>>8) & 0xff;
+ MAKE_RAW_BYTE_56K;
+ val = HDLC_FLAG_VALUE;
+ for (j=0; j<8; j++) {
+ bitcnt++;
+ s_val >>= 1;
+ if (val & 1)
+ s_val |= 0x80;
+ else
+ s_val &= 0x7f;
+ if (bitcnt==7) {
+ s_val >>= 1;
+ s_val |= 0x80;
+ bcs->hw.tiger.sendbuf[s_cnt++] = s_val;
+ bitcnt = 0;
+ }
+ val >>= 1;
+ }
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs,"tiger make_raw_56k: in %ld out %d.%d",
+ bcs->tx_skb->len, s_cnt, bitcnt);
+ if (bitcnt) {
+ while (8>bitcnt++) {
+ s_val >>= 1;
+ s_val |= 0x80;
+ }
+ bcs->hw.tiger.sendbuf[s_cnt++] = s_val;
+ bcs->hw.tiger.sendbuf[s_cnt++] = 0xff; // NJ<->NJ thoughput bug fix
+ }
+ bcs->hw.tiger.sendcnt = s_cnt;
+ bcs->tx_cnt -= bcs->tx_skb->len;
+ bcs->hw.tiger.sp = bcs->hw.tiger.sendbuf;
+ return(0);
+}
+
+static void got_frame(struct BCState *bcs, int count) {
+ struct sk_buff *skb;
+
+ if (!(skb = dev_alloc_skb(count)))
+ printk(KERN_WARNING "TIGER: receive out of memory\n");
+ else {
+ memcpy(skb_put(skb, count), bcs->hw.tiger.rcvbuf, count);
+ skb_queue_tail(&bcs->rqueue, skb);
+ }
+ test_and_set_bit(B_RCVBUFREADY, &bcs->event);
+ schedule_work(&bcs->tqueue);
+
+ if (bcs->cs->debug & L1_DEB_RECEIVE_FRAME)
+ printframe(bcs->cs, bcs->hw.tiger.rcvbuf, count, "rec");
+}
+
+
+
+static void read_raw(struct BCState *bcs, u_int *buf, int cnt){
+ int i;
+ register u_char j;
+ register u_char val;
+ u_int *pend = bcs->hw.tiger.rec +NETJET_DMA_RXSIZE -1;
+ register u_char state = bcs->hw.tiger.r_state;
+ register u_char r_one = bcs->hw.tiger.r_one;
+ register u_char r_val = bcs->hw.tiger.r_val;
+ register u_int bitcnt = bcs->hw.tiger.r_bitcnt;
+ u_int *p = buf;
+ int bits;
+ u_char mask;
+
+ if (bcs->mode == L1_MODE_HDLC) { // it's 64k
+ mask = 0xff;
+ bits = 8;
+ }
+ else { // it's 56K
+ mask = 0x7f;
+ bits = 7;
+ };
+ for (i=0;i<cnt;i++) {
+ val = bcs->channel ? ((*p>>8) & 0xff) : (*p & 0xff);
+ p++;
+ if (p > pend)
+ p = bcs->hw.tiger.rec;
+ if ((val & mask) == mask) {
+ state = HDLC_ZERO_SEARCH;
+ bcs->hw.tiger.r_tot++;
+ bitcnt = 0;
+ r_one = 0;
+ continue;
+ }
+ for (j=0;j<bits;j++) {
+ if (state == HDLC_ZERO_SEARCH) {
+ if (val & 1) {
+ r_one++;
+ } else {
+ r_one=0;
+ state= HDLC_FLAG_SEARCH;
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs,"tiger read_raw: zBit(%d,%d,%d) %x",
+ bcs->hw.tiger.r_tot,i,j,val);
+ }
+ } else if (state == HDLC_FLAG_SEARCH) {
+ if (val & 1) {
+ r_one++;
+ if (r_one>6) {
+ state=HDLC_ZERO_SEARCH;
+ }
+ } else {
+ if (r_one==6) {
+ bitcnt=0;
+ r_val=0;
+ state=HDLC_FLAG_FOUND;
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs,"tiger read_raw: flag(%d,%d,%d) %x",
+ bcs->hw.tiger.r_tot,i,j,val);
+ }
+ r_one=0;
+ }
+ } else if (state == HDLC_FLAG_FOUND) {
+ if (val & 1) {
+ r_one++;
+ if (r_one>6) {
+ state=HDLC_ZERO_SEARCH;
+ } else {
+ r_val >>= 1;
+ r_val |= 0x80;
+ bitcnt++;
+ }
+ } else {
+ if (r_one==6) {
+ bitcnt=0;
+ r_val=0;
+ r_one=0;
+ val >>= 1;
+ continue;
+ } else if (r_one!=5) {
+ r_val >>= 1;
+ r_val &= 0x7f;
+ bitcnt++;
+ }
+ r_one=0;
+ }
+ if ((state != HDLC_ZERO_SEARCH) &&
+ !(bitcnt & 7)) {
+ state=HDLC_FRAME_FOUND;
+ bcs->hw.tiger.r_fcs = PPP_INITFCS;
+ bcs->hw.tiger.rcvbuf[0] = r_val;
+ bcs->hw.tiger.r_fcs = PPP_FCS (bcs->hw.tiger.r_fcs, r_val);
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs,"tiger read_raw: byte1(%d,%d,%d) rval %x val %x i %x",
+ bcs->hw.tiger.r_tot,i,j,r_val,val,
+ bcs->cs->hw.njet.irqstat0);
+ }
+ } else if (state == HDLC_FRAME_FOUND) {
+ if (val & 1) {
+ r_one++;
+ if (r_one>6) {
+ state=HDLC_ZERO_SEARCH;
+ bitcnt=0;
+ } else {
+ r_val >>= 1;
+ r_val |= 0x80;
+ bitcnt++;
+ }
+ } else {
+ if (r_one==6) {
+ r_val=0;
+ r_one=0;
+ bitcnt++;
+ if (bitcnt & 7) {
+ debugl1(bcs->cs, "tiger: frame not byte aligned");
+ state=HDLC_FLAG_SEARCH;
+ bcs->hw.tiger.r_err++;
+#ifdef ERROR_STATISTIC
+ bcs->err_inv++;
+#endif
+ } else {
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs,"tiger frame end(%d,%d): fcs(%x) i %x",
+ i,j,bcs->hw.tiger.r_fcs, bcs->cs->hw.njet.irqstat0);
+ if (bcs->hw.tiger.r_fcs == PPP_GOODFCS) {
+ got_frame(bcs, (bitcnt>>3)-3);
+ } else {
+ if (bcs->cs->debug) {
+ debugl1(bcs->cs, "tiger FCS error");
+ printframe(bcs->cs, bcs->hw.tiger.rcvbuf,
+ (bitcnt>>3)-1, "rec");
+ bcs->hw.tiger.r_err++;
+ }
+#ifdef ERROR_STATISTIC
+ bcs->err_crc++;
+#endif
+ }
+ state=HDLC_FLAG_FOUND;
+ }
+ bitcnt=0;
+ } else if (r_one==5) {
+ val >>= 1;
+ r_one=0;
+ continue;
+ } else {
+ r_val >>= 1;
+ r_val &= 0x7f;
+ bitcnt++;
+ }
+ r_one=0;
+ }
+ if ((state == HDLC_FRAME_FOUND) &&
+ !(bitcnt & 7)) {
+ if ((bitcnt>>3)>=HSCX_BUFMAX) {
+ debugl1(bcs->cs, "tiger: frame too big");
+ r_val=0;
+ state=HDLC_FLAG_SEARCH;
+ bcs->hw.tiger.r_err++;
+#ifdef ERROR_STATISTIC
+ bcs->err_inv++;
+#endif
+ } else {
+ bcs->hw.tiger.rcvbuf[(bitcnt>>3)-1] = r_val;
+ bcs->hw.tiger.r_fcs =
+ PPP_FCS (bcs->hw.tiger.r_fcs, r_val);
+ }
+ }
+ }
+ val >>= 1;
+ }
+ bcs->hw.tiger.r_tot++;
+ }
+ bcs->hw.tiger.r_state = state;
+ bcs->hw.tiger.r_one = r_one;
+ bcs->hw.tiger.r_val = r_val;
+ bcs->hw.tiger.r_bitcnt = bitcnt;
+}
+
+void read_tiger(struct IsdnCardState *cs) {
+ u_int *p;
+ int cnt = NETJET_DMA_RXSIZE/2;
+
+ if ((cs->hw.njet.irqstat0 & cs->hw.njet.last_is0) & NETJET_IRQM0_READ) {
+ debugl1(cs,"tiger warn read double dma %x/%x",
+ cs->hw.njet.irqstat0, cs->hw.njet.last_is0);
+#ifdef ERROR_STATISTIC
+ if (cs->bcs[0].mode)
+ cs->bcs[0].err_rdo++;
+ if (cs->bcs[1].mode)
+ cs->bcs[1].err_rdo++;
+#endif
+ return;
+ } else {
+ cs->hw.njet.last_is0 &= ~NETJET_IRQM0_READ;
+ cs->hw.njet.last_is0 |= (cs->hw.njet.irqstat0 & NETJET_IRQM0_READ);
+ }
+ if (cs->hw.njet.irqstat0 & NETJET_IRQM0_READ_1)
+ p = cs->bcs[0].hw.tiger.rec + NETJET_DMA_RXSIZE - 1;
+ else
+ p = cs->bcs[0].hw.tiger.rec + cnt - 1;
+ if ((cs->bcs[0].mode == L1_MODE_HDLC) || (cs->bcs[0].mode == L1_MODE_HDLC_56K))
+ read_raw(cs->bcs, p, cnt);
+
+ if ((cs->bcs[1].mode == L1_MODE_HDLC) || (cs->bcs[1].mode == L1_MODE_HDLC_56K))
+ read_raw(cs->bcs + 1, p, cnt);
+ cs->hw.njet.irqstat0 &= ~NETJET_IRQM0_READ;
+}
+
+static void write_raw(struct BCState *bcs, u_int *buf, int cnt);
+
+void netjet_fill_dma(struct BCState *bcs)
+{
+ register u_int *p, *sp;
+ register int cnt;
+
+ if (!bcs->tx_skb)
+ return;
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs,"tiger fill_dma1: c%d %4x", bcs->channel,
+ bcs->Flag);
+ if (test_and_set_bit(BC_FLG_BUSY, &bcs->Flag))
+ return;
+ if (bcs->mode == L1_MODE_HDLC) { // it's 64k
+ if (make_raw_data(bcs))
+ return;
+ }
+ else { // it's 56k
+ if (make_raw_data_56k(bcs))
+ return;
+ };
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs,"tiger fill_dma2: c%d %4x", bcs->channel,
+ bcs->Flag);
+ if (test_and_clear_bit(BC_FLG_NOFRAME, &bcs->Flag)) {
+ write_raw(bcs, bcs->hw.tiger.sendp, bcs->hw.tiger.free);
+ } else if (test_and_clear_bit(BC_FLG_HALF, &bcs->Flag)) {
+ p = bus_to_virt(inl(bcs->cs->hw.njet.base + NETJET_DMA_READ_ADR));
+ sp = bcs->hw.tiger.sendp;
+ if (p == bcs->hw.tiger.s_end)
+ p = bcs->hw.tiger.send -1;
+ if (sp == bcs->hw.tiger.s_end)
+ sp = bcs->hw.tiger.send -1;
+ cnt = p - sp;
+ if (cnt <0) {
+ write_raw(bcs, bcs->hw.tiger.sendp, bcs->hw.tiger.free);
+ } else {
+ p++;
+ cnt++;
+ if (p > bcs->hw.tiger.s_end)
+ p = bcs->hw.tiger.send;
+ p++;
+ cnt++;
+ if (p > bcs->hw.tiger.s_end)
+ p = bcs->hw.tiger.send;
+ write_raw(bcs, p, bcs->hw.tiger.free - cnt);
+ }
+ } else if (test_and_clear_bit(BC_FLG_EMPTY, &bcs->Flag)) {
+ p = bus_to_virt(inl(bcs->cs->hw.njet.base + NETJET_DMA_READ_ADR));
+ cnt = bcs->hw.tiger.s_end - p;
+ if (cnt < 2) {
+ p = bcs->hw.tiger.send + 1;
+ cnt = NETJET_DMA_TXSIZE/2 - 2;
+ } else {
+ p++;
+ p++;
+ if (cnt <= (NETJET_DMA_TXSIZE/2))
+ cnt += NETJET_DMA_TXSIZE/2;
+ cnt--;
+ cnt--;
+ }
+ write_raw(bcs, p, cnt);
+ }
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs,"tiger fill_dma3: c%d %4x", bcs->channel,
+ bcs->Flag);
+}
+
+static void write_raw(struct BCState *bcs, u_int *buf, int cnt) {
+ u_int mask, val, *p=buf;
+ u_int i, s_cnt;
+
+ if (cnt <= 0)
+ return;
+ if (test_bit(BC_FLG_BUSY, &bcs->Flag)) {
+ if (bcs->hw.tiger.sendcnt> cnt) {
+ s_cnt = cnt;
+ bcs->hw.tiger.sendcnt -= cnt;
+ } else {
+ s_cnt = bcs->hw.tiger.sendcnt;
+ bcs->hw.tiger.sendcnt = 0;
+ }
+ if (bcs->channel)
+ mask = 0xffff00ff;
+ else
+ mask = 0xffffff00;
+ for (i=0; i<s_cnt; i++) {
+ val = bcs->channel ? ((bcs->hw.tiger.sp[i] <<8) & 0xff00) :
+ (bcs->hw.tiger.sp[i]);
+ *p &= mask;
+ *p++ |= val;
+ if (p>bcs->hw.tiger.s_end)
+ p = bcs->hw.tiger.send;
+ }
+ bcs->hw.tiger.s_tot += s_cnt;
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs,"tiger write_raw: c%d %p-%p %d/%d %d %x", bcs->channel,
+ buf, p, s_cnt, cnt,
+ bcs->hw.tiger.sendcnt, bcs->cs->hw.njet.irqstat0);
+ if (bcs->cs->debug & L1_DEB_HSCX_FIFO)
+ printframe(bcs->cs, bcs->hw.tiger.sp, s_cnt, "snd");
+ bcs->hw.tiger.sp += s_cnt;
+ bcs->hw.tiger.sendp = p;
+ if (!bcs->hw.tiger.sendcnt) {
+ if (!bcs->tx_skb) {
+ debugl1(bcs->cs,"tiger write_raw: NULL skb s_cnt %d", s_cnt);
+ } 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->tx_skb->len;
+ spin_unlock_irqrestore(&bcs->aclock, flags);
+ schedule_event(bcs, B_ACKPENDING);
+ }
+ dev_kfree_skb_any(bcs->tx_skb);
+ bcs->tx_skb = NULL;
+ }
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ bcs->hw.tiger.free = cnt - s_cnt;
+ if (bcs->hw.tiger.free > (NETJET_DMA_TXSIZE/2))
+ test_and_set_bit(BC_FLG_HALF, &bcs->Flag);
+ else {
+ test_and_clear_bit(BC_FLG_HALF, &bcs->Flag);
+ test_and_set_bit(BC_FLG_NOFRAME, &bcs->Flag);
+ }
+ if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+ netjet_fill_dma(bcs);
+ } else {
+ mask ^= 0xffffffff;
+ if (s_cnt < cnt) {
+ for (i=s_cnt; i<cnt;i++) {
+ *p++ |= mask;
+ if (p>bcs->hw.tiger.s_end)
+ p = bcs->hw.tiger.send;
+ }
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs, "tiger write_raw: fill rest %d",
+ cnt - s_cnt);
+ }
+ test_and_set_bit(B_XMTBUFREADY, &bcs->event);
+ schedule_work(&bcs->tqueue);
+ }
+ }
+ } else if (test_and_clear_bit(BC_FLG_NOFRAME, &bcs->Flag)) {
+ test_and_set_bit(BC_FLG_HALF, &bcs->Flag);
+ fill_mem(bcs, buf, cnt, bcs->channel, 0xff);
+ bcs->hw.tiger.free += cnt;
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs,"tiger write_raw: fill half");
+ } else if (test_and_clear_bit(BC_FLG_HALF, &bcs->Flag)) {
+ test_and_set_bit(BC_FLG_EMPTY, &bcs->Flag);
+ fill_mem(bcs, buf, cnt, bcs->channel, 0xff);
+ if (bcs->cs->debug & L1_DEB_HSCX)
+ debugl1(bcs->cs,"tiger write_raw: fill full");
+ }
+}
+
+void write_tiger(struct IsdnCardState *cs) {
+ u_int *p, cnt = NETJET_DMA_TXSIZE/2;
+
+ if ((cs->hw.njet.irqstat0 & cs->hw.njet.last_is0) & NETJET_IRQM0_WRITE) {
+ debugl1(cs,"tiger warn write double dma %x/%x",
+ cs->hw.njet.irqstat0, cs->hw.njet.last_is0);
+#ifdef ERROR_STATISTIC
+ if (cs->bcs[0].mode)
+ cs->bcs[0].err_tx++;
+ if (cs->bcs[1].mode)
+ cs->bcs[1].err_tx++;
+#endif
+ return;
+ } else {
+ cs->hw.njet.last_is0 &= ~NETJET_IRQM0_WRITE;
+ cs->hw.njet.last_is0 |= (cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE);
+ }
+ if (cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE_1)
+ p = cs->bcs[0].hw.tiger.send + NETJET_DMA_TXSIZE - 1;
+ else
+ p = cs->bcs[0].hw.tiger.send + cnt - 1;
+ if ((cs->bcs[0].mode == L1_MODE_HDLC) || (cs->bcs[0].mode == L1_MODE_HDLC_56K))
+ write_raw(cs->bcs, p, cnt);
+ if ((cs->bcs[1].mode == L1_MODE_HDLC) || (cs->bcs[1].mode == L1_MODE_HDLC_56K))
+ write_raw(cs->bcs + 1, p, cnt);
+ cs->hw.njet.irqstat0 &= ~NETJET_IRQM0_WRITE;
+}
+
+static void
+tiger_l2l1(struct PStack *st, int pr, void *arg)
+{
+ struct BCState *bcs = st->l1.bcs;
+ struct sk_buff *skb = arg;
+ 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;
+ 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 "tiger_l2l1: this shouldn't happen\n");
+ } else {
+ bcs->tx_skb = skb;
+ 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);
+ mode_tiger(bcs, st->l1.mode, st->l1.bc);
+ /* 2001/10/04 Christoph Ersfeld, Formula-n Europe AG */
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
+ bcs->cs->cardmsg(bcs->cs, MDL_BC_ASSIGN, (void *)(&st->l1.bc));
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | REQUEST):
+ /* 2001/10/04 Christoph Ersfeld, Formula-n Europe AG */
+ bcs->cs->cardmsg(bcs->cs, MDL_BC_RELEASE, (void *)(&st->l1.bc));
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | CONFIRM):
+ spin_lock_irqsave(&bcs->cs->lock, flags);
+ test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ mode_tiger(bcs, 0, st->l1.bc);
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
+ st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+ break;
+ }
+}
+
+
+static void
+close_tigerstate(struct BCState *bcs)
+{
+ mode_tiger(bcs, 0, bcs->channel);
+ if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+ kfree(bcs->hw.tiger.rcvbuf);
+ bcs->hw.tiger.rcvbuf = NULL;
+ kfree(bcs->hw.tiger.sendbuf);
+ bcs->hw.tiger.sendbuf = 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);
+ }
+ }
+}
+
+static int
+open_tigerstate(struct IsdnCardState *cs, struct BCState *bcs)
+{
+ if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+ if (!(bcs->hw.tiger.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "HiSax: No memory for tiger.rcvbuf\n");
+ return (1);
+ }
+ if (!(bcs->hw.tiger.sendbuf = kmalloc(RAW_BUFMAX, GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "HiSax: No memory for tiger.sendbuf\n");
+ return (1);
+ }
+ skb_queue_head_init(&bcs->rqueue);
+ skb_queue_head_init(&bcs->squeue);
+ }
+ bcs->tx_skb = NULL;
+ bcs->hw.tiger.sendcnt = 0;
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ bcs->event = 0;
+ bcs->tx_cnt = 0;
+ return (0);
+}
+
+static int
+setstack_tiger(struct PStack *st, struct BCState *bcs)
+{
+ bcs->channel = st->l1.bc;
+ if (open_tigerstate(st->l1.hardware, bcs))
+ return (-1);
+ st->l1.bcs = bcs;
+ st->l2.l2l1 = tiger_l2l1;
+ setstack_manager(st);
+ bcs->st = st;
+ setstack_l1_B(st);
+ return (0);
+}
+
+
+void
+inittiger(struct IsdnCardState *cs)
+{
+ if (!(cs->bcs[0].hw.tiger.send = kmalloc(NETJET_DMA_TXSIZE * sizeof(unsigned int),
+ GFP_KERNEL | GFP_DMA))) {
+ printk(KERN_WARNING
+ "HiSax: No memory for tiger.send\n");
+ return;
+ }
+ cs->bcs[0].hw.tiger.s_irq = cs->bcs[0].hw.tiger.send + NETJET_DMA_TXSIZE/2 - 1;
+ cs->bcs[0].hw.tiger.s_end = cs->bcs[0].hw.tiger.send + NETJET_DMA_TXSIZE - 1;
+ cs->bcs[1].hw.tiger.send = cs->bcs[0].hw.tiger.send;
+ cs->bcs[1].hw.tiger.s_irq = cs->bcs[0].hw.tiger.s_irq;
+ cs->bcs[1].hw.tiger.s_end = cs->bcs[0].hw.tiger.s_end;
+
+ memset(cs->bcs[0].hw.tiger.send, 0xff, NETJET_DMA_TXSIZE * sizeof(unsigned int));
+ debugl1(cs, "tiger: send buf %p - %p", cs->bcs[0].hw.tiger.send,
+ cs->bcs[0].hw.tiger.send + NETJET_DMA_TXSIZE - 1);
+ outl(virt_to_bus(cs->bcs[0].hw.tiger.send),
+ cs->hw.njet.base + NETJET_DMA_READ_START);
+ outl(virt_to_bus(cs->bcs[0].hw.tiger.s_irq),
+ cs->hw.njet.base + NETJET_DMA_READ_IRQ);
+ outl(virt_to_bus(cs->bcs[0].hw.tiger.s_end),
+ cs->hw.njet.base + NETJET_DMA_READ_END);
+ if (!(cs->bcs[0].hw.tiger.rec = kmalloc(NETJET_DMA_RXSIZE * sizeof(unsigned int),
+ GFP_KERNEL | GFP_DMA))) {
+ printk(KERN_WARNING
+ "HiSax: No memory for tiger.rec\n");
+ return;
+ }
+ debugl1(cs, "tiger: rec buf %p - %p", cs->bcs[0].hw.tiger.rec,
+ cs->bcs[0].hw.tiger.rec + NETJET_DMA_RXSIZE - 1);
+ cs->bcs[1].hw.tiger.rec = cs->bcs[0].hw.tiger.rec;
+ memset(cs->bcs[0].hw.tiger.rec, 0xff, NETJET_DMA_RXSIZE * sizeof(unsigned int));
+ outl(virt_to_bus(cs->bcs[0].hw.tiger.rec),
+ cs->hw.njet.base + NETJET_DMA_WRITE_START);
+ outl(virt_to_bus(cs->bcs[0].hw.tiger.rec + NETJET_DMA_RXSIZE/2 - 1),
+ cs->hw.njet.base + NETJET_DMA_WRITE_IRQ);
+ outl(virt_to_bus(cs->bcs[0].hw.tiger.rec + NETJET_DMA_RXSIZE - 1),
+ cs->hw.njet.base + NETJET_DMA_WRITE_END);
+ debugl1(cs, "tiger: dmacfg %x/%x pulse=%d",
+ inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR),
+ inl(cs->hw.njet.base + NETJET_DMA_READ_ADR),
+ bytein(cs->hw.njet.base + NETJET_PULSE_CNT));
+ cs->hw.njet.last_is0 = 0;
+ cs->bcs[0].BC_SetStack = setstack_tiger;
+ cs->bcs[1].BC_SetStack = setstack_tiger;
+ cs->bcs[0].BC_Close = close_tigerstate;
+ cs->bcs[1].BC_Close = close_tigerstate;
+}
+
+static void
+releasetiger(struct IsdnCardState *cs)
+{
+ kfree(cs->bcs[0].hw.tiger.send);
+ cs->bcs[0].hw.tiger.send = NULL;
+ cs->bcs[1].hw.tiger.send = NULL;
+ kfree(cs->bcs[0].hw.tiger.rec);
+ cs->bcs[0].hw.tiger.rec = NULL;
+ cs->bcs[1].hw.tiger.rec = NULL;
+}
+
+void
+release_io_netjet(struct IsdnCardState *cs)
+{
+ byteout(cs->hw.njet.base + NETJET_IRQMASK0, 0);
+ byteout(cs->hw.njet.base + NETJET_IRQMASK1, 0);
+ releasetiger(cs);
+ release_region(cs->hw.njet.base, 256);
+}
+
diff --git a/drivers/isdn/hisax/netjet.h b/drivers/isdn/hisax/netjet.h
new file mode 100644
index 0000000..68e504d
--- /dev/null
+++ b/drivers/isdn/hisax/netjet.h
@@ -0,0 +1,70 @@
+/* $Id: netjet.h,v 2.8.2.2 2004/01/12 22:52:28 keil Exp $
+ *
+ * NETjet common header file
+ *
+ * Author Karsten Keil
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ * by Matt Henderson,
+ * Traverse Technologies P/L www.traverse.com.au
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define NETJET_CTRL 0x00
+#define NETJET_DMACTRL 0x01
+#define NETJET_AUXCTRL 0x02
+#define NETJET_AUXDATA 0x03
+#define NETJET_IRQMASK0 0x04
+#define NETJET_IRQMASK1 0x05
+#define NETJET_IRQSTAT0 0x06
+#define NETJET_IRQSTAT1 0x07
+#define NETJET_DMA_READ_START 0x08
+#define NETJET_DMA_READ_IRQ 0x0c
+#define NETJET_DMA_READ_END 0x10
+#define NETJET_DMA_READ_ADR 0x14
+#define NETJET_DMA_WRITE_START 0x18
+#define NETJET_DMA_WRITE_IRQ 0x1c
+#define NETJET_DMA_WRITE_END 0x20
+#define NETJET_DMA_WRITE_ADR 0x24
+#define NETJET_PULSE_CNT 0x28
+
+#define NETJET_ISAC_OFF 0xc0
+#define NETJET_ISACIRQ 0x10
+#define NETJET_IRQM0_READ 0x0c
+#define NETJET_IRQM0_READ_1 0x04
+#define NETJET_IRQM0_READ_2 0x08
+#define NETJET_IRQM0_WRITE 0x03
+#define NETJET_IRQM0_WRITE_1 0x01
+#define NETJET_IRQM0_WRITE_2 0x02
+
+#define NETJET_DMA_TXSIZE 512
+#define NETJET_DMA_RXSIZE 128
+
+#define HDLC_ZERO_SEARCH 0
+#define HDLC_FLAG_SEARCH 1
+#define HDLC_FLAG_FOUND 2
+#define HDLC_FRAME_FOUND 3
+#define HDLC_NULL 4
+#define HDLC_PART 5
+#define HDLC_FULL 6
+
+#define HDLC_FLAG_VALUE 0x7e
+
+u_char NETjet_ReadIC(struct IsdnCardState *cs, u_char offset);
+void NETjet_WriteIC(struct IsdnCardState *cs, u_char offset, u_char value);
+void NETjet_ReadICfifo(struct IsdnCardState *cs, u_char *data, int size);
+void NETjet_WriteICfifo(struct IsdnCardState *cs, u_char *data, int size);
+
+void read_tiger(struct IsdnCardState *cs);
+void write_tiger(struct IsdnCardState *cs);
+
+void netjet_fill_dma(struct BCState *bcs);
+void netjet_interrupt(int intno, void *dev_id);
+void inittiger(struct IsdnCardState *cs);
+void release_io_netjet(struct IsdnCardState *cs);
+
diff --git a/drivers/isdn/hisax/niccy.c b/drivers/isdn/hisax/niccy.c
new file mode 100644
index 0000000..ef00633
--- /dev/null
+++ b/drivers/isdn/hisax/niccy.c
@@ -0,0 +1,380 @@
+/* $Id: niccy.c,v 1.21.2.4 2004/01/13 23:48:39 keil Exp $
+ *
+ * low level stuff for Dr. Neuhaus NICCY PnP and NICCY PCI and
+ * compatible (SAGEM cybermodem)
+ *
+ * Author Karsten Keil
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to Dr. Neuhaus and SAGEM for information
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/isapnp.h>
+
+static const char *niccy_revision = "$Revision: 1.21.2.4 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define ISAC_PCI_DATA 0
+#define HSCX_PCI_DATA 1
+#define ISAC_PCI_ADDR 2
+#define HSCX_PCI_ADDR 3
+#define ISAC_PNP 0
+#define HSCX_PNP 1
+
+/* SUB Types */
+#define NICCY_PNP 1
+#define NICCY_PCI 2
+
+/* PCI stuff */
+#define PCI_IRQ_CTRL_REG 0x38
+#define PCI_IRQ_ENABLE 0x1f00
+#define PCI_IRQ_DISABLE 0xff0000
+#define PCI_IRQ_ASSERT 0x800000
+
+static inline u_char readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+ register u_char ret;
+
+ byteout(ale, off);
+ ret = bytein(adr);
+ return ret;
+}
+
+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(struct IsdnCardState *cs, u_char offset)
+{
+ return readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, offset);
+}
+
+static void WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, offset, value);
+}
+
+static void ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ readfifo(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, 0, data, size);
+}
+
+static void WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ writefifo(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, 0, data, size);
+}
+
+static u_char ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+ return readreg(cs->hw.niccy.hscx_ale,
+ cs->hw.niccy.hscx, offset + (hscx ? 0x40 : 0));
+}
+
+static void WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset,
+ u_char value)
+{
+ writereg(cs->hw.niccy.hscx_ale,
+ cs->hw.niccy.hscx, offset + (hscx ? 0x40 : 0), value);
+}
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.niccy.hscx_ale, \
+ cs->hw.niccy.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.niccy.hscx_ale, \
+ cs->hw.niccy.hscx, reg + (nr ? 0x40 : 0), data)
+
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.niccy.hscx_ale, \
+ cs->hw.niccy.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.niccy.hscx_ale, \
+ cs->hw.niccy.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t niccy_interrupt(int intno, void *dev_id)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char val;
+ u_long flags;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ if (cs->subtyp == NICCY_PCI) {
+ int ival;
+ ival = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+ if (!(ival & PCI_IRQ_ASSERT)) { /* IRQ not for us (shared) */
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_NONE;
+ }
+ outl(ival, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+ }
+ val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx,
+ HSCX_ISTA + 0x40);
+Start_HSCX:
+ if (val)
+ hscx_int_main(cs, val);
+ val = readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_ISTA);
+Start_ISAC:
+ if (val)
+ isac_interrupt(cs, val);
+ val = readreg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx,
+ HSCX_ISTA + 0x40);
+ if (val) {
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "HSCX IntStat after IntRoutine");
+ goto Start_HSCX;
+ }
+ val = readreg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_ISTA);
+ if (val) {
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ISAC IntStat after IntRoutine");
+ goto Start_ISAC;
+ }
+ writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK, 0xFF);
+ writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40,
+ 0xFF);
+ writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_MASK, 0xFF);
+ writereg(cs->hw.niccy.isac_ale, cs->hw.niccy.isac, ISAC_MASK, 0);
+ writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK, 0);
+ writereg(cs->hw.niccy.hscx_ale, cs->hw.niccy.hscx, HSCX_MASK + 0x40,0);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static void release_io_niccy(struct IsdnCardState *cs)
+{
+ if (cs->subtyp == NICCY_PCI) {
+ int val;
+
+ val = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+ val &= PCI_IRQ_DISABLE;
+ outl(val, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+ release_region(cs->hw.niccy.cfg_reg, 0x40);
+ release_region(cs->hw.niccy.isac, 4);
+ } else {
+ release_region(cs->hw.niccy.isac, 2);
+ release_region(cs->hw.niccy.isac_ale, 2);
+ }
+}
+
+static void niccy_reset(struct IsdnCardState *cs)
+{
+ if (cs->subtyp == NICCY_PCI) {
+ int val;
+
+ val = inl(cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+ val |= PCI_IRQ_ENABLE;
+ outl(val, cs->hw.niccy.cfg_reg + PCI_IRQ_CTRL_REG);
+ }
+ inithscxisac(cs, 3);
+}
+
+static int niccy_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ u_long flags;
+
+ switch (mt) {
+ case CARD_RESET:
+ spin_lock_irqsave(&cs->lock, flags);
+ niccy_reset(cs);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return 0;
+ case CARD_RELEASE:
+ release_io_niccy(cs);
+ return 0;
+ case CARD_INIT:
+ spin_lock_irqsave(&cs->lock, flags);
+ niccy_reset(cs);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return 0;
+ case CARD_TEST:
+ return 0;
+ }
+ return 0;
+}
+
+#ifdef __ISAPNP__
+static struct pnp_card *pnp_c __devinitdata = NULL;
+#endif
+
+int __devinit setup_niccy(struct IsdnCard *card)
+{
+ struct IsdnCardState *cs = card->cs;
+ char tmp[64];
+
+ strcpy(tmp, niccy_revision);
+ printk(KERN_INFO "HiSax: Niccy driver Rev. %s\n", HiSax_getrev(tmp));
+ if (cs->typ != ISDN_CTYPE_NICCY)
+ return 0;
+#ifdef __ISAPNP__
+ if (!card->para[1] && isapnp_present()) {
+ struct pnp_dev *pnp_d = NULL;
+ int err;
+
+ pnp_c = pnp_find_card(ISAPNP_VENDOR('S', 'D', 'A'),
+ ISAPNP_FUNCTION(0x0150), pnp_c);
+ if (pnp_c) {
+ pnp_d = pnp_find_dev(pnp_c,
+ ISAPNP_VENDOR('S', 'D', 'A'),
+ ISAPNP_FUNCTION(0x0150), pnp_d);
+ if (!pnp_d) {
+ printk(KERN_ERR "NiccyPnP: PnP error card "
+ "found, no device\n");
+ return 0;
+ }
+ pnp_disable_dev(pnp_d);
+ err = pnp_activate_dev(pnp_d);
+ if (err < 0) {
+ printk(KERN_WARNING "%s: pnp_activate_dev "
+ "ret(%d)\n", __func__, err);
+ return 0;
+ }
+ card->para[1] = pnp_port_start(pnp_d, 0);
+ card->para[2] = pnp_port_start(pnp_d, 1);
+ card->para[0] = pnp_irq(pnp_d, 0);
+ if (!card->para[0] || !card->para[1] ||
+ !card->para[2]) {
+ printk(KERN_ERR "NiccyPnP:some resources are "
+ "missing %ld/%lx/%lx\n",
+ card->para[0], card->para[1],
+ card->para[2]);
+ pnp_disable_dev(pnp_d);
+ return 0;
+ }
+ } else
+ printk(KERN_INFO "NiccyPnP: no ISAPnP card found\n");
+ }
+#endif
+ if (card->para[1]) {
+ cs->hw.niccy.isac = card->para[1] + ISAC_PNP;
+ cs->hw.niccy.hscx = card->para[1] + HSCX_PNP;
+ cs->hw.niccy.isac_ale = card->para[2] + ISAC_PNP;
+ cs->hw.niccy.hscx_ale = card->para[2] + HSCX_PNP;
+ cs->hw.niccy.cfg_reg = 0;
+ cs->subtyp = NICCY_PNP;
+ cs->irq = card->para[0];
+ if (!request_region(cs->hw.niccy.isac, 2, "niccy data")) {
+ printk(KERN_WARNING "HiSax: NICCY data port %x-%x "
+ "already in use\n",
+ cs->hw.niccy.isac, cs->hw.niccy.isac + 1);
+ return 0;
+ }
+ if (!request_region(cs->hw.niccy.isac_ale, 2, "niccy addr")) {
+ printk(KERN_WARNING "HiSax: NICCY address port %x-%x "
+ "already in use\n",
+ cs->hw.niccy.isac_ale,
+ cs->hw.niccy.isac_ale + 1);
+ release_region(cs->hw.niccy.isac, 2);
+ return 0;
+ }
+ } else {
+#ifdef CONFIG_PCI_LEGACY
+ static struct pci_dev *niccy_dev __devinitdata;
+
+ u_int pci_ioaddr;
+ cs->subtyp = 0;
+ if ((niccy_dev = pci_find_device(PCI_VENDOR_ID_SATSAGEM,
+ PCI_DEVICE_ID_SATSAGEM_NICCY,
+ niccy_dev))) {
+ if (pci_enable_device(niccy_dev))
+ return 0;
+ /* get IRQ */
+ if (!niccy_dev->irq) {
+ printk(KERN_WARNING
+ "Niccy: No IRQ for PCI card found\n");
+ return 0;
+ }
+ cs->irq = niccy_dev->irq;
+ cs->hw.niccy.cfg_reg = pci_resource_start(niccy_dev, 0);
+ if (!cs->hw.niccy.cfg_reg) {
+ printk(KERN_WARNING
+ "Niccy: No IO-Adr for PCI cfg found\n");
+ return 0;
+ }
+ pci_ioaddr = pci_resource_start(niccy_dev, 1);
+ if (!pci_ioaddr) {
+ printk(KERN_WARNING
+ "Niccy: No IO-Adr for PCI card found\n");
+ return 0;
+ }
+ cs->subtyp = NICCY_PCI;
+ } else {
+ printk(KERN_WARNING "Niccy: No PCI card found\n");
+ return 0;
+ }
+ cs->irq_flags |= IRQF_SHARED;
+ cs->hw.niccy.isac = pci_ioaddr + ISAC_PCI_DATA;
+ cs->hw.niccy.isac_ale = pci_ioaddr + ISAC_PCI_ADDR;
+ cs->hw.niccy.hscx = pci_ioaddr + HSCX_PCI_DATA;
+ cs->hw.niccy.hscx_ale = pci_ioaddr + HSCX_PCI_ADDR;
+ if (!request_region(cs->hw.niccy.isac, 4, "niccy")) {
+ printk(KERN_WARNING
+ "HiSax: NICCY data port %x-%x already in use\n",
+ cs->hw.niccy.isac, cs->hw.niccy.isac + 4);
+ return 0;
+ }
+ if (!request_region(cs->hw.niccy.cfg_reg, 0x40, "niccy pci")) {
+ printk(KERN_WARNING
+ "HiSax: NICCY pci port %x-%x already in use\n",
+ cs->hw.niccy.cfg_reg,
+ cs->hw.niccy.cfg_reg + 0x40);
+ release_region(cs->hw.niccy.isac, 4);
+ return 0;
+ }
+#else
+ printk(KERN_WARNING "Niccy: io0 0 and NO_PCI_BIOS\n");
+ printk(KERN_WARNING "Niccy: unable to config NICCY PCI\n");
+ return 0;
+#endif /* CONFIG_PCI_LEGACY */
+ }
+ printk(KERN_INFO "HiSax: NICCY %s config irq:%d data:0x%X ale:0x%X\n",
+ (cs->subtyp == 1) ? "PnP" : "PCI",
+ cs->irq, cs->hw.niccy.isac, cs->hw.niccy.isac_ale);
+ setup_isac(cs);
+ cs->readisac = &ReadISAC;
+ cs->writeisac = &WriteISAC;
+ cs->readisacfifo = &ReadISACfifo;
+ cs->writeisacfifo = &WriteISACfifo;
+ cs->BC_Read_Reg = &ReadHSCX;
+ cs->BC_Write_Reg = &WriteHSCX;
+ cs->BC_Send_Data = &hscx_fill_fifo;
+ cs->cardmsg = &niccy_card_msg;
+ cs->irq_func = &niccy_interrupt;
+ ISACVersion(cs, "Niccy:");
+ if (HscxVersion(cs, "Niccy:")) {
+ printk(KERN_WARNING "Niccy: wrong HSCX versions check IO "
+ "address\n");
+ release_io_niccy(cs);
+ return 0;
+ }
+ return 1;
+}
diff --git a/drivers/isdn/hisax/nj_s.c b/drivers/isdn/hisax/nj_s.c
new file mode 100644
index 0000000..8d36ccc
--- /dev/null
+++ b/drivers/isdn/hisax/nj_s.c
@@ -0,0 +1,298 @@
+/* $Id: nj_s.c,v 2.13.2.4 2004/01/16 01:53:48 keil Exp $
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/ppp_defs.h>
+#include "netjet.h"
+
+static const char *NETjet_S_revision = "$Revision: 2.13.2.4 $";
+
+static u_char dummyrr(struct IsdnCardState *cs, int chan, u_char off)
+{
+ return(5);
+}
+
+static void dummywr(struct IsdnCardState *cs, int chan, u_char off, u_char value)
+{
+}
+
+static irqreturn_t
+netjet_s_interrupt(int intno, void *dev_id)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char val, s1val, s0val;
+ u_long flags;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ s1val = bytein(cs->hw.njet.base + NETJET_IRQSTAT1);
+ if (!(s1val & NETJET_ISACIRQ)) {
+ val = NETjet_ReadIC(cs, ISAC_ISTA);
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "tiger: i1 %x %x", s1val, val);
+ if (val) {
+ isac_interrupt(cs, val);
+ NETjet_WriteIC(cs, ISAC_MASK, 0xFF);
+ NETjet_WriteIC(cs, ISAC_MASK, 0x0);
+ }
+ s1val = 1;
+ } else
+ s1val = 0;
+ /*
+ * read/write stat0 is better, because lower IRQ rate
+ * Note the IRQ is on for 125 us if a condition match
+ * thats long on modern CPU and so the IRQ is reentered
+ * all the time.
+ */
+ s0val = bytein(cs->hw.njet.base + NETJET_IRQSTAT0);
+ if ((s0val | s1val)==0) { // shared IRQ
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_NONE;
+ }
+ if (s0val)
+ byteout(cs->hw.njet.base + NETJET_IRQSTAT0, s0val);
+ /* start new code 13/07/00 GE */
+ /* set bits in sval to indicate which page is free */
+ if (inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR) <
+ inl(cs->hw.njet.base + NETJET_DMA_WRITE_IRQ))
+ /* the 2nd write page is free */
+ s0val = 0x08;
+ else /* the 1st write page is free */
+ s0val = 0x04;
+ if (inl(cs->hw.njet.base + NETJET_DMA_READ_ADR) <
+ inl(cs->hw.njet.base + NETJET_DMA_READ_IRQ))
+ /* the 2nd read page is free */
+ s0val |= 0x02;
+ else /* the 1st read page is free */
+ s0val |= 0x01;
+ if (s0val != cs->hw.njet.last_is0) /* we have a DMA interrupt */
+ {
+ if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ printk(KERN_WARNING "nj LOCK_ATOMIC s0val %x->%x\n",
+ cs->hw.njet.last_is0, s0val);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+ }
+ cs->hw.njet.irqstat0 = s0val;
+ if ((cs->hw.njet.irqstat0 & NETJET_IRQM0_READ) !=
+ (cs->hw.njet.last_is0 & NETJET_IRQM0_READ))
+ /* we have a read dma int */
+ read_tiger(cs);
+ if ((cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE) !=
+ (cs->hw.njet.last_is0 & NETJET_IRQM0_WRITE))
+ /* we have a write dma int */
+ write_tiger(cs);
+ /* end new code 13/07/00 GE */
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static void
+reset_netjet_s(struct IsdnCardState *cs)
+{
+ cs->hw.njet.ctrl_reg = 0xff; /* Reset On */
+ byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+ mdelay(10);
+ /* now edge triggered for TJ320 GE 13/07/00 */
+ /* see comment in IRQ function */
+ if (cs->subtyp) /* TJ320 */
+ cs->hw.njet.ctrl_reg = 0x40; /* Reset Off and status read clear */
+ else
+ cs->hw.njet.ctrl_reg = 0x00; /* Reset Off and status read clear */
+ byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+ mdelay(10);
+ cs->hw.njet.auxd = 0;
+ cs->hw.njet.dmactrl = 0;
+ byteout(cs->hw.njet.base + NETJET_AUXCTRL, ~NETJET_ISACIRQ);
+ byteout(cs->hw.njet.base + NETJET_IRQMASK1, NETJET_ISACIRQ);
+ byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+}
+
+static int
+NETjet_S_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ u_long flags;
+
+ switch (mt) {
+ case CARD_RESET:
+ spin_lock_irqsave(&cs->lock, flags);
+ reset_netjet_s(cs);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return(0);
+ case CARD_RELEASE:
+ release_io_netjet(cs);
+ return(0);
+ case CARD_INIT:
+ reset_netjet_s(cs);
+ inittiger(cs);
+ spin_lock_irqsave(&cs->lock, flags);
+ clear_pending_isac_ints(cs);
+ initisac(cs);
+ /* Reenable all IRQ */
+ cs->writeisac(cs, ISAC_MASK, 0);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return(0);
+ case CARD_TEST:
+ return(0);
+ }
+ return(0);
+}
+
+static int __devinit njs_pci_probe(struct pci_dev *dev_netjet,
+ struct IsdnCardState *cs)
+{
+ u32 cfg;
+
+ if (pci_enable_device(dev_netjet))
+ return(0);
+ pci_set_master(dev_netjet);
+ cs->irq = dev_netjet->irq;
+ if (!cs->irq) {
+ printk(KERN_WARNING "NETjet-S: No IRQ for PCI card found\n");
+ return(0);
+ }
+ cs->hw.njet.base = pci_resource_start(dev_netjet, 0);
+ if (!cs->hw.njet.base) {
+ printk(KERN_WARNING "NETjet-S: No IO-Adr for PCI card found\n");
+ return(0);
+ }
+ /* the TJ300 and TJ320 must be detected, the IRQ handling is different
+ * unfortunatly the chips use the same device ID, but the TJ320 has
+ * the bit20 in status PCI cfg register set
+ */
+ pci_read_config_dword(dev_netjet, 0x04, &cfg);
+ if (cfg & 0x00100000)
+ cs->subtyp = 1; /* TJ320 */
+ else
+ cs->subtyp = 0; /* TJ300 */
+ /* 2001/10/04 Christoph Ersfeld, Formula-n Europe AG www.formula-n.com */
+ if ((dev_netjet->subsystem_vendor == 0x55) &&
+ (dev_netjet->subsystem_device == 0x02)) {
+ printk(KERN_WARNING "Netjet: You tried to load this driver with an incompatible TigerJet-card\n");
+ printk(KERN_WARNING "Use type=41 for Formula-n enter:now ISDN PCI and compatible\n");
+ return(0);
+ }
+ /* end new code */
+
+ return(1);
+}
+
+static int __devinit njs_cs_init(struct IsdnCard *card,
+ struct IsdnCardState *cs)
+{
+
+ cs->hw.njet.auxa = cs->hw.njet.base + NETJET_AUXDATA;
+ cs->hw.njet.isac = cs->hw.njet.base | NETJET_ISAC_OFF;
+
+ cs->hw.njet.ctrl_reg = 0xff; /* Reset On */
+ byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+ mdelay(10);
+
+ cs->hw.njet.ctrl_reg = 0x00; /* Reset Off and status read clear */
+ byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+ mdelay(10);
+
+ cs->hw.njet.auxd = 0xC0;
+ cs->hw.njet.dmactrl = 0;
+
+ byteout(cs->hw.njet.base + NETJET_AUXCTRL, ~NETJET_ISACIRQ);
+ byteout(cs->hw.njet.base + NETJET_IRQMASK1, NETJET_ISACIRQ);
+ byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+
+ switch ( ( ( NETjet_ReadIC( cs, ISAC_RBCH ) >> 5 ) & 3 ) )
+ {
+ case 0 :
+ return 1; /* end loop */
+
+ case 3 :
+ printk( KERN_WARNING "NETjet-S: NETspider-U PCI card found\n" );
+ return -1; /* continue looping */
+
+ default :
+ printk( KERN_WARNING "NETjet-S: No PCI card found\n" );
+ return 0; /* end loop & function */
+ }
+ return 1; /* end loop */
+}
+
+static int __devinit njs_cs_init_rest(struct IsdnCard *card,
+ struct IsdnCardState *cs)
+{
+ const int bytecnt = 256;
+
+ printk(KERN_INFO
+ "NETjet-S: %s card configured at %#lx IRQ %d\n",
+ cs->subtyp ? "TJ320" : "TJ300", cs->hw.njet.base, cs->irq);
+ if (!request_region(cs->hw.njet.base, bytecnt, "netjet-s isdn")) {
+ printk(KERN_WARNING
+ "HiSax: NETjet-S config port %#lx-%#lx already in use\n",
+ cs->hw.njet.base,
+ cs->hw.njet.base + bytecnt);
+ return (0);
+ }
+ cs->readisac = &NETjet_ReadIC;
+ cs->writeisac = &NETjet_WriteIC;
+ cs->readisacfifo = &NETjet_ReadICfifo;
+ cs->writeisacfifo = &NETjet_WriteICfifo;
+ cs->BC_Read_Reg = &dummyrr;
+ cs->BC_Write_Reg = &dummywr;
+ cs->BC_Send_Data = &netjet_fill_dma;
+ setup_isac(cs);
+ cs->cardmsg = &NETjet_S_card_msg;
+ cs->irq_func = &netjet_s_interrupt;
+ cs->irq_flags |= IRQF_SHARED;
+ ISACVersion(cs, "NETjet-S:");
+
+ return (1);
+}
+
+static struct pci_dev *dev_netjet __devinitdata = NULL;
+
+int __devinit
+setup_netjet_s(struct IsdnCard *card)
+{
+ int ret;
+ struct IsdnCardState *cs = card->cs;
+ char tmp[64];
+
+#ifdef __BIG_ENDIAN
+#error "not running on big endian machines now"
+#endif
+ strcpy(tmp, NETjet_S_revision);
+ printk(KERN_INFO "HiSax: Traverse Tech. NETjet-S driver Rev. %s\n", HiSax_getrev(tmp));
+ if (cs->typ != ISDN_CTYPE_NETJET_S)
+ return(0);
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+
+ for ( ;; )
+ {
+ if ((dev_netjet = pci_find_device(PCI_VENDOR_ID_TIGERJET,
+ PCI_DEVICE_ID_TIGERJET_300, dev_netjet))) {
+ ret = njs_pci_probe(dev_netjet, cs);
+ if (!ret)
+ return(0);
+ } else {
+ printk(KERN_WARNING "NETjet-S: No PCI card found\n");
+ return(0);
+ }
+
+ ret = njs_cs_init(card, cs);
+ if (!ret)
+ return(0);
+ if (ret > 0)
+ break;
+ /* otherwise, ret < 0, continue looping */
+ }
+
+ return njs_cs_init_rest(card, cs);
+}
diff --git a/drivers/isdn/hisax/nj_u.c b/drivers/isdn/hisax/nj_u.c
new file mode 100644
index 0000000..d306c94
--- /dev/null
+++ b/drivers/isdn/hisax/nj_u.c
@@ -0,0 +1,262 @@
+/* $Id: nj_u.c,v 2.14.2.3 2004/01/13 14:31:26 keil Exp $
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "icc.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/ppp_defs.h>
+#include "netjet.h"
+
+static const char *NETjet_U_revision = "$Revision: 2.14.2.3 $";
+
+static u_char dummyrr(struct IsdnCardState *cs, int chan, u_char off)
+{
+ return(5);
+}
+
+static void dummywr(struct IsdnCardState *cs, int chan, u_char off, u_char value)
+{
+}
+
+static irqreturn_t
+netjet_u_interrupt(int intno, void *dev_id)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char val, sval;
+ u_long flags;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ if (!((sval = bytein(cs->hw.njet.base + NETJET_IRQSTAT1)) &
+ NETJET_ISACIRQ)) {
+ val = NETjet_ReadIC(cs, ICC_ISTA);
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "tiger: i1 %x %x", sval, val);
+ if (val) {
+ icc_interrupt(cs, val);
+ NETjet_WriteIC(cs, ICC_MASK, 0xFF);
+ NETjet_WriteIC(cs, ICC_MASK, 0x0);
+ }
+ }
+ /* start new code 13/07/00 GE */
+ /* set bits in sval to indicate which page is free */
+ if (inl(cs->hw.njet.base + NETJET_DMA_WRITE_ADR) <
+ inl(cs->hw.njet.base + NETJET_DMA_WRITE_IRQ))
+ /* the 2nd write page is free */
+ sval = 0x08;
+ else /* the 1st write page is free */
+ sval = 0x04;
+ if (inl(cs->hw.njet.base + NETJET_DMA_READ_ADR) <
+ inl(cs->hw.njet.base + NETJET_DMA_READ_IRQ))
+ /* the 2nd read page is free */
+ sval = sval | 0x02;
+ else /* the 1st read page is free */
+ sval = sval | 0x01;
+ if (sval != cs->hw.njet.last_is0) /* we have a DMA interrupt */
+ {
+ if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) {
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+ }
+ cs->hw.njet.irqstat0 = sval;
+ if ((cs->hw.njet.irqstat0 & NETJET_IRQM0_READ) !=
+ (cs->hw.njet.last_is0 & NETJET_IRQM0_READ))
+ /* we have a read dma int */
+ read_tiger(cs);
+ if ((cs->hw.njet.irqstat0 & NETJET_IRQM0_WRITE) !=
+ (cs->hw.njet.last_is0 & NETJET_IRQM0_WRITE))
+ /* we have a write dma int */
+ write_tiger(cs);
+ /* end new code 13/07/00 GE */
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static void
+reset_netjet_u(struct IsdnCardState *cs)
+{
+ cs->hw.njet.ctrl_reg = 0xff; /* Reset On */
+ byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+ mdelay(10);
+ cs->hw.njet.ctrl_reg = 0x40; /* Reset Off and status read clear */
+ /* now edge triggered for TJ320 GE 13/07/00 */
+ byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+ mdelay(10);
+ cs->hw.njet.auxd = 0xC0;
+ cs->hw.njet.dmactrl = 0;
+ byteout(cs->hw.njet.auxa, 0);
+ byteout(cs->hw.njet.base + NETJET_AUXCTRL, ~NETJET_ISACIRQ);
+ byteout(cs->hw.njet.base + NETJET_IRQMASK1, NETJET_ISACIRQ);
+ byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+}
+
+static int
+NETjet_U_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ u_long flags;
+
+ switch (mt) {
+ case CARD_RESET:
+ spin_lock_irqsave(&cs->lock, flags);
+ reset_netjet_u(cs);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return(0);
+ case CARD_RELEASE:
+ release_io_netjet(cs);
+ return(0);
+ case CARD_INIT:
+ spin_lock_irqsave(&cs->lock, flags);
+ inittiger(cs);
+ reset_netjet_u(cs);
+ clear_pending_icc_ints(cs);
+ initicc(cs);
+ /* Reenable all IRQ */
+ cs->writeisac(cs, ICC_MASK, 0);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return(0);
+ case CARD_TEST:
+ return(0);
+ }
+ return(0);
+}
+
+static int __devinit nju_pci_probe(struct pci_dev *dev_netjet,
+ struct IsdnCardState *cs)
+{
+ if (pci_enable_device(dev_netjet))
+ return(0);
+ pci_set_master(dev_netjet);
+ cs->irq = dev_netjet->irq;
+ if (!cs->irq) {
+ printk(KERN_WARNING "NETspider-U: No IRQ for PCI card found\n");
+ return(0);
+ }
+ cs->hw.njet.base = pci_resource_start(dev_netjet, 0);
+ if (!cs->hw.njet.base) {
+ printk(KERN_WARNING "NETspider-U: No IO-Adr for PCI card found\n");
+ return(0);
+ }
+
+ return (1);
+}
+
+static int __devinit nju_cs_init(struct IsdnCard *card,
+ struct IsdnCardState *cs)
+{
+ cs->hw.njet.auxa = cs->hw.njet.base + NETJET_AUXDATA;
+ cs->hw.njet.isac = cs->hw.njet.base | NETJET_ISAC_OFF;
+ mdelay(10);
+
+ cs->hw.njet.ctrl_reg = 0xff; /* Reset On */
+ byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+ mdelay(10);
+
+ cs->hw.njet.ctrl_reg = 0x00; /* Reset Off and status read clear */
+ byteout(cs->hw.njet.base + NETJET_CTRL, cs->hw.njet.ctrl_reg);
+ mdelay(10);
+
+ cs->hw.njet.auxd = 0xC0;
+ cs->hw.njet.dmactrl = 0;
+
+ byteout(cs->hw.njet.auxa, 0);
+ byteout(cs->hw.njet.base + NETJET_AUXCTRL, ~NETJET_ISACIRQ);
+ byteout(cs->hw.njet.base + NETJET_IRQMASK1, NETJET_ISACIRQ);
+ byteout(cs->hw.njet.auxa, cs->hw.njet.auxd);
+
+ switch ( ( ( NETjet_ReadIC( cs, ICC_RBCH ) >> 5 ) & 3 ) )
+ {
+ case 3 :
+ return 1; /* end loop */
+
+ case 0 :
+ printk( KERN_WARNING "NETspider-U: NETjet-S PCI card found\n" );
+ return -1; /* continue looping */
+
+ default :
+ printk( KERN_WARNING "NETspider-U: No PCI card found\n" );
+ return 0; /* end loop & function */
+ }
+ return 1; /* end loop */
+}
+
+static int __devinit nju_cs_init_rest(struct IsdnCard *card,
+ struct IsdnCardState *cs)
+{
+ const int bytecnt = 256;
+
+ printk(KERN_INFO
+ "NETspider-U: PCI card configured at %#lx IRQ %d\n",
+ cs->hw.njet.base, cs->irq);
+ if (!request_region(cs->hw.njet.base, bytecnt, "netspider-u isdn")) {
+ printk(KERN_WARNING
+ "HiSax: NETspider-U config port %#lx-%#lx "
+ "already in use\n",
+ cs->hw.njet.base,
+ cs->hw.njet.base + bytecnt);
+ return (0);
+ }
+ setup_icc(cs);
+ cs->readisac = &NETjet_ReadIC;
+ cs->writeisac = &NETjet_WriteIC;
+ cs->readisacfifo = &NETjet_ReadICfifo;
+ cs->writeisacfifo = &NETjet_WriteICfifo;
+ cs->BC_Read_Reg = &dummyrr;
+ cs->BC_Write_Reg = &dummywr;
+ cs->BC_Send_Data = &netjet_fill_dma;
+ cs->cardmsg = &NETjet_U_card_msg;
+ cs->irq_func = &netjet_u_interrupt;
+ cs->irq_flags |= IRQF_SHARED;
+ ICCVersion(cs, "NETspider-U:");
+
+ return (1);
+}
+
+static struct pci_dev *dev_netjet __devinitdata = NULL;
+
+int __devinit
+setup_netjet_u(struct IsdnCard *card)
+{
+ int ret;
+ struct IsdnCardState *cs = card->cs;
+ char tmp[64];
+
+#ifdef __BIG_ENDIAN
+#error "not running on big endian machines now"
+#endif
+
+ strcpy(tmp, NETjet_U_revision);
+ printk(KERN_INFO "HiSax: Traverse Tech. NETspider-U driver Rev. %s\n", HiSax_getrev(tmp));
+ if (cs->typ != ISDN_CTYPE_NETJET_U)
+ return(0);
+ test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags);
+
+ for ( ;; )
+ {
+ if ((dev_netjet = pci_find_device(PCI_VENDOR_ID_TIGERJET,
+ PCI_DEVICE_ID_TIGERJET_300, dev_netjet))) {
+ ret = nju_pci_probe(dev_netjet, cs);
+ if (!ret)
+ return(0);
+ } else {
+ printk(KERN_WARNING "NETspider-U: No PCI card found\n");
+ return(0);
+ }
+
+ ret = nju_cs_init(card, cs);
+ if (!ret)
+ return (0);
+ if (ret > 0)
+ break;
+ /* ret < 0 == continue looping */
+ }
+
+ return nju_cs_init_rest(card, cs);
+}
diff --git a/drivers/isdn/hisax/q931.c b/drivers/isdn/hisax/q931.c
new file mode 100644
index 0000000..aacbf0d
--- /dev/null
+++ b/drivers/isdn/hisax/q931.c
@@ -0,0 +1,1522 @@
+/* $Id: q931.c,v 1.12.2.3 2004/01/13 14:31:26 keil Exp $
+ *
+ * code to decode ITU Q.931 call control messages
+ *
+ * Author Jan den Ouden
+ * Copyright by Jan den Ouden
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Changelog:
+ *
+ * Pauline Middelink general improvements
+ * Beat Doebeli cause texts, display information element
+ * Karsten Keil cause texts, display information element for 1TR6
+ *
+ */
+
+
+#include "hisax.h"
+#include "l3_1tr6.h"
+
+void
+iecpy(u_char * dest, u_char * iestart, int ieoffset)
+{
+ u_char *p;
+ int l;
+
+ p = iestart + ieoffset + 2;
+ l = iestart[1] - ieoffset;
+ while (l--)
+ *dest++ = *p++;
+ *dest++ = '\0';
+}
+
+/*
+ * According to Table 4-2/Q.931
+ */
+static
+struct MessageType {
+ u_char nr;
+ char *descr;
+} mtlist[] = {
+
+ {
+ 0x1, "ALERTING"
+ },
+ {
+ 0x2, "CALL PROCEEDING"
+ },
+ {
+ 0x7, "CONNECT"
+ },
+ {
+ 0xf, "CONNECT ACKNOWLEDGE"
+ },
+ {
+ 0x3, "PROGRESS"
+ },
+ {
+ 0x5, "SETUP"
+ },
+ {
+ 0xd, "SETUP ACKNOWLEDGE"
+ },
+ {
+ 0x24, "HOLD"
+ },
+ {
+ 0x28, "HOLD ACKNOWLEDGE"
+ },
+ {
+ 0x30, "HOLD REJECT"
+ },
+ {
+ 0x31, "RETRIEVE"
+ },
+ {
+ 0x33, "RETRIEVE ACKNOWLEDGE"
+ },
+ {
+ 0x37, "RETRIEVE REJECT"
+ },
+ {
+ 0x26, "RESUME"
+ },
+ {
+ 0x2e, "RESUME ACKNOWLEDGE"
+ },
+ {
+ 0x22, "RESUME REJECT"
+ },
+ {
+ 0x25, "SUSPEND"
+ },
+ {
+ 0x2d, "SUSPEND ACKNOWLEDGE"
+ },
+ {
+ 0x21, "SUSPEND REJECT"
+ },
+ {
+ 0x20, "USER INFORMATION"
+ },
+ {
+ 0x45, "DISCONNECT"
+ },
+ {
+ 0x4d, "RELEASE"
+ },
+ {
+ 0x5a, "RELEASE COMPLETE"
+ },
+ {
+ 0x46, "RESTART"
+ },
+ {
+ 0x4e, "RESTART ACKNOWLEDGE"
+ },
+ {
+ 0x60, "SEGMENT"
+ },
+ {
+ 0x79, "CONGESTION CONTROL"
+ },
+ {
+ 0x7b, "INFORMATION"
+ },
+ {
+ 0x62, "FACILITY"
+ },
+ {
+ 0x6e, "NOTIFY"
+ },
+ {
+ 0x7d, "STATUS"
+ },
+ {
+ 0x75, "STATUS ENQUIRY"
+ }
+};
+
+#define MTSIZE sizeof(mtlist)/sizeof(struct MessageType)
+
+static
+struct MessageType mt_n0[] =
+{
+ {MT_N0_REG_IND, "REGister INDication"},
+ {MT_N0_CANC_IND, "CANCel INDication"},
+ {MT_N0_FAC_STA, "FACility STAtus"},
+ {MT_N0_STA_ACK, "STAtus ACKnowledge"},
+ {MT_N0_STA_REJ, "STAtus REJect"},
+ {MT_N0_FAC_INF, "FACility INFormation"},
+ {MT_N0_INF_ACK, "INFormation ACKnowledge"},
+ {MT_N0_INF_REJ, "INFormation REJect"},
+ {MT_N0_CLOSE, "CLOSE"},
+ {MT_N0_CLO_ACK, "CLOse ACKnowledge"}
+};
+
+#define MT_N0_LEN (sizeof(mt_n0) / sizeof(struct MessageType))
+
+static
+struct MessageType mt_n1[] =
+{
+ {MT_N1_ESC, "ESCape"},
+ {MT_N1_ALERT, "ALERT"},
+ {MT_N1_CALL_SENT, "CALL SENT"},
+ {MT_N1_CONN, "CONNect"},
+ {MT_N1_CONN_ACK, "CONNect ACKnowledge"},
+ {MT_N1_SETUP, "SETUP"},
+ {MT_N1_SETUP_ACK, "SETUP ACKnowledge"},
+ {MT_N1_RES, "RESume"},
+ {MT_N1_RES_ACK, "RESume ACKnowledge"},
+ {MT_N1_RES_REJ, "RESume REJect"},
+ {MT_N1_SUSP, "SUSPend"},
+ {MT_N1_SUSP_ACK, "SUSPend ACKnowledge"},
+ {MT_N1_SUSP_REJ, "SUSPend REJect"},
+ {MT_N1_USER_INFO, "USER INFO"},
+ {MT_N1_DET, "DETach"},
+ {MT_N1_DISC, "DISConnect"},
+ {MT_N1_REL, "RELease"},
+ {MT_N1_REL_ACK, "RELease ACKnowledge"},
+ {MT_N1_CANC_ACK, "CANCel ACKnowledge"},
+ {MT_N1_CANC_REJ, "CANCel REJect"},
+ {MT_N1_CON_CON, "CONgestion CONtrol"},
+ {MT_N1_FAC, "FACility"},
+ {MT_N1_FAC_ACK, "FACility ACKnowledge"},
+ {MT_N1_FAC_CAN, "FACility CANcel"},
+ {MT_N1_FAC_REG, "FACility REGister"},
+ {MT_N1_FAC_REJ, "FACility REJect"},
+ {MT_N1_INFO, "INFOrmation"},
+ {MT_N1_REG_ACK, "REGister ACKnowledge"},
+ {MT_N1_REG_REJ, "REGister REJect"},
+ {MT_N1_STAT, "STATus"}
+};
+
+#define MT_N1_LEN (sizeof(mt_n1) / sizeof(struct MessageType))
+
+
+static int
+prbits(char *dest, u_char b, int start, int len)
+{
+ char *dp = dest;
+
+ b = b << (8 - start);
+ while (len--) {
+ if (b & 0x80)
+ *dp++ = '1';
+ else
+ *dp++ = '0';
+ b = b << 1;
+ }
+ return (dp - dest);
+}
+
+static
+u_char *
+skipext(u_char * p)
+{
+ while (!(*p++ & 0x80));
+ return (p);
+}
+
+/*
+ * Cause Values According to Q.850
+ * edescr: English description
+ * ddescr: German description used by Swissnet II (Swiss Telecom
+ * not yet written...
+ */
+
+static
+struct CauseValue {
+ u_char nr;
+ char *edescr;
+ char *ddescr;
+} cvlist[] = {
+
+ {
+ 0x01, "Unallocated (unassigned) number", "Nummer nicht zugeteilt"
+ },
+ {
+ 0x02, "No route to specified transit network", ""
+ },
+ {
+ 0x03, "No route to destination", ""
+ },
+ {
+ 0x04, "Send special information tone", ""
+ },
+ {
+ 0x05, "Misdialled trunk prefix", ""
+ },
+ {
+ 0x06, "Channel unacceptable", "Kanal nicht akzeptierbar"
+ },
+ {
+ 0x07, "Channel awarded and being delivered in an established channel", ""
+ },
+ {
+ 0x08, "Preemption", ""
+ },
+ {
+ 0x09, "Preemption - circuit reserved for reuse", ""
+ },
+ {
+ 0x10, "Normal call clearing", "Normale Ausloesung"
+ },
+ {
+ 0x11, "User busy", "TNB besetzt"
+ },
+ {
+ 0x12, "No user responding", ""
+ },
+ {
+ 0x13, "No answer from user (user alerted)", ""
+ },
+ {
+ 0x14, "Subscriber absent", ""
+ },
+ {
+ 0x15, "Call rejected", ""
+ },
+ {
+ 0x16, "Number changed", ""
+ },
+ {
+ 0x1a, "non-selected user clearing", ""
+ },
+ {
+ 0x1b, "Destination out of order", ""
+ },
+ {
+ 0x1c, "Invalid number format (address incomplete)", ""
+ },
+ {
+ 0x1d, "Facility rejected", ""
+ },
+ {
+ 0x1e, "Response to Status enquiry", ""
+ },
+ {
+ 0x1f, "Normal, unspecified", ""
+ },
+ {
+ 0x22, "No circuit/channel available", ""
+ },
+ {
+ 0x26, "Network out of order", ""
+ },
+ {
+ 0x27, "Permanent frame mode connection out-of-service", ""
+ },
+ {
+ 0x28, "Permanent frame mode connection operational", ""
+ },
+ {
+ 0x29, "Temporary failure", ""
+ },
+ {
+ 0x2a, "Switching equipment congestion", ""
+ },
+ {
+ 0x2b, "Access information discarded", ""
+ },
+ {
+ 0x2c, "Requested circuit/channel not available", ""
+ },
+ {
+ 0x2e, "Precedence call blocked", ""
+ },
+ {
+ 0x2f, "Resource unavailable, unspecified", ""
+ },
+ {
+ 0x31, "Quality of service unavailable", ""
+ },
+ {
+ 0x32, "Requested facility not subscribed", ""
+ },
+ {
+ 0x35, "Outgoing calls barred within CUG", ""
+ },
+ {
+ 0x37, "Incoming calls barred within CUG", ""
+ },
+ {
+ 0x39, "Bearer capability not authorized", ""
+ },
+ {
+ 0x3a, "Bearer capability not presently available", ""
+ },
+ {
+ 0x3e, "Inconsistency in designated outgoing access information and subscriber class ", " "
+ },
+ {
+ 0x3f, "Service or option not available, unspecified", ""
+ },
+ {
+ 0x41, "Bearer capability not implemented", ""
+ },
+ {
+ 0x42, "Channel type not implemented", ""
+ },
+ {
+ 0x43, "Requested facility not implemented", ""
+ },
+ {
+ 0x44, "Only restricted digital information bearer capability is available", ""
+ },
+ {
+ 0x4f, "Service or option not implemented", ""
+ },
+ {
+ 0x51, "Invalid call reference value", ""
+ },
+ {
+ 0x52, "Identified channel does not exist", ""
+ },
+ {
+ 0x53, "A suspended call exists, but this call identity does not", ""
+ },
+ {
+ 0x54, "Call identity in use", ""
+ },
+ {
+ 0x55, "No call suspended", ""
+ },
+ {
+ 0x56, "Call having the requested call identity has been cleared", ""
+ },
+ {
+ 0x57, "User not member of CUG", ""
+ },
+ {
+ 0x58, "Incompatible destination", ""
+ },
+ {
+ 0x5a, "Non-existent CUG", ""
+ },
+ {
+ 0x5b, "Invalid transit network selection", ""
+ },
+ {
+ 0x5f, "Invalid message, unspecified", ""
+ },
+ {
+ 0x60, "Mandatory information element is missing", ""
+ },
+ {
+ 0x61, "Message type non-existent or not implemented", ""
+ },
+ {
+ 0x62, "Message not compatible with call state or message type non-existent or not implemented ", " "
+ },
+ {
+ 0x63, "Information element/parameter non-existent or not implemented", ""
+ },
+ {
+ 0x64, "Invalid information element contents", ""
+ },
+ {
+ 0x65, "Message not compatible with call state", ""
+ },
+ {
+ 0x66, "Recovery on timer expiry", ""
+ },
+ {
+ 0x67, "Parameter non-existent or not implemented - passed on", ""
+ },
+ {
+ 0x6e, "Message with unrecognized parameter discarded", ""
+ },
+ {
+ 0x6f, "Protocol error, unspecified", ""
+ },
+ {
+ 0x7f, "Interworking, unspecified", ""
+ },
+};
+
+#define CVSIZE sizeof(cvlist)/sizeof(struct CauseValue)
+
+static
+int
+prcause(char *dest, u_char * p)
+{
+ u_char *end;
+ char *dp = dest;
+ int i, cause;
+
+ end = p + p[1] + 1;
+ p += 2;
+ dp += sprintf(dp, " coding ");
+ dp += prbits(dp, *p, 7, 2);
+ dp += sprintf(dp, " location ");
+ dp += prbits(dp, *p, 4, 4);
+ *dp++ = '\n';
+ p = skipext(p);
+
+ cause = 0x7f & *p++;
+
+ /* locate cause value */
+ for (i = 0; i < CVSIZE; i++)
+ if (cvlist[i].nr == cause)
+ break;
+
+ /* display cause value if it exists */
+ if (i == CVSIZE)
+ dp += sprintf(dp, "Unknown cause type %x!\n", cause);
+ else
+ dp += sprintf(dp, " cause value %x : %s \n", cause, cvlist[i].edescr);
+
+ while (!0) {
+ if (p > end)
+ break;
+ dp += sprintf(dp, " diag attribute %d ", *p++ & 0x7f);
+ dp += sprintf(dp, " rej %d ", *p & 0x7f);
+ if (*p & 0x80) {
+ *dp++ = '\n';
+ break;
+ } else
+ dp += sprintf(dp, " av %d\n", (*++p) & 0x7f);
+ }
+ return (dp - dest);
+
+}
+
+static
+struct MessageType cause_1tr6[] =
+{
+ {CAUSE_InvCRef, "Invalid Call Reference"},
+ {CAUSE_BearerNotImpl, "Bearer Service Not Implemented"},
+ {CAUSE_CIDunknown, "Caller Identity unknown"},
+ {CAUSE_CIDinUse, "Caller Identity in Use"},
+ {CAUSE_NoChans, "No Channels available"},
+ {CAUSE_FacNotImpl, "Facility Not Implemented"},
+ {CAUSE_FacNotSubscr, "Facility Not Subscribed"},
+ {CAUSE_OutgoingBarred, "Outgoing calls barred"},
+ {CAUSE_UserAccessBusy, "User Access Busy"},
+ {CAUSE_NegativeGBG, "Negative GBG"},
+ {CAUSE_UnknownGBG, "Unknown GBG"},
+ {CAUSE_NoSPVknown, "No SPV known"},
+ {CAUSE_DestNotObtain, "Destination not obtainable"},
+ {CAUSE_NumberChanged, "Number changed"},
+ {CAUSE_OutOfOrder, "Out Of Order"},
+ {CAUSE_NoUserResponse, "No User Response"},
+ {CAUSE_UserBusy, "User Busy"},
+ {CAUSE_IncomingBarred, "Incoming Barred"},
+ {CAUSE_CallRejected, "Call Rejected"},
+ {CAUSE_NetworkCongestion, "Network Congestion"},
+ {CAUSE_RemoteUser, "Remote User initiated"},
+ {CAUSE_LocalProcErr, "Local Procedure Error"},
+ {CAUSE_RemoteProcErr, "Remote Procedure Error"},
+ {CAUSE_RemoteUserSuspend, "Remote User Suspend"},
+ {CAUSE_RemoteUserResumed, "Remote User Resumed"},
+ {CAUSE_UserInfoDiscarded, "User Info Discarded"}
+};
+
+static int cause_1tr6_len = (sizeof(cause_1tr6) / sizeof(struct MessageType));
+
+static int
+prcause_1tr6(char *dest, u_char * p)
+{
+ char *dp = dest;
+ int i, cause;
+
+ p++;
+ if (0 == *p) {
+ dp += sprintf(dp, " OK (cause length=0)\n");
+ return (dp - dest);
+ } else if (*p > 1) {
+ dp += sprintf(dp, " coding ");
+ dp += prbits(dp, p[2], 7, 2);
+ dp += sprintf(dp, " location ");
+ dp += prbits(dp, p[2], 4, 4);
+ *dp++ = '\n';
+ }
+ p++;
+ cause = 0x7f & *p;
+
+ /* locate cause value */
+ for (i = 0; i < cause_1tr6_len; i++)
+ if (cause_1tr6[i].nr == cause)
+ break;
+
+ /* display cause value if it exists */
+ if (i == cause_1tr6_len)
+ dp += sprintf(dp, "Unknown cause type %x!\n", cause);
+ else
+ dp += sprintf(dp, " cause value %x : %s \n", cause, cause_1tr6[i].descr);
+
+ return (dp - dest);
+
+}
+
+static int
+prchident(char *dest, u_char * p)
+{
+ char *dp = dest;
+
+ p += 2;
+ dp += sprintf(dp, " octet 3 ");
+ dp += prbits(dp, *p, 8, 8);
+ *dp++ = '\n';
+ return (dp - dest);
+}
+
+static int
+prcalled(char *dest, u_char * p)
+{
+ int l;
+ char *dp = dest;
+
+ p++;
+ l = *p++ - 1;
+ dp += sprintf(dp, " octet 3 ");
+ dp += prbits(dp, *p++, 8, 8);
+ *dp++ = '\n';
+ dp += sprintf(dp, " number digits ");
+ while (l--)
+ *dp++ = *p++;
+ *dp++ = '\n';
+ return (dp - dest);
+}
+static int
+prcalling(char *dest, u_char * p)
+{
+ int l;
+ char *dp = dest;
+
+ p++;
+ l = *p++ - 1;
+ dp += sprintf(dp, " octet 3 ");
+ dp += prbits(dp, *p, 8, 8);
+ *dp++ = '\n';
+ if (!(*p & 0x80)) {
+ dp += sprintf(dp, " octet 3a ");
+ dp += prbits(dp, *++p, 8, 8);
+ *dp++ = '\n';
+ l--;
+ };
+ p++;
+
+ dp += sprintf(dp, " number digits ");
+ while (l--)
+ *dp++ = *p++;
+ *dp++ = '\n';
+ return (dp - dest);
+}
+
+static
+int
+prbearer(char *dest, u_char * p)
+{
+ char *dp = dest, ch;
+
+ p += 2;
+ dp += sprintf(dp, " octet 3 ");
+ dp += prbits(dp, *p++, 8, 8);
+ *dp++ = '\n';
+ dp += sprintf(dp, " octet 4 ");
+ dp += prbits(dp, *p, 8, 8);
+ *dp++ = '\n';
+ if ((*p++ & 0x1f) == 0x18) {
+ dp += sprintf(dp, " octet 4.1 ");
+ dp += prbits(dp, *p++, 8, 8);
+ *dp++ = '\n';
+ }
+ /* check for user information layer 1 */
+ if ((*p & 0x60) == 0x20) {
+ ch = ' ';
+ do {
+ dp += sprintf(dp, " octet 5%c ", ch);
+ dp += prbits(dp, *p, 8, 8);
+ *dp++ = '\n';
+ if (ch == ' ')
+ ch = 'a';
+ else
+ ch++;
+ }
+ while (!(*p++ & 0x80));
+ }
+ /* check for user information layer 2 */
+ if ((*p & 0x60) == 0x40) {
+ dp += sprintf(dp, " octet 6 ");
+ dp += prbits(dp, *p++, 8, 8);
+ *dp++ = '\n';
+ }
+ /* check for user information layer 3 */
+ if ((*p & 0x60) == 0x60) {
+ dp += sprintf(dp, " octet 7 ");
+ dp += prbits(dp, *p++, 8, 8);
+ *dp++ = '\n';
+ }
+ return (dp - dest);
+}
+
+
+static
+int
+prbearer_ni1(char *dest, u_char * p)
+{
+ char *dp = dest;
+ u_char len;
+
+ p++;
+ len = *p++;
+ dp += sprintf(dp, " octet 3 ");
+ dp += prbits(dp, *p, 8, 8);
+ switch (*p++) {
+ case 0x80:
+ dp += sprintf(dp, " Speech");
+ break;
+ case 0x88:
+ dp += sprintf(dp, " Unrestricted digital information");
+ break;
+ case 0x90:
+ dp += sprintf(dp, " 3.1 kHz audio");
+ break;
+ default:
+ dp += sprintf(dp, " Unknown information-transfer capability");
+ }
+ *dp++ = '\n';
+ dp += sprintf(dp, " octet 4 ");
+ dp += prbits(dp, *p, 8, 8);
+ switch (*p++) {
+ case 0x90:
+ dp += sprintf(dp, " 64 kbps, circuit mode");
+ break;
+ case 0xc0:
+ dp += sprintf(dp, " Packet mode");
+ break;
+ default:
+ dp += sprintf(dp, " Unknown transfer mode");
+ }
+ *dp++ = '\n';
+ if (len > 2) {
+ dp += sprintf(dp, " octet 5 ");
+ dp += prbits(dp, *p, 8, 8);
+ switch (*p++) {
+ case 0x21:
+ dp += sprintf(dp, " Rate adaption\n");
+ dp += sprintf(dp, " octet 5a ");
+ dp += prbits(dp, *p, 8, 8);
+ break;
+ case 0xa2:
+ dp += sprintf(dp, " u-law");
+ break;
+ default:
+ dp += sprintf(dp, " Unknown UI layer 1 protocol");
+ }
+ *dp++ = '\n';
+ }
+ return (dp - dest);
+}
+
+static int
+general(char *dest, u_char * p)
+{
+ char *dp = dest;
+ char ch = ' ';
+ int l, octet = 3;
+
+ p++;
+ l = *p++;
+ /* Iterate over all octets in the information element */
+ while (l--) {
+ dp += sprintf(dp, " octet %d%c ", octet, ch);
+ dp += prbits(dp, *p++, 8, 8);
+ *dp++ = '\n';
+
+ /* last octet in group? */
+ if (*p & 0x80) {
+ octet++;
+ ch = ' ';
+ } else if (ch == ' ')
+ ch = 'a';
+ else
+ ch++;
+ }
+ return (dp - dest);
+}
+
+static int
+general_ni1(char *dest, u_char * p)
+{
+ char *dp = dest;
+ char ch = ' ';
+ int l, octet = 3;
+
+ p++;
+ l = *p++;
+ /* Iterate over all octets in the information element */
+ while (l--) {
+ dp += sprintf(dp, " octet %d%c ", octet, ch);
+ dp += prbits(dp, *p, 8, 8);
+ *dp++ = '\n';
+
+ /* last octet in group? */
+ if (*p++ & 0x80) {
+ octet++;
+ ch = ' ';
+ } else if (ch == ' ')
+ ch = 'a';
+ else
+ ch++;
+ }
+ return (dp - dest);
+}
+
+static int
+prcharge(char *dest, u_char * p)
+{
+ char *dp = dest;
+ int l;
+
+ p++;
+ l = *p++ - 1;
+ dp += sprintf(dp, " GEA ");
+ dp += prbits(dp, *p++, 8, 8);
+ dp += sprintf(dp, " Anzahl: ");
+ /* Iterate over all octets in the * information element */
+ while (l--)
+ *dp++ = *p++;
+ *dp++ = '\n';
+ return (dp - dest);
+}
+static int
+prtext(char *dest, u_char * p)
+{
+ char *dp = dest;
+ int l;
+
+ p++;
+ l = *p++;
+ dp += sprintf(dp, " ");
+ /* Iterate over all octets in the * information element */
+ while (l--)
+ *dp++ = *p++;
+ *dp++ = '\n';
+ return (dp - dest);
+}
+
+static int
+prfeatureind(char *dest, u_char * p)
+{
+ char *dp = dest;
+
+ p += 2; /* skip id, len */
+ dp += sprintf(dp, " octet 3 ");
+ dp += prbits(dp, *p, 8, 8);
+ *dp++ = '\n';
+ if (!(*p++ & 80)) {
+ dp += sprintf(dp, " octet 4 ");
+ dp += prbits(dp, *p++, 8, 8);
+ *dp++ = '\n';
+ }
+ dp += sprintf(dp, " Status: ");
+ switch (*p) {
+ case 0:
+ dp += sprintf(dp, "Idle");
+ break;
+ case 1:
+ dp += sprintf(dp, "Active");
+ break;
+ case 2:
+ dp += sprintf(dp, "Prompt");
+ break;
+ case 3:
+ dp += sprintf(dp, "Pending");
+ break;
+ default:
+ dp += sprintf(dp, "(Reserved)");
+ break;
+ }
+ *dp++ = '\n';
+ return (dp - dest);
+}
+
+static
+struct DTag { /* Display tags */
+ u_char nr;
+ char *descr;
+} dtaglist[] = {
+ { 0x82, "Continuation" },
+ { 0x83, "Called address" },
+ { 0x84, "Cause" },
+ { 0x85, "Progress indicator" },
+ { 0x86, "Notification indicator" },
+ { 0x87, "Prompt" },
+ { 0x88, "Accumlated digits" },
+ { 0x89, "Status" },
+ { 0x8a, "Inband" },
+ { 0x8b, "Calling address" },
+ { 0x8c, "Reason" },
+ { 0x8d, "Calling party name" },
+ { 0x8e, "Called party name" },
+ { 0x8f, "Orignal called name" },
+ { 0x90, "Redirecting name" },
+ { 0x91, "Connected name" },
+ { 0x92, "Originating restrictions" },
+ { 0x93, "Date & time of day" },
+ { 0x94, "Call Appearance ID" },
+ { 0x95, "Feature address" },
+ { 0x96, "Redirection name" },
+ { 0x9e, "Text" },
+};
+#define DTAGSIZE sizeof(dtaglist)/sizeof(struct DTag)
+
+static int
+disptext_ni1(char *dest, u_char * p)
+{
+ char *dp = dest;
+ int l, tag, len, i;
+
+ p++;
+ l = *p++ - 1;
+ if (*p++ != 0x80) {
+ dp += sprintf(dp, " Unknown display type\n");
+ return (dp - dest);
+ }
+ /* Iterate over all tag,length,text fields */
+ while (l > 0) {
+ tag = *p++;
+ len = *p++;
+ l -= len + 2;
+ /* Don't space or skip */
+ if ((tag == 0x80) || (tag == 0x81)) p++;
+ else {
+ for (i = 0; i < DTAGSIZE; i++)
+ if (tag == dtaglist[i].nr)
+ break;
+
+ /* When not found, give appropriate msg */
+ if (i != DTAGSIZE) {
+ dp += sprintf(dp, " %s: ", dtaglist[i].descr);
+ while (len--)
+ *dp++ = *p++;
+ } else {
+ dp += sprintf(dp, " (unknown display tag %2x): ", tag);
+ while (len--)
+ *dp++ = *p++;
+ }
+ dp += sprintf(dp, "\n");
+ }
+ }
+ return (dp - dest);
+}
+static int
+display(char *dest, u_char * p)
+{
+ char *dp = dest;
+ char ch = ' ';
+ int l, octet = 3;
+
+ p++;
+ l = *p++;
+ /* Iterate over all octets in the * display-information element */
+ dp += sprintf(dp, " \"");
+ while (l--) {
+ dp += sprintf(dp, "%c", *p++);
+
+ /* last octet in group? */
+ if (*p & 0x80) {
+ octet++;
+ ch = ' ';
+ } else if (ch == ' ')
+ ch = 'a';
+
+ else
+ ch++;
+ }
+ *dp++ = '\"';
+ *dp++ = '\n';
+ return (dp - dest);
+}
+
+static int
+prfacility(char *dest, u_char * p)
+{
+ char *dp = dest;
+ int l, l2;
+
+ p++;
+ l = *p++;
+ dp += sprintf(dp, " octet 3 ");
+ dp += prbits(dp, *p++, 8, 8);
+ dp += sprintf(dp, "\n");
+ l -= 1;
+
+ while (l > 0) {
+ dp += sprintf(dp, " octet 4 ");
+ dp += prbits(dp, *p++, 8, 8);
+ dp += sprintf(dp, "\n");
+ dp += sprintf(dp, " octet 5 %d\n", l2 = *p++ & 0x7f);
+ l -= 2;
+ dp += sprintf(dp, " contents ");
+ while (l2--) {
+ dp += sprintf(dp, "%2x ", *p++);
+ l--;
+ }
+ dp += sprintf(dp, "\n");
+ }
+
+ return (dp - dest);
+}
+
+static
+struct InformationElement {
+ u_char nr;
+ char *descr;
+ int (*f) (char *, u_char *);
+} ielist[] = {
+
+ {
+ 0x00, "Segmented message", general
+ },
+ {
+ 0x04, "Bearer capability", prbearer
+ },
+ {
+ 0x08, "Cause", prcause
+ },
+ {
+ 0x10, "Call identity", general
+ },
+ {
+ 0x14, "Call state", general
+ },
+ {
+ 0x18, "Channel identification", prchident
+ },
+ {
+ 0x1c, "Facility", prfacility
+ },
+ {
+ 0x1e, "Progress indicator", general
+ },
+ {
+ 0x20, "Network-specific facilities", general
+ },
+ {
+ 0x27, "Notification indicator", general
+ },
+ {
+ 0x28, "Display", display
+ },
+ {
+ 0x29, "Date/Time", general
+ },
+ {
+ 0x2c, "Keypad facility", general
+ },
+ {
+ 0x34, "Signal", general
+ },
+ {
+ 0x40, "Information rate", general
+ },
+ {
+ 0x42, "End-to-end delay", general
+ },
+ {
+ 0x43, "Transit delay selection and indication", general
+ },
+ {
+ 0x44, "Packet layer binary parameters", general
+ },
+ {
+ 0x45, "Packet layer window size", general
+ },
+ {
+ 0x46, "Packet size", general
+ },
+ {
+ 0x47, "Closed user group", general
+ },
+ {
+ 0x4a, "Reverse charge indication", general
+ },
+ {
+ 0x6c, "Calling party number", prcalling
+ },
+ {
+ 0x6d, "Calling party subaddress", general
+ },
+ {
+ 0x70, "Called party number", prcalled
+ },
+ {
+ 0x71, "Called party subaddress", general
+ },
+ {
+ 0x74, "Redirecting number", general
+ },
+ {
+ 0x78, "Transit network selection", general
+ },
+ {
+ 0x79, "Restart indicator", general
+ },
+ {
+ 0x7c, "Low layer compatibility", general
+ },
+ {
+ 0x7d, "High layer compatibility", general
+ },
+ {
+ 0x7e, "User-user", general
+ },
+ {
+ 0x7f, "Escape for extension", general
+ },
+};
+
+
+#define IESIZE sizeof(ielist)/sizeof(struct InformationElement)
+
+static
+struct InformationElement ielist_ni1[] = {
+ { 0x04, "Bearer Capability", prbearer_ni1 },
+ { 0x08, "Cause", prcause },
+ { 0x14, "Call State", general_ni1 },
+ { 0x18, "Channel Identification", prchident },
+ { 0x1e, "Progress Indicator", general_ni1 },
+ { 0x27, "Notification Indicator", general_ni1 },
+ { 0x2c, "Keypad Facility", prtext },
+ { 0x32, "Information Request", general_ni1 },
+ { 0x34, "Signal", general_ni1 },
+ { 0x38, "Feature Activation", general_ni1 },
+ { 0x39, "Feature Indication", prfeatureind },
+ { 0x3a, "Service Profile Identification (SPID)", prtext },
+ { 0x3b, "Endpoint Identifier", general_ni1 },
+ { 0x6c, "Calling Party Number", prcalling },
+ { 0x6d, "Calling Party Subaddress", general_ni1 },
+ { 0x70, "Called Party Number", prcalled },
+ { 0x71, "Called Party Subaddress", general_ni1 },
+ { 0x74, "Redirecting Number", general_ni1 },
+ { 0x78, "Transit Network Selection", general_ni1 },
+ { 0x7c, "Low Layer Compatibility", general_ni1 },
+ { 0x7d, "High Layer Compatibility", general_ni1 },
+};
+
+
+#define IESIZE_NI1 sizeof(ielist_ni1)/sizeof(struct InformationElement)
+
+static
+struct InformationElement ielist_ni1_cs5[] = {
+ { 0x1d, "Operator system access", general_ni1 },
+ { 0x2a, "Display text", disptext_ni1 },
+};
+
+#define IESIZE_NI1_CS5 sizeof(ielist_ni1_cs5)/sizeof(struct InformationElement)
+
+static
+struct InformationElement ielist_ni1_cs6[] = {
+ { 0x7b, "Call appearance", general_ni1 },
+};
+
+#define IESIZE_NI1_CS6 sizeof(ielist_ni1_cs6)/sizeof(struct InformationElement)
+
+static struct InformationElement we_0[] =
+{
+ {WE0_cause, "Cause", prcause_1tr6},
+ {WE0_connAddr, "Connecting Address", prcalled},
+ {WE0_callID, "Call IDentity", general},
+ {WE0_chanID, "Channel IDentity", general},
+ {WE0_netSpecFac, "Network Specific Facility", general},
+ {WE0_display, "Display", general},
+ {WE0_keypad, "Keypad", general},
+ {WE0_origAddr, "Origination Address", prcalled},
+ {WE0_destAddr, "Destination Address", prcalled},
+ {WE0_userInfo, "User Info", general}
+};
+
+#define WE_0_LEN (sizeof(we_0) / sizeof(struct InformationElement))
+
+static struct InformationElement we_6[] =
+{
+ {WE6_serviceInd, "Service Indicator", general},
+ {WE6_chargingInfo, "Charging Information", prcharge},
+ {WE6_date, "Date", prtext},
+ {WE6_facSelect, "Facility Select", general},
+ {WE6_facStatus, "Facility Status", general},
+ {WE6_statusCalled, "Status Called", general},
+ {WE6_addTransAttr, "Additional Transmission Attributes", general}
+};
+#define WE_6_LEN (sizeof(we_6) / sizeof(struct InformationElement))
+
+int
+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);
+}
+
+void
+LogFrame(struct IsdnCardState *cs, u_char * buf, int size)
+{
+ char *dp;
+
+ if (size < 1)
+ return;
+ dp = cs->dlog;
+ if (size < MAX_DLOG_SPACE / 3 - 10) {
+ *dp++ = 'H';
+ *dp++ = 'E';
+ *dp++ = 'X';
+ *dp++ = ':';
+ dp += QuickHex(dp, buf, size);
+ dp--;
+ *dp++ = '\n';
+ *dp = 0;
+ HiSax_putstatus(cs, NULL, cs->dlog);
+ } else
+ HiSax_putstatus(cs, "LogFrame: ", "warning Frame too big (%d)", size);
+}
+
+void
+dlogframe(struct IsdnCardState *cs, struct sk_buff *skb, int dir)
+{
+ u_char *bend, *buf;
+ char *dp;
+ unsigned char pd, cr_l, cr, mt;
+ unsigned char sapi, tei, ftyp;
+ int i, cset = 0, cs_old = 0, cs_fest = 0;
+ int size, finish = 0;
+
+ if (skb->len < 3)
+ return;
+ /* display header */
+ dp = cs->dlog;
+ dp += jiftime(dp, jiffies);
+ *dp++ = ' ';
+ sapi = skb->data[0] >> 2;
+ tei = skb->data[1] >> 1;
+ ftyp = skb->data[2];
+ buf = skb->data;
+ dp += sprintf(dp, "frame %s ", dir ? "network->user" : "user->network");
+ size = skb->len;
+
+ if (tei == GROUP_TEI) {
+ if (sapi == CTRL_SAPI) { /* sapi 0 */
+ if (ftyp == 3) {
+ dp += sprintf(dp, "broadcast\n");
+ buf += 3;
+ size -= 3;
+ } else {
+ dp += sprintf(dp, "no UI broadcast\n");
+ finish = 1;
+ }
+ } else if (sapi == TEI_SAPI) {
+ dp += sprintf(dp, "tei management\n");
+ finish = 1;
+ } else {
+ dp += sprintf(dp, "unknown sapi %d broadcast\n", sapi);
+ finish = 1;
+ }
+ } else {
+ if (sapi == CTRL_SAPI) {
+ if (!(ftyp & 1)) { /* IFrame */
+ dp += sprintf(dp, "with tei %d\n", tei);
+ buf += 4;
+ size -= 4;
+ } else {
+ dp += sprintf(dp, "SFrame with tei %d\n", tei);
+ finish = 1;
+ }
+ } else {
+ dp += sprintf(dp, "unknown sapi %d tei %d\n", sapi, tei);
+ finish = 1;
+ }
+ }
+ bend = skb->data + skb->len;
+ if (buf >= bend) {
+ dp += sprintf(dp, "frame too short\n");
+ finish = 1;
+ }
+ if (finish) {
+ *dp = 0;
+ HiSax_putstatus(cs, NULL, cs->dlog);
+ return;
+ }
+ if ((0xfe & buf[0]) == PROTO_DIS_N0) { /* 1TR6 */
+ /* locate message type */
+ pd = *buf++;
+ cr_l = *buf++;
+ if (cr_l)
+ cr = *buf++;
+ else
+ cr = 0;
+ mt = *buf++;
+ if (pd == PROTO_DIS_N0) { /* N0 */
+ for (i = 0; i < MT_N0_LEN; i++)
+ if (mt_n0[i].nr == mt)
+ break;
+ /* display message type if it exists */
+ if (i == MT_N0_LEN)
+ dp += sprintf(dp, "callref %d %s size %d unknown message type N0 %x!\n",
+ cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+ size, mt);
+ else
+ dp += sprintf(dp, "callref %d %s size %d message type %s\n",
+ cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+ size, mt_n0[i].descr);
+ } else { /* N1 */
+ for (i = 0; i < MT_N1_LEN; i++)
+ if (mt_n1[i].nr == mt)
+ break;
+ /* display message type if it exists */
+ if (i == MT_N1_LEN)
+ dp += sprintf(dp, "callref %d %s size %d unknown message type N1 %x!\n",
+ cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+ size, mt);
+ else
+ dp += sprintf(dp, "callref %d %s size %d message type %s\n",
+ cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+ size, mt_n1[i].descr);
+ }
+
+ /* display each information element */
+ while (buf < bend) {
+ /* Is it a single octet information element? */
+ if (*buf & 0x80) {
+ switch ((*buf >> 4) & 7) {
+ case 1:
+ dp += sprintf(dp, " Shift %x\n", *buf & 0xf);
+ cs_old = cset;
+ cset = *buf & 7;
+ cs_fest = *buf & 8;
+ break;
+ case 3:
+ dp += sprintf(dp, " Congestion level %x\n", *buf & 0xf);
+ break;
+ case 2:
+ if (*buf == 0xa0) {
+ dp += sprintf(dp, " More data\n");
+ break;
+ }
+ if (*buf == 0xa1) {
+ dp += sprintf(dp, " Sending complete\n");
+ }
+ break;
+ /* fall through */
+ default:
+ dp += sprintf(dp, " Reserved %x\n", *buf);
+ break;
+ }
+ buf++;
+ continue;
+ }
+ /* No, locate it in the table */
+ if (cset == 0) {
+ for (i = 0; i < WE_0_LEN; i++)
+ if (*buf == we_0[i].nr)
+ break;
+
+ /* When found, give appropriate msg */
+ if (i != WE_0_LEN) {
+ dp += sprintf(dp, " %s\n", we_0[i].descr);
+ dp += we_0[i].f(dp, buf);
+ } else
+ dp += sprintf(dp, " Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]);
+ } else if (cset == 6) {
+ for (i = 0; i < WE_6_LEN; i++)
+ if (*buf == we_6[i].nr)
+ break;
+
+ /* When found, give appropriate msg */
+ if (i != WE_6_LEN) {
+ dp += sprintf(dp, " %s\n", we_6[i].descr);
+ dp += we_6[i].f(dp, buf);
+ } else
+ dp += sprintf(dp, " Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]);
+ } else
+ dp += sprintf(dp, " Unknown Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]);
+ /* Skip to next element */
+ if (cs_fest == 8) {
+ cset = cs_old;
+ cs_old = 0;
+ cs_fest = 0;
+ }
+ buf += buf[1] + 2;
+ }
+ } else if ((buf[0] == 8) && (cs->protocol == ISDN_PTYPE_NI1)) { /* NI-1 */
+ /* locate message type */
+ buf++;
+ cr_l = *buf++;
+ if (cr_l)
+ cr = *buf++;
+ else
+ cr = 0;
+ mt = *buf++;
+ for (i = 0; i < MTSIZE; i++)
+ if (mtlist[i].nr == mt)
+ break;
+
+ /* display message type if it exists */
+ if (i == MTSIZE)
+ dp += sprintf(dp, "callref %d %s size %d unknown message type %x!\n",
+ cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+ size, mt);
+ else
+ dp += sprintf(dp, "callref %d %s size %d message type %s\n",
+ cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+ size, mtlist[i].descr);
+
+ /* display each information element */
+ while (buf < bend) {
+ /* Is it a single octet information element? */
+ if (*buf & 0x80) {
+ switch ((*buf >> 4) & 7) {
+ case 1:
+ dp += sprintf(dp, " Shift %x\n", *buf & 0xf);
+ cs_old = cset;
+ cset = *buf & 7;
+ cs_fest = *buf & 8;
+ break;
+ default:
+ dp += sprintf(dp, " Unknown single-octet IE %x\n", *buf);
+ break;
+ }
+ buf++;
+ continue;
+ }
+ /* No, locate it in the table */
+ if (cset == 0) {
+ for (i = 0; i < IESIZE_NI1; i++)
+ if (*buf == ielist_ni1[i].nr)
+ break;
+
+ /* When not found, give appropriate msg */
+ if (i != IESIZE_NI1) {
+ dp += sprintf(dp, " %s\n", ielist_ni1[i].descr);
+ dp += ielist_ni1[i].f(dp, buf);
+ } else
+ dp += sprintf(dp, " attribute %x attribute size %d\n", *buf, buf[1]);
+ } else if (cset == 5) {
+ for (i = 0; i < IESIZE_NI1_CS5; i++)
+ if (*buf == ielist_ni1_cs5[i].nr)
+ break;
+
+ /* When not found, give appropriate msg */
+ if (i != IESIZE_NI1_CS5) {
+ dp += sprintf(dp, " %s\n", ielist_ni1_cs5[i].descr);
+ dp += ielist_ni1_cs5[i].f(dp, buf);
+ } else
+ dp += sprintf(dp, " attribute %x attribute size %d\n", *buf, buf[1]);
+ } else if (cset == 6) {
+ for (i = 0; i < IESIZE_NI1_CS6; i++)
+ if (*buf == ielist_ni1_cs6[i].nr)
+ break;
+
+ /* When not found, give appropriate msg */
+ if (i != IESIZE_NI1_CS6) {
+ dp += sprintf(dp, " %s\n", ielist_ni1_cs6[i].descr);
+ dp += ielist_ni1_cs6[i].f(dp, buf);
+ } else
+ dp += sprintf(dp, " attribute %x attribute size %d\n", *buf, buf[1]);
+ } else
+ dp += sprintf(dp, " Unknown Codeset %d attribute %x attribute size %d\n", cset, *buf, buf[1]);
+
+ /* Skip to next element */
+ if (cs_fest == 8) {
+ cset = cs_old;
+ cs_old = 0;
+ cs_fest = 0;
+ }
+ buf += buf[1] + 2;
+ }
+ } else if ((buf[0] == 8) && (cs->protocol == ISDN_PTYPE_EURO)) { /* EURO */
+ /* locate message type */
+ buf++;
+ cr_l = *buf++;
+ if (cr_l)
+ cr = *buf++;
+ else
+ cr = 0;
+ mt = *buf++;
+ for (i = 0; i < MTSIZE; i++)
+ if (mtlist[i].nr == mt)
+ break;
+
+ /* display message type if it exists */
+ if (i == MTSIZE)
+ dp += sprintf(dp, "callref %d %s size %d unknown message type %x!\n",
+ cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+ size, mt);
+ else
+ dp += sprintf(dp, "callref %d %s size %d message type %s\n",
+ cr & 0x7f, (cr & 0x80) ? "called" : "caller",
+ size, mtlist[i].descr);
+
+ /* display each information element */
+ while (buf < bend) {
+ /* Is it a single octet information element? */
+ if (*buf & 0x80) {
+ switch ((*buf >> 4) & 7) {
+ case 1:
+ dp += sprintf(dp, " Shift %x\n", *buf & 0xf);
+ break;
+ case 3:
+ dp += sprintf(dp, " Congestion level %x\n", *buf & 0xf);
+ break;
+ case 5:
+ dp += sprintf(dp, " Repeat indicator %x\n", *buf & 0xf);
+ break;
+ case 2:
+ if (*buf == 0xa0) {
+ dp += sprintf(dp, " More data\n");
+ break;
+ }
+ if (*buf == 0xa1) {
+ dp += sprintf(dp, " Sending complete\n");
+ }
+ break;
+ /* fall through */
+ default:
+ dp += sprintf(dp, " Reserved %x\n", *buf);
+ break;
+ }
+ buf++;
+ continue;
+ }
+ /* No, locate it in the table */
+ for (i = 0; i < IESIZE; i++)
+ if (*buf == ielist[i].nr)
+ break;
+
+ /* When not found, give appropriate msg */
+ if (i != IESIZE) {
+ dp += sprintf(dp, " %s\n", ielist[i].descr);
+ dp += ielist[i].f(dp, buf);
+ } else
+ dp += sprintf(dp, " attribute %x attribute size %d\n", *buf, buf[1]);
+
+ /* Skip to next element */
+ buf += buf[1] + 2;
+ }
+ } else {
+ dp += sprintf(dp, "Unknown protocol %x!", buf[0]);
+ }
+ *dp = 0;
+ HiSax_putstatus(cs, NULL, cs->dlog);
+}
diff --git a/drivers/isdn/hisax/s0box.c b/drivers/isdn/hisax/s0box.c
new file mode 100644
index 0000000..16d00b5
--- /dev/null
+++ b/drivers/isdn/hisax/s0box.c
@@ -0,0 +1,261 @@
+/* $Id: s0box.c,v 2.6.2.4 2004/01/13 23:48:39 keil Exp $
+ *
+ * low level stuff for Creatix S0BOX
+ *
+ * Author Enrik Berkhan
+ * Copyright by Enrik Berkhan <enrik@starfleet.inka.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+static const char *s0box_revision = "$Revision: 2.6.2.4 $";
+
+static inline void
+writereg(unsigned int padr, signed int addr, u_char off, u_char val) {
+ outb_p(0x1c,padr+2);
+ outb_p(0x14,padr+2);
+ outb_p((addr+off)&0x7f,padr);
+ outb_p(0x16,padr+2);
+ outb_p(val,padr);
+ outb_p(0x17,padr+2);
+ outb_p(0x14,padr+2);
+ outb_p(0x1c,padr+2);
+}
+
+static u_char nibtab[] = { 1, 9, 5, 0xd, 3, 0xb, 7, 0xf,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 8, 4, 0xc, 2, 0xa, 6, 0xe } ;
+
+static inline u_char
+readreg(unsigned int padr, signed int addr, u_char off) {
+ register u_char n1, n2;
+
+ outb_p(0x1c,padr+2);
+ outb_p(0x14,padr+2);
+ outb_p((addr+off)|0x80,padr);
+ outb_p(0x16,padr+2);
+ outb_p(0x17,padr+2);
+ n1 = (inb_p(padr+1) >> 3) & 0x17;
+ outb_p(0x16,padr+2);
+ n2 = (inb_p(padr+1) >> 3) & 0x17;
+ outb_p(0x14,padr+2);
+ outb_p(0x1c,padr+2);
+ return nibtab[n1] | (nibtab[n2] << 4);
+}
+
+static inline void
+read_fifo(unsigned int padr, signed int adr, u_char * data, int size)
+{
+ int i;
+ register u_char n1, n2;
+
+ outb_p(0x1c, padr+2);
+ outb_p(0x14, padr+2);
+ outb_p(adr|0x80, padr);
+ outb_p(0x16, padr+2);
+ for (i=0; i<size; i++) {
+ outb_p(0x17, padr+2);
+ n1 = (inb_p(padr+1) >> 3) & 0x17;
+ outb_p(0x16,padr+2);
+ n2 = (inb_p(padr+1) >> 3) & 0x17;
+ *(data++)=nibtab[n1] | (nibtab[n2] << 4);
+ }
+ outb_p(0x14,padr+2);
+ outb_p(0x1c,padr+2);
+ return;
+}
+
+static inline void
+write_fifo(unsigned int padr, signed int adr, u_char * data, int size)
+{
+ int i;
+ outb_p(0x1c, padr+2);
+ outb_p(0x14, padr+2);
+ outb_p(adr&0x7f, padr);
+ for (i=0; i<size; i++) {
+ outb_p(0x16, padr+2);
+ outb_p(*(data++), padr);
+ outb_p(0x17, padr+2);
+ }
+ outb_p(0x14,padr+2);
+ outb_p(0x1c,padr+2);
+ return;
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+ return (readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ read_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.isacfifo, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ write_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.isacfifo, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+ return (readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[hscx], offset));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+ writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[hscx], offset, value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[nr], reg)
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[nr], reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscxfifo[nr], ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscxfifo[nr], ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+s0box_interrupt(int intno, void *dev_id)
+{
+#define MAXCOUNT 5
+ struct IsdnCardState *cs = dev_id;
+ u_char val;
+ u_long flags;
+ int count = 0;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_ISTA);
+ Start_HSCX:
+ if (val)
+ hscx_int_main(cs, val);
+ val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_ISTA);
+ Start_ISAC:
+ if (val)
+ isac_interrupt(cs, val);
+ count++;
+ val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_ISTA);
+ if (val && count < MAXCOUNT) {
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "HSCX IntStat after IntRoutine");
+ goto Start_HSCX;
+ }
+ val = readreg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_ISTA);
+ if (val && count < MAXCOUNT) {
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ISAC IntStat after IntRoutine");
+ goto Start_ISAC;
+ }
+ if (count >= MAXCOUNT)
+ printk(KERN_WARNING "S0Box: more than %d loops in s0box_interrupt\n", count);
+ writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[0], HSCX_MASK, 0xFF);
+ writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_MASK, 0xFF);
+ writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_MASK, 0xFF);
+ writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.isac, ISAC_MASK, 0x0);
+ writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[0], HSCX_MASK, 0x0);
+ writereg(cs->hw.teles3.cfg_reg, cs->hw.teles3.hscx[1], HSCX_MASK, 0x0);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static void
+release_io_s0box(struct IsdnCardState *cs)
+{
+ release_region(cs->hw.teles3.cfg_reg, 8);
+}
+
+static int
+S0Box_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ u_long flags;
+
+ switch (mt) {
+ case CARD_RESET:
+ break;
+ case CARD_RELEASE:
+ release_io_s0box(cs);
+ break;
+ case CARD_INIT:
+ spin_lock_irqsave(&cs->lock, flags);
+ inithscxisac(cs, 3);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case CARD_TEST:
+ break;
+ }
+ return(0);
+}
+
+int __devinit
+setup_s0box(struct IsdnCard *card)
+{
+ struct IsdnCardState *cs = card->cs;
+ char tmp[64];
+
+ strcpy(tmp, s0box_revision);
+ printk(KERN_INFO "HiSax: S0Box IO driver Rev. %s\n", HiSax_getrev(tmp));
+ if (cs->typ != ISDN_CTYPE_S0BOX)
+ return (0);
+
+ cs->hw.teles3.cfg_reg = card->para[1];
+ cs->hw.teles3.hscx[0] = -0x20;
+ cs->hw.teles3.hscx[1] = 0x0;
+ cs->hw.teles3.isac = 0x20;
+ cs->hw.teles3.isacfifo = cs->hw.teles3.isac + 0x3e;
+ cs->hw.teles3.hscxfifo[0] = cs->hw.teles3.hscx[0] + 0x3e;
+ cs->hw.teles3.hscxfifo[1] = cs->hw.teles3.hscx[1] + 0x3e;
+ cs->irq = card->para[0];
+ if (!request_region(cs->hw.teles3.cfg_reg,8, "S0Box parallel I/O")) {
+ printk(KERN_WARNING "HiSax: S0Box ports %x-%x already in use\n",
+ cs->hw.teles3.cfg_reg,
+ cs->hw.teles3.cfg_reg + 7);
+ return 0;
+ }
+ printk(KERN_INFO "HiSax: S0Box config irq:%d isac:0x%x cfg:0x%x\n",
+ cs->irq,
+ cs->hw.teles3.isac, cs->hw.teles3.cfg_reg);
+ printk(KERN_INFO "HiSax: hscx A:0x%x hscx B:0x%x\n",
+ cs->hw.teles3.hscx[0], cs->hw.teles3.hscx[1]);
+ setup_isac(cs);
+ cs->readisac = &ReadISAC;
+ cs->writeisac = &WriteISAC;
+ cs->readisacfifo = &ReadISACfifo;
+ cs->writeisacfifo = &WriteISACfifo;
+ cs->BC_Read_Reg = &ReadHSCX;
+ cs->BC_Write_Reg = &WriteHSCX;
+ cs->BC_Send_Data = &hscx_fill_fifo;
+ cs->cardmsg = &S0Box_card_msg;
+ cs->irq_func = &s0box_interrupt;
+ ISACVersion(cs, "S0Box:");
+ if (HscxVersion(cs, "S0Box:")) {
+ printk(KERN_WARNING
+ "S0Box: wrong HSCX versions check IO address\n");
+ release_io_s0box(cs);
+ return (0);
+ }
+ return (1);
+}
diff --git a/drivers/isdn/hisax/saphir.c b/drivers/isdn/hisax/saphir.c
new file mode 100644
index 0000000..b34a81d
--- /dev/null
+++ b/drivers/isdn/hisax/saphir.c
@@ -0,0 +1,298 @@
+/* $Id: saphir.c,v 1.10.2.4 2004/01/13 23:48:39 keil Exp $
+ *
+ * low level stuff for HST Saphir 1
+ *
+ * Author Karsten Keil
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to HST High Soft Tech GmbH
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+static char *saphir_rev = "$Revision: 1.10.2.4 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define ISAC_DATA 0
+#define HSCX_DATA 1
+#define ADDRESS_REG 2
+#define IRQ_REG 3
+#define SPARE_REG 4
+#define RESET_REG 5
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+ register u_char ret;
+
+ byteout(ale, off);
+ ret = bytein(adr);
+ return (ret);
+}
+
+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(struct IsdnCardState *cs, u_char offset)
+{
+ return (readreg(cs->hw.saphir.ale, cs->hw.saphir.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ writereg(cs->hw.saphir.ale, cs->hw.saphir.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ readfifo(cs->hw.saphir.ale, cs->hw.saphir.isac, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ writefifo(cs->hw.saphir.ale, cs->hw.saphir.isac, 0, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+ return (readreg(cs->hw.saphir.ale, cs->hw.saphir.hscx,
+ offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+ writereg(cs->hw.saphir.ale, cs->hw.saphir.hscx,
+ offset + (hscx ? 0x40 : 0), value);
+}
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.saphir.ale, \
+ cs->hw.saphir.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.saphir.ale, \
+ cs->hw.saphir.hscx, reg + (nr ? 0x40 : 0), data)
+
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.saphir.ale, \
+ cs->hw.saphir.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.saphir.ale, \
+ cs->hw.saphir.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+saphir_interrupt(int intno, void *dev_id)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char val;
+ u_long flags;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ val = readreg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_ISTA + 0x40);
+ Start_HSCX:
+ if (val)
+ hscx_int_main(cs, val);
+ val = readreg(cs->hw.saphir.ale, cs->hw.saphir.isac, ISAC_ISTA);
+ Start_ISAC:
+ if (val)
+ isac_interrupt(cs, val);
+ val = readreg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_ISTA + 0x40);
+ if (val) {
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "HSCX IntStat after IntRoutine");
+ goto Start_HSCX;
+ }
+ val = readreg(cs->hw.saphir.ale, cs->hw.saphir.isac, ISAC_ISTA);
+ if (val) {
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ISAC IntStat after IntRoutine");
+ goto Start_ISAC;
+ }
+ /* Watchdog */
+ if (cs->hw.saphir.timer.function)
+ mod_timer(&cs->hw.saphir.timer, jiffies+1*HZ);
+ else
+ printk(KERN_WARNING "saphir: Spurious timer!\n");
+ writereg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_MASK, 0xFF);
+ writereg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_MASK + 0x40, 0xFF);
+ writereg(cs->hw.saphir.ale, cs->hw.saphir.isac, ISAC_MASK, 0xFF);
+ writereg(cs->hw.saphir.ale, cs->hw.saphir.isac, ISAC_MASK, 0);
+ writereg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_MASK, 0);
+ writereg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_MASK + 0x40, 0);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static void
+SaphirWatchDog(struct IsdnCardState *cs)
+{
+ u_long flags;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ /* 5 sec WatchDog, so read at least every 4 sec */
+ cs->readisac(cs, ISAC_RBCH);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ mod_timer(&cs->hw.saphir.timer, jiffies+1*HZ);
+}
+
+static void
+release_io_saphir(struct IsdnCardState *cs)
+{
+ byteout(cs->hw.saphir.cfg_reg + IRQ_REG, 0xff);
+ del_timer(&cs->hw.saphir.timer);
+ cs->hw.saphir.timer.function = NULL;
+ if (cs->hw.saphir.cfg_reg)
+ release_region(cs->hw.saphir.cfg_reg, 6);
+}
+
+static int
+saphir_reset(struct IsdnCardState *cs)
+{
+ u_char irq_val;
+
+ switch(cs->irq) {
+ case 5: irq_val = 0;
+ break;
+ case 3: irq_val = 1;
+ break;
+ case 11:
+ irq_val = 2;
+ break;
+ case 12:
+ irq_val = 3;
+ break;
+ case 15:
+ irq_val = 4;
+ break;
+ default:
+ printk(KERN_WARNING "HiSax: saphir wrong IRQ %d\n",
+ cs->irq);
+ return (1);
+ }
+ byteout(cs->hw.saphir.cfg_reg + IRQ_REG, irq_val);
+ byteout(cs->hw.saphir.cfg_reg + RESET_REG, 1);
+ mdelay(10);
+ byteout(cs->hw.saphir.cfg_reg + RESET_REG, 0);
+ mdelay(10);
+ byteout(cs->hw.saphir.cfg_reg + IRQ_REG, irq_val);
+ byteout(cs->hw.saphir.cfg_reg + SPARE_REG, 0x02);
+ return (0);
+}
+
+static int
+saphir_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ u_long flags;
+
+ switch (mt) {
+ case CARD_RESET:
+ spin_lock_irqsave(&cs->lock, flags);
+ saphir_reset(cs);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return(0);
+ case CARD_RELEASE:
+ release_io_saphir(cs);
+ return(0);
+ case CARD_INIT:
+ spin_lock_irqsave(&cs->lock, flags);
+ inithscxisac(cs, 3);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return(0);
+ case CARD_TEST:
+ return(0);
+ }
+ return(0);
+}
+
+
+int __devinit
+setup_saphir(struct IsdnCard *card)
+{
+ struct IsdnCardState *cs = card->cs;
+ char tmp[64];
+
+ strcpy(tmp, saphir_rev);
+ printk(KERN_INFO "HiSax: HST Saphir driver Rev. %s\n", HiSax_getrev(tmp));
+ if (cs->typ != ISDN_CTYPE_HSTSAPHIR)
+ return (0);
+
+ /* IO-Ports */
+ cs->hw.saphir.cfg_reg = card->para[1];
+ cs->hw.saphir.isac = card->para[1] + ISAC_DATA;
+ cs->hw.saphir.hscx = card->para[1] + HSCX_DATA;
+ cs->hw.saphir.ale = card->para[1] + ADDRESS_REG;
+ cs->irq = card->para[0];
+ if (!request_region(cs->hw.saphir.cfg_reg, 6, "saphir")) {
+ printk(KERN_WARNING
+ "HiSax: HST Saphir config port %x-%x already in use\n",
+ cs->hw.saphir.cfg_reg,
+ cs->hw.saphir.cfg_reg + 5);
+ return (0);
+ }
+
+ printk(KERN_INFO "HiSax: HST Saphir config irq:%d io:0x%X\n",
+ cs->irq, cs->hw.saphir.cfg_reg);
+
+ setup_isac(cs);
+ cs->hw.saphir.timer.function = (void *) SaphirWatchDog;
+ cs->hw.saphir.timer.data = (long) cs;
+ init_timer(&cs->hw.saphir.timer);
+ cs->hw.saphir.timer.expires = jiffies + 4*HZ;
+ add_timer(&cs->hw.saphir.timer);
+ if (saphir_reset(cs)) {
+ release_io_saphir(cs);
+ return (0);
+ }
+ cs->readisac = &ReadISAC;
+ cs->writeisac = &WriteISAC;
+ cs->readisacfifo = &ReadISACfifo;
+ cs->writeisacfifo = &WriteISACfifo;
+ cs->BC_Read_Reg = &ReadHSCX;
+ cs->BC_Write_Reg = &WriteHSCX;
+ cs->BC_Send_Data = &hscx_fill_fifo;
+ cs->cardmsg = &saphir_card_msg;
+ cs->irq_func = &saphir_interrupt;
+ ISACVersion(cs, "saphir:");
+ if (HscxVersion(cs, "saphir:")) {
+ printk(KERN_WARNING
+ "saphir: wrong HSCX versions check IO address\n");
+ release_io_saphir(cs);
+ return (0);
+ }
+ return (1);
+}
diff --git a/drivers/isdn/hisax/sedlbauer.c b/drivers/isdn/hisax/sedlbauer.c
new file mode 100644
index 0000000..a10dfa8
--- /dev/null
+++ b/drivers/isdn/hisax/sedlbauer.c
@@ -0,0 +1,880 @@
+/* $Id: sedlbauer.c,v 1.34.2.6 2004/01/24 20:47:24 keil Exp $
+ *
+ * low level stuff for Sedlbauer cards
+ * includes support for the Sedlbauer speed star (speed star II),
+ * support for the Sedlbauer speed fax+,
+ * support for the Sedlbauer ISDN-Controller PC/104 and
+ * support for the Sedlbauer speed pci
+ * derived from the original file asuscom.c from Karsten Keil
+ *
+ * Author Marcus Niemann
+ * Copyright by Marcus Niemann <niemann@www-bib.fh-bielefeld.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to Karsten Keil
+ * Sedlbauer AG for informations
+ * Edgar Toernig
+ *
+ */
+
+/* Supported cards:
+ * Card: Chip: Configuration: Comment:
+ * ---------------------------------------------------------------------
+ * Speed Card ISAC_HSCX DIP-SWITCH
+ * Speed Win ISAC_HSCX ISAPNP
+ * Speed Fax+ ISAC_ISAR ISAPNP Full analog support
+ * Speed Star ISAC_HSCX CARDMGR
+ * Speed Win2 IPAC ISAPNP
+ * ISDN PC/104 IPAC DIP-SWITCH
+ * Speed Star2 IPAC CARDMGR
+ * Speed PCI IPAC PCI PNP
+ * 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.
+ * For example: hisaxctrl <DriverID> 9 ISAR.BIN
+*/
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "ipac.h"
+#include "hscx.h"
+#include "isar.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+#include <linux/isapnp.h>
+
+extern const char *CardType[];
+
+static const char *Sedlbauer_revision = "$Revision: 1.34.2.6 $";
+
+static const char *Sedlbauer_Types[] =
+ {"None", "speed card/win", "speed star", "speed fax+",
+ "speed win II / ISDN PC/104", "speed star II", "speed pci",
+ "speed fax+ pyramid", "speed fax+ pci", "HST Saphir III"};
+
+#define PCI_SUBVENDOR_SPEEDFAX_PYRAMID 0x51
+#define PCI_SUBVENDOR_HST_SAPHIR3 0x52
+#define PCI_SUBVENDOR_SEDLBAUER_PCI 0x53
+#define PCI_SUBVENDOR_SPEEDFAX_PCI 0x54
+#define PCI_SUB_ID_SEDLBAUER 0x01
+
+#define SEDL_SPEED_CARD_WIN 1
+#define SEDL_SPEED_STAR 2
+#define SEDL_SPEED_FAX 3
+#define SEDL_SPEED_WIN2_PC104 4
+#define SEDL_SPEED_STAR2 5
+#define SEDL_SPEED_PCI 6
+#define SEDL_SPEEDFAX_PYRAMID 7
+#define SEDL_SPEEDFAX_PCI 8
+#define HST_SAPHIR3 9
+
+#define SEDL_CHIP_TEST 0
+#define SEDL_CHIP_ISAC_HSCX 1
+#define SEDL_CHIP_ISAC_ISAR 2
+#define SEDL_CHIP_IPAC 3
+
+#define SEDL_BUS_ISA 1
+#define SEDL_BUS_PCI 2
+#define SEDL_BUS_PCMCIA 3
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define SEDL_HSCX_ISA_RESET_ON 0
+#define SEDL_HSCX_ISA_RESET_OFF 1
+#define SEDL_HSCX_ISA_ISAC 2
+#define SEDL_HSCX_ISA_HSCX 3
+#define SEDL_HSCX_ISA_ADR 4
+
+#define SEDL_HSCX_PCMCIA_RESET 0
+#define SEDL_HSCX_PCMCIA_ISAC 1
+#define SEDL_HSCX_PCMCIA_HSCX 2
+#define SEDL_HSCX_PCMCIA_ADR 4
+
+#define SEDL_ISAR_ISA_ISAC 4
+#define SEDL_ISAR_ISA_ISAR 6
+#define SEDL_ISAR_ISA_ADR 8
+#define SEDL_ISAR_ISA_ISAR_RESET_ON 10
+#define SEDL_ISAR_ISA_ISAR_RESET_OFF 12
+
+#define SEDL_IPAC_ANY_ADR 0
+#define SEDL_IPAC_ANY_IPAC 2
+
+#define SEDL_IPAC_PCI_BASE 0
+#define SEDL_IPAC_PCI_ADR 0xc0
+#define SEDL_IPAC_PCI_IPAC 0xc8
+#define SEDL_ISAR_PCI_ADR 0xc8
+#define SEDL_ISAR_PCI_ISAC 0xd0
+#define SEDL_ISAR_PCI_ISAR 0xe0
+#define SEDL_ISAR_PCI_ISAR_RESET_ON 0x01
+#define SEDL_ISAR_PCI_ISAR_RESET_OFF 0x18
+#define SEDL_ISAR_PCI_LED1 0x08
+#define SEDL_ISAR_PCI_LED2 0x10
+
+#define SEDL_RESET 0x3 /* same as DOS driver */
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+ register u_char ret;
+
+ byteout(ale, off);
+ ret = bytein(adr);
+ return (ret);
+}
+
+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(struct IsdnCardState *cs, u_char offset)
+{
+ return (readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ readfifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ writefifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0, data, size);
+}
+
+static u_char
+ReadISAC_IPAC(struct IsdnCardState *cs, u_char offset)
+{
+ return (readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset|0x80));
+}
+
+static void
+WriteISAC_IPAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, offset|0x80, value);
+}
+
+static void
+ReadISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+ readfifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0x80, data, size);
+}
+
+static void
+WriteISACfifo_IPAC(struct IsdnCardState *cs, u_char * data, int size)
+{
+ writefifo(cs->hw.sedl.adr, cs->hw.sedl.isac, 0x80, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+ return (readreg(cs->hw.sedl.adr,
+ cs->hw.sedl.hscx, offset + (hscx ? 0x40 : 0)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+ writereg(cs->hw.sedl.adr,
+ cs->hw.sedl.hscx, offset + (hscx ? 0x40 : 0), value);
+}
+
+/* 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(struct IsdnCardState *cs, int mode, u_char offset)
+{
+ if (mode == 0)
+ return (readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, offset));
+ else if (mode == 1)
+ byteout(cs->hw.sedl.adr, offset);
+ return(bytein(cs->hw.sedl.hscx));
+}
+
+static void
+WriteISAR(struct IsdnCardState *cs, int mode, u_char offset, u_char value)
+{
+ if (mode == 0)
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, offset, value);
+ else {
+ if (mode == 1)
+ byteout(cs->hw.sedl.adr, offset);
+ byteout(cs->hw.sedl.hscx, value);
+ }
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.sedl.adr, \
+ cs->hw.sedl.hscx, reg + (nr ? 0x40 : 0))
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.sedl.adr, \
+ cs->hw.sedl.hscx, reg + (nr ? 0x40 : 0), data)
+
+#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.sedl.adr, \
+ cs->hw.sedl.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.sedl.adr, \
+ cs->hw.sedl.hscx, (nr ? 0x40 : 0), ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+sedlbauer_interrupt(int intno, void *dev_id)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char val;
+ u_long flags;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ if ((cs->hw.sedl.bus == SEDL_BUS_PCMCIA) && (*cs->busy_flag == 1)) {
+ /* The card tends to generate interrupts while being removed
+ causing us to just crash the kernel. bad. */
+ spin_unlock_irqrestore(&cs->lock, flags);
+ printk(KERN_WARNING "Sedlbauer: card not available!\n");
+ return IRQ_NONE;
+ }
+
+ val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_ISTA + 0x40);
+ Start_HSCX:
+ if (val)
+ hscx_int_main(cs, val);
+ val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA);
+ Start_ISAC:
+ if (val)
+ isac_interrupt(cs, val);
+ val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_ISTA + 0x40);
+ if (val) {
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "HSCX IntStat after IntRoutine");
+ goto Start_HSCX;
+ }
+ val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA);
+ if (val) {
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ISAC IntStat after IntRoutine");
+ goto Start_ISAC;
+ }
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK, 0xFF);
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK + 0x40, 0xFF);
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0xFF);
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0x0);
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK, 0x0);
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_MASK + 0x40, 0x0);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t
+sedlbauer_interrupt_ipac(int intno, void *dev_id)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char ista, val, icnt = 5;
+ u_long flags;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ ista = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_ISTA);
+Start_IPAC:
+ if (cs->debug & L1_DEB_IPAC)
+ debugl1(cs, "IPAC ISTA %02X", ista);
+ if (ista & 0x0f) {
+ val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, HSCX_ISTA + 0x40);
+ if (ista & 0x01)
+ val |= 0x01;
+ if (ista & 0x04)
+ val |= 0x02;
+ if (ista & 0x08)
+ val |= 0x04;
+ if (val)
+ hscx_int_main(cs, val);
+ }
+ if (ista & 0x20) {
+ val = 0xfe & readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA | 0x80);
+ if (val) {
+ isac_interrupt(cs, val);
+ }
+ }
+ if (ista & 0x10) {
+ val = 0x01;
+ isac_interrupt(cs, val);
+ }
+ ista = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_ISTA);
+ if ((ista & 0x3f) && icnt) {
+ icnt--;
+ goto Start_IPAC;
+ }
+ if (!icnt)
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "Sedlbauer IRQ LOOP");
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_MASK, 0xFF);
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_MASK, 0xC0);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t
+sedlbauer_interrupt_isar(int intno, void *dev_id)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char val;
+ int cnt = 5;
+ u_long flags;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, ISAR_IRQBIT);
+ Start_ISAR:
+ if (val & ISAR_IRQSTA)
+ isar_int_main(cs);
+ val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA);
+ Start_ISAC:
+ if (val)
+ isac_interrupt(cs, val);
+ val = readreg(cs->hw.sedl.adr, cs->hw.sedl.hscx, ISAR_IRQBIT);
+ if ((val & ISAR_IRQSTA) && --cnt) {
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "ISAR IntStat after IntRoutine");
+ goto Start_ISAR;
+ }
+ val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_ISTA);
+ if (val && --cnt) {
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ISAC IntStat after IntRoutine");
+ goto Start_ISAC;
+ }
+ if (!cnt)
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "Sedlbauer IRQ LOOP");
+
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, ISAR_IRQBIT, 0);
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0xFF);
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, ISAC_MASK, 0x0);
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx, ISAR_IRQBIT, ISAR_IRQMSK);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static void
+release_io_sedlbauer(struct IsdnCardState *cs)
+{
+ int bytecnt = 8;
+
+ if (cs->subtyp == SEDL_SPEED_FAX) {
+ bytecnt = 16;
+ } else if (cs->hw.sedl.bus == SEDL_BUS_PCI) {
+ bytecnt = 256;
+ }
+ if (cs->hw.sedl.cfg_reg)
+ release_region(cs->hw.sedl.cfg_reg, bytecnt);
+}
+
+static void
+reset_sedlbauer(struct IsdnCardState *cs)
+{
+ printk(KERN_INFO "Sedlbauer: resetting card\n");
+
+ if (!((cs->hw.sedl.bus == SEDL_BUS_PCMCIA) &&
+ (cs->hw.sedl.chip == SEDL_CHIP_ISAC_HSCX))) {
+ if (cs->hw.sedl.chip == SEDL_CHIP_IPAC) {
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_POTA2, 0x20);
+ mdelay(2);
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_POTA2, 0x0);
+ mdelay(10);
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_CONF, 0x0);
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_ACFG, 0xff);
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_AOE, 0x0);
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_MASK, 0xc0);
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_PCFG, 0x12);
+ } else if ((cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) &&
+ (cs->hw.sedl.bus == SEDL_BUS_PCI)) {
+ byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_on);
+ mdelay(2);
+ byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_off);
+ mdelay(10);
+ } else {
+ byteout(cs->hw.sedl.reset_on, SEDL_RESET); /* Reset On */
+ mdelay(2);
+ byteout(cs->hw.sedl.reset_off, 0); /* Reset Off */
+ mdelay(10);
+ }
+ }
+}
+
+static int
+Sedl_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ u_long flags;
+
+ switch (mt) {
+ case CARD_RESET:
+ spin_lock_irqsave(&cs->lock, flags);
+ reset_sedlbauer(cs);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return(0);
+ case CARD_RELEASE:
+ if (cs->hw.sedl.bus == SEDL_BUS_PCI)
+ /* disable all IRQ */
+ byteout(cs->hw.sedl.cfg_reg+ 5, 0);
+ if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) {
+ spin_lock_irqsave(&cs->lock, flags);
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx,
+ ISAR_IRQBIT, 0);
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.isac,
+ ISAC_MASK, 0xFF);
+ reset_sedlbauer(cs);
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx,
+ ISAR_IRQBIT, 0);
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.isac,
+ ISAC_MASK, 0xFF);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ }
+ release_io_sedlbauer(cs);
+ return(0);
+ case CARD_INIT:
+ spin_lock_irqsave(&cs->lock, flags);
+ if (cs->hw.sedl.bus == SEDL_BUS_PCI)
+ /* enable all IRQ */
+ byteout(cs->hw.sedl.cfg_reg+ 5, 0x02);
+ reset_sedlbauer(cs);
+ if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) {
+ clear_pending_isac_ints(cs);
+ writereg(cs->hw.sedl.adr, cs->hw.sedl.hscx,
+ ISAR_IRQBIT, 0);
+ initisac(cs);
+ initisar(cs);
+ /* Reenable all IRQ */
+ cs->writeisac(cs, ISAC_MASK, 0);
+ /* RESET Receiver and Transmitter */
+ cs->writeisac(cs, ISAC_CMDR, 0x41);
+ } else {
+ inithscxisac(cs, 3);
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return(0);
+ case CARD_TEST:
+ return(0);
+ case MDL_INFO_CONN:
+ if (cs->subtyp != SEDL_SPEEDFAX_PYRAMID)
+ return(0);
+ spin_lock_irqsave(&cs->lock, flags);
+ if ((long) arg)
+ cs->hw.sedl.reset_off &= ~SEDL_ISAR_PCI_LED2;
+ else
+ cs->hw.sedl.reset_off &= ~SEDL_ISAR_PCI_LED1;
+ byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_off);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case MDL_INFO_REL:
+ if (cs->subtyp != SEDL_SPEEDFAX_PYRAMID)
+ return(0);
+ spin_lock_irqsave(&cs->lock, flags);
+ if ((long) arg)
+ cs->hw.sedl.reset_off |= SEDL_ISAR_PCI_LED2;
+ else
+ cs->hw.sedl.reset_off |= SEDL_ISAR_PCI_LED1;
+ byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_off);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ }
+ return(0);
+}
+
+#ifdef __ISAPNP__
+static struct isapnp_device_id sedl_ids[] __devinitdata = {
+ { ISAPNP_VENDOR('S', 'A', 'G'), ISAPNP_FUNCTION(0x01),
+ ISAPNP_VENDOR('S', 'A', 'G'), ISAPNP_FUNCTION(0x01),
+ (unsigned long) "Speed win" },
+ { ISAPNP_VENDOR('S', 'A', 'G'), ISAPNP_FUNCTION(0x02),
+ ISAPNP_VENDOR('S', 'A', 'G'), ISAPNP_FUNCTION(0x02),
+ (unsigned long) "Speed Fax+" },
+ { 0, }
+};
+
+static struct isapnp_device_id *ipid __devinitdata = &sedl_ids[0];
+static struct pnp_card *pnp_c __devinitdata = NULL;
+
+static int __devinit
+setup_sedlbauer_isapnp(struct IsdnCard *card, int *bytecnt)
+{
+ struct IsdnCardState *cs = card->cs;
+ struct pnp_dev *pnp_d;
+
+ if (!isapnp_present())
+ return -1;
+
+ while(ipid->card_vendor) {
+ if ((pnp_c = pnp_find_card(ipid->card_vendor,
+ ipid->card_device, pnp_c))) {
+ pnp_d = NULL;
+ if ((pnp_d = pnp_find_dev(pnp_c,
+ ipid->vendor, ipid->function, pnp_d))) {
+ int err;
+
+ printk(KERN_INFO "HiSax: %s detected\n",
+ (char *)ipid->driver_data);
+ pnp_disable_dev(pnp_d);
+ err = pnp_activate_dev(pnp_d);
+ if (err<0) {
+ printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
+ __func__, err);
+ return(0);
+ }
+ card->para[1] = pnp_port_start(pnp_d, 0);
+ card->para[0] = pnp_irq(pnp_d, 0);
+
+ if (!card->para[0] || !card->para[1]) {
+ printk(KERN_ERR "Sedlbauer PnP:some resources are missing %ld/%lx\n",
+ card->para[0], card->para[1]);
+ pnp_disable_dev(pnp_d);
+ return(0);
+ }
+ cs->hw.sedl.cfg_reg = card->para[1];
+ cs->irq = card->para[0];
+ if (ipid->function == ISAPNP_FUNCTION(0x2)) {
+ cs->subtyp = SEDL_SPEED_FAX;
+ cs->hw.sedl.chip = SEDL_CHIP_ISAC_ISAR;
+ *bytecnt = 16;
+ } else {
+ cs->subtyp = SEDL_SPEED_CARD_WIN;
+ cs->hw.sedl.chip = SEDL_CHIP_TEST;
+ }
+
+ return (1);
+ } else {
+ printk(KERN_ERR "Sedlbauer PnP: PnP error card found, no device\n");
+ return(0);
+ }
+ }
+ ipid++;
+ pnp_c = NULL;
+ }
+
+ printk(KERN_INFO "Sedlbauer PnP: no ISAPnP card found\n");
+ return -1;
+}
+#else
+
+static int __devinit
+setup_sedlbauer_isapnp(struct IsdnCard *card, int *bytecnt)
+{
+ return -1;
+}
+#endif /* __ISAPNP__ */
+
+#ifdef CONFIG_PCI_LEGACY
+static struct pci_dev *dev_sedl __devinitdata = NULL;
+
+static int __devinit
+setup_sedlbauer_pci(struct IsdnCard *card)
+{
+ struct IsdnCardState *cs = card->cs;
+ u16 sub_vendor_id, sub_id;
+
+ if ((dev_sedl = pci_find_device(PCI_VENDOR_ID_TIGERJET,
+ PCI_DEVICE_ID_TIGERJET_100, dev_sedl))) {
+ if (pci_enable_device(dev_sedl))
+ return(0);
+ cs->irq = dev_sedl->irq;
+ if (!cs->irq) {
+ printk(KERN_WARNING "Sedlbauer: No IRQ for PCI card found\n");
+ return(0);
+ }
+ cs->hw.sedl.cfg_reg = pci_resource_start(dev_sedl, 0);
+ } else {
+ printk(KERN_WARNING "Sedlbauer: No PCI card found\n");
+ return(0);
+ }
+ cs->irq_flags |= IRQF_SHARED;
+ cs->hw.sedl.bus = SEDL_BUS_PCI;
+ sub_vendor_id = dev_sedl->subsystem_vendor;
+ sub_id = dev_sedl->subsystem_device;
+ printk(KERN_INFO "Sedlbauer: PCI subvendor:%x subid %x\n",
+ sub_vendor_id, sub_id);
+ printk(KERN_INFO "Sedlbauer: PCI base adr %#x\n",
+ cs->hw.sedl.cfg_reg);
+ if (sub_id != PCI_SUB_ID_SEDLBAUER) {
+ printk(KERN_ERR "Sedlbauer: unknown sub id %#x\n", sub_id);
+ return(0);
+ }
+ if (sub_vendor_id == PCI_SUBVENDOR_SPEEDFAX_PYRAMID) {
+ cs->hw.sedl.chip = SEDL_CHIP_ISAC_ISAR;
+ cs->subtyp = SEDL_SPEEDFAX_PYRAMID;
+ } else if (sub_vendor_id == PCI_SUBVENDOR_SPEEDFAX_PCI) {
+ cs->hw.sedl.chip = SEDL_CHIP_ISAC_ISAR;
+ cs->subtyp = SEDL_SPEEDFAX_PCI;
+ } else if (sub_vendor_id == PCI_SUBVENDOR_HST_SAPHIR3) {
+ cs->hw.sedl.chip = SEDL_CHIP_IPAC;
+ cs->subtyp = HST_SAPHIR3;
+ } else if (sub_vendor_id == PCI_SUBVENDOR_SEDLBAUER_PCI) {
+ cs->hw.sedl.chip = SEDL_CHIP_IPAC;
+ cs->subtyp = SEDL_SPEED_PCI;
+ } else {
+ printk(KERN_ERR "Sedlbauer: unknown sub vendor id %#x\n",
+ sub_vendor_id);
+ return(0);
+ }
+
+ cs->hw.sedl.reset_on = SEDL_ISAR_PCI_ISAR_RESET_ON;
+ cs->hw.sedl.reset_off = SEDL_ISAR_PCI_ISAR_RESET_OFF;
+ byteout(cs->hw.sedl.cfg_reg, 0xff);
+ byteout(cs->hw.sedl.cfg_reg, 0x00);
+ byteout(cs->hw.sedl.cfg_reg+ 2, 0xdd);
+ byteout(cs->hw.sedl.cfg_reg+ 5, 0); /* disable all IRQ */
+ byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_on);
+ mdelay(2);
+ byteout(cs->hw.sedl.cfg_reg +3, cs->hw.sedl.reset_off);
+ mdelay(10);
+
+ return (1);
+}
+
+#else
+
+static int __devinit
+setup_sedlbauer_pci(struct IsdnCard *card)
+{
+ return (1);
+}
+
+#endif /* CONFIG_PCI_LEGACY */
+
+int __devinit
+setup_sedlbauer(struct IsdnCard *card)
+{
+ int bytecnt = 8, ver, val, rc;
+ struct IsdnCardState *cs = card->cs;
+ char tmp[64];
+
+ strcpy(tmp, Sedlbauer_revision);
+ printk(KERN_INFO "HiSax: Sedlbauer driver Rev. %s\n", HiSax_getrev(tmp));
+
+ if (cs->typ == ISDN_CTYPE_SEDLBAUER) {
+ cs->subtyp = SEDL_SPEED_CARD_WIN;
+ cs->hw.sedl.bus = SEDL_BUS_ISA;
+ cs->hw.sedl.chip = SEDL_CHIP_TEST;
+ } else if (cs->typ == ISDN_CTYPE_SEDLBAUER_PCMCIA) {
+ cs->subtyp = SEDL_SPEED_STAR;
+ cs->hw.sedl.bus = SEDL_BUS_PCMCIA;
+ cs->hw.sedl.chip = SEDL_CHIP_TEST;
+ } else if (cs->typ == ISDN_CTYPE_SEDLBAUER_FAX) {
+ cs->subtyp = SEDL_SPEED_FAX;
+ cs->hw.sedl.bus = SEDL_BUS_ISA;
+ cs->hw.sedl.chip = SEDL_CHIP_ISAC_ISAR;
+ } else
+ return (0);
+
+ bytecnt = 8;
+ if (card->para[1]) {
+ cs->hw.sedl.cfg_reg = card->para[1];
+ cs->irq = card->para[0];
+ if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) {
+ bytecnt = 16;
+ }
+ } else {
+ rc = setup_sedlbauer_isapnp(card, &bytecnt);
+ if (!rc)
+ return (0);
+ if (rc > 0)
+ goto ready;
+
+ /* Probe for Sedlbauer speed pci */
+ rc = setup_sedlbauer_pci(card);
+ if (!rc)
+ return (0);
+
+ bytecnt = 256;
+ }
+
+ready:
+
+ /* In case of the sedlbauer pcmcia card, this region is in use,
+ * reserved for us by the card manager. So we do not check it
+ * here, it would fail.
+ */
+ if (cs->hw.sedl.bus != SEDL_BUS_PCMCIA &&
+ !request_region(cs->hw.sedl.cfg_reg, bytecnt, "sedlbauer isdn")) {
+ printk(KERN_WARNING
+ "HiSax: %s config port %x-%x already in use\n",
+ CardType[card->typ],
+ cs->hw.sedl.cfg_reg,
+ cs->hw.sedl.cfg_reg + bytecnt);
+ return (0);
+ }
+
+ printk(KERN_INFO
+ "Sedlbauer: defined at 0x%x-0x%x IRQ %d\n",
+ cs->hw.sedl.cfg_reg,
+ cs->hw.sedl.cfg_reg + bytecnt,
+ cs->irq);
+
+ cs->BC_Read_Reg = &ReadHSCX;
+ cs->BC_Write_Reg = &WriteHSCX;
+ cs->BC_Send_Data = &hscx_fill_fifo;
+ cs->cardmsg = &Sedl_card_msg;
+
+/*
+ * testing ISA and PCMCIA Cards for IPAC, default is ISAC
+ * do not test for PCI card, because ports are different
+ * and PCI card uses only IPAC (for the moment)
+ */
+ if (cs->hw.sedl.bus != SEDL_BUS_PCI) {
+ val = readreg(cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_ADR,
+ cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_IPAC, IPAC_ID);
+ printk(KERN_DEBUG "Sedlbauer: testing IPAC version %x\n", val);
+ if ((val == 1) || (val == 2)) {
+ /* IPAC */
+ cs->subtyp = SEDL_SPEED_WIN2_PC104;
+ if (cs->hw.sedl.bus == SEDL_BUS_PCMCIA) {
+ cs->subtyp = SEDL_SPEED_STAR2;
+ }
+ cs->hw.sedl.chip = SEDL_CHIP_IPAC;
+ } else {
+ /* ISAC_HSCX oder ISAC_ISAR */
+ if (cs->hw.sedl.chip == SEDL_CHIP_TEST) {
+ cs->hw.sedl.chip = SEDL_CHIP_ISAC_HSCX;
+ }
+ }
+ }
+
+/*
+ * hw.sedl.chip is now properly set
+ */
+ printk(KERN_INFO "Sedlbauer: %s detected\n",
+ Sedlbauer_Types[cs->subtyp]);
+
+ setup_isac(cs);
+ if (cs->hw.sedl.chip == SEDL_CHIP_IPAC) {
+ if (cs->hw.sedl.bus == SEDL_BUS_PCI) {
+ cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_IPAC_PCI_ADR;
+ cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_IPAC_PCI_IPAC;
+ cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_IPAC_PCI_IPAC;
+ } else {
+ cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_ADR;
+ cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_IPAC;
+ cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_IPAC_ANY_IPAC;
+ }
+ test_and_set_bit(HW_IPAC, &cs->HW_Flags);
+ cs->readisac = &ReadISAC_IPAC;
+ cs->writeisac = &WriteISAC_IPAC;
+ cs->readisacfifo = &ReadISACfifo_IPAC;
+ cs->writeisacfifo = &WriteISACfifo_IPAC;
+ cs->irq_func = &sedlbauer_interrupt_ipac;
+ val = readreg(cs->hw.sedl.adr, cs->hw.sedl.isac, IPAC_ID);
+ printk(KERN_INFO "Sedlbauer: IPAC version %x\n", val);
+ } else {
+ /* ISAC_HSCX oder ISAC_ISAR */
+ cs->readisac = &ReadISAC;
+ cs->writeisac = &WriteISAC;
+ cs->readisacfifo = &ReadISACfifo;
+ cs->writeisacfifo = &WriteISACfifo;
+ if (cs->hw.sedl.chip == SEDL_CHIP_ISAC_ISAR) {
+ if (cs->hw.sedl.bus == SEDL_BUS_PCI) {
+ cs->hw.sedl.adr = cs->hw.sedl.cfg_reg +
+ SEDL_ISAR_PCI_ADR;
+ cs->hw.sedl.isac = cs->hw.sedl.cfg_reg +
+ SEDL_ISAR_PCI_ISAC;
+ cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg +
+ SEDL_ISAR_PCI_ISAR;
+ } else {
+ cs->hw.sedl.adr = cs->hw.sedl.cfg_reg +
+ SEDL_ISAR_ISA_ADR;
+ cs->hw.sedl.isac = cs->hw.sedl.cfg_reg +
+ SEDL_ISAR_ISA_ISAC;
+ cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg +
+ SEDL_ISAR_ISA_ISAR;
+ cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg +
+ SEDL_ISAR_ISA_ISAR_RESET_ON;
+ cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg +
+ SEDL_ISAR_ISA_ISAR_RESET_OFF;
+ }
+ cs->bcs[0].hw.isar.reg = &cs->hw.sedl.isar;
+ cs->bcs[1].hw.isar.reg = &cs->hw.sedl.isar;
+ test_and_set_bit(HW_ISAR, &cs->HW_Flags);
+ cs->irq_func = &sedlbauer_interrupt_isar;
+ cs->auxcmd = &isar_auxcmd;
+ ISACVersion(cs, "Sedlbauer:");
+ cs->BC_Read_Reg = &ReadISAR;
+ cs->BC_Write_Reg = &WriteISAR;
+ cs->BC_Send_Data = &isar_fill_fifo;
+ bytecnt = 3;
+ while (bytecnt) {
+ ver = ISARVersion(cs, "Sedlbauer:");
+ if (ver < 0)
+ printk(KERN_WARNING
+ "Sedlbauer: wrong ISAR version (ret = %d)\n", ver);
+ else
+ break;
+ reset_sedlbauer(cs);
+ bytecnt--;
+ }
+ if (!bytecnt) {
+ release_io_sedlbauer(cs);
+ return (0);
+ }
+ } else {
+ if (cs->hw.sedl.bus == SEDL_BUS_PCMCIA) {
+ cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_ADR;
+ cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_ISAC;
+ cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_HSCX;
+ cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_RESET;
+ cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + SEDL_HSCX_PCMCIA_RESET;
+ cs->irq_flags |= IRQF_SHARED;
+ } else {
+ cs->hw.sedl.adr = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_ADR;
+ cs->hw.sedl.isac = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_ISAC;
+ cs->hw.sedl.hscx = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_HSCX;
+ cs->hw.sedl.reset_on = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_RESET_ON;
+ cs->hw.sedl.reset_off = cs->hw.sedl.cfg_reg + SEDL_HSCX_ISA_RESET_OFF;
+ }
+ cs->irq_func = &sedlbauer_interrupt;
+ ISACVersion(cs, "Sedlbauer:");
+
+ if (HscxVersion(cs, "Sedlbauer:")) {
+ printk(KERN_WARNING
+ "Sedlbauer: wrong HSCX versions check IO address\n");
+ release_io_sedlbauer(cs);
+ return (0);
+ }
+ }
+ }
+ return (1);
+}
diff --git a/drivers/isdn/hisax/sedlbauer_cs.c b/drivers/isdn/hisax/sedlbauer_cs.c
new file mode 100644
index 0000000..9a3c9f5
--- /dev/null
+++ b/drivers/isdn/hisax/sedlbauer_cs.c
@@ -0,0 +1,479 @@
+/*======================================================================
+
+ A Sedlbauer PCMCIA client driver
+
+ This driver is for the Sedlbauer Speed Star and Speed Star II,
+ which are ISDN PCMCIA Cards.
+
+ The contents of this file are subject to the Mozilla Public
+ License Version 1.1 (the "License"); you may not use this file
+ except in compliance with the License. You may obtain a copy of
+ the License at http://www.mozilla.org/MPL/
+
+ Software distributed under the License is distributed on an "AS
+ IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ implied. See the License for the specific language governing
+ rights and limitations under the License.
+
+ The initial developer of the original code is David A. Hinds
+ <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
+ are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
+
+ Modifications from dummy_cs.c are Copyright (C) 1999-2001 Marcus Niemann
+ <maniemann@users.sourceforge.net>. All Rights Reserved.
+
+ Alternatively, the contents of this file may be used under the
+ terms of the GNU General Public License version 2 (the "GPL"), in
+ which case the provisions of the GPL are applicable instead of the
+ above. If you wish to allow the use of your version of this file
+ only under the terms of the GPL and not to allow others to use
+ your version of this file under the MPL, indicate your decision
+ by deleting the provisions above and replace them with the notice
+ and other provisions required by the GPL. If you do not delete
+ the provisions above, a recipient may use your version of this
+ file under either the MPL or the GPL.
+
+======================================================================*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+#include "hisax_cfg.h"
+
+MODULE_DESCRIPTION("ISDN4Linux: PCMCIA client driver for Sedlbauer cards");
+MODULE_AUTHOR("Marcus Niemann");
+MODULE_LICENSE("Dual MPL/GPL");
+
+/*
+ All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If
+ you do not define PCMCIA_DEBUG at all, all the debug code will be
+ left out. If you compile with PCMCIA_DEBUG=0, the debug code will
+ be present but disabled -- but it can then be enabled for specific
+ modules at load time with a 'pc_debug=#' option to insmod.
+*/
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args);
+static char *version =
+"sedlbauer_cs.c 1.1a 2001/01/28 15:04:04 (M.Niemann)";
+#else
+#define DEBUG(n, args...)
+#endif
+
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+static int protocol = 2; /* EURO-ISDN Default */
+module_param(protocol, int, 0);
+
+/*====================================================================*/
+
+/*
+ The event() function is this driver's Card Services event handler.
+ It will be called by Card Services when an appropriate card status
+ event is received. The config() and release() entry points are
+ used to configure or release a socket, in response to card
+ insertion and ejection events. They are invoked from the sedlbauer
+ event handler.
+*/
+
+static int sedlbauer_config(struct pcmcia_device *link);
+static void sedlbauer_release(struct pcmcia_device *link);
+
+/*
+ The attach() and detach() entry points are used to create and destroy
+ "instances" of the driver, where each instance represents everything
+ needed to manage one actual PCMCIA card.
+*/
+
+static void sedlbauer_detach(struct pcmcia_device *p_dev);
+
+/*
+ You'll also need to prototype all the functions that will actually
+ be used to talk to your device. See 'memory_cs' for a good example
+ of a fully self-sufficient driver; the other drivers rely more or
+ less on other parts of the kernel.
+*/
+
+/*
+ A driver needs to provide a dev_node_t structure for each device
+ on a card. In some cases, there is only one device per card (for
+ example, ethernet cards, modems). In other cases, there may be
+ many actual or logical devices (SCSI adapters, memory cards with
+ multiple partitions). The dev_node_t structures need to be kept
+ in a linked list starting at the 'dev' field of a struct pcmcia_device
+ structure. We allocate them in the card's private data structure,
+ because they generally shouldn't be allocated dynamically.
+
+ In this case, we also provide a flag to indicate if a device is
+ "stopped" due to a power management event, or card ejection. The
+ device IO routines can use a flag like this to throttle IO to a
+ card that is not ready to accept it.
+*/
+
+typedef struct local_info_t {
+ struct pcmcia_device *p_dev;
+ dev_node_t node;
+ int stop;
+ int cardnr;
+} local_info_t;
+
+/*======================================================================
+
+ sedlbauer_attach() creates an "instance" of the driver, allocating
+ local data structures for one device. The device is registered
+ with Card Services.
+
+ The dev_link structure is initialized, but we don't actually
+ configure the card at this point -- we wait until we receive a
+ card insertion event.
+
+======================================================================*/
+
+static int sedlbauer_probe(struct pcmcia_device *link)
+{
+ local_info_t *local;
+
+ DEBUG(0, "sedlbauer_attach()\n");
+
+ /* Allocate space for private device-specific data */
+ local = kzalloc(sizeof(local_info_t), GFP_KERNEL);
+ if (!local) return -ENOMEM;
+ local->cardnr = -1;
+
+ local->p_dev = link;
+ link->priv = local;
+
+ /* Interrupt setup */
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
+ link->irq.IRQInfo1 = IRQ_LEVEL_ID;
+ link->irq.Handler = NULL;
+
+ /*
+ General socket configuration defaults can go here. In this
+ client, we assume very little, and rely on the CIS for almost
+ everything. In most clients, many details (i.e., number, sizes,
+ and attributes of IO windows) are fixed by the nature of the
+ device, and can be hard-wired here.
+ */
+
+ /* from old sedl_cs
+ */
+ /* The io structure describes IO port mapping */
+ link->io.NumPorts1 = 8;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ link->io.IOAddrLines = 3;
+
+ link->conf.Attributes = 0;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+
+ return sedlbauer_config(link);
+} /* sedlbauer_attach */
+
+/*======================================================================
+
+ This deletes a driver "instance". The device is de-registered
+ with Card Services. If it has been released, all local data
+ structures are freed. Otherwise, the structures will be freed
+ when the device is released.
+
+======================================================================*/
+
+static void sedlbauer_detach(struct pcmcia_device *link)
+{
+ DEBUG(0, "sedlbauer_detach(0x%p)\n", link);
+
+ ((local_info_t *)link->priv)->stop = 1;
+ sedlbauer_release(link);
+
+ /* This points to the parent local_info_t struct */
+ kfree(link->priv);
+} /* sedlbauer_detach */
+
+/*======================================================================
+
+ sedlbauer_config() is scheduled to run after a CARD_INSERTION event
+ is received, to configure the PCMCIA socket, and to make the
+ device available to the system.
+
+======================================================================*/
+#define CS_CHECK(fn, ret) \
+do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
+
+static int sedlbauer_config_check(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cfg,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ win_req_t *req = priv_data;
+
+ if (cfg->index == 0)
+ return -ENODEV;
+
+ /* Does this card need audio output? */
+ if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
+ p_dev->conf.Attributes |= CONF_ENABLE_SPKR;
+ p_dev->conf.Status = CCSR_AUDIO_ENA;
+ }
+
+ /* Use power settings for Vcc and Vpp if present */
+ /* Note that the CIS values need to be rescaled */
+ if (cfg->vcc.present & (1<<CISTPL_POWER_VNOM)) {
+ if (vcc != cfg->vcc.param[CISTPL_POWER_VNOM]/10000)
+ return -ENODEV;
+ } else if (dflt->vcc.present & (1<<CISTPL_POWER_VNOM)) {
+ if (vcc != dflt->vcc.param[CISTPL_POWER_VNOM]/10000)
+ return -ENODEV;
+ }
+
+ if (cfg->vpp1.present & (1<<CISTPL_POWER_VNOM))
+ p_dev->conf.Vpp = cfg->vpp1.param[CISTPL_POWER_VNOM]/10000;
+ else if (dflt->vpp1.present & (1<<CISTPL_POWER_VNOM))
+ p_dev->conf.Vpp = dflt->vpp1.param[CISTPL_POWER_VNOM]/10000;
+
+ /* Do we need to allocate an interrupt? */
+ if (cfg->irq.IRQInfo1 || dflt->irq.IRQInfo1)
+ p_dev->conf.Attributes |= CONF_ENABLE_IRQ;
+
+ /* IO window settings */
+ p_dev->io.NumPorts1 = p_dev->io.NumPorts2 = 0;
+ if ((cfg->io.nwin > 0) || (dflt->io.nwin > 0)) {
+ cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt->io;
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ if (!(io->flags & CISTPL_IO_8BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_16;
+ if (!(io->flags & CISTPL_IO_16BIT))
+ p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
+ p_dev->io.BasePort1 = io->win[0].base;
+ p_dev->io.NumPorts1 = io->win[0].len;
+ if (io->nwin > 1) {
+ p_dev->io.Attributes2 = p_dev->io.Attributes1;
+ p_dev->io.BasePort2 = io->win[1].base;
+ p_dev->io.NumPorts2 = io->win[1].len;
+ }
+ /* This reserves IO space but doesn't actually enable it */
+ if (pcmcia_request_io(p_dev, &p_dev->io) != 0)
+ return -ENODEV;
+ }
+
+ /*
+ Now set up a common memory window, if needed. There is room
+ in the struct pcmcia_device structure for one memory window handle,
+ but if the base addresses need to be saved, or if multiple
+ windows are needed, the info should go in the private data
+ structure for this device.
+
+ Note that the memory window base is a physical address, and
+ needs to be mapped to virtual space with ioremap() before it
+ is used.
+ */
+ if ((cfg->mem.nwin > 0) || (dflt->mem.nwin > 0)) {
+ cistpl_mem_t *mem = (cfg->mem.nwin) ? &cfg->mem : &dflt->mem;
+ memreq_t map;
+ req->Attributes = WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM;
+ req->Attributes |= WIN_ENABLE;
+ req->Base = mem->win[0].host_addr;
+ req->Size = mem->win[0].len;
+ req->AccessSpeed = 0;
+ if (pcmcia_request_window(&p_dev, req, &p_dev->win) != 0)
+ return -ENODEV;
+ map.Page = 0;
+ map.CardOffset = mem->win[0].card_addr;
+ if (pcmcia_map_mem_page(p_dev->win, &map) != 0)
+ return -ENODEV;
+ }
+ return 0;
+}
+
+
+
+static int sedlbauer_config(struct pcmcia_device *link)
+{
+ local_info_t *dev = link->priv;
+ win_req_t *req;
+ int last_fn, last_ret;
+ IsdnCard_t icard;
+
+ DEBUG(0, "sedlbauer_config(0x%p)\n", link);
+
+ req = kzalloc(sizeof(win_req_t), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ /*
+ In this loop, we scan the CIS for configuration table entries,
+ each of which describes a valid card configuration, including
+ voltage, IO window, memory window, and interrupt settings.
+
+ We make no assumptions about the card to be configured: we use
+ just the information available in the CIS. In an ideal world,
+ this would work for any PCMCIA card, but it requires a complete
+ and accurate CIS. In practice, a driver usually "knows" most of
+ these things without consulting the CIS, and most client drivers
+ will only use the CIS to fill in implementation-defined details.
+ */
+ last_ret = pcmcia_loop_config(link, sedlbauer_config_check, req);
+ if (last_ret)
+ goto failed;
+
+ /*
+ Allocate an interrupt line. Note that this does not assign a
+ handler to the interrupt, unless the 'Handler' member of the
+ irq structure is initialized.
+ */
+ if (link->conf.Attributes & CONF_ENABLE_IRQ)
+ CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
+
+ /*
+ This actually configures the PCMCIA socket -- setting up
+ the I/O windows and the interrupt mapping, and putting the
+ card and host interface into "Memory and IO" mode.
+ */
+ CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
+
+ /*
+ At this point, the dev_node_t structure(s) need to be
+ initialized and arranged in a linked list at link->dev.
+ */
+ sprintf(dev->node.dev_name, "sedlbauer");
+ dev->node.major = dev->node.minor = 0;
+ link->dev_node = &dev->node;
+
+ /* Finally, report what we've done */
+ printk(KERN_INFO "%s: index 0x%02x:",
+ dev->node.dev_name, link->conf.ConfigIndex);
+ if (link->conf.Vpp)
+ printk(", Vpp %d.%d", link->conf.Vpp/10, link->conf.Vpp%10);
+ if (link->conf.Attributes & CONF_ENABLE_IRQ)
+ printk(", irq %d", link->irq.AssignedIRQ);
+ if (link->io.NumPorts1)
+ printk(", io 0x%04x-0x%04x", link->io.BasePort1,
+ link->io.BasePort1+link->io.NumPorts1-1);
+ if (link->io.NumPorts2)
+ printk(" & 0x%04x-0x%04x", link->io.BasePort2,
+ link->io.BasePort2+link->io.NumPorts2-1);
+ if (link->win)
+ printk(", mem 0x%06lx-0x%06lx", req->Base,
+ req->Base+req->Size-1);
+ printk("\n");
+
+ icard.para[0] = link->irq.AssignedIRQ;
+ icard.para[1] = link->io.BasePort1;
+ icard.protocol = protocol;
+ icard.typ = ISDN_CTYPE_SEDLBAUER_PCMCIA;
+
+ last_ret = hisax_init_pcmcia(link, &(((local_info_t*)link->priv)->stop), &icard);
+ if (last_ret < 0) {
+ printk(KERN_ERR "sedlbauer_cs: failed to initialize SEDLBAUER PCMCIA %d at i/o %#x\n",
+ last_ret, link->io.BasePort1);
+ sedlbauer_release(link);
+ return -ENODEV;
+ } else
+ ((local_info_t*)link->priv)->cardnr = last_ret;
+
+ return 0;
+
+cs_failed:
+ cs_error(link, last_fn, last_ret);
+failed:
+ sedlbauer_release(link);
+ return -ENODEV;
+
+} /* sedlbauer_config */
+
+/*======================================================================
+
+ After a card is removed, sedlbauer_release() will unregister the
+ device, and release the PCMCIA configuration. If the device is
+ still open, this will be postponed until it is closed.
+
+======================================================================*/
+
+static void sedlbauer_release(struct pcmcia_device *link)
+{
+ local_info_t *local = link->priv;
+ DEBUG(0, "sedlbauer_release(0x%p)\n", link);
+
+ if (local) {
+ if (local->cardnr >= 0) {
+ /* no unregister function with hisax */
+ HiSax_closecard(local->cardnr);
+ }
+ }
+
+ pcmcia_disable_device(link);
+} /* sedlbauer_release */
+
+static int sedlbauer_suspend(struct pcmcia_device *link)
+{
+ local_info_t *dev = link->priv;
+
+ dev->stop = 1;
+
+ return 0;
+}
+
+static int sedlbauer_resume(struct pcmcia_device *link)
+{
+ local_info_t *dev = link->priv;
+
+ dev->stop = 0;
+
+ return 0;
+}
+
+
+static struct pcmcia_device_id sedlbauer_ids[] = {
+ PCMCIA_DEVICE_PROD_ID123("SEDLBAUER", "speed star II", "V 3.1", 0x81fb79f5, 0xf3612e1d, 0x6b95c78a),
+ PCMCIA_DEVICE_PROD_ID123("SEDLBAUER", "ISDN-Adapter", "4D67", 0x81fb79f5, 0xe4e9bc12, 0x397b7e90),
+ PCMCIA_DEVICE_PROD_ID123("SEDLBAUER", "ISDN-Adapter", "4D98", 0x81fb79f5, 0xe4e9bc12, 0x2e5c7fce),
+ PCMCIA_DEVICE_PROD_ID123("SEDLBAUER", "ISDN-Adapter", " (C) 93-94 VK", 0x81fb79f5, 0xe4e9bc12, 0x8db143fe),
+ PCMCIA_DEVICE_PROD_ID123("SEDLBAUER", "ISDN-Adapter", " (c) 93-95 VK", 0x81fb79f5, 0xe4e9bc12, 0xb391ab4c),
+ PCMCIA_DEVICE_PROD_ID12("HST High Soft Tech GmbH", "Saphir II B", 0xd79e0b84, 0x21d083ae),
+/* PCMCIA_DEVICE_PROD_ID1234("SEDLBAUER", 0x81fb79f5), */ /* too generic*/
+ PCMCIA_DEVICE_NULL
+};
+MODULE_DEVICE_TABLE(pcmcia, sedlbauer_ids);
+
+static struct pcmcia_driver sedlbauer_driver = {
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = "sedlbauer_cs",
+ },
+ .probe = sedlbauer_probe,
+ .remove = sedlbauer_detach,
+ .id_table = sedlbauer_ids,
+ .suspend = sedlbauer_suspend,
+ .resume = sedlbauer_resume,
+};
+
+static int __init init_sedlbauer_cs(void)
+{
+ return pcmcia_register_driver(&sedlbauer_driver);
+}
+
+static void __exit exit_sedlbauer_cs(void)
+{
+ pcmcia_unregister_driver(&sedlbauer_driver);
+}
+
+module_init(init_sedlbauer_cs);
+module_exit(exit_sedlbauer_cs);
diff --git a/drivers/isdn/hisax/sportster.c b/drivers/isdn/hisax/sportster.c
new file mode 100644
index 0000000..0a53759
--- /dev/null
+++ b/drivers/isdn/hisax/sportster.c
@@ -0,0 +1,269 @@
+/* $Id: sportster.c,v 1.16.2.4 2004/01/13 23:48:39 keil Exp $
+ *
+ * low level stuff for USR Sportster internal TA
+ *
+ * Author Karsten Keil
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to Christian "naddy" Weisgerber (3Com, US Robotics) for documentation
+ *
+ *
+ */
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+static const char *sportster_revision = "$Revision: 1.16.2.4 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+#define SPORTSTER_ISAC 0xC000
+#define SPORTSTER_HSCXA 0x0000
+#define SPORTSTER_HSCXB 0x4000
+#define SPORTSTER_RES_IRQ 0x8000
+#define SPORTSTER_RESET 0x80
+#define SPORTSTER_INTE 0x40
+
+static inline int
+calc_off(unsigned int base, unsigned int off)
+{
+ return(base + ((off & 0xfc)<<8) + ((off & 3)<<1));
+}
+
+static inline void
+read_fifo(unsigned int adr, u_char * data, int size)
+{
+ insb(adr, data, size);
+}
+
+static void
+write_fifo(unsigned int adr, u_char * data, int size)
+{
+ outsb(adr, data, size);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+ return (bytein(calc_off(cs->hw.spt.isac, offset)));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ byteout(calc_off(cs->hw.spt.isac, offset), value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ read_fifo(cs->hw.spt.isac, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ write_fifo(cs->hw.spt.isac, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+ return (bytein(calc_off(cs->hw.spt.hscx[hscx], offset)));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+ byteout(calc_off(cs->hw.spt.hscx[hscx], offset), value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) bytein(calc_off(cs->hw.spt.hscx[nr], reg))
+#define WRITEHSCX(cs, nr, reg, data) byteout(calc_off(cs->hw.spt.hscx[nr], reg), data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.spt.hscx[nr], ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.spt.hscx[nr], ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+sportster_interrupt(int intno, void *dev_id)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char val;
+ u_long flags;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ val = READHSCX(cs, 1, HSCX_ISTA);
+ Start_HSCX:
+ if (val)
+ hscx_int_main(cs, val);
+ val = ReadISAC(cs, ISAC_ISTA);
+ Start_ISAC:
+ if (val)
+ isac_interrupt(cs, val);
+ val = READHSCX(cs, 1, HSCX_ISTA);
+ if (val) {
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "HSCX IntStat after IntRoutine");
+ goto Start_HSCX;
+ }
+ val = ReadISAC(cs, ISAC_ISTA);
+ if (val) {
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ISAC IntStat after IntRoutine");
+ goto Start_ISAC;
+ }
+ /* get a new irq impulse if there any pending */
+ bytein(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ +1);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static void
+release_io_sportster(struct IsdnCardState *cs)
+{
+ int i, adr;
+
+ byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, 0);
+ for (i=0; i<64; i++) {
+ adr = cs->hw.spt.cfg_reg + i *1024;
+ release_region(adr, 8);
+ }
+}
+
+static void
+reset_sportster(struct IsdnCardState *cs)
+{
+ cs->hw.spt.res_irq |= SPORTSTER_RESET; /* Reset On */
+ byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq);
+ mdelay(10);
+ cs->hw.spt.res_irq &= ~SPORTSTER_RESET; /* Reset Off */
+ byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq);
+ mdelay(10);
+}
+
+static int
+Sportster_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ u_long flags;
+
+ switch (mt) {
+ case CARD_RESET:
+ spin_lock_irqsave(&cs->lock, flags);
+ reset_sportster(cs);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return(0);
+ case CARD_RELEASE:
+ release_io_sportster(cs);
+ return(0);
+ case CARD_INIT:
+ spin_lock_irqsave(&cs->lock, flags);
+ reset_sportster(cs);
+ inithscxisac(cs, 1);
+ cs->hw.spt.res_irq |= SPORTSTER_INTE; /* IRQ On */
+ byteout(cs->hw.spt.cfg_reg + SPORTSTER_RES_IRQ, cs->hw.spt.res_irq);
+ inithscxisac(cs, 2);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return(0);
+ case CARD_TEST:
+ return(0);
+ }
+ return(0);
+}
+
+static int __devinit
+get_io_range(struct IsdnCardState *cs)
+{
+ int i, j, adr;
+
+ for (i=0;i<64;i++) {
+ adr = cs->hw.spt.cfg_reg + i *1024;
+ if (!request_region(adr, 8, "sportster")) {
+ printk(KERN_WARNING "HiSax: USR Sportster config port "
+ "%x-%x already in use\n",
+ adr, adr + 8);
+ break;
+ }
+ }
+ if (i==64)
+ return(1);
+ else {
+ for (j=0; j<i; j++) {
+ adr = cs->hw.spt.cfg_reg + j *1024;
+ release_region(adr, 8);
+ }
+ return(0);
+ }
+}
+
+int __devinit
+setup_sportster(struct IsdnCard *card)
+{
+ struct IsdnCardState *cs = card->cs;
+ char tmp[64];
+
+ strcpy(tmp, sportster_revision);
+ printk(KERN_INFO "HiSax: USR Sportster driver Rev. %s\n", HiSax_getrev(tmp));
+ if (cs->typ != ISDN_CTYPE_SPORTSTER)
+ return (0);
+
+ cs->hw.spt.cfg_reg = card->para[1];
+ cs->irq = card->para[0];
+ if (!get_io_range(cs))
+ return (0);
+ cs->hw.spt.isac = cs->hw.spt.cfg_reg + SPORTSTER_ISAC;
+ cs->hw.spt.hscx[0] = cs->hw.spt.cfg_reg + SPORTSTER_HSCXA;
+ cs->hw.spt.hscx[1] = cs->hw.spt.cfg_reg + SPORTSTER_HSCXB;
+
+ switch(cs->irq) {
+ case 5: cs->hw.spt.res_irq = 1;
+ break;
+ case 7: cs->hw.spt.res_irq = 2;
+ break;
+ case 10:cs->hw.spt.res_irq = 3;
+ break;
+ case 11:cs->hw.spt.res_irq = 4;
+ break;
+ case 12:cs->hw.spt.res_irq = 5;
+ break;
+ case 14:cs->hw.spt.res_irq = 6;
+ break;
+ case 15:cs->hw.spt.res_irq = 7;
+ break;
+ default:release_io_sportster(cs);
+ printk(KERN_WARNING "Sportster: wrong IRQ\n");
+ return(0);
+ }
+ printk(KERN_INFO "HiSax: USR Sportster config irq:%d cfg:0x%X\n",
+ cs->irq, cs->hw.spt.cfg_reg);
+ setup_isac(cs);
+ cs->readisac = &ReadISAC;
+ cs->writeisac = &WriteISAC;
+ cs->readisacfifo = &ReadISACfifo;
+ cs->writeisacfifo = &WriteISACfifo;
+ cs->BC_Read_Reg = &ReadHSCX;
+ cs->BC_Write_Reg = &WriteHSCX;
+ cs->BC_Send_Data = &hscx_fill_fifo;
+ cs->cardmsg = &Sportster_card_msg;
+ cs->irq_func = &sportster_interrupt;
+ ISACVersion(cs, "Sportster:");
+ if (HscxVersion(cs, "Sportster:")) {
+ printk(KERN_WARNING
+ "Sportster: wrong HSCX versions check IO address\n");
+ release_io_sportster(cs);
+ return (0);
+ }
+ return (1);
+}
diff --git a/drivers/isdn/hisax/st5481.h b/drivers/isdn/hisax/st5481.h
new file mode 100644
index 0000000..cff7a63
--- /dev/null
+++ b/drivers/isdn/hisax/st5481.h
@@ -0,0 +1,530 @@
+/*
+ * Driver for ST5481 USB ISDN modem
+ *
+ * Author Frode Isaksen
+ * Copyright 2001 by Frode Isaksen <fisaksen@bewan.com>
+ * 2001 by Kai Germaschewski <kai.germaschewski@gmx.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef _ST5481_H_
+#define _ST5481_H_
+
+
+// USB IDs, the Product Id is in the range 0x4810-0x481F
+
+#define ST_VENDOR_ID 0x0483
+#define ST5481_PRODUCT_ID 0x4810
+#define ST5481_PRODUCT_ID_MASK 0xFFF0
+
+// ST5481 endpoints when using alternative setting 3 (2B+D).
+// To get the endpoint address, OR with 0x80 for IN endpoints.
+
+#define EP_CTRL 0x00U /* Control endpoint */
+#define EP_INT 0x01U /* Interrupt endpoint */
+#define EP_B1_OUT 0x02U /* B1 channel out */
+#define EP_B1_IN 0x03U /* B1 channel in */
+#define EP_B2_OUT 0x04U /* B2 channel out */
+#define EP_B2_IN 0x05U /* B2 channel in */
+#define EP_D_OUT 0x06U /* D channel out */
+#define EP_D_IN 0x07U /* D channel in */
+
+// Number of isochronous packets. With 20 packets we get
+// 50 interrupts/sec for each endpoint.
+
+#define NUM_ISO_PACKETS_D 20
+#define NUM_ISO_PACKETS_B 20
+
+// Size of each isochronous packet.
+// In outgoing direction we need to match ISDN data rates:
+// D: 2 bytes / msec -> 16 kbit / s
+// B: 16 bytes / msec -> 64 kbit / s
+#define SIZE_ISO_PACKETS_D_IN 16
+#define SIZE_ISO_PACKETS_D_OUT 2
+#define SIZE_ISO_PACKETS_B_IN 32
+#define SIZE_ISO_PACKETS_B_OUT 8
+
+// If we overrun/underrun, we send one packet with +/- 2 bytes
+#define B_FLOW_ADJUST 2
+
+// Registers that are written using vendor specific device request
+// on endpoint 0.
+
+#define LBA 0x02 /* S loopback */
+#define SET_DEFAULT 0x06 /* Soft reset */
+#define LBB 0x1D /* S maintenance loopback */
+#define STT 0x1e /* S force transmission signals */
+#define SDA_MIN 0x20 /* SDA-sin minimal value */
+#define SDA_MAX 0x21 /* SDA-sin maximal value */
+#define SDELAY_VALUE 0x22 /* Delay between Tx and Rx clock */
+#define IN_D_COUNTER 0x36 /* D receive channel fifo counter */
+#define OUT_D_COUNTER 0x37 /* D transmit channel fifo counter */
+#define IN_B1_COUNTER 0x38 /* B1 receive channel fifo counter */
+#define OUT_B1_COUNTER 0x39 /* B1 transmit channel fifo counter */
+#define IN_B2_COUNTER 0x3a /* B2 receive channel fifo counter */
+#define OUT_B2_COUNTER 0x3b /* B2 transmit channel fifo counter */
+#define FFCTRL_IN_D 0x3C /* D receive channel fifo threshold low */
+#define FFCTRH_IN_D 0x3D /* D receive channel fifo threshold high */
+#define FFCTRL_OUT_D 0x3E /* D transmit channel fifo threshold low */
+#define FFCTRH_OUT_D 0x3F /* D transmit channel fifo threshold high */
+#define FFCTRL_IN_B1 0x40 /* B1 receive channel fifo threshold low */
+#define FFCTRH_IN_B1 0x41 /* B1 receive channel fifo threshold high */
+#define FFCTRL_OUT_B1 0x42 /* B1 transmit channel fifo threshold low */
+#define FFCTRH_OUT_B1 0x43 /* B1 transmit channel fifo threshold high */
+#define FFCTRL_IN_B2 0x44 /* B2 receive channel fifo threshold low */
+#define FFCTRH_IN_B2 0x45 /* B2 receive channel fifo threshold high */
+#define FFCTRL_OUT_B2 0x46 /* B2 transmit channel fifo threshold low */
+#define FFCTRH_OUT_B2 0x47 /* B2 transmit channel fifo threshold high */
+#define MPMSK 0x4A /* Multi purpose interrupt MASK register */
+#define FFMSK_D 0x4c /* D fifo interrupt MASK register */
+#define FFMSK_B1 0x4e /* B1 fifo interrupt MASK register */
+#define FFMSK_B2 0x50 /* B2 fifo interrupt MASK register */
+#define GPIO_DIR 0x52 /* GPIO pins direction registers */
+#define GPIO_OUT 0x53 /* GPIO pins output register */
+#define GPIO_IN 0x54 /* GPIO pins input register */
+#define TXCI 0x56 /* CI command to be transmitted */
+
+
+// Format of the interrupt packet received on endpoint 1:
+//
+// +--------+--------+--------+--------+--------+--------+
+// !MPINT !FFINT_D !FFINT_B1!FFINT_B2!CCIST !GPIO_INT!
+// +--------+--------+--------+--------+--------+--------+
+
+// Offsets in the interrupt packet
+
+#define MPINT 0
+#define FFINT_D 1
+#define FFINT_B1 2
+#define FFINT_B2 3
+#define CCIST 4
+#define GPIO_INT 5
+#define INT_PKT_SIZE 6
+
+// MPINT
+#define LSD_INT 0x80 /* S line activity detected */
+#define RXCI_INT 0x40 /* Indicate primitive arrived */
+#define DEN_INT 0x20 /* Signal enabling data out of D Tx fifo */
+#define DCOLL_INT 0x10 /* D channel collision */
+#define AMIVN_INT 0x04 /* AMI violation number reached 2 */
+#define INFOI_INT 0x04 /* INFOi changed */
+#define DRXON_INT 0x02 /* Reception channel active */
+#define GPCHG_INT 0x01 /* GPIO pin value changed */
+
+// FFINT_x
+#define IN_OVERRUN 0x80 /* In fifo overrun */
+#define OUT_UNDERRUN 0x40 /* Out fifo underrun */
+#define IN_UP 0x20 /* In fifo thresholdh up-crossed */
+#define IN_DOWN 0x10 /* In fifo thresholdl down-crossed */
+#define OUT_UP 0x08 /* Out fifo thresholdh up-crossed */
+#define OUT_DOWN 0x04 /* Out fifo thresholdl down-crossed */
+#define IN_COUNTER_ZEROED 0x02 /* In down-counter reached 0 */
+#define OUT_COUNTER_ZEROED 0x01 /* Out down-counter reached 0 */
+
+#define ANY_REC_INT (IN_OVERRUN+IN_UP+IN_DOWN+IN_COUNTER_ZEROED)
+#define ANY_XMIT_INT (OUT_UNDERRUN+OUT_UP+OUT_DOWN+OUT_COUNTER_ZEROED)
+
+
+// Level 1 commands that are sent using the TXCI device request
+#define ST5481_CMD_DR 0x0 /* Deactivation Request */
+#define ST5481_CMD_RES 0x1 /* state machine RESet */
+#define ST5481_CMD_TM1 0x2 /* Test Mode 1 */
+#define ST5481_CMD_TM2 0x3 /* Test Mode 2 */
+#define ST5481_CMD_PUP 0x7 /* Power UP */
+#define ST5481_CMD_AR8 0x8 /* Activation Request class 1 */
+#define ST5481_CMD_AR10 0x9 /* Activation Request class 2 */
+#define ST5481_CMD_ARL 0xA /* Activation Request Loopback */
+#define ST5481_CMD_PDN 0xF /* Power DoWn */
+
+// Turn on/off the LEDs using the GPIO device request.
+// To use the B LEDs, number_of_leds must be set to 4
+#define B1_LED 0x10U
+#define B2_LED 0x20U
+#define GREEN_LED 0x40U
+#define RED_LED 0x80U
+
+// D channel out states
+enum {
+ ST_DOUT_NONE,
+
+ ST_DOUT_SHORT_INIT,
+ ST_DOUT_SHORT_WAIT_DEN,
+
+ ST_DOUT_LONG_INIT,
+ ST_DOUT_LONG_WAIT_DEN,
+ ST_DOUT_NORMAL,
+
+ ST_DOUT_WAIT_FOR_UNDERRUN,
+ ST_DOUT_WAIT_FOR_NOT_BUSY,
+ ST_DOUT_WAIT_FOR_STOP,
+ ST_DOUT_WAIT_FOR_RESET,
+};
+
+#define DOUT_STATE_COUNT (ST_DOUT_WAIT_FOR_RESET + 1)
+
+// D channel out events
+enum {
+ EV_DOUT_START_XMIT,
+ EV_DOUT_COMPLETE,
+ EV_DOUT_DEN,
+ EV_DOUT_RESETED,
+ EV_DOUT_STOPPED,
+ EV_DOUT_COLL,
+ EV_DOUT_UNDERRUN,
+};
+
+#define DOUT_EVENT_COUNT (EV_DOUT_UNDERRUN + 1)
+
+// ----------------------------------------------------------------------
+
+enum {
+ ST_L1_F3,
+ ST_L1_F4,
+ ST_L1_F6,
+ ST_L1_F7,
+ ST_L1_F8,
+};
+
+#define L1_STATE_COUNT (ST_L1_F8+1)
+
+// The first 16 entries match the Level 1 indications that
+// are found at offset 4 (CCIST) in the interrupt packet
+
+enum {
+ EV_IND_DP, // 0000 Deactivation Pending
+ EV_IND_1, // 0001
+ EV_IND_2, // 0010
+ EV_IND_3, // 0011
+ EV_IND_RSY, // 0100 ReSYnchronizing
+ EV_IND_5, // 0101
+ EV_IND_6, // 0110
+ EV_IND_7, // 0111
+ EV_IND_AP, // 1000 Activation Pending
+ EV_IND_9, // 1001
+ EV_IND_10, // 1010
+ EV_IND_11, // 1011
+ EV_IND_AI8, // 1100 Activation Indication class 8
+ EV_IND_AI10,// 1101 Activation Indication class 10
+ EV_IND_AIL, // 1110 Activation Indication Loopback
+ EV_IND_DI, // 1111 Deactivation Indication
+ EV_PH_ACTIVATE_REQ,
+ EV_PH_DEACTIVATE_REQ,
+ EV_TIMER3,
+};
+
+#define L1_EVENT_COUNT (EV_TIMER3 + 1)
+
+#define ERR(format, arg...) \
+printk(KERN_ERR "%s:%s: " format "\n" , __FILE__, __func__ , ## arg)
+
+#define WARNING(format, arg...) \
+printk(KERN_WARNING "%s:%s: " format "\n" , __FILE__, __func__ , ## arg)
+
+#define INFO(format, arg...) \
+printk(KERN_INFO "%s:%s: " format "\n" , __FILE__, __func__ , ## arg)
+
+#include "isdnhdlc.h"
+#include "fsm.h"
+#include "hisax_if.h"
+#include <linux/skbuff.h>
+
+/* ======================================================================
+ * FIFO handling
+ */
+
+/* Generic FIFO structure */
+struct fifo {
+ u_char r,w,count,size;
+ spinlock_t lock;
+};
+
+/*
+ * Init an FIFO
+ */
+static inline void fifo_init(struct fifo *fifo, int size)
+{
+ fifo->r = fifo->w = fifo->count = 0;
+ fifo->size = size;
+ spin_lock_init(&fifo->lock);
+}
+
+/*
+ * Add an entry to the FIFO
+ */
+static inline int fifo_add(struct fifo *fifo)
+{
+ unsigned long flags;
+ int index;
+
+ if (!fifo) {
+ return -1;
+ }
+
+ spin_lock_irqsave(&fifo->lock, flags);
+ if (fifo->count == fifo->size) {
+ // FIFO full
+ index = -1;
+ } else {
+ // Return index where to get the next data to add to the FIFO
+ index = fifo->w++ & (fifo->size-1);
+ fifo->count++;
+ }
+ spin_unlock_irqrestore(&fifo->lock, flags);
+ return index;
+}
+
+/*
+ * Remove an entry from the FIFO with the index returned.
+ */
+static inline int fifo_remove(struct fifo *fifo)
+{
+ unsigned long flags;
+ int index;
+
+ if (!fifo) {
+ return -1;
+ }
+
+ spin_lock_irqsave(&fifo->lock, flags);
+ if (!fifo->count) {
+ // FIFO empty
+ index = -1;
+ } else {
+ // Return index where to get the next data from the FIFO
+ index = fifo->r++ & (fifo->size-1);
+ fifo->count--;
+ }
+ spin_unlock_irqrestore(&fifo->lock, flags);
+
+ return index;
+}
+
+/* ======================================================================
+ * control pipe
+ */
+typedef void (*ctrl_complete_t)(void *);
+
+typedef struct ctrl_msg {
+ struct usb_ctrlrequest dr;
+ ctrl_complete_t complete;
+ void *context;
+} ctrl_msg;
+
+/* FIFO of ctrl messages waiting to be sent */
+#define MAX_EP0_MSG 16
+struct ctrl_msg_fifo {
+ struct fifo f;
+ struct ctrl_msg data[MAX_EP0_MSG];
+};
+
+#define MAX_DFRAME_LEN_L1 300
+#define HSCX_BUFMAX 4096
+
+struct st5481_ctrl {
+ struct ctrl_msg_fifo msg_fifo;
+ unsigned long busy;
+ struct urb *urb;
+};
+
+struct st5481_intr {
+ // struct evt_fifo evt_fifo;
+ struct urb *urb;
+};
+
+struct st5481_d_out {
+ struct isdnhdlc_vars hdlc_state;
+ struct urb *urb[2]; /* double buffering */
+ unsigned long busy;
+ struct sk_buff *tx_skb;
+ struct FsmInst fsm;
+};
+
+struct st5481_b_out {
+ struct isdnhdlc_vars hdlc_state;
+ struct urb *urb[2]; /* double buffering */
+ u_char flow_event;
+ u_long busy;
+ struct sk_buff *tx_skb;
+};
+
+struct st5481_in {
+ struct isdnhdlc_vars hdlc_state;
+ struct urb *urb[2]; /* double buffering */
+ int mode;
+ int bufsize;
+ unsigned int num_packets;
+ unsigned int packet_size;
+ unsigned char ep, counter;
+ unsigned char *rcvbuf;
+ struct st5481_adapter *adapter;
+ struct hisax_if *hisax_if;
+};
+
+int st5481_setup_in(struct st5481_in *in);
+void st5481_release_in(struct st5481_in *in);
+void st5481_in_mode(struct st5481_in *in, int mode);
+
+struct st5481_bcs {
+ struct hisax_b_if b_if;
+ struct st5481_adapter *adapter;
+ struct st5481_in b_in;
+ struct st5481_b_out b_out;
+ int channel;
+ int mode;
+};
+
+struct st5481_adapter {
+ struct list_head list;
+ int number_of_leds;
+ struct usb_device *usb_dev;
+ struct hisax_d_if hisax_d_if;
+
+ struct st5481_ctrl ctrl;
+ struct st5481_intr intr;
+ struct st5481_in d_in;
+ struct st5481_d_out d_out;
+
+ unsigned char leds;
+ unsigned int led_counter;
+
+ unsigned long event;
+
+ struct FsmInst l1m;
+ struct FsmTimer timer;
+
+ struct st5481_bcs bcs[2];
+};
+
+#define TIMER3_VALUE 7000
+
+/* ======================================================================
+ *
+ */
+
+/*
+ * Submit an URB with error reporting. This is a macro so
+ * the __func__ returns the caller function name.
+ */
+#define SUBMIT_URB(urb, mem_flags) \
+({ \
+ int status; \
+ if ((status = usb_submit_urb(urb, mem_flags)) < 0) { \
+ WARNING("usb_submit_urb failed,status=%d", status); \
+ } \
+ status; \
+})
+
+/*
+ * USB double buffering, return the URB index (0 or 1).
+ */
+static inline int get_buf_nr(struct urb *urbs[], struct urb *urb)
+{
+ return (urbs[0]==urb ? 0 : 1);
+}
+
+/* ---------------------------------------------------------------------- */
+
+/* B Channel */
+
+int st5481_setup_b(struct st5481_bcs *bcs);
+void st5481_release_b(struct st5481_bcs *bcs);
+void st5481_d_l2l1(struct hisax_if *hisax_d_if, int pr, void *arg);
+
+/* D Channel */
+
+int st5481_setup_d(struct st5481_adapter *adapter);
+void st5481_release_d(struct st5481_adapter *adapter);
+void st5481_b_l2l1(struct hisax_if *b_if, int pr, void *arg);
+int st5481_d_init(void);
+void st5481_d_exit(void);
+
+/* USB */
+void st5481_ph_command(struct st5481_adapter *adapter, unsigned int command);
+int st5481_setup_isocpipes(struct urb* urb[2], struct usb_device *dev,
+ unsigned int pipe, int num_packets,
+ int packet_size, int buf_size,
+ usb_complete_t complete, void *context);
+void st5481_release_isocpipes(struct urb* urb[2]);
+
+void st5481_usb_pipe_reset(struct st5481_adapter *adapter,
+ u_char pipe, ctrl_complete_t complete, void *context);
+void st5481_usb_device_ctrl_msg(struct st5481_adapter *adapter,
+ u8 request, u16 value,
+ ctrl_complete_t complete, void *context);
+int st5481_setup_usb(struct st5481_adapter *adapter);
+void st5481_release_usb(struct st5481_adapter *adapter);
+void st5481_start(struct st5481_adapter *adapter);
+void st5481_stop(struct st5481_adapter *adapter);
+
+// ----------------------------------------------------------------------
+// debugging macros
+
+#define __debug_variable st5481_debug
+#include "hisax_debug.h"
+
+extern int st5481_debug;
+
+#ifdef CONFIG_HISAX_DEBUG
+
+#define DBG_ISO_PACKET(level,urb) \
+ if (level & __debug_variable) dump_iso_packet(__func__,urb)
+
+static void __attribute__((unused))
+dump_iso_packet(const char *name, struct urb *urb)
+{
+ int i,j;
+ int len,ofs;
+ u_char *data;
+
+ printk(KERN_DEBUG "%s: packets=%d,errors=%d\n",
+ name,urb->number_of_packets,urb->error_count);
+ for (i = 0; i < urb->number_of_packets; ++i) {
+ if (urb->pipe & USB_DIR_IN) {
+ len = urb->iso_frame_desc[i].actual_length;
+ } else {
+ len = urb->iso_frame_desc[i].length;
+ }
+ ofs = urb->iso_frame_desc[i].offset;
+ printk(KERN_DEBUG "len=%.2d,ofs=%.3d ",len,ofs);
+ if (len) {
+ data = urb->transfer_buffer+ofs;
+ for (j=0; j < len; j++) {
+ printk ("%.2x", data[j]);
+ }
+ }
+ printk("\n");
+ }
+}
+
+static inline const char *ST5481_CMD_string(int evt)
+{
+ static char s[16];
+
+ switch (evt) {
+ case ST5481_CMD_DR: return "DR";
+ case ST5481_CMD_RES: return "RES";
+ case ST5481_CMD_TM1: return "TM1";
+ case ST5481_CMD_TM2: return "TM2";
+ case ST5481_CMD_PUP: return "PUP";
+ case ST5481_CMD_AR8: return "AR8";
+ case ST5481_CMD_AR10: return "AR10";
+ case ST5481_CMD_ARL: return "ARL";
+ case ST5481_CMD_PDN: return "PDN";
+ };
+
+ sprintf(s,"0x%x",evt);
+ return s;
+}
+
+#else
+
+#define DBG_ISO_PACKET(level,urb) do {} while (0)
+
+#endif
+
+
+
+#endif
diff --git a/drivers/isdn/hisax/st5481_b.c b/drivers/isdn/hisax/st5481_b.c
new file mode 100644
index 0000000..0074b60
--- /dev/null
+++ b/drivers/isdn/hisax/st5481_b.c
@@ -0,0 +1,377 @@
+/*
+ * Driver for ST5481 USB ISDN modem
+ *
+ * Author Frode Isaksen
+ * Copyright 2001 by Frode Isaksen <fisaksen@bewan.com>
+ * 2001 by Kai Germaschewski <kai.germaschewski@gmx.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include <linux/bitrev.h>
+#include "st5481.h"
+
+static inline void B_L1L2(struct st5481_bcs *bcs, int pr, void *arg)
+{
+ struct hisax_if *ifc = (struct hisax_if *) &bcs->b_if;
+
+ ifc->l1l2(ifc, pr, arg);
+}
+
+/*
+ * Encode and transmit next frame.
+ */
+static void usb_b_out(struct st5481_bcs *bcs,int buf_nr)
+{
+ struct st5481_b_out *b_out = &bcs->b_out;
+ struct st5481_adapter *adapter = bcs->adapter;
+ struct urb *urb;
+ unsigned int packet_size,offset;
+ int len,buf_size,bytes_sent;
+ int i;
+ struct sk_buff *skb;
+
+ if (test_and_set_bit(buf_nr, &b_out->busy)) {
+ DBG(4,"ep %d urb %d busy",(bcs->channel+1)*2,buf_nr);
+ return;
+ }
+ urb = b_out->urb[buf_nr];
+
+ // Adjust isoc buffer size according to flow state
+ if(b_out->flow_event & (OUT_DOWN | OUT_UNDERRUN)) {
+ buf_size = NUM_ISO_PACKETS_B*SIZE_ISO_PACKETS_B_OUT + B_FLOW_ADJUST;
+ packet_size = SIZE_ISO_PACKETS_B_OUT + B_FLOW_ADJUST;
+ DBG(4,"B%d,adjust flow,add %d bytes",bcs->channel+1,B_FLOW_ADJUST);
+ } else if(b_out->flow_event & OUT_UP){
+ buf_size = NUM_ISO_PACKETS_B*SIZE_ISO_PACKETS_B_OUT - B_FLOW_ADJUST;
+ packet_size = SIZE_ISO_PACKETS_B_OUT - B_FLOW_ADJUST;
+ DBG(4,"B%d,adjust flow,remove %d bytes",bcs->channel+1,B_FLOW_ADJUST);
+ } else {
+ buf_size = NUM_ISO_PACKETS_B*SIZE_ISO_PACKETS_B_OUT;
+ packet_size = 8;
+ }
+ b_out->flow_event = 0;
+
+ len = 0;
+ while (len < buf_size) {
+ if ((skb = b_out->tx_skb)) {
+ DBG_SKB(0x100, skb);
+ DBG(4,"B%d,len=%d",bcs->channel+1,skb->len);
+
+ if (bcs->mode == L1_MODE_TRANS) {
+ bytes_sent = buf_size - len;
+ if (skb->len < bytes_sent)
+ bytes_sent = skb->len;
+ { /* swap tx bytes to get hearable audio data */
+ register unsigned char *src = skb->data;
+ register unsigned char *dest = urb->transfer_buffer+len;
+ register unsigned int count;
+ for (count = 0; count < bytes_sent; count++)
+ *dest++ = bitrev8(*src++);
+ }
+ len += bytes_sent;
+ } else {
+ len += isdnhdlc_encode(&b_out->hdlc_state,
+ skb->data, skb->len, &bytes_sent,
+ urb->transfer_buffer+len, buf_size-len);
+ }
+
+ skb_pull(skb, bytes_sent);
+
+ if (!skb->len) {
+ // Frame sent
+ b_out->tx_skb = NULL;
+ B_L1L2(bcs, PH_DATA | CONFIRM, (void *)(unsigned long) skb->truesize);
+ dev_kfree_skb_any(skb);
+
+/* if (!(bcs->tx_skb = skb_dequeue(&bcs->sq))) { */
+/* st5481B_sched_event(bcs, B_XMTBUFREADY); */
+/* } */
+ }
+ } else {
+ if (bcs->mode == L1_MODE_TRANS) {
+ memset(urb->transfer_buffer+len, 0xff, buf_size-len);
+ len = buf_size;
+ } else {
+ // Send flags
+ len += isdnhdlc_encode(&b_out->hdlc_state,
+ NULL, 0, &bytes_sent,
+ urb->transfer_buffer+len, buf_size-len);
+ }
+ }
+ }
+
+ // Prepare the URB
+ for (i = 0, offset = 0; offset < len; i++) {
+ urb->iso_frame_desc[i].offset = offset;
+ urb->iso_frame_desc[i].length = packet_size;
+ offset += packet_size;
+ packet_size = SIZE_ISO_PACKETS_B_OUT;
+ }
+ urb->transfer_buffer_length = len;
+ urb->number_of_packets = i;
+ urb->dev = adapter->usb_dev;
+
+ DBG_ISO_PACKET(0x200,urb);
+
+ SUBMIT_URB(urb, GFP_NOIO);
+}
+
+/*
+ * Start transfering (flags or data) on the B channel, since
+ * FIFO counters has been set to a non-zero value.
+ */
+static void st5481B_start_xfer(void *context)
+{
+ struct st5481_bcs *bcs = context;
+
+ DBG(4,"B%d",bcs->channel+1);
+
+ // Start transmitting (flags or data) on B channel
+
+ usb_b_out(bcs,0);
+ usb_b_out(bcs,1);
+}
+
+/*
+ * If the adapter has only 2 LEDs, the green
+ * LED will blink with a rate depending
+ * on the number of channels opened.
+ */
+static void led_blink(struct st5481_adapter *adapter)
+{
+ u_char leds = adapter->leds;
+
+ // 50 frames/sec for each channel
+ if (++adapter->led_counter % 50) {
+ return;
+ }
+
+ if (adapter->led_counter % 100) {
+ leds |= GREEN_LED;
+ } else {
+ leds &= ~GREEN_LED;
+ }
+
+ st5481_usb_device_ctrl_msg(adapter, GPIO_OUT, leds, NULL, NULL);
+}
+
+static void usb_b_out_complete(struct urb *urb)
+{
+ struct st5481_bcs *bcs = urb->context;
+ struct st5481_b_out *b_out = &bcs->b_out;
+ struct st5481_adapter *adapter = bcs->adapter;
+ int buf_nr;
+
+ buf_nr = get_buf_nr(b_out->urb, urb);
+ test_and_clear_bit(buf_nr, &b_out->busy);
+
+ if (unlikely(urb->status < 0)) {
+ switch (urb->status) {
+ case -ENOENT:
+ case -ESHUTDOWN:
+ case -ECONNRESET:
+ DBG(4,"urb killed status %d", urb->status);
+ return; // Give up
+ default:
+ WARNING("urb status %d",urb->status);
+ if (b_out->busy == 0) {
+ st5481_usb_pipe_reset(adapter, (bcs->channel+1)*2 | USB_DIR_OUT, NULL, NULL);
+ }
+ break;
+ }
+ }
+
+ usb_b_out(bcs,buf_nr);
+
+ if (adapter->number_of_leds == 2)
+ led_blink(adapter);
+}
+
+/*
+ * Start or stop the transfer on the B channel.
+ */
+static void st5481B_mode(struct st5481_bcs *bcs, int mode)
+{
+ struct st5481_b_out *b_out = &bcs->b_out;
+ struct st5481_adapter *adapter = bcs->adapter;
+
+ DBG(4,"B%d,mode=%d", bcs->channel + 1, mode);
+
+ if (bcs->mode == mode)
+ return;
+
+ bcs->mode = mode;
+
+ // Cancel all USB transfers on this B channel
+ usb_unlink_urb(b_out->urb[0]);
+ usb_unlink_urb(b_out->urb[1]);
+ b_out->busy = 0;
+
+ st5481_in_mode(&bcs->b_in, mode);
+ if (bcs->mode != L1_MODE_NULL) {
+ // Open the B channel
+ if (bcs->mode != L1_MODE_TRANS) {
+ isdnhdlc_out_init(&b_out->hdlc_state, 0, bcs->mode == L1_MODE_HDLC_56K);
+ }
+ st5481_usb_pipe_reset(adapter, (bcs->channel+1)*2, NULL, NULL);
+
+ // Enable B channel interrupts
+ st5481_usb_device_ctrl_msg(adapter, FFMSK_B1+(bcs->channel*2),
+ OUT_UP+OUT_DOWN+OUT_UNDERRUN, NULL, NULL);
+
+ // Enable B channel FIFOs
+ st5481_usb_device_ctrl_msg(adapter, OUT_B1_COUNTER+(bcs->channel*2), 32, st5481B_start_xfer, bcs);
+ if (adapter->number_of_leds == 4) {
+ if (bcs->channel == 0) {
+ adapter->leds |= B1_LED;
+ } else {
+ adapter->leds |= B2_LED;
+ }
+ }
+ } else {
+ // Disble B channel interrupts
+ st5481_usb_device_ctrl_msg(adapter, FFMSK_B1+(bcs->channel*2), 0, NULL, NULL);
+
+ // Disable B channel FIFOs
+ st5481_usb_device_ctrl_msg(adapter, OUT_B1_COUNTER+(bcs->channel*2), 0, NULL, NULL);
+
+ if (adapter->number_of_leds == 4) {
+ if (bcs->channel == 0) {
+ adapter->leds &= ~B1_LED;
+ } else {
+ adapter->leds &= ~B2_LED;
+ }
+ } else {
+ st5481_usb_device_ctrl_msg(adapter, GPIO_OUT, adapter->leds, NULL, NULL);
+ }
+ if (b_out->tx_skb) {
+ dev_kfree_skb_any(b_out->tx_skb);
+ b_out->tx_skb = NULL;
+ }
+
+ }
+}
+
+static int st5481_setup_b_out(struct st5481_bcs *bcs)
+{
+ struct usb_device *dev = bcs->adapter->usb_dev;
+ struct usb_interface *intf;
+ struct usb_host_interface *altsetting = NULL;
+ struct usb_host_endpoint *endpoint;
+ struct st5481_b_out *b_out = &bcs->b_out;
+
+ DBG(4,"");
+
+ intf = usb_ifnum_to_if(dev, 0);
+ if (intf)
+ altsetting = usb_altnum_to_altsetting(intf, 3);
+ if (!altsetting)
+ return -ENXIO;
+
+ // Allocate URBs and buffers for the B channel out
+ endpoint = &altsetting->endpoint[EP_B1_OUT - 1 + bcs->channel * 2];
+
+ DBG(4,"endpoint address=%02x,packet size=%d",
+ endpoint->desc.bEndpointAddress, le16_to_cpu(endpoint->desc.wMaxPacketSize));
+
+ // Allocate memory for 8000bytes/sec + extra bytes if underrun
+ return st5481_setup_isocpipes(b_out->urb, dev,
+ usb_sndisocpipe(dev, endpoint->desc.bEndpointAddress),
+ NUM_ISO_PACKETS_B, SIZE_ISO_PACKETS_B_OUT,
+ NUM_ISO_PACKETS_B * SIZE_ISO_PACKETS_B_OUT + B_FLOW_ADJUST,
+ usb_b_out_complete, bcs);
+}
+
+static void st5481_release_b_out(struct st5481_bcs *bcs)
+{
+ struct st5481_b_out *b_out = &bcs->b_out;
+
+ DBG(4,"");
+
+ st5481_release_isocpipes(b_out->urb);
+}
+
+int st5481_setup_b(struct st5481_bcs *bcs)
+{
+ int retval;
+
+ DBG(4,"");
+
+ retval = st5481_setup_b_out(bcs);
+ if (retval)
+ goto err;
+ bcs->b_in.bufsize = HSCX_BUFMAX;
+ bcs->b_in.num_packets = NUM_ISO_PACKETS_B;
+ bcs->b_in.packet_size = SIZE_ISO_PACKETS_B_IN;
+ bcs->b_in.ep = (bcs->channel ? EP_B2_IN : EP_B1_IN) | USB_DIR_IN;
+ bcs->b_in.counter = bcs->channel ? IN_B2_COUNTER : IN_B1_COUNTER;
+ bcs->b_in.adapter = bcs->adapter;
+ bcs->b_in.hisax_if = &bcs->b_if.ifc;
+ retval = st5481_setup_in(&bcs->b_in);
+ if (retval)
+ goto err_b_out;
+
+
+ return 0;
+
+ err_b_out:
+ st5481_release_b_out(bcs);
+ err:
+ return retval;
+}
+
+/*
+ * Release buffers and URBs for the B channels
+ */
+void st5481_release_b(struct st5481_bcs *bcs)
+{
+ DBG(4,"");
+
+ st5481_release_in(&bcs->b_in);
+ st5481_release_b_out(bcs);
+}
+
+/*
+ * st5481_b_l2l1 is the entry point for upper layer routines that want to
+ * transmit on the B channel. PH_DATA | REQUEST is a normal packet that
+ * we either start transmitting (if idle) or queue (if busy).
+ * PH_PULL | REQUEST can be called to request a callback message
+ * (PH_PULL | CONFIRM)
+ * once the link is idle. After a "pull" callback, the upper layer
+ * routines can use PH_PULL | INDICATION to send data.
+ */
+void st5481_b_l2l1(struct hisax_if *ifc, int pr, void *arg)
+{
+ struct st5481_bcs *bcs = ifc->priv;
+ struct sk_buff *skb = arg;
+ long mode;
+
+ DBG(4, "");
+
+ switch (pr) {
+ case PH_DATA | REQUEST:
+ BUG_ON(bcs->b_out.tx_skb);
+ bcs->b_out.tx_skb = skb;
+ break;
+ case PH_ACTIVATE | REQUEST:
+ mode = (long) arg;
+ DBG(4,"B%d,PH_ACTIVATE_REQUEST %ld", bcs->channel + 1, mode);
+ st5481B_mode(bcs, mode);
+ B_L1L2(bcs, PH_ACTIVATE | INDICATION, NULL);
+ break;
+ case PH_DEACTIVATE | REQUEST:
+ DBG(4,"B%d,PH_DEACTIVATE_REQUEST", bcs->channel + 1);
+ st5481B_mode(bcs, L1_MODE_NULL);
+ B_L1L2(bcs, PH_DEACTIVATE | INDICATION, NULL);
+ break;
+ default:
+ WARNING("pr %#x\n", pr);
+ }
+}
diff --git a/drivers/isdn/hisax/st5481_d.c b/drivers/isdn/hisax/st5481_d.c
new file mode 100644
index 0000000..077991c
--- /dev/null
+++ b/drivers/isdn/hisax/st5481_d.c
@@ -0,0 +1,778 @@
+/*
+ * Driver for ST5481 USB ISDN modem
+ *
+ * Author Frode Isaksen
+ * Copyright 2001 by Frode Isaksen <fisaksen@bewan.com>
+ * 2001 by Kai Germaschewski <kai.germaschewski@gmx.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include "st5481.h"
+
+static void ph_connect(struct st5481_adapter *adapter);
+static void ph_disconnect(struct st5481_adapter *adapter);
+
+static struct Fsm l1fsm;
+
+static char *strL1State[] =
+{
+ "ST_L1_F3",
+ "ST_L1_F4",
+ "ST_L1_F6",
+ "ST_L1_F7",
+ "ST_L1_F8",
+};
+
+static char *strL1Event[] =
+{
+ "EV_IND_DP",
+ "EV_IND_1",
+ "EV_IND_2",
+ "EV_IND_3",
+ "EV_IND_RSY",
+ "EV_IND_5",
+ "EV_IND_6",
+ "EV_IND_7",
+ "EV_IND_AP",
+ "EV_IND_9",
+ "EV_IND_10",
+ "EV_IND_11",
+ "EV_IND_AI8",
+ "EV_IND_AI10",
+ "EV_IND_AIL",
+ "EV_IND_DI",
+ "EV_PH_ACTIVATE_REQ",
+ "EV_PH_DEACTIVATE_REQ",
+ "EV_TIMER3",
+};
+
+static inline void D_L1L2(struct st5481_adapter *adapter, int pr, void *arg)
+{
+ struct hisax_if *ifc = (struct hisax_if *) &adapter->hisax_d_if;
+
+ ifc->l1l2(ifc, pr, arg);
+}
+
+static void
+l1_go_f3(struct FsmInst *fi, int event, void *arg)
+{
+ struct st5481_adapter *adapter = fi->userdata;
+
+ if (fi->state == ST_L1_F7)
+ ph_disconnect(adapter);
+
+ FsmChangeState(fi, ST_L1_F3);
+ D_L1L2(adapter, PH_DEACTIVATE | INDICATION, NULL);
+}
+
+static void
+l1_go_f6(struct FsmInst *fi, int event, void *arg)
+{
+ struct st5481_adapter *adapter = fi->userdata;
+
+ if (fi->state == ST_L1_F7)
+ ph_disconnect(adapter);
+
+ FsmChangeState(fi, ST_L1_F6);
+}
+
+static void
+l1_go_f7(struct FsmInst *fi, int event, void *arg)
+{
+ struct st5481_adapter *adapter = fi->userdata;
+
+ FsmDelTimer(&adapter->timer, 0);
+ ph_connect(adapter);
+ FsmChangeState(fi, ST_L1_F7);
+ D_L1L2(adapter, PH_ACTIVATE | INDICATION, NULL);
+}
+
+static void
+l1_go_f8(struct FsmInst *fi, int event, void *arg)
+{
+ struct st5481_adapter *adapter = fi->userdata;
+
+ if (fi->state == ST_L1_F7)
+ ph_disconnect(adapter);
+
+ FsmChangeState(fi, ST_L1_F8);
+}
+
+static void
+l1_timer3(struct FsmInst *fi, int event, void *arg)
+{
+ struct st5481_adapter *adapter = fi->userdata;
+
+ st5481_ph_command(adapter, ST5481_CMD_DR);
+ FsmChangeState(fi, ST_L1_F3);
+ D_L1L2(adapter, PH_DEACTIVATE | INDICATION, NULL);
+}
+
+static void
+l1_ignore(struct FsmInst *fi, int event, void *arg)
+{
+}
+
+static void
+l1_activate(struct FsmInst *fi, int event, void *arg)
+{
+ struct st5481_adapter *adapter = fi->userdata;
+
+ st5481_ph_command(adapter, ST5481_CMD_DR);
+ st5481_ph_command(adapter, ST5481_CMD_PUP);
+ FsmRestartTimer(&adapter->timer, TIMER3_VALUE, EV_TIMER3, NULL, 2);
+ st5481_ph_command(adapter, ST5481_CMD_AR8);
+ FsmChangeState(fi, ST_L1_F4);
+}
+
+static struct FsmNode L1FnList[] __initdata =
+{
+ {ST_L1_F3, EV_IND_DP, l1_ignore},
+ {ST_L1_F3, EV_IND_AP, l1_go_f6},
+ {ST_L1_F3, EV_IND_AI8, l1_go_f7},
+ {ST_L1_F3, EV_IND_AI10, l1_go_f7},
+ {ST_L1_F3, EV_PH_ACTIVATE_REQ, l1_activate},
+
+ {ST_L1_F4, EV_TIMER3, l1_timer3},
+ {ST_L1_F4, EV_IND_DP, l1_go_f3},
+ {ST_L1_F4, EV_IND_AP, l1_go_f6},
+ {ST_L1_F4, EV_IND_AI8, l1_go_f7},
+ {ST_L1_F4, EV_IND_AI10, l1_go_f7},
+
+ {ST_L1_F6, EV_TIMER3, l1_timer3},
+ {ST_L1_F6, EV_IND_DP, l1_go_f3},
+ {ST_L1_F6, EV_IND_AP, l1_ignore},
+ {ST_L1_F6, EV_IND_AI8, l1_go_f7},
+ {ST_L1_F6, EV_IND_AI10, l1_go_f7},
+ {ST_L1_F7, EV_IND_RSY, l1_go_f8},
+
+ {ST_L1_F7, EV_IND_DP, l1_go_f3},
+ {ST_L1_F7, EV_IND_AP, l1_go_f6},
+ {ST_L1_F7, EV_IND_AI8, l1_ignore},
+ {ST_L1_F7, EV_IND_AI10, l1_ignore},
+ {ST_L1_F7, EV_IND_RSY, l1_go_f8},
+
+ {ST_L1_F8, EV_TIMER3, l1_timer3},
+ {ST_L1_F8, EV_IND_DP, l1_go_f3},
+ {ST_L1_F8, EV_IND_AP, l1_go_f6},
+ {ST_L1_F8, EV_IND_AI8, l1_go_f8},
+ {ST_L1_F8, EV_IND_AI10, l1_go_f8},
+ {ST_L1_F8, EV_IND_RSY, l1_ignore},
+};
+
+static void l1m_debug(struct FsmInst *fi, char *fmt, ...)
+{
+ va_list args;
+ char buf[256];
+
+ va_start(args, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, args);
+ DBG(8, "%s", buf);
+ va_end(args);
+}
+
+/* ======================================================================
+ * D-Channel out
+ */
+
+/*
+ D OUT state machine:
+ ====================
+
+ Transmit short frame (< 16 bytes of encoded data):
+
+ L1 FRAME D_OUT_STATE USB D CHANNEL
+ -------- ----------- --- ---------
+
+ FIXME
+
+ -> [xx..xx] SHORT_INIT -> [7Exx..xxC1C27EFF]
+ SHORT_WAIT_DEN <> OUT_D_COUNTER=16
+
+ END_OF_SHORT <- DEN_EVENT -> 7Exx
+ xxxx
+ xxxx
+ xxxx
+ xxxx
+ xxxx
+ C1C1
+ 7EFF
+ WAIT_FOR_RESET_IDLE <- D_UNDERRUN <- (8ms)
+ IDLE <> Reset pipe
+
+
+
+ Transmit long frame (>= 16 bytes of encoded data):
+
+ L1 FRAME D_OUT_STATE USB D CHANNEL
+ -------- ----------- --- ---------
+
+ -> [xx...xx] IDLE
+ WAIT_FOR_STOP <> OUT_D_COUNTER=0
+ WAIT_FOR_RESET <> Reset pipe
+ STOP
+ INIT_LONG_FRAME -> [7Exx..xx]
+ WAIT_DEN <> OUT_D_COUNTER=16
+ OUT_NORMAL <- DEN_EVENT -> 7Exx
+ END_OF_FRAME_BUSY -> [xxxx] xxxx
+ END_OF_FRAME_NOT_BUSY -> [xxxx] xxxx
+ -> [xxxx] xxxx
+ -> [C1C2] xxxx
+ -> [7EFF] xxxx
+ xxxx
+ xxxx
+ ....
+ xxxx
+ C1C2
+ 7EFF
+ <- D_UNDERRUN <- (> 8ms)
+ WAIT_FOR_STOP <> OUT_D_COUNTER=0
+ WAIT_FOR_RESET <> Reset pipe
+ STOP
+
+*/
+
+static struct Fsm dout_fsm;
+
+static char *strDoutState[] =
+{
+ "ST_DOUT_NONE",
+
+ "ST_DOUT_SHORT_INIT",
+ "ST_DOUT_SHORT_WAIT_DEN",
+
+ "ST_DOUT_LONG_INIT",
+ "ST_DOUT_LONG_WAIT_DEN",
+ "ST_DOUT_NORMAL",
+
+ "ST_DOUT_WAIT_FOR_UNDERRUN",
+ "ST_DOUT_WAIT_FOR_NOT_BUSY",
+ "ST_DOUT_WAIT_FOR_STOP",
+ "ST_DOUT_WAIT_FOR_RESET",
+};
+
+static char *strDoutEvent[] =
+{
+ "EV_DOUT_START_XMIT",
+ "EV_DOUT_COMPLETE",
+ "EV_DOUT_DEN",
+ "EV_DOUT_RESETED",
+ "EV_DOUT_STOPPED",
+ "EV_DOUT_COLL",
+ "EV_DOUT_UNDERRUN",
+};
+
+static void dout_debug(struct FsmInst *fi, char *fmt, ...)
+{
+ va_list args;
+ char buf[256];
+
+ va_start(args, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, args);
+ DBG(0x2, "%s", buf);
+ va_end(args);
+}
+
+static void dout_stop_event(void *context)
+{
+ struct st5481_adapter *adapter = context;
+
+ FsmEvent(&adapter->d_out.fsm, EV_DOUT_STOPPED, NULL);
+}
+
+/*
+ * Start the transfer of a D channel frame.
+ */
+static void usb_d_out(struct st5481_adapter *adapter, int buf_nr)
+{
+ struct st5481_d_out *d_out = &adapter->d_out;
+ struct urb *urb;
+ unsigned int num_packets, packet_offset;
+ int len, buf_size, bytes_sent;
+ struct sk_buff *skb;
+ struct usb_iso_packet_descriptor *desc;
+
+ if (d_out->fsm.state != ST_DOUT_NORMAL)
+ return;
+
+ if (test_and_set_bit(buf_nr, &d_out->busy)) {
+ DBG(2, "ep %d urb %d busy %#lx", EP_D_OUT, buf_nr, d_out->busy);
+ return;
+ }
+ urb = d_out->urb[buf_nr];
+
+ skb = d_out->tx_skb;
+
+ buf_size = NUM_ISO_PACKETS_D * SIZE_ISO_PACKETS_D_OUT;
+
+ if (skb) {
+ len = isdnhdlc_encode(&d_out->hdlc_state,
+ skb->data, skb->len, &bytes_sent,
+ urb->transfer_buffer, buf_size);
+ skb_pull(skb,bytes_sent);
+ } else {
+ // Send flags or idle
+ len = isdnhdlc_encode(&d_out->hdlc_state,
+ NULL, 0, &bytes_sent,
+ urb->transfer_buffer, buf_size);
+ }
+
+ if (len < buf_size) {
+ FsmChangeState(&d_out->fsm, ST_DOUT_WAIT_FOR_UNDERRUN);
+ }
+ if (skb && !skb->len) {
+ d_out->tx_skb = NULL;
+ D_L1L2(adapter, PH_DATA | CONFIRM, NULL);
+ dev_kfree_skb_any(skb);
+ }
+
+ // Prepare the URB
+ urb->transfer_buffer_length = len;
+ num_packets = 0;
+ packet_offset = 0;
+ while (packet_offset < len) {
+ desc = &urb->iso_frame_desc[num_packets];
+ desc->offset = packet_offset;
+ desc->length = SIZE_ISO_PACKETS_D_OUT;
+ if (len - packet_offset < desc->length)
+ desc->length = len - packet_offset;
+ num_packets++;
+ packet_offset += desc->length;
+ }
+ urb->number_of_packets = num_packets;
+
+ // Prepare the URB
+ urb->dev = adapter->usb_dev;
+ // Need to transmit the next buffer 2ms after the DEN_EVENT
+ urb->transfer_flags = 0;
+ urb->start_frame = usb_get_current_frame_number(adapter->usb_dev)+2;
+
+ DBG_ISO_PACKET(0x20,urb);
+
+ if (usb_submit_urb(urb, GFP_KERNEL) < 0) {
+ // There is another URB queued up
+ urb->transfer_flags = URB_ISO_ASAP;
+ SUBMIT_URB(urb, GFP_KERNEL);
+ }
+}
+
+static void fifo_reseted(void *context)
+{
+ struct st5481_adapter *adapter = context;
+
+ FsmEvent(&adapter->d_out.fsm, EV_DOUT_RESETED, NULL);
+}
+
+static void usb_d_out_complete(struct urb *urb)
+{
+ struct st5481_adapter *adapter = urb->context;
+ struct st5481_d_out *d_out = &adapter->d_out;
+ long buf_nr;
+
+ DBG(2, "");
+
+ buf_nr = get_buf_nr(d_out->urb, urb);
+ test_and_clear_bit(buf_nr, &d_out->busy);
+
+ if (unlikely(urb->status < 0)) {
+ switch (urb->status) {
+ case -ENOENT:
+ case -ESHUTDOWN:
+ case -ECONNRESET:
+ DBG(1,"urb killed status %d", urb->status);
+ break;
+ default:
+ WARNING("urb status %d",urb->status);
+ if (d_out->busy == 0) {
+ st5481_usb_pipe_reset(adapter, EP_D_OUT | USB_DIR_OUT, fifo_reseted, adapter);
+ }
+ break;
+ }
+ return; // Give up
+ }
+
+ FsmEvent(&adapter->d_out.fsm, EV_DOUT_COMPLETE, (void *) buf_nr);
+}
+
+/* ====================================================================== */
+
+static void dout_start_xmit(struct FsmInst *fsm, int event, void *arg)
+{
+ // FIXME unify?
+ struct st5481_adapter *adapter = fsm->userdata;
+ struct st5481_d_out *d_out = &adapter->d_out;
+ struct urb *urb;
+ int len, bytes_sent;
+ struct sk_buff *skb;
+ int buf_nr = 0;
+
+ skb = d_out->tx_skb;
+
+ DBG(2,"len=%d",skb->len);
+
+ isdnhdlc_out_init(&d_out->hdlc_state, 1, 0);
+
+ if (test_and_set_bit(buf_nr, &d_out->busy)) {
+ WARNING("ep %d urb %d busy %#lx", EP_D_OUT, buf_nr, d_out->busy);
+ return;
+ }
+ urb = d_out->urb[buf_nr];
+
+ DBG_SKB(0x10, skb);
+ len = isdnhdlc_encode(&d_out->hdlc_state,
+ skb->data, skb->len, &bytes_sent,
+ urb->transfer_buffer, 16);
+ skb_pull(skb, bytes_sent);
+
+ if(len < 16)
+ FsmChangeState(&d_out->fsm, ST_DOUT_SHORT_INIT);
+ else
+ FsmChangeState(&d_out->fsm, ST_DOUT_LONG_INIT);
+
+ if (skb->len == 0) {
+ d_out->tx_skb = NULL;
+ D_L1L2(adapter, PH_DATA | CONFIRM, NULL);
+ dev_kfree_skb_any(skb);
+ }
+
+// Prepare the URB
+ urb->transfer_buffer_length = len;
+
+ urb->iso_frame_desc[0].offset = 0;
+ urb->iso_frame_desc[0].length = len;
+ urb->number_of_packets = 1;
+
+ // Prepare the URB
+ urb->dev = adapter->usb_dev;
+ urb->transfer_flags = URB_ISO_ASAP;
+
+ DBG_ISO_PACKET(0x20,urb);
+ SUBMIT_URB(urb, GFP_KERNEL);
+}
+
+static void dout_short_fifo(struct FsmInst *fsm, int event, void *arg)
+{
+ struct st5481_adapter *adapter = fsm->userdata;
+ struct st5481_d_out *d_out = &adapter->d_out;
+
+ FsmChangeState(&d_out->fsm, ST_DOUT_SHORT_WAIT_DEN);
+ st5481_usb_device_ctrl_msg(adapter, OUT_D_COUNTER, 16, NULL, NULL);
+}
+
+static void dout_end_short_frame(struct FsmInst *fsm, int event, void *arg)
+{
+ struct st5481_adapter *adapter = fsm->userdata;
+ struct st5481_d_out *d_out = &adapter->d_out;
+
+ FsmChangeState(&d_out->fsm, ST_DOUT_WAIT_FOR_UNDERRUN);
+}
+
+static void dout_long_enable_fifo(struct FsmInst *fsm, int event, void *arg)
+{
+ struct st5481_adapter *adapter = fsm->userdata;
+ struct st5481_d_out *d_out = &adapter->d_out;
+
+ st5481_usb_device_ctrl_msg(adapter, OUT_D_COUNTER, 16, NULL, NULL);
+ FsmChangeState(&d_out->fsm, ST_DOUT_LONG_WAIT_DEN);
+}
+
+static void dout_long_den(struct FsmInst *fsm, int event, void *arg)
+{
+ struct st5481_adapter *adapter = fsm->userdata;
+ struct st5481_d_out *d_out = &adapter->d_out;
+
+ FsmChangeState(&d_out->fsm, ST_DOUT_NORMAL);
+ usb_d_out(adapter, 0);
+ usb_d_out(adapter, 1);
+}
+
+static void dout_reset(struct FsmInst *fsm, int event, void *arg)
+{
+ struct st5481_adapter *adapter = fsm->userdata;
+ struct st5481_d_out *d_out = &adapter->d_out;
+
+ FsmChangeState(&d_out->fsm, ST_DOUT_WAIT_FOR_RESET);
+ st5481_usb_pipe_reset(adapter, EP_D_OUT | USB_DIR_OUT, fifo_reseted, adapter);
+}
+
+static void dout_stop(struct FsmInst *fsm, int event, void *arg)
+{
+ struct st5481_adapter *adapter = fsm->userdata;
+ struct st5481_d_out *d_out = &adapter->d_out;
+
+ FsmChangeState(&d_out->fsm, ST_DOUT_WAIT_FOR_STOP);
+ st5481_usb_device_ctrl_msg(adapter, OUT_D_COUNTER, 0, dout_stop_event, adapter);
+}
+
+static void dout_underrun(struct FsmInst *fsm, int event, void *arg)
+{
+ struct st5481_adapter *adapter = fsm->userdata;
+ struct st5481_d_out *d_out = &adapter->d_out;
+
+ if (test_bit(0, &d_out->busy) || test_bit(1, &d_out->busy)) {
+ FsmChangeState(&d_out->fsm, ST_DOUT_WAIT_FOR_NOT_BUSY);
+ } else {
+ dout_stop(fsm, event, arg);
+ }
+}
+
+static void dout_check_busy(struct FsmInst *fsm, int event, void *arg)
+{
+ struct st5481_adapter *adapter = fsm->userdata;
+ struct st5481_d_out *d_out = &adapter->d_out;
+
+ if (!test_bit(0, &d_out->busy) && !test_bit(1, &d_out->busy))
+ dout_stop(fsm, event, arg);
+}
+
+static void dout_reseted(struct FsmInst *fsm, int event, void *arg)
+{
+ struct st5481_adapter *adapter = fsm->userdata;
+ struct st5481_d_out *d_out = &adapter->d_out;
+
+ FsmChangeState(&d_out->fsm, ST_DOUT_NONE);
+ // FIXME locking
+ if (d_out->tx_skb)
+ FsmEvent(&d_out->fsm, EV_DOUT_START_XMIT, NULL);
+}
+
+static void dout_complete(struct FsmInst *fsm, int event, void *arg)
+{
+ struct st5481_adapter *adapter = fsm->userdata;
+ long buf_nr = (long) arg;
+
+ usb_d_out(adapter, buf_nr);
+}
+
+static void dout_ignore(struct FsmInst *fsm, int event, void *arg)
+{
+}
+
+static struct FsmNode DoutFnList[] __initdata =
+{
+ {ST_DOUT_NONE, EV_DOUT_START_XMIT, dout_start_xmit},
+
+ {ST_DOUT_SHORT_INIT, EV_DOUT_COMPLETE, dout_short_fifo},
+
+ {ST_DOUT_SHORT_WAIT_DEN, EV_DOUT_DEN, dout_end_short_frame},
+ {ST_DOUT_SHORT_WAIT_DEN, EV_DOUT_UNDERRUN, dout_underrun},
+
+ {ST_DOUT_LONG_INIT, EV_DOUT_COMPLETE, dout_long_enable_fifo},
+
+ {ST_DOUT_LONG_WAIT_DEN, EV_DOUT_DEN, dout_long_den},
+ {ST_DOUT_LONG_WAIT_DEN, EV_DOUT_UNDERRUN, dout_underrun},
+
+ {ST_DOUT_NORMAL, EV_DOUT_UNDERRUN, dout_underrun},
+ {ST_DOUT_NORMAL, EV_DOUT_COMPLETE, dout_complete},
+
+ {ST_DOUT_WAIT_FOR_UNDERRUN, EV_DOUT_UNDERRUN, dout_underrun},
+ {ST_DOUT_WAIT_FOR_UNDERRUN, EV_DOUT_COMPLETE, dout_ignore},
+
+ {ST_DOUT_WAIT_FOR_NOT_BUSY, EV_DOUT_COMPLETE, dout_check_busy},
+
+ {ST_DOUT_WAIT_FOR_STOP, EV_DOUT_STOPPED, dout_reset},
+
+ {ST_DOUT_WAIT_FOR_RESET, EV_DOUT_RESETED, dout_reseted},
+};
+
+void st5481_d_l2l1(struct hisax_if *hisax_d_if, int pr, void *arg)
+{
+ struct st5481_adapter *adapter = hisax_d_if->priv;
+ struct sk_buff *skb = arg;
+
+ switch (pr) {
+ case PH_ACTIVATE | REQUEST:
+ FsmEvent(&adapter->l1m, EV_PH_ACTIVATE_REQ, NULL);
+ break;
+ case PH_DEACTIVATE | REQUEST:
+ FsmEvent(&adapter->l1m, EV_PH_DEACTIVATE_REQ, NULL);
+ break;
+ case PH_DATA | REQUEST:
+ DBG(2, "PH_DATA REQUEST len %d", skb->len);
+ BUG_ON(adapter->d_out.tx_skb);
+ adapter->d_out.tx_skb = skb;
+ FsmEvent(&adapter->d_out.fsm, EV_DOUT_START_XMIT, NULL);
+ break;
+ default:
+ WARNING("pr %#x\n", pr);
+ break;
+ }
+}
+
+/* ======================================================================
+ */
+
+/*
+ * Start receiving on the D channel since entered state F7.
+ */
+static void ph_connect(struct st5481_adapter *adapter)
+{
+ struct st5481_d_out *d_out = &adapter->d_out;
+ struct st5481_in *d_in = &adapter->d_in;
+
+ DBG(8,"");
+
+ FsmChangeState(&d_out->fsm, ST_DOUT_NONE);
+
+ // st5481_usb_device_ctrl_msg(adapter, FFMSK_D, OUT_UNDERRUN, NULL, NULL);
+ st5481_usb_device_ctrl_msg(adapter, FFMSK_D, 0xfc, NULL, NULL);
+ st5481_in_mode(d_in, L1_MODE_HDLC);
+
+#ifdef LOOPBACK
+ // Turn loopback on (data sent on B and D looped back)
+ st5481_usb_device_ctrl_msg(cs, LBB, 0x04, NULL, NULL);
+#endif
+
+ st5481_usb_pipe_reset(adapter, EP_D_OUT | USB_DIR_OUT, NULL, NULL);
+
+ // Turn on the green LED to tell that we are in state F7
+ adapter->leds |= GREEN_LED;
+ st5481_usb_device_ctrl_msg(adapter, GPIO_OUT, adapter->leds, NULL, NULL);
+}
+
+/*
+ * Stop receiving on the D channel since not in state F7.
+ */
+static void ph_disconnect(struct st5481_adapter *adapter)
+{
+ DBG(8,"");
+
+ st5481_in_mode(&adapter->d_in, L1_MODE_NULL);
+
+ // Turn off the green LED to tell that we left state F7
+ adapter->leds &= ~GREEN_LED;
+ st5481_usb_device_ctrl_msg(adapter, GPIO_OUT, adapter->leds, NULL, NULL);
+}
+
+static int st5481_setup_d_out(struct st5481_adapter *adapter)
+{
+ struct usb_device *dev = adapter->usb_dev;
+ struct usb_interface *intf;
+ struct usb_host_interface *altsetting = NULL;
+ struct usb_host_endpoint *endpoint;
+ struct st5481_d_out *d_out = &adapter->d_out;
+
+ DBG(2,"");
+
+ intf = usb_ifnum_to_if(dev, 0);
+ if (intf)
+ altsetting = usb_altnum_to_altsetting(intf, 3);
+ if (!altsetting)
+ return -ENXIO;
+
+ // Allocate URBs and buffers for the D channel out
+ endpoint = &altsetting->endpoint[EP_D_OUT-1];
+
+ DBG(2,"endpoint address=%02x,packet size=%d",
+ endpoint->desc.bEndpointAddress, le16_to_cpu(endpoint->desc.wMaxPacketSize));
+
+ return st5481_setup_isocpipes(d_out->urb, dev,
+ usb_sndisocpipe(dev, endpoint->desc.bEndpointAddress),
+ NUM_ISO_PACKETS_D, SIZE_ISO_PACKETS_D_OUT,
+ NUM_ISO_PACKETS_D * SIZE_ISO_PACKETS_D_OUT,
+ usb_d_out_complete, adapter);
+}
+
+static void st5481_release_d_out(struct st5481_adapter *adapter)
+{
+ struct st5481_d_out *d_out = &adapter->d_out;
+
+ DBG(2,"");
+
+ st5481_release_isocpipes(d_out->urb);
+}
+
+int st5481_setup_d(struct st5481_adapter *adapter)
+{
+ int retval;
+
+ DBG(2,"");
+
+ retval = st5481_setup_d_out(adapter);
+ if (retval)
+ goto err;
+ adapter->d_in.bufsize = MAX_DFRAME_LEN_L1;
+ adapter->d_in.num_packets = NUM_ISO_PACKETS_D;
+ adapter->d_in.packet_size = SIZE_ISO_PACKETS_D_IN;
+ adapter->d_in.ep = EP_D_IN | USB_DIR_IN;
+ adapter->d_in.counter = IN_D_COUNTER;
+ adapter->d_in.adapter = adapter;
+ adapter->d_in.hisax_if = &adapter->hisax_d_if.ifc;
+ retval = st5481_setup_in(&adapter->d_in);
+ if (retval)
+ goto err_d_out;
+
+ adapter->l1m.fsm = &l1fsm;
+ adapter->l1m.state = ST_L1_F3;
+ adapter->l1m.debug = st5481_debug & 0x100;
+ adapter->l1m.userdata = adapter;
+ adapter->l1m.printdebug = l1m_debug;
+ FsmInitTimer(&adapter->l1m, &adapter->timer);
+
+ adapter->d_out.fsm.fsm = &dout_fsm;
+ adapter->d_out.fsm.state = ST_DOUT_NONE;
+ adapter->d_out.fsm.debug = st5481_debug & 0x100;
+ adapter->d_out.fsm.userdata = adapter;
+ adapter->d_out.fsm.printdebug = dout_debug;
+
+ return 0;
+
+ err_d_out:
+ st5481_release_d_out(adapter);
+ err:
+ return retval;
+}
+
+void st5481_release_d(struct st5481_adapter *adapter)
+{
+ DBG(2,"");
+
+ st5481_release_in(&adapter->d_in);
+ st5481_release_d_out(adapter);
+}
+
+/* ======================================================================
+ * init / exit
+ */
+
+int __init st5481_d_init(void)
+{
+ int retval;
+
+ l1fsm.state_count = L1_STATE_COUNT;
+ l1fsm.event_count = L1_EVENT_COUNT;
+ l1fsm.strEvent = strL1Event;
+ l1fsm.strState = strL1State;
+ retval = FsmNew(&l1fsm, L1FnList, ARRAY_SIZE(L1FnList));
+ if (retval)
+ goto err;
+
+ dout_fsm.state_count = DOUT_STATE_COUNT;
+ dout_fsm.event_count = DOUT_EVENT_COUNT;
+ dout_fsm.strEvent = strDoutEvent;
+ dout_fsm.strState = strDoutState;
+ retval = FsmNew(&dout_fsm, DoutFnList, ARRAY_SIZE(DoutFnList));
+ if (retval)
+ goto err_l1;
+
+ return 0;
+
+ err_l1:
+ FsmFree(&l1fsm);
+ err:
+ return retval;
+}
+
+// can't be __exit
+void st5481_d_exit(void)
+{
+ FsmFree(&l1fsm);
+ FsmFree(&dout_fsm);
+}
diff --git a/drivers/isdn/hisax/st5481_init.c b/drivers/isdn/hisax/st5481_init.c
new file mode 100644
index 0000000..1375123
--- /dev/null
+++ b/drivers/isdn/hisax/st5481_init.c
@@ -0,0 +1,224 @@
+/*
+ * Driver for ST5481 USB ISDN modem
+ *
+ * Author Frode Isaksen
+ * Copyright 2001 by Frode Isaksen <fisaksen@bewan.com>
+ * 2001 by Kai Germaschewski <kai.germaschewski@gmx.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/*
+ * TODO:
+ *
+ * b layer1 delay?
+ * hotplug / unregister issues
+ * mod_inc/dec_use_count
+ * unify parts of d/b channel usb handling
+ * file header
+ * avoid copy to isoc buffer?
+ * improve usb delay?
+ * merge l1 state machines?
+ * clean up debug
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/slab.h>
+#include "st5481.h"
+
+MODULE_DESCRIPTION("ISDN4Linux: driver for ST5481 USB ISDN adapter");
+MODULE_AUTHOR("Frode Isaksen");
+MODULE_LICENSE("GPL");
+
+static int protocol = 2; /* EURO-ISDN Default */
+module_param(protocol, int, 0);
+
+static int number_of_leds = 2; /* 2 LEDs on the adpater default */
+module_param(number_of_leds, int, 0);
+
+#ifdef CONFIG_HISAX_DEBUG
+static int debug = 0;
+module_param(debug, int, 0);
+#endif
+int st5481_debug;
+
+static LIST_HEAD(adapter_list);
+
+/* ======================================================================
+ * registration/deregistration with the USB layer
+ */
+
+/*
+ * This function will be called when the adapter is plugged
+ * into the USB bus.
+ */
+static int probe_st5481(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct st5481_adapter *adapter;
+ struct hisax_b_if *b_if[2];
+ int retval, i;
+
+ printk(KERN_INFO "st541: found adapter VendorId %04x, ProductId %04x, LEDs %d\n",
+ le16_to_cpu(dev->descriptor.idVendor),
+ le16_to_cpu(dev->descriptor.idProduct),
+ number_of_leds);
+
+ adapter = kzalloc(sizeof(struct st5481_adapter), GFP_KERNEL);
+ if (!adapter)
+ return -ENOMEM;
+
+ adapter->number_of_leds = number_of_leds;
+ adapter->usb_dev = dev;
+
+ adapter->hisax_d_if.owner = THIS_MODULE;
+ adapter->hisax_d_if.ifc.priv = adapter;
+ adapter->hisax_d_if.ifc.l2l1 = st5481_d_l2l1;
+
+ for (i = 0; i < 2; i++) {
+ adapter->bcs[i].adapter = adapter;
+ adapter->bcs[i].channel = i;
+ adapter->bcs[i].b_if.ifc.priv = &adapter->bcs[i];
+ adapter->bcs[i].b_if.ifc.l2l1 = st5481_b_l2l1;
+ }
+ list_add(&adapter->list, &adapter_list);
+
+ retval = st5481_setup_usb(adapter);
+ if (retval < 0)
+ goto err;
+
+ retval = st5481_setup_d(adapter);
+ if (retval < 0)
+ goto err_usb;
+
+ retval = st5481_setup_b(&adapter->bcs[0]);
+ if (retval < 0)
+ goto err_d;
+
+ retval = st5481_setup_b(&adapter->bcs[1]);
+ if (retval < 0)
+ goto err_b;
+
+ for (i = 0; i < 2; i++)
+ b_if[i] = &adapter->bcs[i].b_if;
+
+ if (hisax_register(&adapter->hisax_d_if, b_if, "st5481_usb",
+ protocol) != 0)
+ goto err_b1;
+
+ st5481_start(adapter);
+
+ usb_set_intfdata(intf, adapter);
+ return 0;
+
+ err_b1:
+ st5481_release_b(&adapter->bcs[1]);
+ err_b:
+ st5481_release_b(&adapter->bcs[0]);
+ err_d:
+ st5481_release_d(adapter);
+ err_usb:
+ st5481_release_usb(adapter);
+ err:
+ return -EIO;
+}
+
+/*
+ * This function will be called when the adapter is removed
+ * from the USB bus.
+ */
+static void disconnect_st5481(struct usb_interface *intf)
+{
+ struct st5481_adapter *adapter = usb_get_intfdata(intf);
+
+ DBG(1,"");
+
+ usb_set_intfdata(intf, NULL);
+ if (!adapter)
+ return;
+
+ list_del(&adapter->list);
+
+ st5481_stop(adapter);
+ st5481_release_b(&adapter->bcs[1]);
+ st5481_release_b(&adapter->bcs[0]);
+ st5481_release_d(adapter);
+ // we would actually better wait for completion of outstanding urbs
+ mdelay(2);
+ st5481_release_usb(adapter);
+
+ hisax_unregister(&adapter->hisax_d_if);
+
+ kfree(adapter);
+}
+
+/*
+ * The last 4 bits in the Product Id is set with 4 pins on the chip.
+ */
+static struct usb_device_id st5481_ids[] = {
+ { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x0) },
+ { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x1) },
+ { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x2) },
+ { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x3) },
+ { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x4) },
+ { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x5) },
+ { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x6) },
+ { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x7) },
+ { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x8) },
+ { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0x9) },
+ { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0xA) },
+ { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0xB) },
+ { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0xC) },
+ { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0xD) },
+ { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0xE) },
+ { USB_DEVICE(ST_VENDOR_ID, ST5481_PRODUCT_ID+0xF) },
+ { }
+};
+MODULE_DEVICE_TABLE (usb, st5481_ids);
+
+static struct usb_driver st5481_usb_driver = {
+ .name = "st5481_usb",
+ .probe = probe_st5481,
+ .disconnect = disconnect_st5481,
+ .id_table = st5481_ids,
+};
+
+static int __init st5481_usb_init(void)
+{
+ int retval;
+
+#ifdef CONFIG_HISAX_DEBUG
+ st5481_debug = debug;
+#endif
+
+ printk(KERN_INFO "hisax_st5481: ST5481 USB ISDN driver $Revision: 2.4.2.3 $\n");
+
+ retval = st5481_d_init();
+ if (retval < 0)
+ goto out;
+
+ retval = usb_register(&st5481_usb_driver);
+ if (retval < 0)
+ goto out_d_exit;
+
+ return 0;
+
+ out_d_exit:
+ st5481_d_exit();
+ out:
+ return retval;
+}
+
+static void __exit st5481_usb_exit(void)
+{
+ usb_deregister(&st5481_usb_driver);
+ st5481_d_exit();
+}
+
+module_init(st5481_usb_init);
+module_exit(st5481_usb_exit);
diff --git a/drivers/isdn/hisax/st5481_usb.c b/drivers/isdn/hisax/st5481_usb.c
new file mode 100644
index 0000000..ec3c0e5
--- /dev/null
+++ b/drivers/isdn/hisax/st5481_usb.c
@@ -0,0 +1,661 @@
+/*
+ * Driver for ST5481 USB ISDN modem
+ *
+ * Author Frode Isaksen
+ * Copyright 2001 by Frode Isaksen <fisaksen@bewan.com>
+ * 2001 by Kai Germaschewski <kai.germaschewski@gmx.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/slab.h>
+#include "st5481.h"
+
+static int st5481_isoc_flatten(struct urb *urb);
+
+/* ======================================================================
+ * control pipe
+ */
+
+/*
+ * Send the next endpoint 0 request stored in the FIFO.
+ * Called either by the completion or by usb_ctrl_msg.
+ */
+static void usb_next_ctrl_msg(struct urb *urb,
+ struct st5481_adapter *adapter)
+{
+ struct st5481_ctrl *ctrl = &adapter->ctrl;
+ int r_index;
+
+ if (test_and_set_bit(0, &ctrl->busy)) {
+ return;
+ }
+
+ if ((r_index = fifo_remove(&ctrl->msg_fifo.f)) < 0) {
+ test_and_clear_bit(0,&ctrl->busy);
+ return;
+ }
+ urb->setup_packet =
+ (unsigned char *)&ctrl->msg_fifo.data[r_index];
+
+ DBG(1,"request=0x%02x,value=0x%04x,index=%x",
+ ((struct ctrl_msg *)urb->setup_packet)->dr.bRequest,
+ ((struct ctrl_msg *)urb->setup_packet)->dr.wValue,
+ ((struct ctrl_msg *)urb->setup_packet)->dr.wIndex);
+
+ // Prepare the URB
+ urb->dev = adapter->usb_dev;
+
+ SUBMIT_URB(urb, GFP_ATOMIC);
+}
+
+/*
+ * Asynchronous endpoint 0 request (async version of usb_control_msg).
+ * The request will be queued up in a FIFO if the endpoint is busy.
+ */
+static void usb_ctrl_msg(struct st5481_adapter *adapter,
+ u8 request, u8 requesttype, u16 value, u16 index,
+ ctrl_complete_t complete, void *context)
+{
+ struct st5481_ctrl *ctrl = &adapter->ctrl;
+ int w_index;
+ struct ctrl_msg *ctrl_msg;
+
+ if ((w_index = fifo_add(&ctrl->msg_fifo.f)) < 0) {
+ WARNING("control msg FIFO full");
+ return;
+ }
+ ctrl_msg = &ctrl->msg_fifo.data[w_index];
+
+ ctrl_msg->dr.bRequestType = requesttype;
+ ctrl_msg->dr.bRequest = request;
+ ctrl_msg->dr.wValue = cpu_to_le16p(&value);
+ ctrl_msg->dr.wIndex = cpu_to_le16p(&index);
+ ctrl_msg->dr.wLength = 0;
+ ctrl_msg->complete = complete;
+ ctrl_msg->context = context;
+
+ usb_next_ctrl_msg(ctrl->urb, adapter);
+}
+
+/*
+ * Asynchronous endpoint 0 device request.
+ */
+void st5481_usb_device_ctrl_msg(struct st5481_adapter *adapter,
+ u8 request, u16 value,
+ ctrl_complete_t complete, void *context)
+{
+ usb_ctrl_msg(adapter, request,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, 0, complete, context);
+}
+
+/*
+ * Asynchronous pipe reset (async version of usb_clear_halt).
+ */
+void st5481_usb_pipe_reset(struct st5481_adapter *adapter,
+ u_char pipe,
+ ctrl_complete_t complete, void *context)
+{
+ DBG(1,"pipe=%02x",pipe);
+
+ usb_ctrl_msg(adapter,
+ USB_REQ_CLEAR_FEATURE, USB_DIR_OUT | USB_RECIP_ENDPOINT,
+ 0, pipe, complete, context);
+}
+
+
+/*
+ Physical level functions
+*/
+
+void st5481_ph_command(struct st5481_adapter *adapter, unsigned int command)
+{
+ DBG(8,"command=%s", ST5481_CMD_string(command));
+
+ st5481_usb_device_ctrl_msg(adapter, TXCI, command, NULL, NULL);
+}
+
+/*
+ * The request on endpoint 0 has completed.
+ * Call the user provided completion routine and try
+ * to send the next request.
+ */
+static void usb_ctrl_complete(struct urb *urb)
+{
+ struct st5481_adapter *adapter = urb->context;
+ struct st5481_ctrl *ctrl = &adapter->ctrl;
+ struct ctrl_msg *ctrl_msg;
+
+ if (unlikely(urb->status < 0)) {
+ switch (urb->status) {
+ case -ENOENT:
+ case -ESHUTDOWN:
+ case -ECONNRESET:
+ DBG(1,"urb killed status %d", urb->status);
+ return; // Give up
+ default:
+ WARNING("urb status %d",urb->status);
+ break;
+ }
+ }
+
+ ctrl_msg = (struct ctrl_msg *)urb->setup_packet;
+
+ if (ctrl_msg->dr.bRequest == USB_REQ_CLEAR_FEATURE) {
+ /* Special case handling for pipe reset */
+ le16_to_cpus(&ctrl_msg->dr.wIndex);
+
+ /* toggle is reset on clear */
+ usb_settoggle(adapter->usb_dev,
+ ctrl_msg->dr.wIndex & ~USB_DIR_IN,
+ (ctrl_msg->dr.wIndex & USB_DIR_IN) == 0,
+ 0);
+
+
+ }
+
+ if (ctrl_msg->complete)
+ ctrl_msg->complete(ctrl_msg->context);
+
+ clear_bit(0, &ctrl->busy);
+
+ // Try to send next control message
+ usb_next_ctrl_msg(urb, adapter);
+ return;
+}
+
+/* ======================================================================
+ * interrupt pipe
+ */
+
+/*
+ * The interrupt endpoint will be called when any
+ * of the 6 registers changes state (depending on masks).
+ * Decode the register values and schedule a private event.
+ * Called at interrupt.
+ */
+static void usb_int_complete(struct urb *urb)
+{
+ u8 *data = urb->transfer_buffer;
+ u8 irqbyte;
+ struct st5481_adapter *adapter = urb->context;
+ int j;
+ int status;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ DBG(2, "urb shutting down with status: %d", urb->status);
+ return;
+ default:
+ WARNING("nonzero urb status received: %d", urb->status);
+ goto exit;
+ }
+
+
+ DBG_PACKET(2, data, INT_PKT_SIZE);
+
+ if (urb->actual_length == 0) {
+ goto exit;
+ }
+
+ irqbyte = data[MPINT];
+ if (irqbyte & DEN_INT)
+ FsmEvent(&adapter->d_out.fsm, EV_DOUT_DEN, NULL);
+
+ if (irqbyte & DCOLL_INT)
+ FsmEvent(&adapter->d_out.fsm, EV_DOUT_COLL, NULL);
+
+ irqbyte = data[FFINT_D];
+ if (irqbyte & OUT_UNDERRUN)
+ FsmEvent(&adapter->d_out.fsm, EV_DOUT_UNDERRUN, NULL);
+
+ if (irqbyte & OUT_DOWN)
+;// printk("OUT_DOWN\n");
+
+ irqbyte = data[MPINT];
+ if (irqbyte & RXCI_INT)
+ FsmEvent(&adapter->l1m, data[CCIST] & 0x0f, NULL);
+
+ for (j = 0; j < 2; j++)
+ adapter->bcs[j].b_out.flow_event |= data[FFINT_B1 + j];
+
+ urb->actual_length = 0;
+
+exit:
+ status = usb_submit_urb (urb, GFP_ATOMIC);
+ if (status)
+ WARNING("usb_submit_urb failed with result %d", status);
+}
+
+/* ======================================================================
+ * initialization
+ */
+
+int st5481_setup_usb(struct st5481_adapter *adapter)
+{
+ struct usb_device *dev = adapter->usb_dev;
+ struct st5481_ctrl *ctrl = &adapter->ctrl;
+ struct st5481_intr *intr = &adapter->intr;
+ struct usb_interface *intf;
+ struct usb_host_interface *altsetting = NULL;
+ struct usb_host_endpoint *endpoint;
+ int status;
+ struct urb *urb;
+ u8 *buf;
+
+ DBG(2,"");
+
+ if ((status = usb_reset_configuration (dev)) < 0) {
+ WARNING("reset_configuration failed,status=%d",status);
+ return status;
+ }
+
+ intf = usb_ifnum_to_if(dev, 0);
+ if (intf)
+ altsetting = usb_altnum_to_altsetting(intf, 3);
+ if (!altsetting)
+ return -ENXIO;
+
+ // Check if the config is sane
+ if ( altsetting->desc.bNumEndpoints != 7 ) {
+ WARNING("expecting 7 got %d endpoints!", altsetting->desc.bNumEndpoints);
+ return -EINVAL;
+ }
+
+ // The descriptor is wrong for some early samples of the ST5481 chip
+ altsetting->endpoint[3].desc.wMaxPacketSize = __constant_cpu_to_le16(32);
+ altsetting->endpoint[4].desc.wMaxPacketSize = __constant_cpu_to_le16(32);
+
+ // Use alternative setting 3 on interface 0 to have 2B+D
+ if ((status = usb_set_interface (dev, 0, 3)) < 0) {
+ WARNING("usb_set_interface failed,status=%d",status);
+ return status;
+ }
+
+ // Allocate URB for control endpoint
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ return -ENOMEM;
+ }
+ ctrl->urb = urb;
+
+ // Fill the control URB
+ usb_fill_control_urb (urb, dev,
+ usb_sndctrlpipe(dev, 0),
+ NULL, NULL, 0, usb_ctrl_complete, adapter);
+
+
+ fifo_init(&ctrl->msg_fifo.f, ARRAY_SIZE(ctrl->msg_fifo.data));
+
+ // Allocate URBs and buffers for interrupt endpoint
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ return -ENOMEM;
+ }
+ intr->urb = urb;
+
+ buf = kmalloc(INT_PKT_SIZE, GFP_KERNEL);
+ if (!buf) {
+ return -ENOMEM;
+ }
+
+ endpoint = &altsetting->endpoint[EP_INT-1];
+
+ // Fill the interrupt URB
+ usb_fill_int_urb(urb, dev,
+ usb_rcvintpipe(dev, endpoint->desc.bEndpointAddress),
+ buf, INT_PKT_SIZE,
+ usb_int_complete, adapter,
+ endpoint->desc.bInterval);
+
+ return 0;
+}
+
+/*
+ * Release buffers and URBs for the interrupt and control
+ * endpoint.
+ */
+void st5481_release_usb(struct st5481_adapter *adapter)
+{
+ struct st5481_intr *intr = &adapter->intr;
+ struct st5481_ctrl *ctrl = &adapter->ctrl;
+
+ DBG(1,"");
+
+ // Stop and free Control and Interrupt URBs
+ usb_kill_urb(ctrl->urb);
+ kfree(ctrl->urb->transfer_buffer);
+ usb_free_urb(ctrl->urb);
+ ctrl->urb = NULL;
+
+ usb_kill_urb(intr->urb);
+ kfree(intr->urb->transfer_buffer);
+ usb_free_urb(intr->urb);
+ intr->urb = NULL;
+}
+
+/*
+ * Initialize the adapter.
+ */
+void st5481_start(struct st5481_adapter *adapter)
+{
+ static const u8 init_cmd_table[]={
+ SET_DEFAULT,0,
+ STT,0,
+ SDA_MIN,0x0d,
+ SDA_MAX,0x29,
+ SDELAY_VALUE,0x14,
+ GPIO_DIR,0x01,
+ GPIO_OUT,RED_LED,
+// FFCTRL_OUT_D,4,
+// FFCTRH_OUT_D,12,
+ FFCTRL_OUT_B1,6,
+ FFCTRH_OUT_B1,20,
+ FFCTRL_OUT_B2,6,
+ FFCTRH_OUT_B2,20,
+ MPMSK,RXCI_INT+DEN_INT+DCOLL_INT,
+ 0
+ };
+ struct st5481_intr *intr = &adapter->intr;
+ int i = 0;
+ u8 request,value;
+
+ DBG(8,"");
+
+ adapter->leds = RED_LED;
+
+ // Start receiving on the interrupt endpoint
+ SUBMIT_URB(intr->urb, GFP_KERNEL);
+
+ while ((request = init_cmd_table[i++])) {
+ value = init_cmd_table[i++];
+ st5481_usb_device_ctrl_msg(adapter, request, value, NULL, NULL);
+ }
+ st5481_ph_command(adapter, ST5481_CMD_PUP);
+}
+
+/*
+ * Reset the adapter to default values.
+ */
+void st5481_stop(struct st5481_adapter *adapter)
+{
+ DBG(8,"");
+
+ st5481_usb_device_ctrl_msg(adapter, SET_DEFAULT, 0, NULL, NULL);
+}
+
+/* ======================================================================
+ * isochronous USB helpers
+ */
+
+static void
+fill_isoc_urb(struct urb *urb, struct usb_device *dev,
+ unsigned int pipe, void *buf, int num_packets,
+ int packet_size, usb_complete_t complete,
+ void *context)
+{
+ int k;
+
+ urb->dev=dev;
+ urb->pipe=pipe;
+ urb->interval = 1;
+ urb->transfer_buffer=buf;
+ urb->number_of_packets = num_packets;
+ urb->transfer_buffer_length=num_packets*packet_size;
+ urb->actual_length = 0;
+ urb->complete=complete;
+ urb->context=context;
+ urb->transfer_flags=URB_ISO_ASAP;
+ for (k = 0; k < num_packets; k++) {
+ urb->iso_frame_desc[k].offset = packet_size * k;
+ urb->iso_frame_desc[k].length = packet_size;
+ urb->iso_frame_desc[k].actual_length = 0;
+ }
+}
+
+int
+st5481_setup_isocpipes(struct urb* urb[2], struct usb_device *dev,
+ unsigned int pipe, int num_packets,
+ int packet_size, int buf_size,
+ usb_complete_t complete, void *context)
+{
+ int j, retval;
+ unsigned char *buf;
+
+ for (j = 0; j < 2; j++) {
+ retval = -ENOMEM;
+ urb[j] = usb_alloc_urb(num_packets, GFP_KERNEL);
+ if (!urb[j])
+ goto err;
+
+ // Allocate memory for 2000bytes/sec (16Kb/s)
+ buf = kmalloc(buf_size, GFP_KERNEL);
+ if (!buf)
+ goto err;
+
+ // Fill the isochronous URB
+ fill_isoc_urb(urb[j], dev, pipe, buf,
+ num_packets, packet_size, complete,
+ context);
+ }
+ return 0;
+
+ err:
+ for (j = 0; j < 2; j++) {
+ if (urb[j]) {
+ kfree(urb[j]->transfer_buffer);
+ urb[j]->transfer_buffer = NULL;
+ usb_free_urb(urb[j]);
+ urb[j] = NULL;
+ }
+ }
+ return retval;
+}
+
+void st5481_release_isocpipes(struct urb* urb[2])
+{
+ int j;
+
+ for (j = 0; j < 2; j++) {
+ usb_kill_urb(urb[j]);
+ kfree(urb[j]->transfer_buffer);
+ usb_free_urb(urb[j]);
+ urb[j] = NULL;
+ }
+}
+
+/*
+ * Decode frames received on the B/D channel.
+ * Note that this function will be called continously
+ * with 64Kbit/s / 16Kbit/s of data and hence it will be
+ * called 50 times per second with 20 ISOC descriptors.
+ * Called at interrupt.
+ */
+static void usb_in_complete(struct urb *urb)
+{
+ struct st5481_in *in = urb->context;
+ unsigned char *ptr;
+ struct sk_buff *skb;
+ int len, count, status;
+
+ if (unlikely(urb->status < 0)) {
+ switch (urb->status) {
+ case -ENOENT:
+ case -ESHUTDOWN:
+ case -ECONNRESET:
+ DBG(1,"urb killed status %d", urb->status);
+ return; // Give up
+ default:
+ WARNING("urb status %d",urb->status);
+ break;
+ }
+ }
+
+ DBG_ISO_PACKET(0x80,urb);
+
+ len = st5481_isoc_flatten(urb);
+ ptr = urb->transfer_buffer;
+ while (len > 0) {
+ if (in->mode == L1_MODE_TRANS) {
+ memcpy(in->rcvbuf, ptr, len);
+ status = len;
+ len = 0;
+ } else {
+ status = isdnhdlc_decode(&in->hdlc_state, ptr, len, &count,
+ in->rcvbuf, in->bufsize);
+ ptr += count;
+ len -= count;
+ }
+
+ if (status > 0) {
+ // Good frame received
+ DBG(4,"count=%d",status);
+ DBG_PACKET(0x400, in->rcvbuf, status);
+ if (!(skb = dev_alloc_skb(status))) {
+ WARNING("receive out of memory\n");
+ break;
+ }
+ memcpy(skb_put(skb, status), in->rcvbuf, status);
+ in->hisax_if->l1l2(in->hisax_if, PH_DATA | INDICATION, skb);
+ } else if (status == -HDLC_CRC_ERROR) {
+ INFO("CRC error");
+ } else if (status == -HDLC_FRAMING_ERROR) {
+ INFO("framing error");
+ } else if (status == -HDLC_LENGTH_ERROR) {
+ INFO("length error");
+ }
+ }
+
+ // Prepare URB for next transfer
+ urb->dev = in->adapter->usb_dev;
+ urb->actual_length = 0;
+
+ SUBMIT_URB(urb, GFP_ATOMIC);
+}
+
+int st5481_setup_in(struct st5481_in *in)
+{
+ struct usb_device *dev = in->adapter->usb_dev;
+ int retval;
+
+ DBG(4,"");
+
+ in->rcvbuf = kmalloc(in->bufsize, GFP_KERNEL);
+ retval = -ENOMEM;
+ if (!in->rcvbuf)
+ goto err;
+
+ retval = st5481_setup_isocpipes(in->urb, dev,
+ usb_rcvisocpipe(dev, in->ep),
+ in->num_packets, in->packet_size,
+ in->num_packets * in->packet_size,
+ usb_in_complete, in);
+ if (retval)
+ goto err_free;
+ return 0;
+
+ err_free:
+ kfree(in->rcvbuf);
+ err:
+ return retval;
+}
+
+void st5481_release_in(struct st5481_in *in)
+{
+ DBG(2,"");
+
+ st5481_release_isocpipes(in->urb);
+}
+
+/*
+ * Make the transfer_buffer contiguous by
+ * copying from the iso descriptors if necessary.
+ */
+static int st5481_isoc_flatten(struct urb *urb)
+{
+ struct usb_iso_packet_descriptor *pipd,*pend;
+ unsigned char *src,*dst;
+ unsigned int len;
+
+ if (urb->status < 0) {
+ return urb->status;
+ }
+ for (pipd = &urb->iso_frame_desc[0],
+ pend = &urb->iso_frame_desc[urb->number_of_packets],
+ dst = urb->transfer_buffer;
+ pipd < pend;
+ pipd++) {
+
+ if (pipd->status < 0) {
+ return (pipd->status);
+ }
+
+ len = pipd->actual_length;
+ pipd->actual_length = 0;
+ src = urb->transfer_buffer+pipd->offset;
+
+ if (src != dst) {
+ // Need to copy since isoc buffers not full
+ while (len--) {
+ *dst++ = *src++;
+ }
+ } else {
+ // No need to copy, just update destination buffer
+ dst += len;
+ }
+ }
+ // Return size of flattened buffer
+ return (dst - (unsigned char *)urb->transfer_buffer);
+}
+
+static void st5481_start_rcv(void *context)
+{
+ struct st5481_in *in = context;
+ struct st5481_adapter *adapter = in->adapter;
+
+ DBG(4,"");
+
+ in->urb[0]->dev = adapter->usb_dev;
+ SUBMIT_URB(in->urb[0], GFP_KERNEL);
+
+ in->urb[1]->dev = adapter->usb_dev;
+ SUBMIT_URB(in->urb[1], GFP_KERNEL);
+}
+
+void st5481_in_mode(struct st5481_in *in, int mode)
+{
+ if (in->mode == mode)
+ return;
+
+ in->mode = mode;
+
+ usb_unlink_urb(in->urb[0]);
+ usb_unlink_urb(in->urb[1]);
+
+ if (in->mode != L1_MODE_NULL) {
+ if (in->mode != L1_MODE_TRANS)
+ isdnhdlc_rcv_init(&in->hdlc_state,
+ in->mode == L1_MODE_HDLC_56K);
+
+ st5481_usb_pipe_reset(in->adapter, in->ep, NULL, NULL);
+ st5481_usb_device_ctrl_msg(in->adapter, in->counter,
+ in->packet_size,
+ NULL, NULL);
+ st5481_start_rcv(in);
+ } else {
+ st5481_usb_device_ctrl_msg(in->adapter, in->counter,
+ 0, NULL, NULL);
+ }
+}
+
diff --git a/drivers/isdn/hisax/tei.c b/drivers/isdn/hisax/tei.c
new file mode 100644
index 0000000..ceb0df9
--- /dev/null
+++ b/drivers/isdn/hisax/tei.c
@@ -0,0 +1,466 @@
+/* $Id: tei.c,v 2.20.2.3 2004/01/13 14:31:26 keil Exp $
+ *
+ * Author Karsten Keil
+ * based on the teles driver from Jan den Ouden
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For changes and modifications please read
+ * Documentation/isdn/HiSax.cert
+ *
+ * Thanks to Jan den Ouden
+ * Fritz Elfert
+ *
+ */
+
+#include "hisax.h"
+#include "isdnl2.h"
+#include <linux/init.h>
+#include <linux/random.h>
+
+const char *tei_revision = "$Revision: 2.20.2.3 $";
+
+#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;
+
+void tei_handler(struct PStack *st, u_char pr, struct sk_buff *skb);
+
+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_DENIED,
+ EV_CHKREQ,
+ EV_REMOVE,
+ EV_VERIFY,
+ EV_T202,
+};
+
+#define TEI_EVENT_COUNT (EV_T202+1)
+
+static char *strTeiEvent[] =
+{
+ "EV_IDREQ",
+ "EV_ASSIGN",
+ "EV_DENIED",
+ "EV_CHKREQ",
+ "EV_REMOVE",
+ "EV_VERIFY",
+ "EV_T202",
+};
+
+static unsigned int
+random_ri(void)
+{
+ unsigned int x;
+
+ get_random_bytes(&x, sizeof(x));
+ return (x & 0xffff);
+}
+
+static struct PStack *
+findtei(struct PStack *st, int tei)
+{
+ struct PStack *ptr = *(st->l1.stlistp);
+
+ if (tei == 127)
+ return (NULL);
+
+ while (ptr)
+ if (ptr->l2.tei == tei)
+ return (ptr);
+ else
+ ptr = ptr->next;
+ return (NULL);
+}
+
+static void
+put_tei_msg(struct PStack *st, u_char m_id, unsigned int ri, u_char tei)
+{
+ struct sk_buff *skb;
+ u_char *bp;
+
+ if (!(skb = alloc_skb(8, GFP_ATOMIC))) {
+ printk(KERN_WARNING "HiSax: No skb for TEI manager\n");
+ return;
+ }
+ bp = skb_put(skb, 3);
+ bp[0] = (TEI_SAPI << 2);
+ bp[1] = (GROUP_TEI << 1) | 0x1;
+ bp[2] = UI;
+ bp = skb_put(skb, 5);
+ bp[0] = TEI_ENTITY_ID;
+ bp[1] = ri >> 8;
+ bp[2] = ri & 0xff;
+ bp[3] = m_id;
+ bp[4] = (tei << 1) | 1;
+ st->l2.l2l1(st, PH_DATA | REQUEST, skb);
+}
+
+static void
+tei_id_request(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ if (st->l2.tei != -1) {
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "assign request for allready asigned tei %d",
+ st->l2.tei);
+ return;
+ }
+ st->ma.ri = random_ri();
+ if (st->ma.debug)
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "assign request ri %d", st->ma.ri);
+ put_tei_msg(st, ID_REQUEST, st->ma.ri, 127);
+ FsmChangeState(&st->ma.tei_m, ST_TEI_IDREQ);
+ FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 1);
+ st->ma.N202 = 3;
+}
+
+static void
+tei_id_assign(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *ost, *st = fi->userdata;
+ struct sk_buff *skb = arg;
+ struct IsdnCardState *cs;
+ int ri, tei;
+
+ ri = ((unsigned int) skb->data[1] << 8) + skb->data[2];
+ tei = skb->data[4] >> 1;
+ if (st->ma.debug)
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "identity assign ri %d tei %d", ri, tei);
+ if ((ost = findtei(st, tei))) { /* same tei is in use */
+ if (ri != ost->ma.ri) {
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "possible duplicate assignment tei %d", tei);
+ ost->l2.l2tei(ost, MDL_ERROR | RESPONSE, NULL);
+ }
+ } else if (ri == st->ma.ri) {
+ FsmDelTimer(&st->ma.t202, 1);
+ FsmChangeState(&st->ma.tei_m, ST_TEI_NOP);
+ st->l3.l3l2(st, MDL_ASSIGN | REQUEST, (void *) (long) tei);
+ cs = (struct IsdnCardState *) st->l1.hardware;
+ cs->cardmsg(cs, MDL_ASSIGN | REQUEST, NULL);
+ }
+}
+
+static void
+tei_id_test_dup(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *ost, *st = fi->userdata;
+ struct sk_buff *skb = arg;
+ int tei, ri;
+
+ ri = ((unsigned int) skb->data[1] << 8) + skb->data[2];
+ tei = skb->data[4] >> 1;
+ if (st->ma.debug)
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "foreign identity assign ri %d tei %d", ri, tei);
+ if ((ost = findtei(st, tei))) { /* same tei is in use */
+ if (ri != ost->ma.ri) { /* and it wasn't our request */
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "possible duplicate assignment tei %d", tei);
+ FsmEvent(&ost->ma.tei_m, EV_VERIFY, NULL);
+ }
+ }
+}
+
+static void
+tei_id_denied(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct sk_buff *skb = arg;
+ int ri, tei;
+
+ ri = ((unsigned int) skb->data[1] << 8) + skb->data[2];
+ tei = skb->data[4] >> 1;
+ if (st->ma.debug)
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "identity denied ri %d tei %d", ri, tei);
+}
+
+static void
+tei_id_chk_req(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct sk_buff *skb = arg;
+ int tei;
+
+ tei = skb->data[4] >> 1;
+ if (st->ma.debug)
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "identity check req tei %d", tei);
+ if ((st->l2.tei != -1) && ((tei == GROUP_TEI) || (tei == st->l2.tei))) {
+ FsmDelTimer(&st->ma.t202, 4);
+ FsmChangeState(&st->ma.tei_m, ST_TEI_NOP);
+ put_tei_msg(st, ID_CHK_RES, random_ri(), st->l2.tei);
+ }
+}
+
+static void
+tei_id_remove(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct sk_buff *skb = arg;
+ struct IsdnCardState *cs;
+ int tei;
+
+ tei = skb->data[4] >> 1;
+ if (st->ma.debug)
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "identity remove tei %d", tei);
+ if ((st->l2.tei != -1) && ((tei == GROUP_TEI) || (tei == st->l2.tei))) {
+ FsmDelTimer(&st->ma.t202, 5);
+ FsmChangeState(&st->ma.tei_m, ST_TEI_NOP);
+ st->l3.l3l2(st, MDL_REMOVE | REQUEST, NULL);
+ cs = (struct IsdnCardState *) st->l1.hardware;
+ cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL);
+ }
+}
+
+static void
+tei_id_verify(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+
+ if (st->ma.debug)
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "id verify request for tei %d", st->l2.tei);
+ put_tei_msg(st, ID_VERIFY, 0, st->l2.tei);
+ FsmChangeState(&st->ma.tei_m, ST_TEI_IDVERIFY);
+ FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 2);
+ st->ma.N202 = 2;
+}
+
+static void
+tei_id_req_tout(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct IsdnCardState *cs;
+
+ if (--st->ma.N202) {
+ st->ma.ri = random_ri();
+ if (st->ma.debug)
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "assign req(%d) ri %d", 4 - st->ma.N202,
+ st->ma.ri);
+ put_tei_msg(st, ID_REQUEST, st->ma.ri, 127);
+ FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 3);
+ } else {
+ st->ma.tei_m.printdebug(&st->ma.tei_m, "assign req failed");
+ st->l3.l3l2(st, MDL_ERROR | RESPONSE, NULL);
+ cs = (struct IsdnCardState *) st->l1.hardware;
+ cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL);
+ FsmChangeState(fi, ST_TEI_NOP);
+ }
+}
+
+static void
+tei_id_ver_tout(struct FsmInst *fi, int event, void *arg)
+{
+ struct PStack *st = fi->userdata;
+ struct IsdnCardState *cs;
+
+ if (--st->ma.N202) {
+ if (st->ma.debug)
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "id verify req(%d) for tei %d",
+ 3 - st->ma.N202, st->l2.tei);
+ put_tei_msg(st, ID_VERIFY, 0, st->l2.tei);
+ FsmAddTimer(&st->ma.t202, st->ma.T202, EV_T202, NULL, 4);
+ } else {
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "verify req for tei %d failed", st->l2.tei);
+ st->l3.l3l2(st, MDL_REMOVE | REQUEST, NULL);
+ cs = (struct IsdnCardState *) st->l1.hardware;
+ cs->cardmsg(cs, MDL_REMOVE | REQUEST, NULL);
+ FsmChangeState(fi, ST_TEI_NOP);
+ }
+}
+
+static void
+tei_l1l2(struct PStack *st, int pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ int mt;
+
+ if (test_bit(FLG_FIXED_TEI, &st->l2.flag)) {
+ dev_kfree_skb(skb);
+ return;
+ }
+
+ if (pr == (PH_DATA | INDICATION)) {
+ if (skb->len < 3) {
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "short mgr frame %ld/3", skb->len);
+ } else if ((skb->data[0] != ((TEI_SAPI << 2) | 2)) ||
+ (skb->data[1] != ((GROUP_TEI << 1) | 1))) {
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "wrong mgr sapi/tei %x/%x",
+ skb->data[0], skb->data[1]);
+ } else if ((skb->data[2] & 0xef) != UI) {
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "mgr frame is not ui %x", skb->data[2]);
+ } else {
+ skb_pull(skb, 3);
+ if (skb->len < 5) {
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "short mgr frame %ld/5", skb->len);
+ } else if (skb->data[0] != TEI_ENTITY_ID) {
+ /* wrong management entity identifier, ignore */
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "tei handler wrong entity id %x",
+ skb->data[0]);
+ } else {
+ mt = skb->data[3];
+ if (mt == ID_ASSIGNED)
+ FsmEvent(&st->ma.tei_m, EV_ASSIGN, skb);
+ else if (mt == ID_DENIED)
+ FsmEvent(&st->ma.tei_m, EV_DENIED, skb);
+ else if (mt == ID_CHK_REQ)
+ FsmEvent(&st->ma.tei_m, EV_CHKREQ, skb);
+ else if (mt == ID_REMOVE)
+ FsmEvent(&st->ma.tei_m, EV_REMOVE, skb);
+ else {
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "tei handler wrong mt %x\n", mt);
+ }
+ }
+ }
+ } else {
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "tei handler wrong pr %x\n", pr);
+ }
+ dev_kfree_skb(skb);
+}
+
+static void
+tei_l2tei(struct PStack *st, int pr, void *arg)
+{
+ struct IsdnCardState *cs;
+
+ if (test_bit(FLG_FIXED_TEI, &st->l2.flag)) {
+ if (pr == (MDL_ASSIGN | INDICATION)) {
+ if (st->ma.debug)
+ st->ma.tei_m.printdebug(&st->ma.tei_m,
+ "fixed assign tei %d", st->l2.tei);
+ st->l3.l3l2(st, MDL_ASSIGN | REQUEST, (void *) (long) st->l2.tei);
+ cs = (struct IsdnCardState *) st->l1.hardware;
+ cs->cardmsg(cs, MDL_ASSIGN | REQUEST, NULL);
+ }
+ return;
+ }
+ switch (pr) {
+ case (MDL_ASSIGN | INDICATION):
+ FsmEvent(&st->ma.tei_m, EV_IDREQ, arg);
+ break;
+ case (MDL_ERROR | REQUEST):
+ FsmEvent(&st->ma.tei_m, EV_VERIFY, arg);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+tei_debug(struct FsmInst *fi, char *fmt, ...)
+{
+ va_list args;
+ struct PStack *st = fi->userdata;
+
+ va_start(args, fmt);
+ VHiSax_putstatus(st->l1.hardware, "tei ", fmt, args);
+ va_end(args);
+}
+
+void
+setstack_tei(struct PStack *st)
+{
+ st->l2.l2tei = tei_l2tei;
+ st->ma.T202 = 2000; /* T202 2000 milliseconds */
+ st->l1.l1tei = tei_l1l2;
+ st->ma.debug = 1;
+ st->ma.tei_m.fsm = &teifsm;
+ st->ma.tei_m.state = ST_TEI_NOP;
+ st->ma.tei_m.debug = 1;
+ st->ma.tei_m.userdata = st;
+ st->ma.tei_m.userint = 0;
+ st->ma.tei_m.printdebug = tei_debug;
+ FsmInitTimer(&st->ma.tei_m, &st->ma.t202);
+}
+
+void
+init_tei(struct IsdnCardState *cs, int protocol)
+{
+}
+
+void
+release_tei(struct IsdnCardState *cs)
+{
+ struct PStack *st = cs->stlist;
+
+ while (st) {
+ FsmDelTimer(&st->ma.t202, 1);
+ st = st->next;
+ }
+}
+
+static struct FsmNode TeiFnList[] __initdata =
+{
+ {ST_TEI_NOP, EV_IDREQ, tei_id_request},
+ {ST_TEI_NOP, EV_ASSIGN, tei_id_test_dup},
+ {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))
+
+int __init
+TeiNew(void)
+{
+ teifsm.state_count = TEI_STATE_COUNT;
+ teifsm.event_count = TEI_EVENT_COUNT;
+ teifsm.strEvent = strTeiEvent;
+ teifsm.strState = strTeiState;
+ return FsmNew(&teifsm, TeiFnList, TEI_FN_COUNT);
+}
+
+void
+TeiFree(void)
+{
+ FsmFree(&teifsm);
+}
diff --git a/drivers/isdn/hisax/teleint.c b/drivers/isdn/hisax/teleint.c
new file mode 100644
index 0000000..b0ce4ae
--- /dev/null
+++ b/drivers/isdn/hisax/teleint.c
@@ -0,0 +1,336 @@
+/* $Id: teleint.c,v 1.16.2.5 2004/01/19 15:31:50 keil Exp $
+ *
+ * low level stuff for TeleInt isdn cards
+ *
+ * Author Karsten Keil
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hfc_2bs0.h"
+#include "isdnl1.h"
+
+static const char *TeleInt_revision = "$Revision: 1.16.2.5 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+static inline u_char
+readreg(unsigned int ale, unsigned int adr, u_char off)
+{
+ register u_char ret;
+ int max_delay = 2000;
+
+ byteout(ale, off);
+ ret = HFC_BUSY & bytein(ale);
+ while (ret && --max_delay)
+ ret = HFC_BUSY & bytein(ale);
+ if (!max_delay) {
+ printk(KERN_WARNING "TeleInt Busy not inactive\n");
+ return (0);
+ }
+ ret = bytein(adr);
+ return (ret);
+}
+
+static inline void
+readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+ register u_char ret;
+ register int max_delay = 20000;
+ register int i;
+
+ byteout(ale, off);
+ for (i = 0; i<size; i++) {
+ ret = HFC_BUSY & bytein(ale);
+ while (ret && --max_delay)
+ ret = HFC_BUSY & bytein(ale);
+ if (!max_delay) {
+ printk(KERN_WARNING "TeleInt Busy not inactive\n");
+ return;
+ }
+ data[i] = bytein(adr);
+ }
+}
+
+
+static inline void
+writereg(unsigned int ale, unsigned int adr, u_char off, u_char data)
+{
+ register u_char ret;
+ int max_delay = 2000;
+
+ byteout(ale, off);
+ ret = HFC_BUSY & bytein(ale);
+ while (ret && --max_delay)
+ ret = HFC_BUSY & bytein(ale);
+ if (!max_delay) {
+ printk(KERN_WARNING "TeleInt Busy not inactive\n");
+ return;
+ }
+ byteout(adr, data);
+}
+
+static inline void
+writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size)
+{
+ register u_char ret;
+ register int max_delay = 20000;
+ register int i;
+
+ byteout(ale, off);
+ for (i = 0; i<size; i++) {
+ ret = HFC_BUSY & bytein(ale);
+ while (ret && --max_delay)
+ ret = HFC_BUSY & bytein(ale);
+ if (!max_delay) {
+ printk(KERN_WARNING "TeleInt Busy not inactive\n");
+ return;
+ }
+ byteout(adr, data[i]);
+ }
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+ cs->hw.hfc.cip = offset;
+ return (readreg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ cs->hw.hfc.cip = offset;
+ writereg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ cs->hw.hfc.cip = 0;
+ readfifo(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, 0, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ cs->hw.hfc.cip = 0;
+ writefifo(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, 0, data, size);
+}
+
+static u_char
+ReadHFC(struct IsdnCardState *cs, int data, u_char reg)
+{
+ register u_char ret;
+
+ if (data) {
+ cs->hw.hfc.cip = reg;
+ byteout(cs->hw.hfc.addr | 1, reg);
+ ret = bytein(cs->hw.hfc.addr);
+ if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2))
+ debugl1(cs, "hfc RD %02x %02x", reg, ret);
+ } else
+ ret = bytein(cs->hw.hfc.addr | 1);
+ return (ret);
+}
+
+static void
+WriteHFC(struct IsdnCardState *cs, int data, u_char reg, u_char value)
+{
+ byteout(cs->hw.hfc.addr | 1, reg);
+ cs->hw.hfc.cip = reg;
+ if (data)
+ byteout(cs->hw.hfc.addr, value);
+ if (cs->debug & L1_DEB_HSCX_FIFO && (data != 2))
+ debugl1(cs, "hfc W%c %02x %02x", data ? 'D' : 'C', reg, value);
+}
+
+static irqreturn_t
+TeleInt_interrupt(int intno, void *dev_id)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char val;
+ u_long flags;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ val = readreg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_ISTA);
+ Start_ISAC:
+ if (val)
+ isac_interrupt(cs, val);
+ val = readreg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_ISTA);
+ if (val) {
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ISAC IntStat after IntRoutine");
+ goto Start_ISAC;
+ }
+ writereg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_MASK, 0xFF);
+ writereg(cs->hw.hfc.addr | 1, cs->hw.hfc.addr, ISAC_MASK, 0x0);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static void
+TeleInt_Timer(struct IsdnCardState *cs)
+{
+ int stat = 0;
+ u_long flags;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ if (cs->bcs[0].mode) {
+ stat |= 1;
+ main_irq_hfc(&cs->bcs[0]);
+ }
+ if (cs->bcs[1].mode) {
+ stat |= 2;
+ main_irq_hfc(&cs->bcs[1]);
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+ stat = HZ/100;
+ if (!stat)
+ stat = 1;
+ cs->hw.hfc.timer.expires = jiffies + stat;
+ add_timer(&cs->hw.hfc.timer);
+}
+
+static void
+release_io_TeleInt(struct IsdnCardState *cs)
+{
+ del_timer(&cs->hw.hfc.timer);
+ releasehfc(cs);
+ if (cs->hw.hfc.addr)
+ release_region(cs->hw.hfc.addr, 2);
+}
+
+static void
+reset_TeleInt(struct IsdnCardState *cs)
+{
+ printk(KERN_INFO "TeleInt: resetting card\n");
+ cs->hw.hfc.cirm |= HFC_RESET;
+ byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm); /* Reset On */
+ mdelay(10);
+ cs->hw.hfc.cirm &= ~HFC_RESET;
+ byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm); /* Reset Off */
+ mdelay(10);
+}
+
+static int
+TeleInt_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ u_long flags;
+ int delay;
+
+ switch (mt) {
+ case CARD_RESET:
+ spin_lock_irqsave(&cs->lock, flags);
+ reset_TeleInt(cs);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return(0);
+ case CARD_RELEASE:
+ release_io_TeleInt(cs);
+ return(0);
+ case CARD_INIT:
+ spin_lock_irqsave(&cs->lock, flags);
+ reset_TeleInt(cs);
+ inithfc(cs);
+ clear_pending_isac_ints(cs);
+ initisac(cs);
+ /* Reenable all IRQ */
+ cs->writeisac(cs, ISAC_MASK, 0);
+ cs->writeisac(cs, ISAC_CMDR, 0x41);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ delay = HZ/100;
+ if (!delay)
+ delay = 1;
+ cs->hw.hfc.timer.expires = jiffies + delay;
+ add_timer(&cs->hw.hfc.timer);
+ return(0);
+ case CARD_TEST:
+ return(0);
+ }
+ return(0);
+}
+
+int __devinit
+setup_TeleInt(struct IsdnCard *card)
+{
+ struct IsdnCardState *cs = card->cs;
+ char tmp[64];
+
+ strcpy(tmp, TeleInt_revision);
+ printk(KERN_INFO "HiSax: TeleInt driver Rev. %s\n", HiSax_getrev(tmp));
+ if (cs->typ != ISDN_CTYPE_TELEINT)
+ return (0);
+
+ cs->hw.hfc.addr = card->para[1] & 0x3fe;
+ cs->irq = card->para[0];
+ cs->hw.hfc.cirm = HFC_CIRM;
+ cs->hw.hfc.isac_spcr = 0x00;
+ cs->hw.hfc.cip = 0;
+ cs->hw.hfc.ctmt = HFC_CTMT | HFC_CLTIMER;
+ cs->bcs[0].hw.hfc.send = NULL;
+ cs->bcs[1].hw.hfc.send = NULL;
+ cs->hw.hfc.fifosize = 7 * 1024 + 512;
+ cs->hw.hfc.timer.function = (void *) TeleInt_Timer;
+ cs->hw.hfc.timer.data = (long) cs;
+ init_timer(&cs->hw.hfc.timer);
+ if (!request_region(cs->hw.hfc.addr, 2, "TeleInt isdn")) {
+ printk(KERN_WARNING
+ "HiSax: TeleInt config port %x-%x already in use\n",
+ cs->hw.hfc.addr,
+ cs->hw.hfc.addr + 2);
+ return (0);
+ }
+ /* HW IO = IO */
+ byteout(cs->hw.hfc.addr, cs->hw.hfc.addr & 0xff);
+ byteout(cs->hw.hfc.addr | 1, ((cs->hw.hfc.addr & 0x300) >> 8) | 0x54);
+ switch (cs->irq) {
+ case 3:
+ cs->hw.hfc.cirm |= HFC_INTA;
+ break;
+ case 4:
+ cs->hw.hfc.cirm |= HFC_INTB;
+ break;
+ case 5:
+ cs->hw.hfc.cirm |= HFC_INTC;
+ break;
+ case 7:
+ cs->hw.hfc.cirm |= HFC_INTD;
+ break;
+ case 10:
+ cs->hw.hfc.cirm |= HFC_INTE;
+ break;
+ case 11:
+ cs->hw.hfc.cirm |= HFC_INTF;
+ break;
+ default:
+ printk(KERN_WARNING "TeleInt: wrong IRQ\n");
+ release_io_TeleInt(cs);
+ return (0);
+ }
+ byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.cirm);
+ byteout(cs->hw.hfc.addr | 1, cs->hw.hfc.ctmt);
+
+ printk(KERN_INFO "TeleInt: defined at 0x%x IRQ %d\n",
+ cs->hw.hfc.addr, cs->irq);
+
+ setup_isac(cs);
+ cs->readisac = &ReadISAC;
+ cs->writeisac = &WriteISAC;
+ cs->readisacfifo = &ReadISACfifo;
+ cs->writeisacfifo = &WriteISACfifo;
+ cs->BC_Read_Reg = &ReadHFC;
+ cs->BC_Write_Reg = &WriteHFC;
+ cs->cardmsg = &TeleInt_card_msg;
+ cs->irq_func = &TeleInt_interrupt;
+ ISACVersion(cs, "TeleInt:");
+ return (1);
+}
diff --git a/drivers/isdn/hisax/teles0.c b/drivers/isdn/hisax/teles0.c
new file mode 100644
index 0000000..4858133
--- /dev/null
+++ b/drivers/isdn/hisax/teles0.c
@@ -0,0 +1,367 @@
+/* $Id: teles0.c,v 2.15.2.4 2004/01/13 23:48:39 keil Exp $
+ *
+ * low level stuff for Teles Memory IO isdn cards
+ *
+ * Author Karsten Keil
+ * based on the teles driver from Jan den Ouden
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to Jan den Ouden
+ * Fritz Elfert
+ * Beat Doebeli
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isdnl1.h"
+#include "isac.h"
+#include "hscx.h"
+
+extern const char *CardType[];
+
+static const char *teles0_revision = "$Revision: 2.15.2.4 $";
+
+#define TELES_IOMEM_SIZE 0x400
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+static inline u_char
+readisac(void __iomem *adr, u_char off)
+{
+ return readb(adr + ((off & 1) ? 0x2ff : 0x100) + off);
+}
+
+static inline void
+writeisac(void __iomem *adr, u_char off, u_char data)
+{
+ writeb(data, adr + ((off & 1) ? 0x2ff : 0x100) + off); mb();
+}
+
+
+static inline u_char
+readhscx(void __iomem *adr, int hscx, u_char off)
+{
+ return readb(adr + (hscx ? 0x1c0 : 0x180) +
+ ((off & 1) ? 0x1ff : 0) + off);
+}
+
+static inline void
+writehscx(void __iomem *adr, int hscx, u_char off, u_char data)
+{
+ writeb(data, adr + (hscx ? 0x1c0 : 0x180) +
+ ((off & 1) ? 0x1ff : 0) + off); mb();
+}
+
+static inline void
+read_fifo_isac(void __iomem *adr, u_char * data, int size)
+{
+ register int i;
+ register u_char __iomem *ad = adr + 0x100;
+ for (i = 0; i < size; i++)
+ data[i] = readb(ad);
+}
+
+static inline void
+write_fifo_isac(void __iomem *adr, u_char * data, int size)
+{
+ register int i;
+ register u_char __iomem *ad = adr + 0x100;
+ for (i = 0; i < size; i++) {
+ writeb(data[i], ad); mb();
+ }
+}
+
+static inline void
+read_fifo_hscx(void __iomem *adr, int hscx, u_char * data, int size)
+{
+ register int i;
+ register u_char __iomem *ad = adr + (hscx ? 0x1c0 : 0x180);
+ for (i = 0; i < size; i++)
+ data[i] = readb(ad);
+}
+
+static inline void
+write_fifo_hscx(void __iomem *adr, int hscx, u_char * data, int size)
+{
+ int i;
+ register u_char __iomem *ad = adr + (hscx ? 0x1c0 : 0x180);
+ for (i = 0; i < size; i++) {
+ writeb(data[i], ad); mb();
+ }
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+ return (readisac(cs->hw.teles0.membase, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ writeisac(cs->hw.teles0.membase, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ read_fifo_isac(cs->hw.teles0.membase, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ write_fifo_isac(cs->hw.teles0.membase, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+ return (readhscx(cs->hw.teles0.membase, hscx, offset));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+ writehscx(cs->hw.teles0.membase, hscx, offset, value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readhscx(cs->hw.teles0.membase, nr, reg)
+#define WRITEHSCX(cs, nr, reg, data) writehscx(cs->hw.teles0.membase, nr, reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+teles0_interrupt(int intno, void *dev_id)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char val;
+ u_long flags;
+ int count = 0;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ val = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA);
+ Start_HSCX:
+ if (val)
+ hscx_int_main(cs, val);
+ val = readisac(cs->hw.teles0.membase, ISAC_ISTA);
+ Start_ISAC:
+ if (val)
+ isac_interrupt(cs, val);
+ count++;
+ val = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA);
+ if (val && count < 5) {
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "HSCX IntStat after IntRoutine");
+ goto Start_HSCX;
+ }
+ val = readisac(cs->hw.teles0.membase, ISAC_ISTA);
+ if (val && count < 5) {
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ISAC IntStat after IntRoutine");
+ goto Start_ISAC;
+ }
+ writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0xFF);
+ writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0xFF);
+ writeisac(cs->hw.teles0.membase, ISAC_MASK, 0xFF);
+ writeisac(cs->hw.teles0.membase, ISAC_MASK, 0x0);
+ writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0x0);
+ writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0x0);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static void
+release_io_teles0(struct IsdnCardState *cs)
+{
+ if (cs->hw.teles0.cfg_reg)
+ release_region(cs->hw.teles0.cfg_reg, 8);
+ iounmap(cs->hw.teles0.membase);
+ release_mem_region(cs->hw.teles0.phymem, TELES_IOMEM_SIZE);
+}
+
+static int
+reset_teles0(struct IsdnCardState *cs)
+{
+ u_char cfval;
+
+ if (cs->hw.teles0.cfg_reg) {
+ switch (cs->irq) {
+ case 2:
+ case 9:
+ cfval = 0x00;
+ break;
+ case 3:
+ cfval = 0x02;
+ break;
+ case 4:
+ cfval = 0x04;
+ break;
+ case 5:
+ cfval = 0x06;
+ break;
+ case 10:
+ cfval = 0x08;
+ break;
+ case 11:
+ cfval = 0x0A;
+ break;
+ case 12:
+ cfval = 0x0C;
+ break;
+ case 15:
+ cfval = 0x0E;
+ break;
+ default:
+ return(1);
+ }
+ cfval |= ((cs->hw.teles0.phymem >> 9) & 0xF0);
+ byteout(cs->hw.teles0.cfg_reg + 4, cfval);
+ HZDELAY(HZ / 10 + 1);
+ byteout(cs->hw.teles0.cfg_reg + 4, cfval | 1);
+ HZDELAY(HZ / 10 + 1);
+ }
+ writeb(0, cs->hw.teles0.membase + 0x80); mb();
+ HZDELAY(HZ / 5 + 1);
+ writeb(1, cs->hw.teles0.membase + 0x80); mb();
+ HZDELAY(HZ / 5 + 1);
+ return(0);
+}
+
+static int
+Teles_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ u_long flags;
+
+ switch (mt) {
+ case CARD_RESET:
+ spin_lock_irqsave(&cs->lock, flags);
+ reset_teles0(cs);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return(0);
+ case CARD_RELEASE:
+ release_io_teles0(cs);
+ return(0);
+ case CARD_INIT:
+ spin_lock_irqsave(&cs->lock, flags);
+ inithscxisac(cs, 3);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return(0);
+ case CARD_TEST:
+ return(0);
+ }
+ return(0);
+}
+
+int __devinit
+setup_teles0(struct IsdnCard *card)
+{
+ u_char val;
+ struct IsdnCardState *cs = card->cs;
+ char tmp[64];
+
+ strcpy(tmp, teles0_revision);
+ printk(KERN_INFO "HiSax: Teles 8.0/16.0 driver Rev. %s\n", HiSax_getrev(tmp));
+ if ((cs->typ != ISDN_CTYPE_16_0) && (cs->typ != ISDN_CTYPE_8_0))
+ return (0);
+
+ if (cs->typ == ISDN_CTYPE_16_0)
+ cs->hw.teles0.cfg_reg = card->para[2];
+ else /* 8.0 */
+ cs->hw.teles0.cfg_reg = 0;
+
+ if (card->para[1] < 0x10000) {
+ card->para[1] <<= 4;
+ printk(KERN_INFO
+ "Teles0: membase configured DOSish, assuming 0x%lx\n",
+ (unsigned long) card->para[1]);
+ }
+ cs->irq = card->para[0];
+ if (cs->hw.teles0.cfg_reg) {
+ if (!request_region(cs->hw.teles0.cfg_reg, 8, "teles cfg")) {
+ printk(KERN_WARNING
+ "HiSax: %s config port %x-%x already in use\n",
+ CardType[card->typ],
+ cs->hw.teles0.cfg_reg,
+ cs->hw.teles0.cfg_reg + 8);
+ return (0);
+ }
+ }
+ if (cs->hw.teles0.cfg_reg) {
+ if ((val = bytein(cs->hw.teles0.cfg_reg + 0)) != 0x51) {
+ printk(KERN_WARNING "Teles0: 16.0 Byte at %x is %x\n",
+ cs->hw.teles0.cfg_reg + 0, val);
+ release_region(cs->hw.teles0.cfg_reg, 8);
+ return (0);
+ }
+ if ((val = bytein(cs->hw.teles0.cfg_reg + 1)) != 0x93) {
+ printk(KERN_WARNING "Teles0: 16.0 Byte at %x is %x\n",
+ cs->hw.teles0.cfg_reg + 1, val);
+ release_region(cs->hw.teles0.cfg_reg, 8);
+ return (0);
+ }
+ val = bytein(cs->hw.teles0.cfg_reg + 2); /* 0x1e=without AB
+ * 0x1f=with AB
+ * 0x1c 16.3 ???
+ */
+ if (val != 0x1e && val != 0x1f) {
+ printk(KERN_WARNING "Teles0: 16.0 Byte at %x is %x\n",
+ cs->hw.teles0.cfg_reg + 2, val);
+ release_region(cs->hw.teles0.cfg_reg, 8);
+ return (0);
+ }
+ }
+ /* 16.0 and 8.0 designed for IOM1 */
+ test_and_set_bit(HW_IOM1, &cs->HW_Flags);
+ cs->hw.teles0.phymem = card->para[1];
+ if (!request_mem_region(cs->hw.teles0.phymem, TELES_IOMEM_SIZE, "teles iomem")) {
+ printk(KERN_WARNING
+ "HiSax: %s memory region %lx-%lx already in use\n",
+ CardType[card->typ],
+ cs->hw.teles0.phymem,
+ cs->hw.teles0.phymem + TELES_IOMEM_SIZE);
+ if (cs->hw.teles0.cfg_reg)
+ release_region(cs->hw.teles0.cfg_reg, 8);
+ return (0);
+ }
+ cs->hw.teles0.membase = ioremap(cs->hw.teles0.phymem, TELES_IOMEM_SIZE);
+ printk(KERN_INFO
+ "HiSax: %s config irq:%d mem:%p cfg:0x%X\n",
+ CardType[cs->typ], cs->irq,
+ cs->hw.teles0.membase, cs->hw.teles0.cfg_reg);
+ if (reset_teles0(cs)) {
+ printk(KERN_WARNING "Teles0: wrong IRQ\n");
+ release_io_teles0(cs);
+ return (0);
+ }
+ setup_isac(cs);
+ cs->readisac = &ReadISAC;
+ cs->writeisac = &WriteISAC;
+ cs->readisacfifo = &ReadISACfifo;
+ cs->writeisacfifo = &WriteISACfifo;
+ cs->BC_Read_Reg = &ReadHSCX;
+ cs->BC_Write_Reg = &WriteHSCX;
+ cs->BC_Send_Data = &hscx_fill_fifo;
+ cs->cardmsg = &Teles_card_msg;
+ cs->irq_func = &teles0_interrupt;
+ ISACVersion(cs, "Teles0:");
+ if (HscxVersion(cs, "Teles0:")) {
+ printk(KERN_WARNING
+ "Teles0: wrong HSCX versions check IO/MEM addresses\n");
+ release_io_teles0(cs);
+ return (0);
+ }
+ return (1);
+}
diff --git a/drivers/isdn/hisax/teles3.c b/drivers/isdn/hisax/teles3.c
new file mode 100644
index 0000000..5dc9f1a
--- /dev/null
+++ b/drivers/isdn/hisax/teles3.c
@@ -0,0 +1,500 @@
+/* $Id: teles3.c,v 2.19.2.4 2004/01/13 23:48:39 keil Exp $
+ *
+ * low level stuff for Teles 16.3 & PNP isdn cards
+ *
+ * Author Karsten Keil
+ * Copyright by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to Jan den Ouden
+ * Fritz Elfert
+ * Beat Doebeli
+ *
+ */
+#include <linux/init.h>
+#include <linux/isapnp.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+
+extern const char *CardType[];
+static const char *teles3_revision = "$Revision: 2.19.2.4 $";
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+static inline u_char
+readreg(unsigned int adr, u_char off)
+{
+ return (bytein(adr + off));
+}
+
+static inline void
+writereg(unsigned int adr, u_char off, u_char data)
+{
+ byteout(adr + off, data);
+}
+
+
+static inline void
+read_fifo(unsigned int adr, u_char * data, int size)
+{
+ insb(adr, data, size);
+}
+
+static void
+write_fifo(unsigned int adr, u_char * data, int size)
+{
+ outsb(adr, data, size);
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+ return (readreg(cs->hw.teles3.isac, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ writereg(cs->hw.teles3.isac, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ read_fifo(cs->hw.teles3.isacfifo, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ write_fifo(cs->hw.teles3.isacfifo, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+ return (readreg(cs->hw.teles3.hscx[hscx], offset));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+ writereg(cs->hw.teles3.hscx[hscx], offset, value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readreg(cs->hw.teles3.hscx[nr], reg)
+#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.teles3.hscx[nr], reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.teles3.hscxfifo[nr], ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.teles3.hscxfifo[nr], ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+teles3_interrupt(int intno, void *dev_id)
+{
+#define MAXCOUNT 5
+ struct IsdnCardState *cs = dev_id;
+ u_char val;
+ u_long flags;
+ int count = 0;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ val = readreg(cs->hw.teles3.hscx[1], HSCX_ISTA);
+ Start_HSCX:
+ if (val)
+ hscx_int_main(cs, val);
+ val = readreg(cs->hw.teles3.isac, ISAC_ISTA);
+ Start_ISAC:
+ if (val)
+ isac_interrupt(cs, val);
+ count++;
+ val = readreg(cs->hw.teles3.hscx[1], HSCX_ISTA);
+ if (val && count < MAXCOUNT) {
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "HSCX IntStat after IntRoutine");
+ goto Start_HSCX;
+ }
+ val = readreg(cs->hw.teles3.isac, ISAC_ISTA);
+ if (val && count < MAXCOUNT) {
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ISAC IntStat after IntRoutine");
+ goto Start_ISAC;
+ }
+ if (count >= MAXCOUNT)
+ printk(KERN_WARNING "Teles3: more than %d loops in teles3_interrupt\n", count);
+ writereg(cs->hw.teles3.hscx[0], HSCX_MASK, 0xFF);
+ writereg(cs->hw.teles3.hscx[1], HSCX_MASK, 0xFF);
+ writereg(cs->hw.teles3.isac, ISAC_MASK, 0xFF);
+ writereg(cs->hw.teles3.isac, ISAC_MASK, 0x0);
+ writereg(cs->hw.teles3.hscx[0], HSCX_MASK, 0x0);
+ writereg(cs->hw.teles3.hscx[1], HSCX_MASK, 0x0);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static inline void
+release_ioregs(struct IsdnCardState *cs, int mask)
+{
+ if (mask & 1)
+ release_region(cs->hw.teles3.isac + 32, 32);
+ if (mask & 2)
+ release_region(cs->hw.teles3.hscx[0] + 32, 32);
+ if (mask & 4)
+ release_region(cs->hw.teles3.hscx[1] + 32, 32);
+}
+
+static void
+release_io_teles3(struct IsdnCardState *cs)
+{
+ if (cs->typ == ISDN_CTYPE_TELESPCMCIA) {
+ release_region(cs->hw.teles3.hscx[1], 96);
+ } else {
+ if (cs->hw.teles3.cfg_reg) {
+ if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+ release_region(cs->hw.teles3.cfg_reg, 1);
+ } else {
+ release_region(cs->hw.teles3.cfg_reg, 8);
+ }
+ }
+ release_ioregs(cs, 0x7);
+ }
+}
+
+static int
+reset_teles3(struct IsdnCardState *cs)
+{
+ u_char irqcfg;
+
+ if (cs->typ != ISDN_CTYPE_TELESPCMCIA) {
+ if ((cs->hw.teles3.cfg_reg) && (cs->typ != ISDN_CTYPE_COMPAQ_ISA)) {
+ switch (cs->irq) {
+ case 2:
+ case 9:
+ irqcfg = 0x00;
+ break;
+ case 3:
+ irqcfg = 0x02;
+ break;
+ case 4:
+ irqcfg = 0x04;
+ break;
+ case 5:
+ irqcfg = 0x06;
+ break;
+ case 10:
+ irqcfg = 0x08;
+ break;
+ case 11:
+ irqcfg = 0x0A;
+ break;
+ case 12:
+ irqcfg = 0x0C;
+ break;
+ case 15:
+ irqcfg = 0x0E;
+ break;
+ default:
+ return(1);
+ }
+ byteout(cs->hw.teles3.cfg_reg + 4, irqcfg);
+ HZDELAY(HZ / 10 + 1);
+ byteout(cs->hw.teles3.cfg_reg + 4, irqcfg | 1);
+ HZDELAY(HZ / 10 + 1);
+ } else if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+ byteout(cs->hw.teles3.cfg_reg, 0xff);
+ HZDELAY(2);
+ byteout(cs->hw.teles3.cfg_reg, 0x00);
+ HZDELAY(2);
+ } else {
+ /* Reset off for 16.3 PnP , thanks to Georg Acher */
+ byteout(cs->hw.teles3.isac + 0x3c, 0);
+ HZDELAY(2);
+ byteout(cs->hw.teles3.isac + 0x3c, 1);
+ HZDELAY(2);
+ }
+ }
+ return(0);
+}
+
+static int
+Teles_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ u_long flags;
+
+ switch (mt) {
+ case CARD_RESET:
+ spin_lock_irqsave(&cs->lock, flags);
+ reset_teles3(cs);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return(0);
+ case CARD_RELEASE:
+ release_io_teles3(cs);
+ return(0);
+ case CARD_INIT:
+ spin_lock_irqsave(&cs->lock, flags);
+ inithscxisac(cs, 3);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return(0);
+ case CARD_TEST:
+ return(0);
+ }
+ return(0);
+}
+
+#ifdef __ISAPNP__
+
+static struct isapnp_device_id teles_ids[] __devinitdata = {
+ { ISAPNP_VENDOR('T', 'A', 'G'), ISAPNP_FUNCTION(0x2110),
+ ISAPNP_VENDOR('T', 'A', 'G'), ISAPNP_FUNCTION(0x2110),
+ (unsigned long) "Teles 16.3 PnP" },
+ { ISAPNP_VENDOR('C', 'T', 'X'), ISAPNP_FUNCTION(0x0),
+ ISAPNP_VENDOR('C', 'T', 'X'), ISAPNP_FUNCTION(0x0),
+ (unsigned long) "Creatix 16.3 PnP" },
+ { ISAPNP_VENDOR('C', 'P', 'Q'), ISAPNP_FUNCTION(0x1002),
+ ISAPNP_VENDOR('C', 'P', 'Q'), ISAPNP_FUNCTION(0x1002),
+ (unsigned long) "Compaq ISDN S0" },
+ { 0, }
+};
+
+static struct isapnp_device_id *ipid __devinitdata = &teles_ids[0];
+static struct pnp_card *pnp_c __devinitdata = NULL;
+#endif
+
+int __devinit
+setup_teles3(struct IsdnCard *card)
+{
+ u_char val;
+ struct IsdnCardState *cs = card->cs;
+ char tmp[64];
+
+ strcpy(tmp, teles3_revision);
+ printk(KERN_INFO "HiSax: Teles IO driver Rev. %s\n", HiSax_getrev(tmp));
+ if ((cs->typ != ISDN_CTYPE_16_3) && (cs->typ != ISDN_CTYPE_PNP)
+ && (cs->typ != ISDN_CTYPE_TELESPCMCIA) && (cs->typ != ISDN_CTYPE_COMPAQ_ISA))
+ return (0);
+
+#ifdef __ISAPNP__
+ if (!card->para[1] && isapnp_present()) {
+ struct pnp_dev *pnp_d;
+ while(ipid->card_vendor) {
+ if ((pnp_c = pnp_find_card(ipid->card_vendor,
+ ipid->card_device, pnp_c))) {
+ pnp_d = NULL;
+ if ((pnp_d = pnp_find_dev(pnp_c,
+ ipid->vendor, ipid->function, pnp_d))) {
+ int err;
+
+ printk(KERN_INFO "HiSax: %s detected\n",
+ (char *)ipid->driver_data);
+ pnp_disable_dev(pnp_d);
+ err = pnp_activate_dev(pnp_d);
+ if (err<0) {
+ printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n",
+ __func__, err);
+ return(0);
+ }
+ card->para[3] = pnp_port_start(pnp_d, 2);
+ card->para[2] = pnp_port_start(pnp_d, 1);
+ card->para[1] = pnp_port_start(pnp_d, 0);
+ card->para[0] = pnp_irq(pnp_d, 0);
+ if (!card->para[0] || !card->para[1] || !card->para[2]) {
+ printk(KERN_ERR "Teles PnP:some resources are missing %ld/%lx/%lx\n",
+ card->para[0], card->para[1], card->para[2]);
+ pnp_disable_dev(pnp_d);
+ return(0);
+ }
+ break;
+ } else {
+ printk(KERN_ERR "Teles PnP: PnP error card found, no device\n");
+ }
+ }
+ ipid++;
+ pnp_c = NULL;
+ }
+ if (!ipid->card_vendor) {
+ printk(KERN_INFO "Teles PnP: no ISAPnP card found\n");
+ return(0);
+ }
+ }
+#endif
+ if (cs->typ == ISDN_CTYPE_16_3) {
+ cs->hw.teles3.cfg_reg = card->para[1];
+ switch (cs->hw.teles3.cfg_reg) {
+ case 0x180:
+ case 0x280:
+ case 0x380:
+ cs->hw.teles3.cfg_reg |= 0xc00;
+ break;
+ }
+ cs->hw.teles3.isac = cs->hw.teles3.cfg_reg - 0x420;
+ cs->hw.teles3.hscx[0] = cs->hw.teles3.cfg_reg - 0xc20;
+ cs->hw.teles3.hscx[1] = cs->hw.teles3.cfg_reg - 0x820;
+ } else if (cs->typ == ISDN_CTYPE_TELESPCMCIA) {
+ cs->hw.teles3.cfg_reg = 0;
+ cs->hw.teles3.hscx[0] = card->para[1] - 0x20;
+ cs->hw.teles3.hscx[1] = card->para[1];
+ cs->hw.teles3.isac = card->para[1] + 0x20;
+ } else if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+ cs->hw.teles3.cfg_reg = card->para[3];
+ cs->hw.teles3.isac = card->para[2] - 32;
+ cs->hw.teles3.hscx[0] = card->para[1] - 32;
+ cs->hw.teles3.hscx[1] = card->para[1];
+ } else { /* PNP */
+ cs->hw.teles3.cfg_reg = 0;
+ cs->hw.teles3.isac = card->para[1] - 32;
+ cs->hw.teles3.hscx[0] = card->para[2] - 32;
+ cs->hw.teles3.hscx[1] = card->para[2];
+ }
+ cs->irq = card->para[0];
+ cs->hw.teles3.isacfifo = cs->hw.teles3.isac + 0x3e;
+ cs->hw.teles3.hscxfifo[0] = cs->hw.teles3.hscx[0] + 0x3e;
+ cs->hw.teles3.hscxfifo[1] = cs->hw.teles3.hscx[1] + 0x3e;
+ if (cs->typ == ISDN_CTYPE_TELESPCMCIA) {
+ if (!request_region(cs->hw.teles3.hscx[1], 96, "HiSax Teles PCMCIA")) {
+ printk(KERN_WARNING
+ "HiSax: %s ports %x-%x already in use\n",
+ CardType[cs->typ],
+ cs->hw.teles3.hscx[1],
+ cs->hw.teles3.hscx[1] + 96);
+ return (0);
+ }
+ cs->irq_flags |= IRQF_SHARED; /* cardbus can share */
+ } else {
+ if (cs->hw.teles3.cfg_reg) {
+ if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+ if (!request_region(cs->hw.teles3.cfg_reg, 1, "teles3 cfg")) {
+ printk(KERN_WARNING
+ "HiSax: %s config port %x already in use\n",
+ CardType[card->typ],
+ cs->hw.teles3.cfg_reg);
+ return (0);
+ }
+ } else {
+ if (!request_region(cs->hw.teles3.cfg_reg, 8, "teles3 cfg")) {
+ printk(KERN_WARNING
+ "HiSax: %s config port %x-%x already in use\n",
+ CardType[card->typ],
+ cs->hw.teles3.cfg_reg,
+ cs->hw.teles3.cfg_reg + 8);
+ return (0);
+ }
+ }
+ }
+ if (!request_region(cs->hw.teles3.isac + 32, 32, "HiSax isac")) {
+ printk(KERN_WARNING
+ "HiSax: %s isac ports %x-%x already in use\n",
+ CardType[cs->typ],
+ cs->hw.teles3.isac + 32,
+ cs->hw.teles3.isac + 64);
+ if (cs->hw.teles3.cfg_reg) {
+ if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+ release_region(cs->hw.teles3.cfg_reg, 1);
+ } else {
+ release_region(cs->hw.teles3.cfg_reg, 8);
+ }
+ }
+ return (0);
+ }
+ if (!request_region(cs->hw.teles3.hscx[0] + 32, 32, "HiSax hscx A")) {
+ printk(KERN_WARNING
+ "HiSax: %s hscx A ports %x-%x already in use\n",
+ CardType[cs->typ],
+ cs->hw.teles3.hscx[0] + 32,
+ cs->hw.teles3.hscx[0] + 64);
+ if (cs->hw.teles3.cfg_reg) {
+ if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+ release_region(cs->hw.teles3.cfg_reg, 1);
+ } else {
+ release_region(cs->hw.teles3.cfg_reg, 8);
+ }
+ }
+ release_ioregs(cs, 1);
+ return (0);
+ }
+ if (!request_region(cs->hw.teles3.hscx[1] + 32, 32, "HiSax hscx B")) {
+ printk(KERN_WARNING
+ "HiSax: %s hscx B ports %x-%x already in use\n",
+ CardType[cs->typ],
+ cs->hw.teles3.hscx[1] + 32,
+ cs->hw.teles3.hscx[1] + 64);
+ if (cs->hw.teles3.cfg_reg) {
+ if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) {
+ release_region(cs->hw.teles3.cfg_reg, 1);
+ } else {
+ release_region(cs->hw.teles3.cfg_reg, 8);
+ }
+ }
+ release_ioregs(cs, 3);
+ return (0);
+ }
+ }
+ if ((cs->hw.teles3.cfg_reg) && (cs->typ != ISDN_CTYPE_COMPAQ_ISA)) {
+ if ((val = bytein(cs->hw.teles3.cfg_reg + 0)) != 0x51) {
+ printk(KERN_WARNING "Teles: 16.3 Byte at %x is %x\n",
+ cs->hw.teles3.cfg_reg + 0, val);
+ release_io_teles3(cs);
+ return (0);
+ }
+ if ((val = bytein(cs->hw.teles3.cfg_reg + 1)) != 0x93) {
+ printk(KERN_WARNING "Teles: 16.3 Byte at %x is %x\n",
+ cs->hw.teles3.cfg_reg + 1, val);
+ release_io_teles3(cs);
+ return (0);
+ }
+ val = bytein(cs->hw.teles3.cfg_reg + 2);/* 0x1e=without AB
+ * 0x1f=with AB
+ * 0x1c 16.3 ???
+ * 0x39 16.3 1.1
+ * 0x38 16.3 1.3
+ * 0x46 16.3 with AB + Video (Teles-Vision)
+ */
+ if (val != 0x46 && val != 0x39 && val != 0x38 && val != 0x1c && val != 0x1e && val != 0x1f) {
+ printk(KERN_WARNING "Teles: 16.3 Byte at %x is %x\n",
+ cs->hw.teles3.cfg_reg + 2, val);
+ release_io_teles3(cs);
+ return (0);
+ }
+ }
+ printk(KERN_INFO
+ "HiSax: %s config irq:%d isac:0x%X cfg:0x%X\n",
+ CardType[cs->typ], cs->irq,
+ cs->hw.teles3.isac + 32, cs->hw.teles3.cfg_reg);
+ printk(KERN_INFO
+ "HiSax: hscx A:0x%X hscx B:0x%X\n",
+ cs->hw.teles3.hscx[0] + 32, cs->hw.teles3.hscx[1] + 32);
+
+ setup_isac(cs);
+ if (reset_teles3(cs)) {
+ printk(KERN_WARNING "Teles3: wrong IRQ\n");
+ release_io_teles3(cs);
+ return (0);
+ }
+ cs->readisac = &ReadISAC;
+ cs->writeisac = &WriteISAC;
+ cs->readisacfifo = &ReadISACfifo;
+ cs->writeisacfifo = &WriteISACfifo;
+ cs->BC_Read_Reg = &ReadHSCX;
+ cs->BC_Write_Reg = &WriteHSCX;
+ cs->BC_Send_Data = &hscx_fill_fifo;
+ cs->cardmsg = &Teles_card_msg;
+ cs->irq_func = &teles3_interrupt;
+ ISACVersion(cs, "Teles3:");
+ if (HscxVersion(cs, "Teles3:")) {
+ printk(KERN_WARNING
+ "Teles3: wrong HSCX versions check IO address\n");
+ release_io_teles3(cs);
+ return (0);
+ }
+ return (1);
+}
diff --git a/drivers/isdn/hisax/teles_cs.c b/drivers/isdn/hisax/teles_cs.c
new file mode 100644
index 0000000..623d111
--- /dev/null
+++ b/drivers/isdn/hisax/teles_cs.c
@@ -0,0 +1,363 @@
+/* $Id: teles_cs.c,v 1.1.2.2 2004/01/25 15:07:06 keil Exp $ */
+/*======================================================================
+
+ A teles S0 PCMCIA client driver
+
+ Based on skeleton by David Hinds, dhinds@allegro.stanford.edu
+ Written by Christof Petig, christof.petig@wtal.de
+
+ Also inspired by ELSA PCMCIA driver
+ by Klaus Lichtenwalder <Lichtenwalder@ACM.org>
+
+ Extentions to new hisax_pcmcia by Karsten Keil
+
+ minor changes to be compatible with kernel 2.4.x
+ by Jan.Schubert@GMX.li
+
+======================================================================*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/cisreg.h>
+#include <pcmcia/ds.h>
+#include "hisax_cfg.h"
+
+MODULE_DESCRIPTION("ISDN4Linux: PCMCIA client driver for Teles PCMCIA cards");
+MODULE_AUTHOR("Christof Petig, christof.petig@wtal.de, Karsten Keil, kkeil@suse.de");
+MODULE_LICENSE("GPL");
+
+/*
+ All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If
+ you do not define PCMCIA_DEBUG at all, all the debug code will be
+ left out. If you compile with PCMCIA_DEBUG=0, the debug code will
+ be present but disabled -- but it can then be enabled for specific
+ modules at load time with a 'pc_debug=#' option to insmod.
+*/
+
+#ifdef PCMCIA_DEBUG
+static int pc_debug = PCMCIA_DEBUG;
+module_param(pc_debug, int, 0);
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args);
+static char *version =
+"teles_cs.c 2.10 2002/07/30 22:23:34 kkeil";
+#else
+#define DEBUG(n, args...)
+#endif
+
+/*====================================================================*/
+
+/* Parameters that can be set with 'insmod' */
+
+static int protocol = 2; /* EURO-ISDN Default */
+module_param(protocol, int, 0);
+
+/*====================================================================*/
+
+/*
+ The event() function is this driver's Card Services event handler.
+ It will be called by Card Services when an appropriate card status
+ event is received. The config() and release() entry points are
+ used to configure or release a socket, in response to card insertion
+ and ejection events. They are invoked from the teles_cs event
+ handler.
+*/
+
+static int teles_cs_config(struct pcmcia_device *link);
+static void teles_cs_release(struct pcmcia_device *link);
+
+/*
+ The attach() and detach() entry points are used to create and destroy
+ "instances" of the driver, where each instance represents everything
+ needed to manage one actual PCMCIA card.
+*/
+
+static void teles_detach(struct pcmcia_device *p_dev);
+
+/*
+ A linked list of "instances" of the teles_cs device. Each actual
+ PCMCIA card corresponds to one device instance, and is described
+ by one struct pcmcia_device structure (defined in ds.h).
+
+ You may not want to use a linked list for this -- for example, the
+ memory card driver uses an array of struct pcmcia_device pointers, where minor
+ device numbers are used to derive the corresponding array index.
+*/
+
+/*
+ A driver needs to provide a dev_node_t structure for each device
+ on a card. In some cases, there is only one device per card (for
+ example, ethernet cards, modems). In other cases, there may be
+ many actual or logical devices (SCSI adapters, memory cards with
+ multiple partitions). The dev_node_t structures need to be kept
+ in a linked list starting at the 'dev' field of a struct pcmcia_device
+ structure. We allocate them in the card's private data structure,
+ because they generally shouldn't be allocated dynamically.
+ In this case, we also provide a flag to indicate if a device is
+ "stopped" due to a power management event, or card ejection. The
+ device IO routines can use a flag like this to throttle IO to a
+ card that is not ready to accept it.
+*/
+
+typedef struct local_info_t {
+ struct pcmcia_device *p_dev;
+ dev_node_t node;
+ int busy;
+ int cardnr;
+} local_info_t;
+
+/*======================================================================
+
+ teles_attach() creates an "instance" of the driver, allocatingx
+ local data structures for one device. The device is registered
+ with Card Services.
+
+ The dev_link structure is initialized, but we don't actually
+ configure the card at this point -- we wait until we receive a
+ card insertion event.
+
+======================================================================*/
+
+static int teles_probe(struct pcmcia_device *link)
+{
+ local_info_t *local;
+
+ DEBUG(0, "teles_attach()\n");
+
+ /* Allocate space for private device-specific data */
+ local = kzalloc(sizeof(local_info_t), GFP_KERNEL);
+ if (!local) return -ENOMEM;
+ local->cardnr = -1;
+
+ local->p_dev = link;
+ link->priv = local;
+
+ /* Interrupt setup */
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED;
+ link->irq.IRQInfo1 = IRQ_LEVEL_ID|IRQ_SHARE_ID;
+ link->irq.Handler = NULL;
+
+ /*
+ General socket configuration defaults can go here. In this
+ client, we assume very little, and rely on the CIS for almost
+ everything. In most clients, many details (i.e., number, sizes,
+ and attributes of IO windows) are fixed by the nature of the
+ device, and can be hard-wired here.
+ */
+ link->io.NumPorts1 = 96;
+ link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+ link->io.IOAddrLines = 5;
+
+ link->conf.Attributes = CONF_ENABLE_IRQ;
+ link->conf.IntType = INT_MEMORY_AND_IO;
+
+ return teles_cs_config(link);
+} /* teles_attach */
+
+/*======================================================================
+
+ This deletes a driver "instance". The device is de-registered
+ with Card Services. If it has been released, all local data
+ structures are freed. Otherwise, the structures will be freed
+ when the device is released.
+
+======================================================================*/
+
+static void teles_detach(struct pcmcia_device *link)
+{
+ local_info_t *info = link->priv;
+
+ DEBUG(0, "teles_detach(0x%p)\n", link);
+
+ info->busy = 1;
+ teles_cs_release(link);
+
+ kfree(info);
+} /* teles_detach */
+
+/*======================================================================
+
+ teles_cs_config() is scheduled to run after a CARD_INSERTION event
+ is received, to configure the PCMCIA socket, and to make the
+ device available to the system.
+
+======================================================================*/
+
+static int teles_cs_configcheck(struct pcmcia_device *p_dev,
+ cistpl_cftable_entry_t *cf,
+ cistpl_cftable_entry_t *dflt,
+ unsigned int vcc,
+ void *priv_data)
+{
+ int j;
+
+ if ((cf->io.nwin > 0) && cf->io.win[0].base) {
+ printk(KERN_INFO "(teles_cs: looks like the 96 model)\n");
+ p_dev->io.BasePort1 = cf->io.win[0].base;
+ if (!pcmcia_request_io(p_dev, &p_dev->io))
+ return 0;
+ } else {
+ printk(KERN_INFO "(teles_cs: looks like the 97 model)\n");
+ for (j = 0x2f0; j > 0x100; j -= 0x10) {
+ p_dev->io.BasePort1 = j;
+ if (!pcmcia_request_io(p_dev, &p_dev->io))
+ return 0;
+ }
+ }
+ return -ENODEV;
+}
+
+static int teles_cs_config(struct pcmcia_device *link)
+{
+ local_info_t *dev;
+ int i, last_fn;
+ IsdnCard_t icard;
+
+ DEBUG(0, "teles_config(0x%p)\n", link);
+ dev = link->priv;
+
+ i = pcmcia_loop_config(link, teles_cs_configcheck, NULL);
+ if (i != 0) {
+ last_fn = RequestIO;
+ goto cs_failed;
+ }
+
+ i = pcmcia_request_irq(link, &link->irq);
+ if (i != 0) {
+ link->irq.AssignedIRQ = 0;
+ last_fn = RequestIRQ;
+ goto cs_failed;
+ }
+
+ i = pcmcia_request_configuration(link, &link->conf);
+ if (i != 0) {
+ last_fn = RequestConfiguration;
+ goto cs_failed;
+ }
+
+ /* At this point, the dev_node_t structure(s) should be
+ initialized and arranged in a linked list at link->dev. *//* */
+ sprintf(dev->node.dev_name, "teles");
+ dev->node.major = dev->node.minor = 0x0;
+
+ link->dev_node = &dev->node;
+
+ /* Finally, report what we've done */
+ printk(KERN_INFO "%s: index 0x%02x:",
+ dev->node.dev_name, link->conf.ConfigIndex);
+ if (link->conf.Attributes & CONF_ENABLE_IRQ)
+ printk(", irq %d", link->irq.AssignedIRQ);
+ if (link->io.NumPorts1)
+ printk(", io 0x%04x-0x%04x", link->io.BasePort1,
+ link->io.BasePort1+link->io.NumPorts1-1);
+ if (link->io.NumPorts2)
+ printk(" & 0x%04x-0x%04x", link->io.BasePort2,
+ link->io.BasePort2+link->io.NumPorts2-1);
+ printk("\n");
+
+ icard.para[0] = link->irq.AssignedIRQ;
+ icard.para[1] = link->io.BasePort1;
+ icard.protocol = protocol;
+ icard.typ = ISDN_CTYPE_TELESPCMCIA;
+
+ i = hisax_init_pcmcia(link, &(((local_info_t*)link->priv)->busy), &icard);
+ if (i < 0) {
+ printk(KERN_ERR "teles_cs: failed to initialize Teles PCMCIA %d at i/o %#x\n",
+ i, link->io.BasePort1);
+ teles_cs_release(link);
+ return -ENODEV;
+ }
+
+ ((local_info_t*)link->priv)->cardnr = i;
+ return 0;
+
+cs_failed:
+ cs_error(link, last_fn, i);
+ teles_cs_release(link);
+ return -ENODEV;
+} /* teles_cs_config */
+
+/*======================================================================
+
+ After a card is removed, teles_cs_release() will unregister the net
+ device, and release the PCMCIA configuration. If the device is
+ still open, this will be postponed until it is closed.
+
+======================================================================*/
+
+static void teles_cs_release(struct pcmcia_device *link)
+{
+ local_info_t *local = link->priv;
+
+ DEBUG(0, "teles_cs_release(0x%p)\n", link);
+
+ if (local) {
+ if (local->cardnr >= 0) {
+ /* no unregister function with hisax */
+ HiSax_closecard(local->cardnr);
+ }
+ }
+
+ pcmcia_disable_device(link);
+} /* teles_cs_release */
+
+static int teles_suspend(struct pcmcia_device *link)
+{
+ local_info_t *dev = link->priv;
+
+ dev->busy = 1;
+
+ return 0;
+}
+
+static int teles_resume(struct pcmcia_device *link)
+{
+ local_info_t *dev = link->priv;
+
+ dev->busy = 0;
+
+ return 0;
+}
+
+
+static struct pcmcia_device_id teles_ids[] = {
+ PCMCIA_DEVICE_PROD_ID12("TELES", "S0/PC", 0x67b50eae, 0xe9e70119),
+ PCMCIA_DEVICE_NULL,
+};
+MODULE_DEVICE_TABLE(pcmcia, teles_ids);
+
+static struct pcmcia_driver teles_cs_driver = {
+ .owner = THIS_MODULE,
+ .drv = {
+ .name = "teles_cs",
+ },
+ .probe = teles_probe,
+ .remove = teles_detach,
+ .id_table = teles_ids,
+ .suspend = teles_suspend,
+ .resume = teles_resume,
+};
+
+static int __init init_teles_cs(void)
+{
+ return pcmcia_register_driver(&teles_cs_driver);
+}
+
+static void __exit exit_teles_cs(void)
+{
+ pcmcia_unregister_driver(&teles_cs_driver);
+}
+
+module_init(init_teles_cs);
+module_exit(exit_teles_cs);
diff --git a/drivers/isdn/hisax/telespci.c b/drivers/isdn/hisax/telespci.c
new file mode 100644
index 0000000..28b08de
--- /dev/null
+++ b/drivers/isdn/hisax/telespci.c
@@ -0,0 +1,354 @@
+/* $Id: telespci.c,v 2.23.2.3 2004/01/13 14:31:26 keil Exp $
+ *
+ * low level stuff for Teles PCI isdn cards
+ *
+ * Author Ton van Rosmalen
+ * Karsten Keil
+ * Copyright by Ton van Rosmalen
+ * by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "isac.h"
+#include "hscx.h"
+#include "isdnl1.h"
+#include <linux/pci.h>
+
+static const char *telespci_revision = "$Revision: 2.23.2.3 $";
+
+#define ZORAN_PO_RQ_PEN 0x02000000
+#define ZORAN_PO_WR 0x00800000
+#define ZORAN_PO_GID0 0x00000000
+#define ZORAN_PO_GID1 0x00100000
+#define ZORAN_PO_GREG0 0x00000000
+#define ZORAN_PO_GREG1 0x00010000
+#define ZORAN_PO_DMASK 0xFF
+
+#define WRITE_ADDR_ISAC (ZORAN_PO_WR | ZORAN_PO_GID0 | ZORAN_PO_GREG0)
+#define READ_DATA_ISAC (ZORAN_PO_GID0 | ZORAN_PO_GREG1)
+#define WRITE_DATA_ISAC (ZORAN_PO_WR | ZORAN_PO_GID0 | ZORAN_PO_GREG1)
+#define WRITE_ADDR_HSCX (ZORAN_PO_WR | ZORAN_PO_GID1 | ZORAN_PO_GREG0)
+#define READ_DATA_HSCX (ZORAN_PO_GID1 | ZORAN_PO_GREG1)
+#define WRITE_DATA_HSCX (ZORAN_PO_WR | ZORAN_PO_GID1 | ZORAN_PO_GREG1)
+
+#define ZORAN_WAIT_NOBUSY do { \
+ portdata = readl(adr + 0x200); \
+ } while (portdata & ZORAN_PO_RQ_PEN)
+
+static inline u_char
+readisac(void __iomem *adr, u_char off)
+{
+ register unsigned int portdata;
+
+ ZORAN_WAIT_NOBUSY;
+
+ /* set address for ISAC */
+ writel(WRITE_ADDR_ISAC | off, adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+
+ /* read data from ISAC */
+ writel(READ_DATA_ISAC, adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+ return((u_char)(portdata & ZORAN_PO_DMASK));
+}
+
+static inline void
+writeisac(void __iomem *adr, u_char off, u_char data)
+{
+ register unsigned int portdata;
+
+ ZORAN_WAIT_NOBUSY;
+
+ /* set address for ISAC */
+ writel(WRITE_ADDR_ISAC | off, adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+
+ /* write data to ISAC */
+ writel(WRITE_DATA_ISAC | data, adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+}
+
+static inline u_char
+readhscx(void __iomem *adr, int hscx, u_char off)
+{
+ register unsigned int portdata;
+
+ ZORAN_WAIT_NOBUSY;
+ /* set address for HSCX */
+ writel(WRITE_ADDR_HSCX | ((hscx ? 0x40:0) + off), adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+
+ /* read data from HSCX */
+ writel(READ_DATA_HSCX, adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+ return ((u_char)(portdata & ZORAN_PO_DMASK));
+}
+
+static inline void
+writehscx(void __iomem *adr, int hscx, u_char off, u_char data)
+{
+ register unsigned int portdata;
+
+ ZORAN_WAIT_NOBUSY;
+ /* set address for HSCX */
+ writel(WRITE_ADDR_HSCX | ((hscx ? 0x40:0) + off), adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+
+ /* write data to HSCX */
+ writel(WRITE_DATA_HSCX | data, adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+}
+
+static inline void
+read_fifo_isac(void __iomem *adr, u_char * data, int size)
+{
+ register unsigned int portdata;
+ register int i;
+
+ ZORAN_WAIT_NOBUSY;
+ /* read data from ISAC */
+ for (i = 0; i < size; i++) {
+ /* set address for ISAC fifo */
+ writel(WRITE_ADDR_ISAC | 0x1E, adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+ writel(READ_DATA_ISAC, adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+ data[i] = (u_char)(portdata & ZORAN_PO_DMASK);
+ }
+}
+
+static void
+write_fifo_isac(void __iomem *adr, u_char * data, int size)
+{
+ register unsigned int portdata;
+ register int i;
+
+ ZORAN_WAIT_NOBUSY;
+ /* write data to ISAC */
+ for (i = 0; i < size; i++) {
+ /* set address for ISAC fifo */
+ writel(WRITE_ADDR_ISAC | 0x1E, adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+ writel(WRITE_DATA_ISAC | data[i], adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+ }
+}
+
+static inline void
+read_fifo_hscx(void __iomem *adr, int hscx, u_char * data, int size)
+{
+ register unsigned int portdata;
+ register int i;
+
+ ZORAN_WAIT_NOBUSY;
+ /* read data from HSCX */
+ for (i = 0; i < size; i++) {
+ /* set address for HSCX fifo */
+ writel(WRITE_ADDR_HSCX |(hscx ? 0x5F:0x1F), adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+ writel(READ_DATA_HSCX, adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+ data[i] = (u_char) (portdata & ZORAN_PO_DMASK);
+ }
+}
+
+static inline void
+write_fifo_hscx(void __iomem *adr, int hscx, u_char * data, int size)
+{
+ unsigned int portdata;
+ register int i;
+
+ ZORAN_WAIT_NOBUSY;
+ /* write data to HSCX */
+ for (i = 0; i < size; i++) {
+ /* set address for HSCX fifo */
+ writel(WRITE_ADDR_HSCX |(hscx ? 0x5F:0x1F), adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+ writel(WRITE_DATA_HSCX | data[i], adr + 0x200);
+ ZORAN_WAIT_NOBUSY;
+ udelay(10);
+ }
+}
+
+/* Interface functions */
+
+static u_char
+ReadISAC(struct IsdnCardState *cs, u_char offset)
+{
+ return (readisac(cs->hw.teles0.membase, offset));
+}
+
+static void
+WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ writeisac(cs->hw.teles0.membase, offset, value);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ read_fifo_isac(cs->hw.teles0.membase, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ write_fifo_isac(cs->hw.teles0.membase, data, size);
+}
+
+static u_char
+ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset)
+{
+ return (readhscx(cs->hw.teles0.membase, hscx, offset));
+}
+
+static void
+WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
+{
+ writehscx(cs->hw.teles0.membase, hscx, offset, value);
+}
+
+/*
+ * fast interrupt HSCX stuff goes here
+ */
+
+#define READHSCX(cs, nr, reg) readhscx(cs->hw.teles0.membase, nr, reg)
+#define WRITEHSCX(cs, nr, reg, data) writehscx(cs->hw.teles0.membase, nr, reg, data)
+#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt)
+#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo_hscx(cs->hw.teles0.membase, nr, ptr, cnt)
+
+#include "hscx_irq.c"
+
+static irqreturn_t
+telespci_interrupt(int intno, void *dev_id)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char hval, ival;
+ u_long flags;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ hval = readhscx(cs->hw.teles0.membase, 1, HSCX_ISTA);
+ if (hval)
+ hscx_int_main(cs, hval);
+ ival = readisac(cs->hw.teles0.membase, ISAC_ISTA);
+ if ((hval | ival) == 0) {
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_NONE;
+ }
+ if (ival)
+ isac_interrupt(cs, ival);
+ /* Clear interrupt register for Zoran PCI controller */
+ writel(0x70000000, cs->hw.teles0.membase + 0x3C);
+
+ writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0xFF);
+ writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0xFF);
+ writeisac(cs->hw.teles0.membase, ISAC_MASK, 0xFF);
+ writeisac(cs->hw.teles0.membase, ISAC_MASK, 0x0);
+ writehscx(cs->hw.teles0.membase, 0, HSCX_MASK, 0x0);
+ writehscx(cs->hw.teles0.membase, 1, HSCX_MASK, 0x0);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static void
+release_io_telespci(struct IsdnCardState *cs)
+{
+ iounmap(cs->hw.teles0.membase);
+}
+
+static int
+TelesPCI_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ u_long flags;
+
+ switch (mt) {
+ case CARD_RESET:
+ return(0);
+ case CARD_RELEASE:
+ release_io_telespci(cs);
+ return(0);
+ case CARD_INIT:
+ spin_lock_irqsave(&cs->lock, flags);
+ inithscxisac(cs, 3);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return(0);
+ case CARD_TEST:
+ return(0);
+ }
+ return(0);
+}
+
+static struct pci_dev *dev_tel __devinitdata = NULL;
+
+int __devinit
+setup_telespci(struct IsdnCard *card)
+{
+ struct IsdnCardState *cs = card->cs;
+ char tmp[64];
+
+#ifdef __BIG_ENDIAN
+#error "not running on big endian machines now"
+#endif
+
+ strcpy(tmp, telespci_revision);
+ printk(KERN_INFO "HiSax: Teles/PCI driver Rev. %s\n", HiSax_getrev(tmp));
+ if (cs->typ != ISDN_CTYPE_TELESPCI)
+ return (0);
+
+ if ((dev_tel = pci_find_device (PCI_VENDOR_ID_ZORAN, PCI_DEVICE_ID_ZORAN_36120, dev_tel))) {
+ if (pci_enable_device(dev_tel))
+ return(0);
+ cs->irq = dev_tel->irq;
+ if (!cs->irq) {
+ printk(KERN_WARNING "Teles: No IRQ for PCI card found\n");
+ return(0);
+ }
+ cs->hw.teles0.membase = ioremap(pci_resource_start(dev_tel, 0),
+ PAGE_SIZE);
+ printk(KERN_INFO "Found: Zoran, base-address: 0x%llx, irq: 0x%x\n",
+ (unsigned long long)pci_resource_start(dev_tel, 0),
+ dev_tel->irq);
+ } else {
+ printk(KERN_WARNING "TelesPCI: No PCI card found\n");
+ return(0);
+ }
+
+ /* Initialize Zoran PCI controller */
+ writel(0x00000000, cs->hw.teles0.membase + 0x28);
+ writel(0x01000000, cs->hw.teles0.membase + 0x28);
+ writel(0x01000000, cs->hw.teles0.membase + 0x28);
+ writel(0x7BFFFFFF, cs->hw.teles0.membase + 0x2C);
+ writel(0x70000000, cs->hw.teles0.membase + 0x3C);
+ writel(0x61000000, cs->hw.teles0.membase + 0x40);
+ /* writel(0x00800000, cs->hw.teles0.membase + 0x200); */
+
+ printk(KERN_INFO
+ "HiSax: Teles PCI config irq:%d mem:%p\n",
+ cs->irq,
+ cs->hw.teles0.membase);
+
+ setup_isac(cs);
+ cs->readisac = &ReadISAC;
+ cs->writeisac = &WriteISAC;
+ cs->readisacfifo = &ReadISACfifo;
+ cs->writeisacfifo = &WriteISACfifo;
+ cs->BC_Read_Reg = &ReadHSCX;
+ cs->BC_Write_Reg = &WriteHSCX;
+ cs->BC_Send_Data = &hscx_fill_fifo;
+ cs->cardmsg = &TelesPCI_card_msg;
+ cs->irq_func = &telespci_interrupt;
+ cs->irq_flags |= IRQF_SHARED;
+ ISACVersion(cs, "TelesPCI:");
+ if (HscxVersion(cs, "TelesPCI:")) {
+ printk(KERN_WARNING
+ "TelesPCI: wrong HSCX versions check IO/MEM addresses\n");
+ release_io_telespci(cs);
+ return (0);
+ }
+ return (1);
+}
diff --git a/drivers/isdn/hisax/w6692.c b/drivers/isdn/hisax/w6692.c
new file mode 100644
index 0000000..bb1c8dd
--- /dev/null
+++ b/drivers/isdn/hisax/w6692.c
@@ -0,0 +1,1086 @@
+/* $Id: w6692.c,v 1.18.2.4 2004/02/11 13:21:34 keil Exp $
+ *
+ * Winbond W6692 specific routines
+ *
+ * Author Petr Novak
+ * Copyright by Petr Novak <petr.novak@i.cz>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/init.h>
+#include "hisax.h"
+#include "w6692.h"
+#include "isdnl1.h"
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+
+/* 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_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692, "Winbond", "W6692"},
+ {PCI_VENDOR_ID_DYNALINK, PCI_DEVICE_ID_DYNALINK_IS64PH, "Dynalink/AsusCom", "IS64PH"},
+ {0, 0, "U.S.Robotics", "ISDN PCI Card TA"}
+};
+
+#define W6692_SV_USR 0x16ec
+#define W6692_SD_USR 0x3409
+#define W6692_WINBOND 0
+#define W6692_DYNALINK 1
+#define W6692_USR 2
+
+static const char *w6692_revision = "$Revision: 1.18.2.4 $";
+
+#define DBUSY_TIMER_VALUE 80
+
+static char *W6692Ver[] =
+{"W6692 V00", "W6692 V01", "W6692 V10",
+ "W6692 V11"};
+
+static void
+W6692Version(struct IsdnCardState *cs, char *s)
+{
+ int val;
+
+ val = cs->readW6692(cs, W_D_RBCH);
+ printk(KERN_INFO "%s Winbond W6692 version (%x): %s\n", s, val, W6692Ver[(val >> 6) & 3]);
+}
+
+static void
+ph_command(struct IsdnCardState *cs, unsigned int command)
+{
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ph_command %x", command);
+ cs->writeisac(cs, W_CIX, command);
+}
+
+
+static void
+W6692_new_ph(struct IsdnCardState *cs)
+{
+ switch (cs->dc.w6692.ph_state) {
+ case (W_L1CMD_RST):
+ ph_command(cs, W_L1CMD_DRC);
+ l1_msg(cs, HW_RESET | INDICATION, NULL);
+ /* fallthru */
+ case (W_L1IND_CD):
+ l1_msg(cs, HW_DEACTIVATE | CONFIRM, NULL);
+ break;
+ case (W_L1IND_DRD):
+ l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
+ break;
+ case (W_L1IND_CE):
+ l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
+ break;
+ case (W_L1IND_LD):
+ l1_msg(cs, HW_RSYNC | INDICATION, NULL);
+ break;
+ case (W_L1IND_ARD):
+ l1_msg(cs, HW_INFO2 | INDICATION, NULL);
+ break;
+ case (W_L1IND_AI8):
+ l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
+ break;
+ case (W_L1IND_AI10):
+ l1_msg(cs, HW_INFO4_P10 | INDICATION, NULL);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+W6692_bh(struct work_struct *work)
+{
+ struct IsdnCardState *cs =
+ container_of(work, struct IsdnCardState, tqueue);
+ struct PStack *stptr;
+
+ if (!cs)
+ return;
+ if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) {
+ if (cs->debug)
+ debugl1(cs, "D-Channel Busy cleared");
+ stptr = cs->stlist;
+ while (stptr != NULL) {
+ stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL);
+ stptr = stptr->next;
+ }
+ }
+ if (test_and_clear_bit(D_L1STATECHANGE, &cs->event))
+ W6692_new_ph(cs);
+ if (test_and_clear_bit(D_RCVBUFREADY, &cs->event))
+ DChannel_proc_rcv(cs);
+ if (test_and_clear_bit(D_XMTBUFREADY, &cs->event))
+ DChannel_proc_xmt(cs);
+/*
+ if (test_and_clear_bit(D_RX_MON1, &cs->event))
+ arcofi_fsm(cs, ARCOFI_RX_END, NULL);
+ if (test_and_clear_bit(D_TX_MON1, &cs->event))
+ arcofi_fsm(cs, ARCOFI_TX_END, NULL);
+ */
+}
+
+static void
+W6692_empty_fifo(struct IsdnCardState *cs, int count)
+{
+ u_char *ptr;
+
+ if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
+ debugl1(cs, "W6692_empty_fifo");
+
+ if ((cs->rcvidx + count) >= MAX_DFRAME_LEN_L1) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "W6692_empty_fifo overrun %d",
+ cs->rcvidx + count);
+ cs->writeW6692(cs, W_D_CMDR, W_D_CMDR_RACK);
+ cs->rcvidx = 0;
+ return;
+ }
+ ptr = cs->rcvbuf + cs->rcvidx;
+ cs->rcvidx += count;
+ cs->readW6692fifo(cs, ptr, count);
+ cs->writeW6692(cs, W_D_CMDR, W_D_CMDR_RACK);
+ if (cs->debug & L1_DEB_ISAC_FIFO) {
+ char *t = cs->dlog;
+
+ t += sprintf(t, "W6692_empty_fifo cnt %d", count);
+ QuickHex(t, ptr, count);
+ debugl1(cs, cs->dlog);
+ }
+}
+
+static void
+W6692_fill_fifo(struct IsdnCardState *cs)
+{
+ int count, more;
+ u_char *ptr;
+
+ if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO))
+ debugl1(cs, "W6692_fill_fifo");
+
+ if (!cs->tx_skb)
+ return;
+
+ count = cs->tx_skb->len;
+ if (count <= 0)
+ return;
+
+ more = 0;
+ if (count > W_D_FIFO_THRESH) {
+ more = !0;
+ count = W_D_FIFO_THRESH;
+ }
+ ptr = cs->tx_skb->data;
+ skb_pull(cs->tx_skb, count);
+ cs->tx_cnt += count;
+ cs->writeW6692fifo(cs, ptr, count);
+ cs->writeW6692(cs, W_D_CMDR, more ? W_D_CMDR_XMS : (W_D_CMDR_XMS | W_D_CMDR_XME));
+ if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+ debugl1(cs, "W6692_fill_fifo dbusytimer running");
+ del_timer(&cs->dbusytimer);
+ }
+ init_timer(&cs->dbusytimer);
+ cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ) / 1000);
+ add_timer(&cs->dbusytimer);
+ if (cs->debug & L1_DEB_ISAC_FIFO) {
+ char *t = cs->dlog;
+
+ t += sprintf(t, "W6692_fill_fifo cnt %d", count);
+ QuickHex(t, ptr, count);
+ debugl1(cs, cs->dlog);
+ }
+}
+
+static void
+W6692B_empty_fifo(struct BCState *bcs, int count)
+{
+ u_char *ptr;
+ struct IsdnCardState *cs = bcs->cs;
+
+ if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+ debugl1(cs, "W6692B_empty_fifo");
+
+ if (bcs->hw.w6692.rcvidx + count > HSCX_BUFMAX) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "W6692B_empty_fifo: incoming packet too large");
+ cs->BC_Write_Reg(cs, bcs->channel, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);
+ bcs->hw.w6692.rcvidx = 0;
+ return;
+ }
+ ptr = bcs->hw.w6692.rcvbuf + bcs->hw.w6692.rcvidx;
+ bcs->hw.w6692.rcvidx += count;
+ READW6692BFIFO(cs, bcs->channel, ptr, count);
+ cs->BC_Write_Reg(cs, bcs->channel, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);
+ if (cs->debug & L1_DEB_HSCX_FIFO) {
+ char *t = bcs->blog;
+
+ t += sprintf(t, "W6692B_empty_fifo %c cnt %d",
+ bcs->channel + '1', count);
+ QuickHex(t, ptr, count);
+ debugl1(cs, bcs->blog);
+ }
+}
+
+static void
+W6692B_fill_fifo(struct BCState *bcs)
+{
+ struct IsdnCardState *cs = bcs->cs;
+ int more, count;
+ u_char *ptr;
+
+ if (!bcs->tx_skb)
+ return;
+ if (bcs->tx_skb->len <= 0)
+ return;
+
+ more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0;
+ if (bcs->tx_skb->len > W_B_FIFO_THRESH) {
+ more = 1;
+ count = W_B_FIFO_THRESH;
+ } else
+ count = bcs->tx_skb->len;
+
+ if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO))
+ debugl1(cs, "W6692B_fill_fifo%s%d", (more ? " ": " last "), count);
+
+ ptr = bcs->tx_skb->data;
+ skb_pull(bcs->tx_skb, count);
+ bcs->tx_cnt -= count;
+ bcs->hw.w6692.count += count;
+ WRITEW6692BFIFO(cs, bcs->channel, ptr, count);
+ cs->BC_Write_Reg(cs, bcs->channel, W_B_CMDR, W_B_CMDR_RACT | W_B_CMDR_XMS | (more ? 0 : W_B_CMDR_XME));
+ if (cs->debug & L1_DEB_HSCX_FIFO) {
+ char *t = bcs->blog;
+
+ t += sprintf(t, "W6692B_fill_fifo %c cnt %d",
+ bcs->channel + '1', count);
+ QuickHex(t, ptr, count);
+ debugl1(cs, bcs->blog);
+ }
+}
+
+static void
+W6692B_interrupt(struct IsdnCardState *cs, u_char bchan)
+{
+ u_char val;
+ u_char r;
+ struct BCState *bcs;
+ struct sk_buff *skb;
+ int count;
+
+ bcs = (cs->bcs->channel == bchan) ? cs->bcs : (cs->bcs+1);
+ val = cs->BC_Read_Reg(cs, bchan, W_B_EXIR);
+ debugl1(cs, "W6692B chan %d B_EXIR 0x%02X", bchan, val);
+
+ if (!test_bit(BC_FLG_INIT, &bcs->Flag)) {
+ debugl1(cs, "W6692B not INIT yet");
+ return;
+ }
+ if (val & W_B_EXI_RME) { /* RME */
+ r = cs->BC_Read_Reg(cs, bchan, W_B_STAR);
+ if (r & (W_B_STAR_RDOV | W_B_STAR_CRCE | W_B_STAR_RMB)) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "W6692 B STAR %x", r);
+ if ((r & W_B_STAR_RDOV) && bcs->mode)
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "W6692 B RDOV mode=%d",
+ bcs->mode);
+ if (r & W_B_STAR_CRCE)
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "W6692 B CRC error");
+ cs->BC_Write_Reg(cs, bchan, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RRST | W_B_CMDR_RACT);
+ } else {
+ count = cs->BC_Read_Reg(cs, bchan, W_B_RBCL) & (W_B_FIFO_THRESH - 1);
+ if (count == 0)
+ count = W_B_FIFO_THRESH;
+ W6692B_empty_fifo(bcs, count);
+ if ((count = bcs->hw.w6692.rcvidx) > 0) {
+ if (cs->debug & L1_DEB_HSCX_FIFO)
+ debugl1(cs, "W6692 Bchan Frame %d", count);
+ if (!(skb = dev_alloc_skb(count)))
+ printk(KERN_WARNING "W6692: Bchan receive out of memory\n");
+ else {
+ memcpy(skb_put(skb, count), bcs->hw.w6692.rcvbuf, count);
+ skb_queue_tail(&bcs->rqueue, skb);
+ }
+ }
+ }
+ bcs->hw.w6692.rcvidx = 0;
+ schedule_event(bcs, B_RCVBUFREADY);
+ }
+ if (val & W_B_EXI_RMR) { /* RMR */
+ W6692B_empty_fifo(bcs, W_B_FIFO_THRESH);
+ r = cs->BC_Read_Reg(cs, bchan, W_B_STAR);
+ if (r & W_B_STAR_RDOV) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "W6692 B RDOV(RMR) mode=%d",bcs->mode);
+ cs->BC_Write_Reg(cs, bchan, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RRST | W_B_CMDR_RACT);
+ if (bcs->mode != L1_MODE_TRANS)
+ bcs->hw.w6692.rcvidx = 0;
+ }
+ if (bcs->mode == L1_MODE_TRANS) {
+ /* receive audio data */
+ if (!(skb = dev_alloc_skb(W_B_FIFO_THRESH)))
+ printk(KERN_WARNING "HiSax: receive out of memory\n");
+ else {
+ memcpy(skb_put(skb, W_B_FIFO_THRESH), bcs->hw.w6692.rcvbuf, W_B_FIFO_THRESH);
+ skb_queue_tail(&bcs->rqueue, skb);
+ }
+ bcs->hw.w6692.rcvidx = 0;
+ schedule_event(bcs, B_RCVBUFREADY);
+ }
+ }
+ if (val & W_B_EXI_XDUN) { /* XDUN */
+ cs->BC_Write_Reg(cs, bchan, W_B_CMDR, W_B_CMDR_XRST | W_B_CMDR_RACT);
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "W6692 B EXIR %x Lost TX", val);
+ if (bcs->mode == 1)
+ W6692B_fill_fifo(bcs);
+ else {
+ /* Here we lost an TX interrupt, so
+ * restart transmitting the whole frame.
+ */
+ if (bcs->tx_skb) {
+ skb_push(bcs->tx_skb, bcs->hw.w6692.count);
+ bcs->tx_cnt += bcs->hw.w6692.count;
+ bcs->hw.w6692.count = 0;
+ }
+ }
+ return;
+ }
+ if (val & W_B_EXI_XFR) { /* XFR */
+ r = cs->BC_Read_Reg(cs, bchan, W_B_STAR);
+ if (r & W_B_STAR_XDOW) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "W6692 B STAR %x XDOW", r);
+ cs->BC_Write_Reg(cs, bchan, W_B_CMDR, W_B_CMDR_XRST | W_B_CMDR_RACT);
+ if (bcs->tx_skb && (bcs->mode != 1)) {
+ skb_push(bcs->tx_skb, bcs->hw.w6692.count);
+ bcs->tx_cnt += bcs->hw.w6692.count;
+ bcs->hw.w6692.count = 0;
+ }
+ }
+ if (bcs->tx_skb) {
+ if (bcs->tx_skb->len) {
+ W6692B_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.w6692.count;
+ spin_unlock_irqrestore(&bcs->aclock, flags);
+ schedule_event(bcs, B_ACKPENDING);
+ }
+ dev_kfree_skb_irq(bcs->tx_skb);
+ bcs->hw.w6692.count = 0;
+ bcs->tx_skb = NULL;
+ }
+ }
+ if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
+ bcs->hw.w6692.count = 0;
+ test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+ W6692B_fill_fifo(bcs);
+ } else {
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ schedule_event(bcs, B_XMTBUFREADY);
+ }
+ }
+}
+
+static irqreturn_t
+W6692_interrupt(int intno, void *dev_id)
+{
+ struct IsdnCardState *cs = dev_id;
+ u_char val, exval, v1;
+ struct sk_buff *skb;
+ u_int count;
+ u_long flags;
+ int icnt = 5;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ val = cs->readW6692(cs, W_ISTA);
+ if (!val) {
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_NONE;
+ }
+ StartW6692:
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "W6692 ISTA %x", val);
+
+ if (val & W_INT_D_RME) { /* RME */
+ exval = cs->readW6692(cs, W_D_RSTA);
+ if (exval & (W_D_RSTA_RDOV | W_D_RSTA_CRCE | W_D_RSTA_RMB)) {
+ if (exval & W_D_RSTA_RDOV)
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "W6692 RDOV");
+ if (exval & W_D_RSTA_CRCE)
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "W6692 D-channel CRC error");
+ if (exval & W_D_RSTA_RMB)
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "W6692 D-channel ABORT");
+ cs->writeW6692(cs, W_D_CMDR, W_D_CMDR_RACK | W_D_CMDR_RRST);
+ } else {
+ count = cs->readW6692(cs, W_D_RBCL) & (W_D_FIFO_THRESH - 1);
+ if (count == 0)
+ count = W_D_FIFO_THRESH;
+ W6692_empty_fifo(cs, count);
+ if ((count = cs->rcvidx) > 0) {
+ cs->rcvidx = 0;
+ if (!(skb = alloc_skb(count, GFP_ATOMIC)))
+ printk(KERN_WARNING "HiSax: D receive out of memory\n");
+ else {
+ memcpy(skb_put(skb, count), cs->rcvbuf, count);
+ skb_queue_tail(&cs->rq, skb);
+ }
+ }
+ }
+ cs->rcvidx = 0;
+ schedule_event(cs, D_RCVBUFREADY);
+ }
+ if (val & W_INT_D_RMR) { /* RMR */
+ W6692_empty_fifo(cs, W_D_FIFO_THRESH);
+ }
+ if (val & W_INT_D_XFR) { /* XFR */
+ if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+ del_timer(&cs->dbusytimer);
+ if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+ schedule_event(cs, D_CLEARBUSY);
+ if (cs->tx_skb) {
+ if (cs->tx_skb->len) {
+ W6692_fill_fifo(cs);
+ goto afterXFR;
+ } else {
+ dev_kfree_skb_irq(cs->tx_skb);
+ cs->tx_cnt = 0;
+ cs->tx_skb = NULL;
+ }
+ }
+ if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
+ cs->tx_cnt = 0;
+ W6692_fill_fifo(cs);
+ } else
+ schedule_event(cs, D_XMTBUFREADY);
+ }
+ afterXFR:
+ if (val & (W_INT_XINT0 | W_INT_XINT1)) { /* XINT0/1 - never */
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "W6692 spurious XINT!");
+ }
+ if (val & W_INT_D_EXI) { /* EXI */
+ exval = cs->readW6692(cs, W_D_EXIR);
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "W6692 D_EXIR %02x", exval);
+ if (exval & (W_D_EXI_XDUN | W_D_EXI_XCOL)) { /* Transmit underrun/collision */
+ debugl1(cs, "W6692 D-chan underrun/collision");
+ printk(KERN_WARNING "HiSax: W6692 XDUN/XCOL\n");
+ if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+ del_timer(&cs->dbusytimer);
+ if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+ schedule_event(cs, D_CLEARBUSY);
+ if (cs->tx_skb) { /* Restart frame */
+ skb_push(cs->tx_skb, cs->tx_cnt);
+ cs->tx_cnt = 0;
+ W6692_fill_fifo(cs);
+ } else {
+ printk(KERN_WARNING "HiSax: W6692 XDUN/XCOL no skb\n");
+ debugl1(cs, "W6692 XDUN/XCOL no skb");
+ cs->writeW6692(cs, W_D_CMDR, W_D_CMDR_XRST);
+ }
+ }
+ if (exval & W_D_EXI_RDOV) { /* RDOV */
+ debugl1(cs, "W6692 D-channel RDOV");
+ printk(KERN_WARNING "HiSax: W6692 D-RDOV\n");
+ cs->writeW6692(cs, W_D_CMDR, W_D_CMDR_RRST);
+ }
+ if (exval & W_D_EXI_TIN2) { /* TIN2 - never */
+ debugl1(cs, "W6692 spurious TIN2 interrupt");
+ }
+ if (exval & W_D_EXI_MOC) { /* MOC - not supported */
+ debugl1(cs, "W6692 spurious MOC interrupt");
+ v1 = cs->readW6692(cs, W_MOSR);
+ debugl1(cs, "W6692 MOSR %02x", v1);
+ }
+ if (exval & W_D_EXI_ISC) { /* ISC - Level1 change */
+ v1 = cs->readW6692(cs, W_CIR);
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "W6692 ISC CIR=0x%02X", v1);
+ if (v1 & W_CIR_ICC) {
+ cs->dc.w6692.ph_state = v1 & W_CIR_COD_MASK;
+ if (cs->debug & L1_DEB_ISAC)
+ debugl1(cs, "ph_state_change %x", cs->dc.w6692.ph_state);
+ schedule_event(cs, D_L1STATECHANGE);
+ }
+ if (v1 & W_CIR_SCC) {
+ v1 = cs->readW6692(cs, W_SQR);
+ debugl1(cs, "W6692 SCC SQR=0x%02X", v1);
+ }
+ }
+ if (exval & W_D_EXI_WEXP) {
+ debugl1(cs, "W6692 spurious WEXP interrupt!");
+ }
+ if (exval & W_D_EXI_TEXP) {
+ debugl1(cs, "W6692 spurious TEXP interrupt!");
+ }
+ }
+ if (val & W_INT_B1_EXI) {
+ debugl1(cs, "W6692 B channel 1 interrupt");
+ W6692B_interrupt(cs, 0);
+ }
+ if (val & W_INT_B2_EXI) {
+ debugl1(cs, "W6692 B channel 2 interrupt");
+ W6692B_interrupt(cs, 1);
+ }
+ val = cs->readW6692(cs, W_ISTA);
+ if (val && icnt) {
+ icnt--;
+ goto StartW6692;
+ }
+ if (!icnt) {
+ printk(KERN_WARNING "W6692 IRQ LOOP\n");
+ cs->writeW6692(cs, W_IMASK, 0xff);
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+ return IRQ_HANDLED;
+}
+
+static void
+W6692_l1hw(struct PStack *st, int pr, void *arg)
+{
+ struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
+ struct sk_buff *skb = arg;
+ u_long flags;
+ int val;
+
+ switch (pr) {
+ case (PH_DATA | REQUEST):
+ if (cs->debug & DEB_DLOG_HEX)
+ LogFrame(cs, skb->data, skb->len);
+ if (cs->debug & DEB_DLOG_VERBOSE)
+ dlogframe(cs, skb, 0);
+ spin_lock_irqsave(&cs->lock, flags);
+ if (cs->tx_skb) {
+ skb_queue_tail(&cs->sq, skb);
+#ifdef L2FRAME_DEBUG /* psa */
+ if (cs->debug & L1_DEB_LAPD)
+ Logl2Frame(cs, skb, "PH_DATA Queued", 0);
+#endif
+ } else {
+ cs->tx_skb = skb;
+ cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG /* psa */
+ if (cs->debug & L1_DEB_LAPD)
+ Logl2Frame(cs, skb, "PH_DATA", 0);
+#endif
+ W6692_fill_fifo(cs);
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (PH_PULL | INDICATION):
+ spin_lock_irqsave(&cs->lock, flags);
+ if (cs->tx_skb) {
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, " l2l1 tx_skb exist this shouldn't happen");
+ skb_queue_tail(&cs->sq, skb);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ }
+ if (cs->debug & DEB_DLOG_HEX)
+ LogFrame(cs, skb->data, skb->len);
+ if (cs->debug & DEB_DLOG_VERBOSE)
+ dlogframe(cs, skb, 0);
+ cs->tx_skb = skb;
+ cs->tx_cnt = 0;
+#ifdef L2FRAME_DEBUG /* psa */
+ if (cs->debug & L1_DEB_LAPD)
+ Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
+#endif
+ W6692_fill_fifo(cs);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (PH_PULL | REQUEST):
+#ifdef L2FRAME_DEBUG /* psa */
+ if (cs->debug & L1_DEB_LAPD)
+ debugl1(cs, "-> PH_REQUEST_PULL");
+#endif
+ if (!cs->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 (HW_RESET | REQUEST):
+ spin_lock_irqsave(&cs->lock, flags);
+ if ((cs->dc.w6692.ph_state == W_L1IND_DRD)) {
+ ph_command(cs, W_L1CMD_ECK);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ } else {
+ ph_command(cs, W_L1CMD_RST);
+ cs->dc.w6692.ph_state = W_L1CMD_RST;
+ spin_unlock_irqrestore(&cs->lock, flags);
+ W6692_new_ph(cs);
+ }
+ break;
+ case (HW_ENABLE | REQUEST):
+ spin_lock_irqsave(&cs->lock, flags);
+ ph_command(cs, W_L1CMD_ECK);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (HW_INFO3 | REQUEST):
+ spin_lock_irqsave(&cs->lock, flags);
+ ph_command(cs, W_L1CMD_AR8);
+ spin_unlock_irqrestore(&cs->lock, flags);
+ break;
+ case (HW_TESTLOOP | REQUEST):
+ val = 0;
+ if (1 & (long) arg)
+ val |= 0x0c;
+ if (2 & (long) arg)
+ val |= 0x3;
+ /* !!! not implemented yet */
+ break;
+ case (HW_DEACTIVATE | RESPONSE):
+ skb_queue_purge(&cs->rq);
+ skb_queue_purge(&cs->sq);
+ if (cs->tx_skb) {
+ dev_kfree_skb_any(cs->tx_skb);
+ cs->tx_skb = NULL;
+ }
+ if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
+ del_timer(&cs->dbusytimer);
+ if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags))
+ schedule_event(cs, D_CLEARBUSY);
+ break;
+ default:
+ if (cs->debug & L1_DEB_WARN)
+ debugl1(cs, "W6692_l1hw unknown %04x", pr);
+ break;
+ }
+}
+
+static void
+setstack_W6692(struct PStack *st, struct IsdnCardState *cs)
+{
+ st->l1.l1hw = W6692_l1hw;
+}
+
+static void
+DC_Close_W6692(struct IsdnCardState *cs)
+{
+}
+
+static void
+dbusy_timer_handler(struct IsdnCardState *cs)
+{
+ struct PStack *stptr;
+ int rbch, star;
+ u_long flags;
+
+ spin_lock_irqsave(&cs->lock, flags);
+ if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
+ rbch = cs->readW6692(cs, W_D_RBCH);
+ star = cs->readW6692(cs, W_D_STAR);
+ if (cs->debug)
+ debugl1(cs, "D-Channel Busy D_RBCH %02x D_STAR %02x",
+ rbch, star);
+ if (star & W_D_STAR_XBZ) { /* D-Channel Busy */
+ test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
+ stptr = cs->stlist;
+ while (stptr != NULL) {
+ stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL);
+ stptr = stptr->next;
+ }
+ } else {
+ /* discard frame; reset transceiver */
+ test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags);
+ if (cs->tx_skb) {
+ dev_kfree_skb_any(cs->tx_skb);
+ cs->tx_cnt = 0;
+ cs->tx_skb = NULL;
+ } else {
+ printk(KERN_WARNING "HiSax: W6692 D-Channel Busy no skb\n");
+ debugl1(cs, "D-Channel Busy no skb");
+ }
+ cs->writeW6692(cs, W_D_CMDR, W_D_CMDR_XRST); /* Transmitter reset */
+ spin_unlock_irqrestore(&cs->lock, flags);
+ cs->irq_func(cs->irq, cs);
+ return;
+ }
+ }
+ spin_unlock_irqrestore(&cs->lock, flags);
+}
+
+static void
+W6692Bmode(struct BCState *bcs, int mode, int bchan)
+{
+ struct IsdnCardState *cs = bcs->cs;
+
+ if (cs->debug & L1_DEB_HSCX)
+ debugl1(cs, "w6692 %c mode %d ichan %d",
+ '1' + bchan, mode, bchan);
+ bcs->mode = mode;
+ bcs->channel = bchan;
+ bcs->hw.w6692.bchan = bchan;
+
+ switch (mode) {
+ case (L1_MODE_NULL):
+ cs->BC_Write_Reg(cs, bchan, W_B_MODE, 0);
+ break;
+ case (L1_MODE_TRANS):
+ cs->BC_Write_Reg(cs, bchan, W_B_MODE, W_B_MODE_MMS);
+ break;
+ case (L1_MODE_HDLC):
+ cs->BC_Write_Reg(cs, bchan, W_B_MODE, W_B_MODE_ITF);
+ cs->BC_Write_Reg(cs, bchan, W_B_ADM1, 0xff);
+ cs->BC_Write_Reg(cs, bchan, W_B_ADM2, 0xff);
+ break;
+ }
+ if (mode)
+ cs->BC_Write_Reg(cs, bchan, W_B_CMDR, W_B_CMDR_RRST |
+ W_B_CMDR_RACT | W_B_CMDR_XRST);
+ cs->BC_Write_Reg(cs, bchan, W_B_EXIM, 0x00);
+}
+
+static void
+W6692_l2l1(struct PStack *st, int pr, void *arg)
+{
+ struct sk_buff *skb = arg;
+ struct BCState *bcs = st->l1.bcs;
+ 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);
+ bcs->hw.w6692.count = 0;
+ bcs->cs->BC_Send_Data(bcs);
+ }
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
+ break;
+ case (PH_PULL | INDICATION):
+ if (bcs->tx_skb) {
+ printk(KERN_WARNING "W6692_l2l1: this shouldn't happen\n");
+ break;
+ }
+ spin_lock_irqsave(&bcs->cs->lock, flags);
+ test_and_set_bit(BC_FLG_BUSY, &bcs->Flag);
+ bcs->tx_skb = skb;
+ bcs->hw.w6692.count = 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);
+ W6692Bmode(bcs, st->l1.mode, st->l1.bc);
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | REQUEST):
+ l1_msg_b(st, pr, arg);
+ break;
+ case (PH_DEACTIVATE | CONFIRM):
+ spin_lock_irqsave(&bcs->cs->lock, flags);
+ test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag);
+ test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag);
+ W6692Bmode(bcs, 0, st->l1.bc);
+ spin_unlock_irqrestore(&bcs->cs->lock, flags);
+ st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
+ break;
+ }
+}
+
+static void
+close_w6692state(struct BCState *bcs)
+{
+ W6692Bmode(bcs, 0, bcs->channel);
+ if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) {
+ kfree(bcs->hw.w6692.rcvbuf);
+ bcs->hw.w6692.rcvbuf = NULL;
+ kfree(bcs->blog);
+ bcs->blog = 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);
+ }
+ }
+}
+
+static int
+open_w6692state(struct IsdnCardState *cs, struct BCState *bcs)
+{
+ if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) {
+ if (!(bcs->hw.w6692.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "HiSax: No memory for w6692.rcvbuf\n");
+ test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+ return (1);
+ }
+ if (!(bcs->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) {
+ printk(KERN_WARNING
+ "HiSax: No memory for bcs->blog\n");
+ test_and_clear_bit(BC_FLG_INIT, &bcs->Flag);
+ kfree(bcs->hw.w6692.rcvbuf);
+ bcs->hw.w6692.rcvbuf = NULL;
+ return (2);
+ }
+ 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);
+ bcs->event = 0;
+ bcs->hw.w6692.rcvidx = 0;
+ bcs->tx_cnt = 0;
+ return (0);
+}
+
+static int
+setstack_w6692(struct PStack *st, struct BCState *bcs)
+{
+ bcs->channel = st->l1.bc;
+ if (open_w6692state(st->l1.hardware, bcs))
+ return (-1);
+ st->l1.bcs = bcs;
+ st->l2.l2l1 = W6692_l2l1;
+ setstack_manager(st);
+ bcs->st = st;
+ setstack_l1_B(st);
+ return (0);
+}
+
+static void resetW6692(struct IsdnCardState *cs)
+{
+ cs->writeW6692(cs, W_D_CTL, W_D_CTL_SRST);
+ mdelay(10);
+ cs->writeW6692(cs, W_D_CTL, 0x00);
+ mdelay(10);
+ cs->writeW6692(cs, W_IMASK, 0xff);
+ cs->writeW6692(cs, W_D_SAM, 0xff);
+ cs->writeW6692(cs, W_D_TAM, 0xff);
+ cs->writeW6692(cs, W_D_EXIM, 0x00);
+ cs->writeW6692(cs, W_D_MODE, W_D_MODE_RACT);
+ cs->writeW6692(cs, W_IMASK, 0x18);
+ if (cs->subtyp == W6692_USR) {
+ /* seems that USR implemented some power control features
+ * Pin 79 is connected to the oscilator circuit so we
+ * have to handle it here
+ */
+ cs->writeW6692(cs, W_PCTL, 0x80);
+ cs->writeW6692(cs, W_XDATA, 0x00);
+ }
+}
+
+static void initW6692(struct IsdnCardState *cs, int part)
+{
+ if (part & 1) {
+ cs->setstack_d = setstack_W6692;
+ cs->DC_Close = DC_Close_W6692;
+ cs->dbusytimer.function = (void *) dbusy_timer_handler;
+ cs->dbusytimer.data = (long) cs;
+ init_timer(&cs->dbusytimer);
+ resetW6692(cs);
+ ph_command(cs, W_L1CMD_RST);
+ cs->dc.w6692.ph_state = W_L1CMD_RST;
+ W6692_new_ph(cs);
+ ph_command(cs, W_L1CMD_ECK);
+
+ cs->bcs[0].BC_SetStack = setstack_w6692;
+ cs->bcs[1].BC_SetStack = setstack_w6692;
+ cs->bcs[0].BC_Close = close_w6692state;
+ cs->bcs[1].BC_Close = close_w6692state;
+ W6692Bmode(cs->bcs, 0, 0);
+ W6692Bmode(cs->bcs + 1, 0, 0);
+ }
+ if (part & 2) {
+ /* Reenable all IRQ */
+ cs->writeW6692(cs, W_IMASK, 0x18);
+ cs->writeW6692(cs, W_D_EXIM, 0x00);
+ cs->BC_Write_Reg(cs, 0, W_B_EXIM, 0x00);
+ cs->BC_Write_Reg(cs, 1, W_B_EXIM, 0x00);
+ /* Reset D-chan receiver and transmitter */
+ cs->writeW6692(cs, W_D_CMDR, W_D_CMDR_RRST | W_D_CMDR_XRST);
+ }
+}
+
+/* Interface functions */
+
+static u_char
+ReadW6692(struct IsdnCardState *cs, u_char offset)
+{
+ return (inb(cs->hw.w6692.iobase + offset));
+}
+
+static void
+WriteW6692(struct IsdnCardState *cs, u_char offset, u_char value)
+{
+ outb(value, cs->hw.w6692.iobase + offset);
+}
+
+static void
+ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ insb(cs->hw.w6692.iobase + W_D_RFIFO, data, size);
+}
+
+static void
+WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size)
+{
+ outsb(cs->hw.w6692.iobase + W_D_XFIFO, data, size);
+}
+
+static u_char
+ReadW6692B(struct IsdnCardState *cs, int bchan, u_char offset)
+{
+ return (inb(cs->hw.w6692.iobase + (bchan ? 0x40 : 0) + offset));
+}
+
+static void
+WriteW6692B(struct IsdnCardState *cs, int bchan, u_char offset, u_char value)
+{
+ outb(value, cs->hw.w6692.iobase + (bchan ? 0x40 : 0) + offset);
+}
+
+static int
+w6692_card_msg(struct IsdnCardState *cs, int mt, void *arg)
+{
+ switch (mt) {
+ case CARD_RESET:
+ resetW6692(cs);
+ return (0);
+ case CARD_RELEASE:
+ cs->writeW6692(cs, W_IMASK, 0xff);
+ release_region(cs->hw.w6692.iobase, 256);
+ if (cs->subtyp == W6692_USR) {
+ cs->writeW6692(cs, W_XDATA, 0x04);
+ }
+ return (0);
+ case CARD_INIT:
+ initW6692(cs, 3);
+ return (0);
+ case CARD_TEST:
+ return (0);
+ }
+ return (0);
+}
+
+static int id_idx ;
+
+static struct pci_dev *dev_w6692 __devinitdata = NULL;
+
+int __devinit
+setup_w6692(struct IsdnCard *card)
+{
+ struct IsdnCardState *cs = card->cs;
+ char tmp[64];
+ u_char found = 0;
+ u_char pci_irq = 0;
+ u_int pci_ioaddr = 0;
+
+ strcpy(tmp, w6692_revision);
+ printk(KERN_INFO "HiSax: W6692 driver Rev. %s\n", HiSax_getrev(tmp));
+ if (cs->typ != ISDN_CTYPE_W6692)
+ return (0);
+
+ while (id_list[id_idx].vendor_id) {
+ dev_w6692 = pci_find_device(id_list[id_idx].vendor_id,
+ id_list[id_idx].device_id,
+ dev_w6692);
+ if (dev_w6692) {
+ if (pci_enable_device(dev_w6692))
+ continue;
+ cs->subtyp = id_idx;
+ break;
+ }
+ id_idx++;
+ }
+ if (dev_w6692) {
+ found = 1;
+ pci_irq = dev_w6692->irq;
+ /* I think address 0 is allways the configuration area */
+ /* and address 1 is the real IO space KKe 03.09.99 */
+ pci_ioaddr = pci_resource_start(dev_w6692, 1);
+ /* USR ISDN PCI card TA need some special handling */
+ if (cs->subtyp == W6692_WINBOND) {
+ if ((W6692_SV_USR == dev_w6692->subsystem_vendor) &&
+ (W6692_SD_USR == dev_w6692->subsystem_device)) {
+ cs->subtyp = W6692_USR;
+ }
+ }
+ }
+ if (!found) {
+ printk(KERN_WARNING "W6692: No PCI card found\n");
+ return (0);
+ }
+ cs->irq = pci_irq;
+ if (!cs->irq) {
+ printk(KERN_WARNING "W6692: No IRQ for PCI card found\n");
+ return (0);
+ }
+ if (!pci_ioaddr) {
+ printk(KERN_WARNING "W6692: NO I/O Base Address found\n");
+ return (0);
+ }
+ cs->hw.w6692.iobase = pci_ioaddr;
+ printk(KERN_INFO "Found: %s %s, I/O base: 0x%x, irq: %d\n",
+ id_list[cs->subtyp].vendor_name, id_list[cs->subtyp].card_name,
+ pci_ioaddr, pci_irq);
+ if (!request_region(cs->hw.w6692.iobase, 256, id_list[cs->subtyp].card_name)) {
+ printk(KERN_WARNING
+ "HiSax: %s I/O ports %x-%x already in use\n",
+ id_list[cs->subtyp].card_name,
+ cs->hw.w6692.iobase,
+ cs->hw.w6692.iobase + 255);
+ return (0);
+ }
+
+ printk(KERN_INFO
+ "HiSax: %s config irq:%d I/O:%x\n",
+ id_list[cs->subtyp].card_name, cs->irq,
+ cs->hw.w6692.iobase);
+
+ INIT_WORK(&cs->tqueue, W6692_bh);
+ cs->readW6692 = &ReadW6692;
+ cs->writeW6692 = &WriteW6692;
+ cs->readisacfifo = &ReadISACfifo;
+ cs->writeisacfifo = &WriteISACfifo;
+ cs->BC_Read_Reg = &ReadW6692B;
+ cs->BC_Write_Reg = &WriteW6692B;
+ cs->BC_Send_Data = &W6692B_fill_fifo;
+ cs->cardmsg = &w6692_card_msg;
+ cs->irq_func = &W6692_interrupt;
+ cs->irq_flags |= IRQF_SHARED;
+ W6692Version(cs, "W6692:");
+ printk(KERN_INFO "W6692 ISTA=0x%X\n", ReadW6692(cs, W_ISTA));
+ printk(KERN_INFO "W6692 IMASK=0x%X\n", ReadW6692(cs, W_IMASK));
+ printk(KERN_INFO "W6692 D_EXIR=0x%X\n", ReadW6692(cs, W_D_EXIR));
+ printk(KERN_INFO "W6692 D_EXIM=0x%X\n", ReadW6692(cs, W_D_EXIM));
+ printk(KERN_INFO "W6692 D_RSTA=0x%X\n", ReadW6692(cs, W_D_RSTA));
+ return (1);
+}
diff --git a/drivers/isdn/hisax/w6692.h b/drivers/isdn/hisax/w6692.h
new file mode 100644
index 0000000..c79c81e
--- /dev/null
+++ b/drivers/isdn/hisax/w6692.h
@@ -0,0 +1,184 @@
+/* $Id: w6692.h,v 1.4.2.2 2004/01/12 22:52:29 keil Exp $
+ *
+ * Winbond W6692 specific defines
+ *
+ * Author Petr Novak
+ * Copyright by Petr Novak <petr.novak@i.cz>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/* map W6692 functions to ISAC functions */
+#define readW6692 readisac
+#define writeW6692 writeisac
+#define readW6692fifo readisacfifo
+#define writeW6692fifo writeisacfifo
+
+/* B-channel FIFO read/write routines */
+
+#define READW6692BFIFO(cs,bchan,ptr,count) \
+ insb(cs->hw.w6692.iobase+W_B_RFIFO+(bchan?0x40:0),ptr,count)
+
+#define WRITEW6692BFIFO(cs,bchan,ptr,count) \
+ outsb(cs->hw.w6692.iobase+W_B_XFIFO+(bchan?0x40:0),ptr,count)
+
+/* 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_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 --git a/drivers/isdn/hysdn/Kconfig b/drivers/isdn/hysdn/Kconfig
new file mode 100644
index 0000000..c9e4231
--- /dev/null
+++ b/drivers/isdn/hysdn/Kconfig
@@ -0,0 +1,18 @@
+#
+# Config.in for HYSDN ISDN driver
+#
+config HYSDN
+ tristate "Hypercope HYSDN cards (Champ, Ergo, Metro) support (module only)"
+ depends on m && PROC_FS && PCI
+ help
+ Say Y here if you have one of Hypercope's active PCI ISDN cards
+ Champ, Ergo and Metro. You will then get a module called hysdn.
+ Please read the file <file:Documentation/isdn/README.hysdn> for more
+ information.
+
+config HYSDN_CAPI
+ bool "HYSDN CAPI 2.0 support"
+ depends on HYSDN && ISDN_CAPI
+ help
+ Say Y here if you like to use Hypercope's CAPI 2.0 interface.
+
diff --git a/drivers/isdn/hysdn/Makefile b/drivers/isdn/hysdn/Makefile
new file mode 100644
index 0000000..da63b63
--- /dev/null
+++ b/drivers/isdn/hysdn/Makefile
@@ -0,0 +1,11 @@
+# Makefile for the hysdn ISDN device driver
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_HYSDN) += hysdn.o
+
+# Multipart objects.
+
+hysdn-y := hysdn_procconf.o hysdn_proclog.o boardergo.o \
+ hysdn_boot.o hysdn_sched.o hysdn_net.o hysdn_init.o
+hysdn-$(CONFIG_HYSDN_CAPI) += hycapi.o
diff --git a/drivers/isdn/hysdn/boardergo.c b/drivers/isdn/hysdn/boardergo.c
new file mode 100644
index 0000000..3eb096f
--- /dev/null
+++ b/drivers/isdn/hysdn/boardergo.c
@@ -0,0 +1,445 @@
+/* $Id: boardergo.c,v 1.5.6.7 2001/11/06 21:58:19 kai Exp $
+ *
+ * Linux driver for HYSDN cards, specific routines for ergo type boards.
+ *
+ * Author Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * As all Linux supported cards Champ2, Ergo and Metro2/4 use the same
+ * DPRAM interface and layout with only minor differences all related
+ * stuff is done here, not in separate modules.
+ *
+ */
+
+#include <linux/signal.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+
+#include "hysdn_defs.h"
+#include "boardergo.h"
+
+#define byteout(addr,val) outb(val,addr)
+#define bytein(addr) inb(addr)
+
+/***************************************************/
+/* The cards interrupt handler. Called from system */
+/***************************************************/
+static irqreturn_t
+ergo_interrupt(int intno, void *dev_id)
+{
+ hysdn_card *card = dev_id; /* parameter from irq */
+ tErgDpram *dpr;
+ unsigned long flags;
+ unsigned char volatile b;
+
+ if (!card)
+ return IRQ_NONE; /* error -> spurious interrupt */
+ if (!card->irq_enabled)
+ return IRQ_NONE; /* other device interrupting or irq switched off */
+
+ spin_lock_irqsave(&card->hysdn_lock, flags); /* no further irqs allowed */
+
+ if (!(bytein(card->iobase + PCI9050_INTR_REG) & PCI9050_INTR_REG_STAT1)) {
+ spin_unlock_irqrestore(&card->hysdn_lock, flags); /* restore old state */
+ return IRQ_NONE; /* no interrupt requested by E1 */
+ }
+ /* clear any pending ints on the board */
+ dpr = card->dpram;
+ b = dpr->ToPcInt; /* clear for ergo */
+ b |= dpr->ToPcIntMetro; /* same for metro */
+ b |= dpr->ToHyInt; /* and for champ */
+
+ /* start kernel task immediately after leaving all interrupts */
+ if (!card->hw_lock)
+ schedule_work(&card->irq_queue);
+ spin_unlock_irqrestore(&card->hysdn_lock, flags);
+ return IRQ_HANDLED;
+} /* ergo_interrupt */
+
+/******************************************************************************/
+/* ergo_irq_bh will be called as part of the kernel clearing its shared work */
+/* queue sometime after a call to schedule_work has been made passing our */
+/* work_struct. This task is the only one handling data transfer from or to */
+/* the card after booting. The task may be queued from everywhere */
+/* (interrupts included). */
+/******************************************************************************/
+static void
+ergo_irq_bh(struct work_struct *ugli_api)
+{
+ hysdn_card * card = container_of(ugli_api, hysdn_card, irq_queue);
+ tErgDpram *dpr;
+ int again;
+ unsigned long flags;
+
+ if (card->state != CARD_STATE_RUN)
+ return; /* invalid call */
+
+ dpr = card->dpram; /* point to DPRAM */
+
+ spin_lock_irqsave(&card->hysdn_lock, flags);
+ if (card->hw_lock) {
+ spin_unlock_irqrestore(&card->hysdn_lock, flags); /* hardware currently unavailable */
+ return;
+ }
+ card->hw_lock = 1; /* we now lock the hardware */
+
+ do {
+ again = 0; /* assume loop not to be repeated */
+
+ if (!dpr->ToHyFlag) {
+ /* we are able to send a buffer */
+
+ if (hysdn_sched_tx(card, dpr->ToHyBuf, &dpr->ToHySize, &dpr->ToHyChannel,
+ ERG_TO_HY_BUF_SIZE)) {
+ dpr->ToHyFlag = 1; /* enable tx */
+ again = 1; /* restart loop */
+ }
+ } /* we are able to send a buffer */
+ if (dpr->ToPcFlag) {
+ /* a message has arrived for us, handle it */
+
+ if (hysdn_sched_rx(card, dpr->ToPcBuf, dpr->ToPcSize, dpr->ToPcChannel)) {
+ dpr->ToPcFlag = 0; /* we worked the data */
+ again = 1; /* restart loop */
+ }
+ } /* a message has arrived for us */
+ if (again) {
+ dpr->ToHyInt = 1;
+ dpr->ToPcInt = 1; /* interrupt to E1 for all cards */
+ } else
+ card->hw_lock = 0; /* free hardware again */
+ } while (again); /* until nothing more to do */
+
+ spin_unlock_irqrestore(&card->hysdn_lock, flags);
+} /* ergo_irq_bh */
+
+
+/*********************************************************/
+/* stop the card (hardware reset) and disable interrupts */
+/*********************************************************/
+static void
+ergo_stopcard(hysdn_card * card)
+{
+ unsigned long flags;
+ unsigned char val;
+
+ hysdn_net_release(card); /* first release the net device if existing */
+#ifdef CONFIG_HYSDN_CAPI
+ hycapi_capi_stop(card);
+#endif /* CONFIG_HYSDN_CAPI */
+ spin_lock_irqsave(&card->hysdn_lock, flags);
+ val = bytein(card->iobase + PCI9050_INTR_REG); /* get actual value */
+ val &= ~(PCI9050_INTR_REG_ENPCI | PCI9050_INTR_REG_EN1); /* mask irq */
+ byteout(card->iobase + PCI9050_INTR_REG, val);
+ card->irq_enabled = 0;
+ byteout(card->iobase + PCI9050_USER_IO, PCI9050_E1_RESET); /* reset E1 processor */
+ card->state = CARD_STATE_UNUSED;
+ card->err_log_state = ERRLOG_STATE_OFF; /* currently no log active */
+
+ spin_unlock_irqrestore(&card->hysdn_lock, flags);
+} /* ergo_stopcard */
+
+/**************************************************************************/
+/* enable or disable the cards error log. The event is queued if possible */
+/**************************************************************************/
+static void
+ergo_set_errlog_state(hysdn_card * card, int on)
+{
+ unsigned long flags;
+
+ if (card->state != CARD_STATE_RUN) {
+ card->err_log_state = ERRLOG_STATE_OFF; /* must be off */
+ return;
+ }
+ spin_lock_irqsave(&card->hysdn_lock, flags);
+
+ if (((card->err_log_state == ERRLOG_STATE_OFF) && !on) ||
+ ((card->err_log_state == ERRLOG_STATE_ON) && on)) {
+ spin_unlock_irqrestore(&card->hysdn_lock, flags);
+ return; /* nothing to do */
+ }
+ if (on)
+ card->err_log_state = ERRLOG_STATE_START; /* request start */
+ else
+ card->err_log_state = ERRLOG_STATE_STOP; /* request stop */
+
+ spin_unlock_irqrestore(&card->hysdn_lock, flags);
+ schedule_work(&card->irq_queue);
+} /* ergo_set_errlog_state */
+
+/******************************************/
+/* test the cards RAM and return 0 if ok. */
+/******************************************/
+static const char TestText[36] = "This Message is filler, why read it";
+
+static int
+ergo_testram(hysdn_card * card)
+{
+ tErgDpram *dpr = card->dpram;
+
+ memset(dpr->TrapTable, 0, sizeof(dpr->TrapTable)); /* clear all Traps */
+ dpr->ToHyInt = 1; /* E1 INTR state forced */
+
+ memcpy(&dpr->ToHyBuf[ERG_TO_HY_BUF_SIZE - sizeof(TestText)], TestText,
+ sizeof(TestText));
+ if (memcmp(&dpr->ToHyBuf[ERG_TO_HY_BUF_SIZE - sizeof(TestText)], TestText,
+ sizeof(TestText)))
+ return (-1);
+
+ memcpy(&dpr->ToPcBuf[ERG_TO_PC_BUF_SIZE - sizeof(TestText)], TestText,
+ sizeof(TestText));
+ if (memcmp(&dpr->ToPcBuf[ERG_TO_PC_BUF_SIZE - sizeof(TestText)], TestText,
+ sizeof(TestText)))
+ return (-1);
+
+ return (0);
+} /* ergo_testram */
+
+/*****************************************************************************/
+/* this function is intended to write stage 1 boot image to the cards buffer */
+/* this is done in two steps. First the 1024 hi-words are written (offs=0), */
+/* then the 1024 lo-bytes are written. The remaining DPRAM is cleared, the */
+/* PCI-write-buffers flushed and the card is taken out of reset. */
+/* The function then waits for a reaction of the E1 processor or a timeout. */
+/* Negative return values are interpreted as errors. */
+/*****************************************************************************/
+static int
+ergo_writebootimg(struct HYSDN_CARD *card, unsigned char *buf,
+ unsigned long offs)
+{
+ unsigned char *dst;
+ tErgDpram *dpram;
+ int cnt = (BOOT_IMG_SIZE >> 2); /* number of words to move and swap (byte order!) */
+
+ if (card->debug_flags & LOG_POF_CARD)
+ hysdn_addlog(card, "ERGO: write bootldr offs=0x%lx ", offs);
+
+ dst = card->dpram; /* pointer to start of DPRAM */
+ dst += (offs + ERG_DPRAM_FILL_SIZE); /* offset in the DPRAM */
+ while (cnt--) {
+ *dst++ = *(buf + 1); /* high byte */
+ *dst++ = *buf; /* low byte */
+ dst += 2; /* point to next longword */
+ buf += 2; /* buffer only filled with words */
+ }
+
+ /* if low words (offs = 2) have been written, clear the rest of the DPRAM, */
+ /* flush the PCI-write-buffer and take the E1 out of reset */
+ if (offs) {
+ memset(card->dpram, 0, ERG_DPRAM_FILL_SIZE); /* fill the DPRAM still not cleared */
+ dpram = card->dpram; /* get pointer to dpram structure */
+ dpram->ToHyNoDpramErrLog = 0xFF; /* write a dpram register */
+ while (!dpram->ToHyNoDpramErrLog); /* reread volatile register to flush PCI */
+
+ byteout(card->iobase + PCI9050_USER_IO, PCI9050_E1_RUN); /* start E1 processor */
+ /* the interrupts are still masked */
+
+ msleep_interruptible(20); /* Timeout 20ms */
+
+ if (((tDpramBootSpooler *) card->dpram)->Len != DPRAM_SPOOLER_DATA_SIZE) {
+ if (card->debug_flags & LOG_POF_CARD)
+ hysdn_addlog(card, "ERGO: write bootldr no answer");
+ return (-ERR_BOOTIMG_FAIL);
+ }
+ } /* start_boot_img */
+ return (0); /* successful */
+} /* ergo_writebootimg */
+
+/********************************************************************************/
+/* ergo_writebootseq writes the buffer containing len bytes to the E1 processor */
+/* using the boot spool mechanism. If everything works fine 0 is returned. In */
+/* case of errors a negative error value is returned. */
+/********************************************************************************/
+static int
+ergo_writebootseq(struct HYSDN_CARD *card, unsigned char *buf, int len)
+{
+ tDpramBootSpooler *sp = (tDpramBootSpooler *) card->dpram;
+ unsigned char *dst;
+ unsigned char buflen;
+ int nr_write;
+ unsigned char tmp_rdptr;
+ unsigned char wr_mirror;
+ int i;
+
+ if (card->debug_flags & LOG_POF_CARD)
+ hysdn_addlog(card, "ERGO: write boot seq len=%d ", len);
+
+ dst = sp->Data; /* point to data in spool structure */
+ buflen = sp->Len; /* maximum len of spooled data */
+ wr_mirror = sp->WrPtr; /* only once read */
+
+ /* try until all bytes written or error */
+ i = 0x1000; /* timeout value */
+ while (len) {
+
+ /* first determine the number of bytes that may be buffered */
+ do {
+ tmp_rdptr = sp->RdPtr; /* first read the pointer */
+ i--; /* decrement timeout */
+ } while (i && (tmp_rdptr != sp->RdPtr)); /* wait for stable pointer */
+
+ if (!i) {
+ if (card->debug_flags & LOG_POF_CARD)
+ hysdn_addlog(card, "ERGO: write boot seq timeout");
+ return (-ERR_BOOTSEQ_FAIL); /* value not stable -> timeout */
+ }
+ if ((nr_write = tmp_rdptr - wr_mirror - 1) < 0)
+ nr_write += buflen; /* now we got number of free bytes - 1 in buffer */
+
+ if (!nr_write)
+ continue; /* no free bytes in buffer */
+
+ if (nr_write > len)
+ nr_write = len; /* limit if last few bytes */
+ i = 0x1000; /* reset timeout value */
+
+ /* now we know how much bytes we may put in the puffer */
+ len -= nr_write; /* we savely could adjust len before output */
+ while (nr_write--) {
+ *(dst + wr_mirror) = *buf++; /* output one byte */
+ if (++wr_mirror >= buflen)
+ wr_mirror = 0;
+ sp->WrPtr = wr_mirror; /* announce the next byte to E1 */
+ } /* while (nr_write) */
+
+ } /* while (len) */
+ return (0);
+} /* ergo_writebootseq */
+
+/***********************************************************************************/
+/* ergo_waitpofready waits for a maximum of 10 seconds for the completition of the */
+/* boot process. If the process has been successful 0 is returned otherwise a */
+/* negative error code is returned. */
+/***********************************************************************************/
+static int
+ergo_waitpofready(struct HYSDN_CARD *card)
+{
+ tErgDpram *dpr = card->dpram; /* pointer to DPRAM structure */
+ int timecnt = 10000 / 50; /* timeout is 10 secs max. */
+ unsigned long flags;
+ int msg_size;
+ int i;
+
+ if (card->debug_flags & LOG_POF_CARD)
+ hysdn_addlog(card, "ERGO: waiting for pof ready");
+ while (timecnt--) {
+ /* wait until timeout */
+
+ if (dpr->ToPcFlag) {
+ /* data has arrived */
+
+ if ((dpr->ToPcChannel != CHAN_SYSTEM) ||
+ (dpr->ToPcSize < MIN_RDY_MSG_SIZE) ||
+ (dpr->ToPcSize > MAX_RDY_MSG_SIZE) ||
+ ((*(unsigned long *) dpr->ToPcBuf) != RDY_MAGIC))
+ break; /* an error occurred */
+
+ /* Check for additional data delivered during SysReady */
+ msg_size = dpr->ToPcSize - RDY_MAGIC_SIZE;
+ if (msg_size > 0)
+ if (EvalSysrTokData(card, dpr->ToPcBuf + RDY_MAGIC_SIZE, msg_size))
+ break;
+
+ if (card->debug_flags & LOG_POF_RECORD)
+ hysdn_addlog(card, "ERGO: pof boot success");
+ spin_lock_irqsave(&card->hysdn_lock, flags);
+
+ card->state = CARD_STATE_RUN; /* now card is running */
+ /* enable the cards interrupt */
+ byteout(card->iobase + PCI9050_INTR_REG,
+ bytein(card->iobase + PCI9050_INTR_REG) |
+ (PCI9050_INTR_REG_ENPCI | PCI9050_INTR_REG_EN1));
+ card->irq_enabled = 1; /* we are ready to receive interrupts */
+
+ dpr->ToPcFlag = 0; /* reset data indicator */
+ dpr->ToHyInt = 1;
+ dpr->ToPcInt = 1; /* interrupt to E1 for all cards */
+
+ spin_unlock_irqrestore(&card->hysdn_lock, flags);
+ if ((hynet_enable & (1 << card->myid))
+ && (i = hysdn_net_create(card)))
+ {
+ ergo_stopcard(card);
+ card->state = CARD_STATE_BOOTERR;
+ return (i);
+ }
+#ifdef CONFIG_HYSDN_CAPI
+ if((i = hycapi_capi_create(card))) {
+ printk(KERN_WARNING "HYSDN: failed to create capi-interface.\n");
+ }
+#endif /* CONFIG_HYSDN_CAPI */
+ return (0); /* success */
+ } /* data has arrived */
+ msleep_interruptible(50); /* Timeout 50ms */
+ } /* wait until timeout */
+
+ if (card->debug_flags & LOG_POF_CARD)
+ hysdn_addlog(card, "ERGO: pof boot ready timeout");
+ return (-ERR_POF_TIMEOUT);
+} /* ergo_waitpofready */
+
+
+
+/************************************************************************************/
+/* release the cards hardware. Before releasing do a interrupt disable and hardware */
+/* reset. Also unmap dpram. */
+/* Use only during module release. */
+/************************************************************************************/
+static void
+ergo_releasehardware(hysdn_card * card)
+{
+ ergo_stopcard(card); /* first stop the card if not already done */
+ free_irq(card->irq, card); /* release interrupt */
+ release_region(card->iobase + PCI9050_INTR_REG, 1); /* release all io ports */
+ release_region(card->iobase + PCI9050_USER_IO, 1);
+ iounmap(card->dpram);
+ card->dpram = NULL; /* release shared mem */
+} /* ergo_releasehardware */
+
+
+/*********************************************************************************/
+/* acquire the needed hardware ports and map dpram. If an error occurs a nonzero */
+/* value is returned. */
+/* Use only during module init. */
+/*********************************************************************************/
+int
+ergo_inithardware(hysdn_card * card)
+{
+ if (!request_region(card->iobase + PCI9050_INTR_REG, 1, "HYSDN"))
+ return (-1);
+ if (!request_region(card->iobase + PCI9050_USER_IO, 1, "HYSDN")) {
+ release_region(card->iobase + PCI9050_INTR_REG, 1);
+ return (-1); /* ports already in use */
+ }
+ card->memend = card->membase + ERG_DPRAM_PAGE_SIZE - 1;
+ if (!(card->dpram = ioremap(card->membase, ERG_DPRAM_PAGE_SIZE))) {
+ release_region(card->iobase + PCI9050_INTR_REG, 1);
+ release_region(card->iobase + PCI9050_USER_IO, 1);
+ return (-1);
+ }
+
+ ergo_stopcard(card); /* disable interrupts */
+ if (request_irq(card->irq, ergo_interrupt, IRQF_SHARED, "HYSDN", card)) {
+ ergo_releasehardware(card); /* return the acquired hardware */
+ return (-1);
+ }
+ /* success, now setup the function pointers */
+ card->stopcard = ergo_stopcard;
+ card->releasehardware = ergo_releasehardware;
+ card->testram = ergo_testram;
+ card->writebootimg = ergo_writebootimg;
+ card->writebootseq = ergo_writebootseq;
+ card->waitpofready = ergo_waitpofready;
+ card->set_errlog_state = ergo_set_errlog_state;
+ INIT_WORK(&card->irq_queue, ergo_irq_bh);
+ spin_lock_init(&card->hysdn_lock);
+
+ return (0);
+} /* ergo_inithardware */
diff --git a/drivers/isdn/hysdn/boardergo.h b/drivers/isdn/hysdn/boardergo.h
new file mode 100644
index 0000000..c59422a
--- /dev/null
+++ b/drivers/isdn/hysdn/boardergo.h
@@ -0,0 +1,100 @@
+/* $Id: boardergo.h,v 1.2.6.1 2001/09/23 22:24:54 kai Exp $
+ *
+ * Linux driver for HYSDN cards, definitions for ergo type boards (buffers..).
+ *
+ * Author Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+
+/************************************************/
+/* defines for the dual port memory of the card */
+/************************************************/
+#define ERG_DPRAM_PAGE_SIZE 0x2000 /* DPRAM occupies a 8K page */
+#define BOOT_IMG_SIZE 4096
+#define ERG_DPRAM_FILL_SIZE (ERG_DPRAM_PAGE_SIZE - BOOT_IMG_SIZE)
+
+#define ERG_TO_HY_BUF_SIZE 0x0E00 /* 3072 bytes buffer size to card */
+#define ERG_TO_PC_BUF_SIZE 0x0E00 /* 3072 bytes to PC, too */
+
+/* following DPRAM layout copied from OS2-driver boarderg.h */
+typedef struct ErgDpram_tag {
+/*0000 */ unsigned char ToHyBuf[ERG_TO_HY_BUF_SIZE];
+/*0E00 */ unsigned char ToPcBuf[ERG_TO_PC_BUF_SIZE];
+
+ /*1C00 */ unsigned char bSoftUart[SIZE_RSV_SOFT_UART];
+ /* size 0x1B0 */
+
+ /*1DB0 *//* tErrLogEntry */ unsigned char volatile ErrLogMsg[64];
+ /* size 64 bytes */
+ /*1DB0 unsigned long ulErrType; */
+ /*1DB4 unsigned long ulErrSubtype; */
+ /*1DB8 unsigned long ucTextSize; */
+ /*1DB9 unsigned long ucText[ERRLOG_TEXT_SIZE]; *//* ASCIIZ of len ucTextSize-1 */
+ /*1DF0 */
+
+/*1DF0 */ unsigned short volatile ToHyChannel;
+/*1DF2 */ unsigned short volatile ToHySize;
+ /*1DF4 */ unsigned char volatile ToHyFlag;
+ /* !=0: msg for Hy waiting */
+ /*1DF5 */ unsigned char volatile ToPcFlag;
+ /* !=0: msg for PC waiting */
+/*1DF6 */ unsigned short volatile ToPcChannel;
+/*1DF8 */ unsigned short volatile ToPcSize;
+ /*1DFA */ unsigned char bRes1DBA[0x1E00 - 0x1DFA];
+ /* 6 bytes */
+
+/*1E00 */ unsigned char bRestOfEntryTbl[0x1F00 - 0x1E00];
+/*1F00 */ unsigned long TrapTable[62];
+ /*1FF8 */ unsigned char bRes1FF8[0x1FFB - 0x1FF8];
+ /* low part of reset vetor */
+/*1FFB */ unsigned char ToPcIntMetro;
+ /* notes:
+ * - metro has 32-bit boot ram - accessing
+ * ToPcInt and ToHyInt would be the same;
+ * so we moved ToPcInt to 1FFB.
+ * Because on the PC side both vars are
+ * readonly (reseting on int from E1 to PC),
+ * we can read both vars on both cards
+ * without destroying anything.
+ * - 1FFB is the high byte of the reset vector,
+ * so E1 side should NOT change this byte
+ * when writing!
+ */
+/*1FFC */ unsigned char volatile ToHyNoDpramErrLog;
+ /* note: ToHyNoDpramErrLog is used to inform
+ * boot loader, not to use DPRAM based
+ * ErrLog; when DOS driver is rewritten
+ * this becomes obsolete
+ */
+/*1FFD */ unsigned char bRes1FFD;
+ /*1FFE */ unsigned char ToPcInt;
+ /* E1_intclear; on CHAMP2: E1_intset */
+ /*1FFF */ unsigned char ToHyInt;
+ /* E1_intset; on CHAMP2: E1_intclear */
+} tErgDpram;
+
+/**********************************************/
+/* PCI9050 controller local register offsets: */
+/* copied from boarderg.c */
+/**********************************************/
+#define PCI9050_INTR_REG 0x4C /* Interrupt register */
+#define PCI9050_USER_IO 0x51 /* User I/O register */
+
+ /* bitmask for PCI9050_INTR_REG: */
+#define PCI9050_INTR_REG_EN1 0x01 /* 1= enable (def.), 0= disable */
+#define PCI9050_INTR_REG_POL1 0x02 /* 1= active high (def.), 0= active low */
+#define PCI9050_INTR_REG_STAT1 0x04 /* 1= intr. active, 0= intr. not active (def.) */
+#define PCI9050_INTR_REG_ENPCI 0x40 /* 1= PCI interrupts enable (def.) */
+
+ /* bitmask for PCI9050_USER_IO: */
+#define PCI9050_USER_IO_EN3 0x02 /* 1= disable , 0= enable (def.) */
+#define PCI9050_USER_IO_DIR3 0x04 /* 1= output (def.), 0= input */
+#define PCI9050_USER_IO_DAT3 0x08 /* 1= high (def.) , 0= low */
+
+#define PCI9050_E1_RESET ( PCI9050_USER_IO_DIR3) /* 0x04 */
+#define PCI9050_E1_RUN (PCI9050_USER_IO_DAT3|PCI9050_USER_IO_DIR3) /* 0x0C */
diff --git a/drivers/isdn/hysdn/hycapi.c b/drivers/isdn/hysdn/hycapi.c
new file mode 100644
index 0000000..53f6ad1
--- /dev/null
+++ b/drivers/isdn/hysdn/hycapi.c
@@ -0,0 +1,798 @@
+/* $Id: hycapi.c,v 1.8.6.4 2001/09/23 22:24:54 kai Exp $
+ *
+ * Linux driver for HYSDN cards, CAPI2.0-Interface.
+ *
+ * Author Ulrich Albrecht <u.albrecht@hypercope.de> for Hypercope GmbH
+ * Copyright 2000 by Hypercope GmbH
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/signal.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+
+#define VER_DRIVER 0
+#define VER_CARDTYPE 1
+#define VER_HWID 2
+#define VER_SERIAL 3
+#define VER_OPTION 4
+#define VER_PROTO 5
+#define VER_PROFILE 6
+#define VER_CAPI 7
+
+#include "hysdn_defs.h"
+#include <linux/kernelcapi.h>
+
+static char hycapi_revision[]="$Revision: 1.8.6.4 $";
+
+unsigned int hycapi_enable = 0xffffffff;
+module_param(hycapi_enable, uint, 0);
+
+typedef struct _hycapi_appl {
+ unsigned int ctrl_mask;
+ capi_register_params rp;
+ struct sk_buff *listen_req[CAPI_MAXCONTR];
+} hycapi_appl;
+
+static hycapi_appl hycapi_applications[CAPI_MAXAPPL];
+
+static u16 hycapi_send_message(struct capi_ctr *ctrl, struct sk_buff *skb);
+
+static inline int _hycapi_appCheck(int app_id, int ctrl_no)
+{
+ if((ctrl_no <= 0) || (ctrl_no > CAPI_MAXCONTR) || (app_id <= 0) ||
+ (app_id > CAPI_MAXAPPL))
+ {
+ printk(KERN_ERR "HYCAPI: Invalid request app_id %d for controller %d", app_id, ctrl_no);
+ return -1;
+ }
+ return ((hycapi_applications[app_id-1].ctrl_mask & (1 << (ctrl_no-1))) != 0);
+}
+
+/******************************
+Kernel-Capi callback reset_ctr
+******************************/
+
+static void
+hycapi_reset_ctr(struct capi_ctr *ctrl)
+{
+ hycapictrl_info *cinfo = ctrl->driverdata;
+
+#ifdef HYCAPI_PRINTFNAMES
+ printk(KERN_NOTICE "HYCAPI hycapi_reset_ctr\n");
+#endif
+ capilib_release(&cinfo->ncci_head);
+ capi_ctr_reseted(ctrl);
+}
+
+/******************************
+Kernel-Capi callback remove_ctr
+******************************/
+
+static void
+hycapi_remove_ctr(struct capi_ctr *ctrl)
+{
+ int i;
+ hycapictrl_info *cinfo = NULL;
+ hysdn_card *card = NULL;
+#ifdef HYCAPI_PRINTFNAMES
+ printk(KERN_NOTICE "HYCAPI hycapi_remove_ctr\n");
+#endif
+ cinfo = (hycapictrl_info *)(ctrl->driverdata);
+ if(!cinfo) {
+ printk(KERN_ERR "No hycapictrl_info set!");
+ return;
+ }
+ card = cinfo->card;
+ capi_ctr_suspend_output(ctrl);
+ for(i=0; i<CAPI_MAXAPPL;i++) {
+ if(hycapi_applications[i].listen_req[ctrl->cnr-1]) {
+ kfree_skb(hycapi_applications[i].listen_req[ctrl->cnr-1]);
+ hycapi_applications[i].listen_req[ctrl->cnr-1] = NULL;
+ }
+ }
+ detach_capi_ctr(ctrl);
+ ctrl->driverdata = NULL;
+ kfree(card->hyctrlinfo);
+
+
+ card->hyctrlinfo = NULL;
+}
+
+/***********************************************************
+
+Queue a CAPI-message to the controller.
+
+***********************************************************/
+
+static void
+hycapi_sendmsg_internal(struct capi_ctr *ctrl, struct sk_buff *skb)
+{
+ hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
+ hysdn_card *card = cinfo->card;
+
+ spin_lock_irq(&cinfo->lock);
+#ifdef HYCAPI_PRINTFNAMES
+ printk(KERN_NOTICE "hycapi_send_message\n");
+#endif
+ cinfo->skbs[cinfo->in_idx++] = skb; /* add to buffer list */
+ if (cinfo->in_idx >= HYSDN_MAX_CAPI_SKB)
+ cinfo->in_idx = 0; /* wrap around */
+ cinfo->sk_count++; /* adjust counter */
+ if (cinfo->sk_count >= HYSDN_MAX_CAPI_SKB) {
+ /* inform upper layers we're full */
+ printk(KERN_ERR "HYSDN Card%d: CAPI-buffer overrun!\n",
+ card->myid);
+ capi_ctr_suspend_output(ctrl);
+ }
+ cinfo->tx_skb = skb;
+ spin_unlock_irq(&cinfo->lock);
+ schedule_work(&card->irq_queue);
+}
+
+/***********************************************************
+hycapi_register_internal
+
+Send down the CAPI_REGISTER-Command to the controller.
+This functions will also be used if the adapter has been rebooted to
+re-register any applications in the private list.
+
+************************************************************/
+
+static void
+hycapi_register_internal(struct capi_ctr *ctrl, __u16 appl,
+ capi_register_params *rp)
+{
+ char ExtFeatureDefaults[] = "49 /0/0/0/0,*/1,*/2,*/3,*/4,*/5,*/6,*/7,*/8,*/9,*";
+ hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
+ hysdn_card *card = cinfo->card;
+ struct sk_buff *skb;
+ __u16 len;
+ __u8 _command = 0xa0, _subcommand = 0x80;
+ __u16 MessageNumber = 0x0000;
+ __u16 MessageBufferSize = 0;
+ int slen = strlen(ExtFeatureDefaults);
+#ifdef HYCAPI_PRINTFNAMES
+ printk(KERN_NOTICE "hycapi_register_appl\n");
+#endif
+ MessageBufferSize = rp->level3cnt * rp->datablkcnt * rp->datablklen;
+
+ len = CAPI_MSG_BASELEN + 8 + slen + 1;
+ if (!(skb = alloc_skb(len, GFP_ATOMIC))) {
+ printk(KERN_ERR "HYSDN card%d: memory squeeze in hycapi_register_appl\n",
+ card->myid);
+ return;
+ }
+ memcpy(skb_put(skb,sizeof(__u16)), &len, sizeof(__u16));
+ memcpy(skb_put(skb,sizeof(__u16)), &appl, sizeof(__u16));
+ memcpy(skb_put(skb,sizeof(__u8)), &_command, sizeof(_command));
+ memcpy(skb_put(skb,sizeof(__u8)), &_subcommand, sizeof(_subcommand));
+ memcpy(skb_put(skb,sizeof(__u16)), &MessageNumber, sizeof(__u16));
+ memcpy(skb_put(skb,sizeof(__u16)), &MessageBufferSize, sizeof(__u16));
+ memcpy(skb_put(skb,sizeof(__u16)), &(rp->level3cnt), sizeof(__u16));
+ memcpy(skb_put(skb,sizeof(__u16)), &(rp->datablkcnt), sizeof(__u16));
+ memcpy(skb_put(skb,sizeof(__u16)), &(rp->datablklen), sizeof(__u16));
+ memcpy(skb_put(skb,slen), ExtFeatureDefaults, slen);
+ hycapi_applications[appl-1].ctrl_mask |= (1 << (ctrl->cnr-1));
+ hycapi_send_message(ctrl, skb);
+}
+
+/************************************************************
+hycapi_restart_internal
+
+After an adapter has been rebootet, re-register all applications and
+send a LISTEN_REQ (if there has been such a thing )
+
+*************************************************************/
+
+static void hycapi_restart_internal(struct capi_ctr *ctrl)
+{
+ int i;
+ struct sk_buff *skb;
+#ifdef HYCAPI_PRINTFNAMES
+ printk(KERN_WARNING "HYSDN: hycapi_restart_internal");
+#endif
+ for(i=0; i<CAPI_MAXAPPL; i++) {
+ if(_hycapi_appCheck(i+1, ctrl->cnr) == 1) {
+ hycapi_register_internal(ctrl, i+1,
+ &hycapi_applications[i].rp);
+ if(hycapi_applications[i].listen_req[ctrl->cnr-1]) {
+ skb = skb_copy(hycapi_applications[i].listen_req[ctrl->cnr-1], GFP_ATOMIC);
+ hycapi_sendmsg_internal(ctrl, skb);
+ }
+ }
+ }
+}
+
+/*************************************************************
+Register an application.
+Error-checking is done for CAPI-compliance.
+
+The application is recorded in the internal list.
+*************************************************************/
+
+static void
+hycapi_register_appl(struct capi_ctr *ctrl, __u16 appl,
+ capi_register_params *rp)
+{
+ int MaxLogicalConnections = 0, MaxBDataBlocks = 0, MaxBDataLen = 0;
+ hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
+ hysdn_card *card = cinfo->card;
+ int chk = _hycapi_appCheck(appl, ctrl->cnr);
+ if(chk < 0) {
+ return;
+ }
+ if(chk == 1) {
+ printk(KERN_INFO "HYSDN: apl %d already registered\n", appl);
+ return;
+ }
+ MaxBDataBlocks = rp->datablkcnt > CAPI_MAXDATAWINDOW ? CAPI_MAXDATAWINDOW : rp->datablkcnt;
+ rp->datablkcnt = MaxBDataBlocks;
+ MaxBDataLen = rp->datablklen < 1024 ? 1024 : rp->datablklen ;
+ rp->datablklen = MaxBDataLen;
+
+ MaxLogicalConnections = rp->level3cnt;
+ if (MaxLogicalConnections < 0) {
+ MaxLogicalConnections = card->bchans * -MaxLogicalConnections;
+ }
+ if (MaxLogicalConnections == 0) {
+ MaxLogicalConnections = card->bchans;
+ }
+
+ rp->level3cnt = MaxLogicalConnections;
+ memcpy(&hycapi_applications[appl-1].rp,
+ rp, sizeof(capi_register_params));
+}
+
+/*********************************************************************
+
+hycapi_release_internal
+
+Send down a CAPI_RELEASE to the controller.
+*********************************************************************/
+
+static void hycapi_release_internal(struct capi_ctr *ctrl, __u16 appl)
+{
+ hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
+ hysdn_card *card = cinfo->card;
+ struct sk_buff *skb;
+ __u16 len;
+ __u8 _command = 0xa1, _subcommand = 0x80;
+ __u16 MessageNumber = 0x0000;
+
+ capilib_release_appl(&cinfo->ncci_head, appl);
+
+#ifdef HYCAPI_PRINTFNAMES
+ printk(KERN_NOTICE "hycapi_release_appl\n");
+#endif
+ len = CAPI_MSG_BASELEN;
+ if (!(skb = alloc_skb(len, GFP_ATOMIC))) {
+ printk(KERN_ERR "HYSDN card%d: memory squeeze in hycapi_register_appl\n",
+ card->myid);
+ return;
+ }
+ memcpy(skb_put(skb,sizeof(__u16)), &len, sizeof(__u16));
+ memcpy(skb_put(skb,sizeof(__u16)), &appl, sizeof(__u16));
+ memcpy(skb_put(skb,sizeof(__u8)), &_command, sizeof(_command));
+ memcpy(skb_put(skb,sizeof(__u8)), &_subcommand, sizeof(_subcommand));
+ memcpy(skb_put(skb,sizeof(__u16)), &MessageNumber, sizeof(__u16));
+ hycapi_send_message(ctrl, skb);
+ hycapi_applications[appl-1].ctrl_mask &= ~(1 << (ctrl->cnr-1));
+}
+
+/******************************************************************
+hycapi_release_appl
+
+Release the application from the internal list an remove it's
+registration at controller-level
+******************************************************************/
+
+static void
+hycapi_release_appl(struct capi_ctr *ctrl, __u16 appl)
+{
+ int chk;
+
+ chk = _hycapi_appCheck(appl, ctrl->cnr);
+ if(chk<0) {
+ printk(KERN_ERR "HYCAPI: Releasing invalid appl %d on controller %d\n", appl, ctrl->cnr);
+ return;
+ }
+ if(hycapi_applications[appl-1].listen_req[ctrl->cnr-1]) {
+ kfree_skb(hycapi_applications[appl-1].listen_req[ctrl->cnr-1]);
+ hycapi_applications[appl-1].listen_req[ctrl->cnr-1] = NULL;
+ }
+ if(chk == 1)
+ {
+ hycapi_release_internal(ctrl, appl);
+ }
+}
+
+
+/**************************************************************
+Kill a single controller.
+**************************************************************/
+
+int hycapi_capi_release(hysdn_card *card)
+{
+ hycapictrl_info *cinfo = card->hyctrlinfo;
+ struct capi_ctr *ctrl;
+#ifdef HYCAPI_PRINTFNAMES
+ printk(KERN_NOTICE "hycapi_capi_release\n");
+#endif
+ if(cinfo) {
+ ctrl = &cinfo->capi_ctrl;
+ hycapi_remove_ctr(ctrl);
+ }
+ return 0;
+}
+
+/**************************************************************
+hycapi_capi_stop
+
+Stop CAPI-Output on a card. (e.g. during reboot)
+***************************************************************/
+
+int hycapi_capi_stop(hysdn_card *card)
+{
+ hycapictrl_info *cinfo = card->hyctrlinfo;
+ struct capi_ctr *ctrl;
+#ifdef HYCAPI_PRINTFNAMES
+ printk(KERN_NOTICE "hycapi_capi_stop\n");
+#endif
+ if(cinfo) {
+ ctrl = &cinfo->capi_ctrl;
+/* ctrl->suspend_output(ctrl); */
+ capi_ctr_reseted(ctrl);
+ }
+ return 0;
+}
+
+/***************************************************************
+hycapi_send_message
+
+Send a message to the controller.
+
+Messages are parsed for their Command/Subcommand-type, and appropriate
+action's are performed.
+
+Note that we have to muck around with a 64Bit-DATA_REQ as there are
+firmware-releases that do not check the MsgLen-Indication!
+
+***************************************************************/
+
+static u16 hycapi_send_message(struct capi_ctr *ctrl, struct sk_buff *skb)
+{
+ __u16 appl_id;
+ int _len, _len2;
+ __u8 msghead[64];
+ hycapictrl_info *cinfo = ctrl->driverdata;
+ u16 retval = CAPI_NOERROR;
+
+ appl_id = CAPIMSG_APPID(skb->data);
+ switch(_hycapi_appCheck(appl_id, ctrl->cnr))
+ {
+ case 0:
+/* printk(KERN_INFO "Need to register\n"); */
+ hycapi_register_internal(ctrl,
+ appl_id,
+ &(hycapi_applications[appl_id-1].rp));
+ break;
+ case 1:
+ break;
+ default:
+ printk(KERN_ERR "HYCAPI: Controller mixup!\n");
+ retval = CAPI_ILLAPPNR;
+ goto out;
+ }
+ switch(CAPIMSG_CMD(skb->data)) {
+ case CAPI_DISCONNECT_B3_RESP:
+ capilib_free_ncci(&cinfo->ncci_head, appl_id,
+ CAPIMSG_NCCI(skb->data));
+ break;
+ case CAPI_DATA_B3_REQ:
+ _len = CAPIMSG_LEN(skb->data);
+ if (_len > 22) {
+ _len2 = _len - 22;
+ skb_copy_from_linear_data(skb, msghead, 22);
+ skb_copy_to_linear_data_offset(skb, _len2,
+ msghead, 22);
+ skb_pull(skb, _len2);
+ CAPIMSG_SETLEN(skb->data, 22);
+ retval = capilib_data_b3_req(&cinfo->ncci_head,
+ CAPIMSG_APPID(skb->data),
+ CAPIMSG_NCCI(skb->data),
+ CAPIMSG_MSGID(skb->data));
+ }
+ break;
+ case CAPI_LISTEN_REQ:
+ if(hycapi_applications[appl_id-1].listen_req[ctrl->cnr-1])
+ {
+ kfree_skb(hycapi_applications[appl_id-1].listen_req[ctrl->cnr-1]);
+ hycapi_applications[appl_id-1].listen_req[ctrl->cnr-1] = NULL;
+ }
+ if (!(hycapi_applications[appl_id-1].listen_req[ctrl->cnr-1] = skb_copy(skb, GFP_ATOMIC)))
+ {
+ printk(KERN_ERR "HYSDN: memory squeeze in private_listen\n");
+ }
+ break;
+ default:
+ break;
+ }
+ out:
+ if (retval == CAPI_NOERROR)
+ hycapi_sendmsg_internal(ctrl, skb);
+ else
+ dev_kfree_skb_any(skb);
+
+ return retval;
+}
+
+/*********************************************************************
+hycapi_read_proc
+
+Informations provided in the /proc/capi-entries.
+
+*********************************************************************/
+
+static int hycapi_read_proc(char *page, char **start, off_t off,
+ int count, int *eof, struct capi_ctr *ctrl)
+{
+ hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
+ hysdn_card *card = cinfo->card;
+ int len = 0;
+ char *s;
+#ifdef HYCAPI_PRINTFNAMES
+ printk(KERN_NOTICE "hycapi_read_proc\n");
+#endif
+ len += sprintf(page+len, "%-16s %s\n", "name", cinfo->cardname);
+ len += sprintf(page+len, "%-16s 0x%x\n", "io", card->iobase);
+ len += sprintf(page+len, "%-16s %d\n", "irq", card->irq);
+
+ switch (card->brdtype) {
+ case BD_PCCARD: s = "HYSDN Hycard"; break;
+ case BD_ERGO: s = "HYSDN Ergo2"; break;
+ case BD_METRO: s = "HYSDN Metro4"; break;
+ case BD_CHAMP2: s = "HYSDN Champ2"; break;
+ case BD_PLEXUS: s = "HYSDN Plexus30"; break;
+ default: s = "???"; break;
+ }
+ len += sprintf(page+len, "%-16s %s\n", "type", s);
+ if ((s = cinfo->version[VER_DRIVER]) != NULL)
+ len += sprintf(page+len, "%-16s %s\n", "ver_driver", s);
+ if ((s = cinfo->version[VER_CARDTYPE]) != NULL)
+ len += sprintf(page+len, "%-16s %s\n", "ver_cardtype", s);
+ if ((s = cinfo->version[VER_SERIAL]) != NULL)
+ len += sprintf(page+len, "%-16s %s\n", "ver_serial", s);
+
+ len += sprintf(page+len, "%-16s %s\n", "cardname", cinfo->cardname);
+
+ if (off+count >= len)
+ *eof = 1;
+ if (len < off)
+ return 0;
+ *start = page + off;
+ return ((count < len-off) ? count : len-off);
+}
+
+/**************************************************************
+hycapi_load_firmware
+
+This does NOT load any firmware, but the callback somehow is needed
+on capi-interface registration.
+
+**************************************************************/
+
+static int hycapi_load_firmware(struct capi_ctr *ctrl, capiloaddata *data)
+{
+#ifdef HYCAPI_PRINTFNAMES
+ printk(KERN_NOTICE "hycapi_load_firmware\n");
+#endif
+ return 0;
+}
+
+
+static char *hycapi_procinfo(struct capi_ctr *ctrl)
+{
+ hycapictrl_info *cinfo = (hycapictrl_info *)(ctrl->driverdata);
+#ifdef HYCAPI_PRINTFNAMES
+ printk(KERN_NOTICE "hycapi_proc_info\n");
+#endif
+ if (!cinfo)
+ return "";
+ sprintf(cinfo->infobuf, "%s %s 0x%x %d %s",
+ cinfo->cardname[0] ? cinfo->cardname : "-",
+ cinfo->version[VER_DRIVER] ? cinfo->version[VER_DRIVER] : "-",
+ cinfo->card ? cinfo->card->iobase : 0x0,
+ cinfo->card ? cinfo->card->irq : 0,
+ hycapi_revision
+ );
+ return cinfo->infobuf;
+}
+
+/******************************************************************
+hycapi_rx_capipkt
+
+Receive a capi-message.
+
+All B3_DATA_IND are converted to 64K-extension compatible format.
+New nccis are created if necessary.
+*******************************************************************/
+
+void
+hycapi_rx_capipkt(hysdn_card * card, unsigned char *buf, unsigned short len)
+{
+ struct sk_buff *skb;
+ hycapictrl_info *cinfo = card->hyctrlinfo;
+ struct capi_ctr *ctrl;
+ __u16 ApplId;
+ __u16 MsgLen, info;
+ __u16 len2, CapiCmd;
+ __u32 CP64[2] = {0,0};
+#ifdef HYCAPI_PRINTFNAMES
+ printk(KERN_NOTICE "hycapi_rx_capipkt\n");
+#endif
+ if(!cinfo) {
+ return;
+ }
+ ctrl = &cinfo->capi_ctrl;
+ if(len < CAPI_MSG_BASELEN) {
+ printk(KERN_ERR "HYSDN Card%d: invalid CAPI-message, length %d!\n",
+ card->myid, len);
+ return;
+ }
+ MsgLen = CAPIMSG_LEN(buf);
+ ApplId = CAPIMSG_APPID(buf);
+ CapiCmd = CAPIMSG_CMD(buf);
+
+ if((CapiCmd == CAPI_DATA_B3_IND) && (MsgLen < 30)) {
+ len2 = len + (30 - MsgLen);
+ if (!(skb = alloc_skb(len2, GFP_ATOMIC))) {
+ printk(KERN_ERR "HYSDN Card%d: incoming packet dropped\n",
+ card->myid);
+ return;
+ }
+ memcpy(skb_put(skb, MsgLen), buf, MsgLen);
+ memcpy(skb_put(skb, 2*sizeof(__u32)), CP64, 2* sizeof(__u32));
+ memcpy(skb_put(skb, len - MsgLen), buf + MsgLen,
+ len - MsgLen);
+ CAPIMSG_SETLEN(skb->data, 30);
+ } else {
+ if (!(skb = alloc_skb(len, GFP_ATOMIC))) {
+ printk(KERN_ERR "HYSDN Card%d: incoming packet dropped\n",
+ card->myid);
+ return;
+ }
+ memcpy(skb_put(skb, len), buf, len);
+ }
+ switch(CAPIMSG_CMD(skb->data))
+ {
+ case CAPI_CONNECT_B3_CONF:
+/* Check info-field for error-indication: */
+ info = CAPIMSG_U16(skb->data, 12);
+ switch(info)
+ {
+ case 0:
+ capilib_new_ncci(&cinfo->ncci_head, ApplId, CAPIMSG_NCCI(skb->data),
+ hycapi_applications[ApplId-1].rp.datablkcnt);
+
+ break;
+ case 0x0001:
+ printk(KERN_ERR "HYSDN Card%d: NCPI not supported by current "
+ "protocol. NCPI ignored.\n", card->myid);
+ break;
+ case 0x2001:
+ printk(KERN_ERR "HYSDN Card%d: Message not supported in"
+ " current state\n", card->myid);
+ break;
+ case 0x2002:
+ printk(KERN_ERR "HYSDN Card%d: invalid PLCI\n", card->myid);
+ break;
+ case 0x2004:
+ printk(KERN_ERR "HYSDN Card%d: out of NCCI\n", card->myid);
+ break;
+ case 0x3008:
+ printk(KERN_ERR "HYSDN Card%d: NCPI not supported\n",
+ card->myid);
+ break;
+ default:
+ printk(KERN_ERR "HYSDN Card%d: Info in CONNECT_B3_CONF: %d\n",
+ card->myid, info);
+ break;
+ }
+ break;
+ case CAPI_CONNECT_B3_IND:
+ capilib_new_ncci(&cinfo->ncci_head, ApplId,
+ CAPIMSG_NCCI(skb->data),
+ hycapi_applications[ApplId-1].rp.datablkcnt);
+ break;
+ case CAPI_DATA_B3_CONF:
+ capilib_data_b3_conf(&cinfo->ncci_head, ApplId,
+ CAPIMSG_NCCI(skb->data),
+ CAPIMSG_MSGID(skb->data));
+ break;
+ default:
+ break;
+ }
+ capi_ctr_handle_message(ctrl, ApplId, skb);
+}
+
+/******************************************************************
+hycapi_tx_capiack
+
+Internally acknowledge a msg sent. This will remove the msg from the
+internal queue.
+
+*******************************************************************/
+
+void hycapi_tx_capiack(hysdn_card * card)
+{
+ hycapictrl_info *cinfo = card->hyctrlinfo;
+#ifdef HYCAPI_PRINTFNAMES
+ printk(KERN_NOTICE "hycapi_tx_capiack\n");
+#endif
+ if(!cinfo) {
+ return;
+ }
+ spin_lock_irq(&cinfo->lock);
+ kfree_skb(cinfo->skbs[cinfo->out_idx]); /* free skb */
+ cinfo->skbs[cinfo->out_idx++] = NULL;
+ if (cinfo->out_idx >= HYSDN_MAX_CAPI_SKB)
+ cinfo->out_idx = 0; /* wrap around */
+
+ if (cinfo->sk_count-- == HYSDN_MAX_CAPI_SKB) /* dec usage count */
+ capi_ctr_resume_output(&cinfo->capi_ctrl);
+ spin_unlock_irq(&cinfo->lock);
+}
+
+/***************************************************************
+hycapi_tx_capiget(hysdn_card *card)
+
+This is called when polling for messages to SEND.
+
+****************************************************************/
+
+struct sk_buff *
+hycapi_tx_capiget(hysdn_card *card)
+{
+ hycapictrl_info *cinfo = card->hyctrlinfo;
+ if(!cinfo) {
+ return (struct sk_buff *)NULL;
+ }
+ if (!cinfo->sk_count)
+ return (struct sk_buff *)NULL; /* nothing available */
+
+ return (cinfo->skbs[cinfo->out_idx]); /* next packet to send */
+}
+
+
+/**********************************************************
+int hycapi_init()
+
+attach the capi-driver to the kernel-capi.
+
+***********************************************************/
+
+int hycapi_init(void)
+{
+ int i;
+ for(i=0;i<CAPI_MAXAPPL;i++) {
+ memset(&(hycapi_applications[i]), 0, sizeof(hycapi_appl));
+ }
+ return(0);
+}
+
+/**************************************************************
+hycapi_cleanup(void)
+
+detach the capi-driver to the kernel-capi. Actually this should
+free some more ressources. Do that later.
+**************************************************************/
+
+void
+hycapi_cleanup(void)
+{
+}
+
+/********************************************************************
+hycapi_capi_create(hysdn_card *card)
+
+Attach the card with its capi-ctrl.
+*********************************************************************/
+
+static void hycapi_fill_profile(hysdn_card *card)
+{
+ hycapictrl_info *cinfo = NULL;
+ struct capi_ctr *ctrl = NULL;
+ cinfo = card->hyctrlinfo;
+ if(!cinfo) return;
+ ctrl = &cinfo->capi_ctrl;
+ strcpy(ctrl->manu, "Hypercope");
+ ctrl->version.majorversion = 2;
+ ctrl->version.minorversion = 0;
+ ctrl->version.majormanuversion = 3;
+ ctrl->version.minormanuversion = 2;
+ ctrl->profile.ncontroller = card->myid;
+ ctrl->profile.nbchannel = card->bchans;
+ ctrl->profile.goptions = GLOBAL_OPTION_INTERNAL_CONTROLLER |
+ GLOBAL_OPTION_B_CHANNEL_OPERATION;
+ ctrl->profile.support1 = B1_PROT_64KBIT_HDLC |
+ (card->faxchans ? B1_PROT_T30 : 0) |
+ B1_PROT_64KBIT_TRANSPARENT;
+ ctrl->profile.support2 = B2_PROT_ISO7776 |
+ (card->faxchans ? B2_PROT_T30 : 0) |
+ B2_PROT_TRANSPARENT;
+ ctrl->profile.support3 = B3_PROT_TRANSPARENT |
+ B3_PROT_T90NL |
+ (card->faxchans ? B3_PROT_T30 : 0) |
+ (card->faxchans ? B3_PROT_T30EXT : 0) |
+ B3_PROT_ISO8208;
+}
+
+int
+hycapi_capi_create(hysdn_card *card)
+{
+ hycapictrl_info *cinfo = NULL;
+ struct capi_ctr *ctrl = NULL;
+ int retval;
+#ifdef HYCAPI_PRINTFNAMES
+ printk(KERN_NOTICE "hycapi_capi_create\n");
+#endif
+ if((hycapi_enable & (1 << card->myid)) == 0) {
+ return 1;
+ }
+ if (!card->hyctrlinfo) {
+ cinfo = kzalloc(sizeof(hycapictrl_info), GFP_ATOMIC);
+ if (!cinfo) {
+ printk(KERN_WARNING "HYSDN: no memory for capi-ctrl.\n");
+ return -ENOMEM;
+ }
+ card->hyctrlinfo = cinfo;
+ cinfo->card = card;
+ spin_lock_init(&cinfo->lock);
+ INIT_LIST_HEAD(&cinfo->ncci_head);
+
+ switch (card->brdtype) {
+ case BD_PCCARD: strcpy(cinfo->cardname,"HYSDN Hycard"); break;
+ case BD_ERGO: strcpy(cinfo->cardname,"HYSDN Ergo2"); break;
+ case BD_METRO: strcpy(cinfo->cardname,"HYSDN Metro4"); break;
+ case BD_CHAMP2: strcpy(cinfo->cardname,"HYSDN Champ2"); break;
+ case BD_PLEXUS: strcpy(cinfo->cardname,"HYSDN Plexus30"); break;
+ default: strcpy(cinfo->cardname,"HYSDN ???"); break;
+ }
+
+ ctrl = &cinfo->capi_ctrl;
+ ctrl->driver_name = "hycapi";
+ ctrl->driverdata = cinfo;
+ ctrl->register_appl = hycapi_register_appl;
+ ctrl->release_appl = hycapi_release_appl;
+ ctrl->send_message = hycapi_send_message;
+ ctrl->load_firmware = hycapi_load_firmware;
+ ctrl->reset_ctr = hycapi_reset_ctr;
+ ctrl->procinfo = hycapi_procinfo;
+ ctrl->ctr_read_proc = hycapi_read_proc;
+ strcpy(ctrl->name, cinfo->cardname);
+ ctrl->owner = THIS_MODULE;
+
+ retval = attach_capi_ctr(ctrl);
+ if (retval) {
+ printk(KERN_ERR "hycapi: attach controller failed.\n");
+ return -EBUSY;
+ }
+ /* fill in the blanks: */
+ hycapi_fill_profile(card);
+ capi_ctr_ready(ctrl);
+ } else {
+ /* resume output on stopped ctrl */
+ ctrl = &card->hyctrlinfo->capi_ctrl;
+ hycapi_fill_profile(card);
+ capi_ctr_ready(ctrl);
+ hycapi_restart_internal(ctrl);
+/* ctrl->resume_output(ctrl); */
+ }
+ return 0;
+}
diff --git a/drivers/isdn/hysdn/hysdn_boot.c b/drivers/isdn/hysdn/hysdn_boot.c
new file mode 100644
index 0000000..be787e1
--- /dev/null
+++ b/drivers/isdn/hysdn/hysdn_boot.c
@@ -0,0 +1,398 @@
+/* $Id: hysdn_boot.c,v 1.4.6.4 2001/09/23 22:24:54 kai Exp $
+ *
+ * Linux driver for HYSDN cards
+ * specific routines for booting and pof handling
+ *
+ * Author Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+
+#include "hysdn_defs.h"
+#include "hysdn_pof.h"
+
+/********************************/
+/* defines for pof read handler */
+/********************************/
+#define POF_READ_FILE_HEAD 0
+#define POF_READ_TAG_HEAD 1
+#define POF_READ_TAG_DATA 2
+
+/************************************************************/
+/* definition of boot specific data area. This data is only */
+/* needed during boot and so allocated dynamically. */
+/************************************************************/
+struct boot_data {
+ unsigned short Cryptor; /* for use with Decrypt function */
+ unsigned short Nrecs; /* records remaining in file */
+ unsigned char pof_state;/* actual state of read handler */
+ unsigned char is_crypted;/* card data is crypted */
+ int BufSize; /* actual number of bytes bufferd */
+ int last_error; /* last occurred error */
+ unsigned short pof_recid;/* actual pof recid */
+ unsigned long pof_reclen;/* total length of pof record data */
+ unsigned long pof_recoffset;/* actual offset inside pof record */
+ union {
+ unsigned char BootBuf[BOOT_BUF_SIZE];/* buffer as byte count */
+ tPofRecHdr PofRecHdr; /* header for actual record/chunk */
+ tPofFileHdr PofFileHdr; /* header from POF file */
+ tPofTimeStamp PofTime; /* time information */
+ } buf;
+};
+
+/*****************************************************/
+/* start decryption of successive POF file chuncks. */
+/* */
+/* to be called at start of POF file reading, */
+/* before starting any decryption on any POF record. */
+/*****************************************************/
+static void
+StartDecryption(struct boot_data *boot)
+{
+ boot->Cryptor = CRYPT_STARTTERM;
+} /* StartDecryption */
+
+
+/***************************************************************/
+/* decrypt complete BootBuf */
+/* NOTE: decryption must be applied to all or none boot tags - */
+/* to HI and LO boot loader and (all) seq tags, because */
+/* global Cryptor is started for whole POF. */
+/***************************************************************/
+static void
+DecryptBuf(struct boot_data *boot, int cnt)
+{
+ unsigned char *bufp = boot->buf.BootBuf;
+
+ while (cnt--) {
+ boot->Cryptor = (boot->Cryptor >> 1) ^ ((boot->Cryptor & 1U) ? CRYPT_FEEDTERM : 0);
+ *bufp++ ^= (unsigned char)boot->Cryptor;
+ }
+} /* DecryptBuf */
+
+/********************************************************************************/
+/* pof_handle_data executes the required actions dependent on the active record */
+/* id. If successful 0 is returned, a negative value shows an error. */
+/********************************************************************************/
+static int
+pof_handle_data(hysdn_card * card, int datlen)
+{
+ struct boot_data *boot = card->boot; /* pointer to boot specific data */
+ long l;
+ unsigned char *imgp;
+ int img_len;
+
+ /* handle the different record types */
+ switch (boot->pof_recid) {
+
+ case TAG_TIMESTMP:
+ if (card->debug_flags & LOG_POF_RECORD)
+ hysdn_addlog(card, "POF created %s", boot->buf.PofTime.DateTimeText);
+ break;
+
+ case TAG_CBOOTDTA:
+ DecryptBuf(boot, datlen); /* we need to encrypt the buffer */
+ case TAG_BOOTDTA:
+ if (card->debug_flags & LOG_POF_RECORD)
+ hysdn_addlog(card, "POF got %s len=%d offs=0x%lx",
+ (boot->pof_recid == TAG_CBOOTDTA) ? "CBOOTDATA" : "BOOTDTA",
+ datlen, boot->pof_recoffset);
+
+ if (boot->pof_reclen != POF_BOOT_LOADER_TOTAL_SIZE) {
+ boot->last_error = EPOF_BAD_IMG_SIZE; /* invalid length */
+ return (boot->last_error);
+ }
+ imgp = boot->buf.BootBuf; /* start of buffer */
+ img_len = datlen; /* maximum length to transfer */
+
+ l = POF_BOOT_LOADER_OFF_IN_PAGE -
+ (boot->pof_recoffset & (POF_BOOT_LOADER_PAGE_SIZE - 1));
+ if (l > 0) {
+ /* buffer needs to be truncated */
+ imgp += l; /* advance pointer */
+ img_len -= l; /* adjust len */
+ }
+ /* at this point no special handling for data wrapping over buffer */
+ /* is necessary, because the boot image always will be adjusted to */
+ /* match a page boundary inside the buffer. */
+ /* The buffer for the boot image on the card is filled in 2 cycles */
+ /* first the 1024 hi-words are put in the buffer, then the low 1024 */
+ /* word are handled in the same way with different offset. */
+
+ if (img_len > 0) {
+ /* data available for copy */
+ if ((boot->last_error =
+ card->writebootimg(card, imgp,
+ (boot->pof_recoffset > POF_BOOT_LOADER_PAGE_SIZE) ? 2 : 0)) < 0)
+ return (boot->last_error);
+ }
+ break; /* end of case boot image hi/lo */
+
+ case TAG_CABSDATA:
+ DecryptBuf(boot, datlen); /* we need to encrypt the buffer */
+ case TAG_ABSDATA:
+ if (card->debug_flags & LOG_POF_RECORD)
+ hysdn_addlog(card, "POF got %s len=%d offs=0x%lx",
+ (boot->pof_recid == TAG_CABSDATA) ? "CABSDATA" : "ABSDATA",
+ datlen, boot->pof_recoffset);
+
+ if ((boot->last_error = card->writebootseq(card, boot->buf.BootBuf, datlen) < 0))
+ return (boot->last_error); /* error writing data */
+
+ if (boot->pof_recoffset + datlen >= boot->pof_reclen)
+ return (card->waitpofready(card)); /* data completely spooled, wait for ready */
+
+ break; /* end of case boot seq data */
+
+ default:
+ if (card->debug_flags & LOG_POF_RECORD)
+ hysdn_addlog(card, "POF got data(id=0x%lx) len=%d offs=0x%lx", boot->pof_recid,
+ datlen, boot->pof_recoffset);
+
+ break; /* simply skip record */
+ } /* switch boot->pof_recid */
+
+ return (0);
+} /* pof_handle_data */
+
+
+/******************************************************************************/
+/* pof_write_buffer is called when the buffer has been filled with the needed */
+/* number of data bytes. The number delivered is additionally supplied for */
+/* verification. The functions handles the data and returns the needed number */
+/* of bytes for the next action. If the returned value is 0 or less an error */
+/* occurred and booting must be aborted. */
+/******************************************************************************/
+int
+pof_write_buffer(hysdn_card * card, int datlen)
+{
+ struct boot_data *boot = card->boot; /* pointer to boot specific data */
+
+ if (!boot)
+ return (-EFAULT); /* invalid call */
+ if (boot->last_error < 0)
+ return (boot->last_error); /* repeated error */
+
+ if (card->debug_flags & LOG_POF_WRITE)
+ hysdn_addlog(card, "POF write: got %d bytes ", datlen);
+
+ switch (boot->pof_state) {
+ case POF_READ_FILE_HEAD:
+ if (card->debug_flags & LOG_POF_WRITE)
+ hysdn_addlog(card, "POF write: checking file header");
+
+ if (datlen != sizeof(tPofFileHdr)) {
+ boot->last_error = -EPOF_INTERNAL;
+ break;
+ }
+ if (boot->buf.PofFileHdr.Magic != TAGFILEMAGIC) {
+ boot->last_error = -EPOF_BAD_MAGIC;
+ break;
+ }
+ /* Setup the new state and vars */
+ boot->Nrecs = (unsigned short)(boot->buf.PofFileHdr.N_PofRecs); /* limited to 65535 */
+ boot->pof_state = POF_READ_TAG_HEAD; /* now start with single tags */
+ boot->last_error = sizeof(tPofRecHdr); /* new length */
+ break;
+
+ case POF_READ_TAG_HEAD:
+ if (card->debug_flags & LOG_POF_WRITE)
+ hysdn_addlog(card, "POF write: checking tag header");
+
+ if (datlen != sizeof(tPofRecHdr)) {
+ boot->last_error = -EPOF_INTERNAL;
+ break;
+ }
+ boot->pof_recid = boot->buf.PofRecHdr.PofRecId; /* actual pof recid */
+ boot->pof_reclen = boot->buf.PofRecHdr.PofRecDataLen; /* total length */
+ boot->pof_recoffset = 0; /* no starting offset */
+
+ if (card->debug_flags & LOG_POF_RECORD)
+ hysdn_addlog(card, "POF: got record id=0x%lx length=%ld ",
+ boot->pof_recid, boot->pof_reclen);
+
+ boot->pof_state = POF_READ_TAG_DATA; /* now start with tag data */
+ if (boot->pof_reclen < BOOT_BUF_SIZE)
+ boot->last_error = boot->pof_reclen; /* limit size */
+ else
+ boot->last_error = BOOT_BUF_SIZE; /* maximum */
+
+ if (!boot->last_error) { /* no data inside record */
+ boot->pof_state = POF_READ_TAG_HEAD; /* now start with single tags */
+ boot->last_error = sizeof(tPofRecHdr); /* new length */
+ }
+ break;
+
+ case POF_READ_TAG_DATA:
+ if (card->debug_flags & LOG_POF_WRITE)
+ hysdn_addlog(card, "POF write: getting tag data");
+
+ if (datlen != boot->last_error) {
+ boot->last_error = -EPOF_INTERNAL;
+ break;
+ }
+ if ((boot->last_error = pof_handle_data(card, datlen)) < 0)
+ return (boot->last_error); /* an error occurred */
+ boot->pof_recoffset += datlen;
+ if (boot->pof_recoffset >= boot->pof_reclen) {
+ boot->pof_state = POF_READ_TAG_HEAD; /* now start with single tags */
+ boot->last_error = sizeof(tPofRecHdr); /* new length */
+ } else {
+ if (boot->pof_reclen - boot->pof_recoffset < BOOT_BUF_SIZE)
+ boot->last_error = boot->pof_reclen - boot->pof_recoffset; /* limit size */
+ else
+ boot->last_error = BOOT_BUF_SIZE; /* maximum */
+ }
+ break;
+
+ default:
+ boot->last_error = -EPOF_INTERNAL; /* unknown state */
+ break;
+ } /* switch (boot->pof_state) */
+
+ return (boot->last_error);
+} /* pof_write_buffer */
+
+
+/*******************************************************************************/
+/* pof_write_open is called when an open for boot on the cardlog device occurs. */
+/* The function returns the needed number of bytes for the next operation. If */
+/* the returned number is less or equal 0 an error specified by this code */
+/* occurred. Additionally the pointer to the buffer data area is set on success */
+/*******************************************************************************/
+int
+pof_write_open(hysdn_card * card, unsigned char **bufp)
+{
+ struct boot_data *boot; /* pointer to boot specific data */
+
+ if (card->boot) {
+ if (card->debug_flags & LOG_POF_OPEN)
+ hysdn_addlog(card, "POF open: already opened for boot");
+ return (-ERR_ALREADY_BOOT); /* boot already active */
+ }
+ /* error no mem available */
+ if (!(boot = kzalloc(sizeof(struct boot_data), GFP_KERNEL))) {
+ if (card->debug_flags & LOG_MEM_ERR)
+ hysdn_addlog(card, "POF open: unable to allocate mem");
+ return (-EFAULT);
+ }
+ card->boot = boot;
+ card->state = CARD_STATE_BOOTING;
+
+ card->stopcard(card); /* first stop the card */
+ if (card->testram(card)) {
+ if (card->debug_flags & LOG_POF_OPEN)
+ hysdn_addlog(card, "POF open: DPRAM test failure");
+ boot->last_error = -ERR_BOARD_DPRAM;
+ card->state = CARD_STATE_BOOTERR; /* show boot error */
+ return (boot->last_error);
+ }
+ boot->BufSize = 0; /* Buffer is empty */
+ boot->pof_state = POF_READ_FILE_HEAD; /* read file header */
+ StartDecryption(boot); /* if POF File should be encrypted */
+
+ if (card->debug_flags & LOG_POF_OPEN)
+ hysdn_addlog(card, "POF open: success");
+
+ *bufp = boot->buf.BootBuf; /* point to buffer */
+ return (sizeof(tPofFileHdr));
+} /* pof_write_open */
+
+/********************************************************************************/
+/* pof_write_close is called when an close of boot on the cardlog device occurs. */
+/* The return value must be 0 if everything has happened as desired. */
+/********************************************************************************/
+int
+pof_write_close(hysdn_card * card)
+{
+ struct boot_data *boot = card->boot; /* pointer to boot specific data */
+
+ if (!boot)
+ return (-EFAULT); /* invalid call */
+
+ card->boot = NULL; /* no boot active */
+ kfree(boot);
+
+ if (card->state == CARD_STATE_RUN)
+ card->set_errlog_state(card, 1); /* activate error log */
+
+ if (card->debug_flags & LOG_POF_OPEN)
+ hysdn_addlog(card, "POF close: success");
+
+ return (0);
+} /* pof_write_close */
+
+/*********************************************************************************/
+/* EvalSysrTokData checks additional records delivered with the Sysready Message */
+/* when POF has been booted. A return value of 0 is used if no error occurred. */
+/*********************************************************************************/
+int
+EvalSysrTokData(hysdn_card *card, unsigned char *cp, int len)
+{
+ u_char *p;
+ u_char crc;
+
+ if (card->debug_flags & LOG_POF_RECORD)
+ hysdn_addlog(card, "SysReady Token data length %d", len);
+
+ if (len < 2) {
+ hysdn_addlog(card, "SysReady Token Data to short");
+ return (1);
+ }
+ for (p = cp, crc = 0; p < (cp + len - 2); p++)
+ if ((crc & 0x80))
+ crc = (((u_char) (crc << 1)) + 1) + *p;
+ else
+ crc = ((u_char) (crc << 1)) + *p;
+ crc = ~crc;
+ if (crc != *(cp + len - 1)) {
+ hysdn_addlog(card, "SysReady Token Data invalid CRC");
+ return (1);
+ }
+ len--; /* don't check CRC byte */
+ while (len > 0) {
+
+ if (*cp == SYSR_TOK_END)
+ return (0); /* End of Token stream */
+
+ if (len < (*(cp + 1) + 2)) {
+ hysdn_addlog(card, "token 0x%x invalid length %d", *cp, *(cp + 1));
+ return (1);
+ }
+ switch (*cp) {
+ case SYSR_TOK_B_CHAN: /* 1 */
+ if (*(cp + 1) != 1)
+ return (1); /* length invalid */
+ card->bchans = *(cp + 2);
+ break;
+
+ case SYSR_TOK_FAX_CHAN: /* 2 */
+ if (*(cp + 1) != 1)
+ return (1); /* length invalid */
+ card->faxchans = *(cp + 2);
+ break;
+
+ case SYSR_TOK_MAC_ADDR: /* 3 */
+ if (*(cp + 1) != 6)
+ return (1); /* length invalid */
+ memcpy(card->mac_addr, cp + 2, 6);
+ break;
+
+ default:
+ hysdn_addlog(card, "unknown token 0x%02x length %d", *cp, *(cp + 1));
+ break;
+ }
+ len -= (*(cp + 1) + 2); /* adjust len */
+ cp += (*(cp + 1) + 2); /* and pointer */
+ }
+
+ hysdn_addlog(card, "no end token found");
+ return (1);
+} /* EvalSysrTokData */
diff --git a/drivers/isdn/hysdn/hysdn_defs.h b/drivers/isdn/hysdn/hysdn_defs.h
new file mode 100644
index 0000000..729df40
--- /dev/null
+++ b/drivers/isdn/hysdn/hysdn_defs.h
@@ -0,0 +1,284 @@
+/* $Id: hysdn_defs.h,v 1.5.6.3 2001/09/23 22:24:54 kai Exp $
+ *
+ * Linux driver for HYSDN cards
+ * global definitions and exported vars and functions.
+ *
+ * Author Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef HYSDN_DEFS_H
+#define HYSDN_DEFS_H
+
+#include <linux/hysdn_if.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/skbuff.h>
+
+#include "ince1pc.h"
+
+#ifdef CONFIG_HYSDN_CAPI
+#include <linux/capi.h>
+#include <linux/isdn/capicmd.h>
+#include <linux/isdn/capiutil.h>
+#include <linux/isdn/capilli.h>
+
+/***************************/
+/* CAPI-Profile values. */
+/***************************/
+
+#define GLOBAL_OPTION_INTERNAL_CONTROLLER 0x0001
+#define GLOBAL_OPTION_EXTERNAL_CONTROLLER 0x0002
+#define GLOBAL_OPTION_HANDSET 0x0004
+#define GLOBAL_OPTION_DTMF 0x0008
+#define GLOBAL_OPTION_SUPPL_SERVICES 0x0010
+#define GLOBAL_OPTION_CHANNEL_ALLOCATION 0x0020
+#define GLOBAL_OPTION_B_CHANNEL_OPERATION 0x0040
+
+#define B1_PROT_64KBIT_HDLC 0x0001
+#define B1_PROT_64KBIT_TRANSPARENT 0x0002
+#define B1_PROT_V110_ASYNCH 0x0004
+#define B1_PROT_V110_SYNCH 0x0008
+#define B1_PROT_T30 0x0010
+#define B1_PROT_64KBIT_INV_HDLC 0x0020
+#define B1_PROT_56KBIT_TRANSPARENT 0x0040
+
+#define B2_PROT_ISO7776 0x0001
+#define B2_PROT_TRANSPARENT 0x0002
+#define B2_PROT_SDLC 0x0004
+#define B2_PROT_LAPD 0x0008
+#define B2_PROT_T30 0x0010
+#define B2_PROT_PPP 0x0020
+#define B2_PROT_TRANSPARENT_IGNORE_B1_FRAMING_ERRORS 0x0040
+
+#define B3_PROT_TRANSPARENT 0x0001
+#define B3_PROT_T90NL 0x0002
+#define B3_PROT_ISO8208 0x0004
+#define B3_PROT_X25_DCE 0x0008
+#define B3_PROT_T30 0x0010
+#define B3_PROT_T30EXT 0x0020
+
+#define HYSDN_MAXVERSION 8
+
+/* Number of sendbuffers in CAPI-queue */
+#define HYSDN_MAX_CAPI_SKB 20
+
+#endif /* CONFIG_HYSDN_CAPI*/
+
+/************************************************/
+/* constants and bits for debugging/log outputs */
+/************************************************/
+#define LOG_MAX_LINELEN 120
+#define DEB_OUT_SYSLOG 0x80000000 /* output to syslog instead of proc fs */
+#define LOG_MEM_ERR 0x00000001 /* log memory errors like kmalloc failure */
+#define LOG_POF_OPEN 0x00000010 /* log pof open and close activities */
+#define LOG_POF_RECORD 0x00000020 /* log pof record parser */
+#define LOG_POF_WRITE 0x00000040 /* log detailed pof write operation */
+#define LOG_POF_CARD 0x00000080 /* log pof related card functions */
+#define LOG_CNF_LINE 0x00000100 /* all conf lines are put to procfs */
+#define LOG_CNF_DATA 0x00000200 /* non comment conf lines are shown with channel */
+#define LOG_CNF_MISC 0x00000400 /* additional conf line debug outputs */
+#define LOG_SCHED_ASYN 0x00001000 /* debug schedulers async tx routines */
+#define LOG_PROC_OPEN 0x00100000 /* open and close from procfs are logged */
+#define LOG_PROC_ALL 0x00200000 /* all actions from procfs are logged */
+#define LOG_NET_INIT 0x00010000 /* network init and deinit logging */
+
+#define DEF_DEB_FLAGS 0x7fff000f /* everything is logged to procfs */
+
+/**********************************/
+/* proc filesystem name constants */
+/**********************************/
+#define PROC_SUBDIR_NAME "hysdn"
+#define PROC_CONF_BASENAME "cardconf"
+#define PROC_LOG_BASENAME "cardlog"
+
+/***********************************/
+/* PCI 32 bit parms for IO and MEM */
+/***********************************/
+#define PCI_REG_PLX_MEM_BASE 0
+#define PCI_REG_PLX_IO_BASE 1
+#define PCI_REG_MEMORY_BASE 3
+
+/**************/
+/* card types */
+/**************/
+#define BD_NONE 0U
+#define BD_PERFORMANCE 1U
+#define BD_VALUE 2U
+#define BD_PCCARD 3U
+#define BD_ERGO 4U
+#define BD_METRO 5U
+#define BD_CHAMP2 6U
+#define BD_PLEXUS 7U
+
+/******************************************************/
+/* defined states for cards shown by reading cardconf */
+/******************************************************/
+#define CARD_STATE_UNUSED 0 /* never been used or booted */
+#define CARD_STATE_BOOTING 1 /* booting is in progress */
+#define CARD_STATE_BOOTERR 2 /* a previous boot was aborted */
+#define CARD_STATE_RUN 3 /* card is active */
+
+/*******************************/
+/* defines for error_log_state */
+/*******************************/
+#define ERRLOG_STATE_OFF 0 /* error log is switched off, nothing to do */
+#define ERRLOG_STATE_ON 1 /* error log is switched on, wait for data */
+#define ERRLOG_STATE_START 2 /* start error logging */
+#define ERRLOG_STATE_STOP 3 /* stop error logging */
+
+/*******************************/
+/* data structure for one card */
+/*******************************/
+typedef struct HYSDN_CARD {
+
+ /* general variables for the cards */
+ int myid; /* own driver card id */
+ unsigned char bus; /* pci bus the card is connected to */
+ unsigned char devfn; /* slot+function bit encoded */
+ unsigned short subsysid;/* PCI subsystem id */
+ unsigned char brdtype; /* type of card */
+ unsigned int bchans; /* number of available B-channels */
+ unsigned int faxchans; /* number of available fax-channels */
+ unsigned char mac_addr[6];/* MAC Address read from card */
+ unsigned int irq; /* interrupt number */
+ unsigned int iobase; /* IO-port base address */
+ unsigned long plxbase; /* PLX memory base */
+ unsigned long membase; /* DPRAM memory base */
+ unsigned long memend; /* DPRAM memory end */
+ void *dpram; /* mapped dpram */
+ int state; /* actual state of card -> CARD_STATE_** */
+ struct HYSDN_CARD *next; /* pointer to next card */
+
+ /* data areas for the /proc file system */
+ void *proclog; /* pointer to proclog filesystem specific data */
+ void *procconf; /* pointer to procconf filesystem specific data */
+
+ /* debugging and logging */
+ unsigned char err_log_state;/* actual error log state of the card */
+ unsigned long debug_flags;/* tells what should be debugged and where */
+ void (*set_errlog_state) (struct HYSDN_CARD *, int);
+
+ /* interrupt handler + interrupt synchronisation */
+ struct work_struct irq_queue; /* interrupt task queue */
+ unsigned char volatile irq_enabled;/* interrupt enabled if != 0 */
+ unsigned char volatile hw_lock;/* hardware is currently locked -> no access */
+
+ /* boot process */
+ void *boot; /* pointer to boot private data */
+ int (*writebootimg) (struct HYSDN_CARD *, unsigned char *, unsigned long);
+ int (*writebootseq) (struct HYSDN_CARD *, unsigned char *, int);
+ int (*waitpofready) (struct HYSDN_CARD *);
+ int (*testram) (struct HYSDN_CARD *);
+
+ /* scheduler for data transfer (only async parts) */
+ unsigned char async_data[256];/* async data to be sent (normally for config) */
+ unsigned short volatile async_len;/* length of data to sent */
+ unsigned short volatile async_channel;/* channel number for async transfer */
+ int volatile async_busy; /* flag != 0 sending in progress */
+ int volatile net_tx_busy; /* a network packet tx is in progress */
+
+ /* network interface */
+ void *netif; /* pointer to network structure */
+
+ /* init and deinit stopcard for booting, too */
+ void (*stopcard) (struct HYSDN_CARD *);
+ void (*releasehardware) (struct HYSDN_CARD *);
+
+ spinlock_t hysdn_lock;
+#ifdef CONFIG_HYSDN_CAPI
+ struct hycapictrl_info {
+ char cardname[32];
+ spinlock_t lock;
+ int versionlen;
+ char versionbuf[1024];
+ char *version[HYSDN_MAXVERSION];
+
+ char infobuf[128]; /* for function procinfo */
+
+ struct HYSDN_CARD *card;
+ struct capi_ctr capi_ctrl;
+ struct sk_buff *skbs[HYSDN_MAX_CAPI_SKB];
+ int in_idx, out_idx; /* indexes to buffer ring */
+ int sk_count; /* number of buffers currently in ring */
+ struct sk_buff *tx_skb; /* buffer for tx operation */
+
+ struct list_head ncci_head;
+ } *hyctrlinfo;
+#endif /* CONFIG_HYSDN_CAPI */
+} hysdn_card;
+
+#ifdef CONFIG_HYSDN_CAPI
+typedef struct hycapictrl_info hycapictrl_info;
+#endif /* CONFIG_HYSDN_CAPI */
+
+
+/*****************/
+/* exported vars */
+/*****************/
+extern hysdn_card *card_root; /* pointer to first card */
+
+
+
+/*************************/
+/* im/exported functions */
+/*************************/
+extern char *hysdn_getrev(const char *);
+
+/* hysdn_procconf.c */
+extern int hysdn_procconf_init(void); /* init proc config filesys */
+extern void hysdn_procconf_release(void); /* deinit proc config filesys */
+
+/* hysdn_proclog.c */
+extern int hysdn_proclog_init(hysdn_card *); /* init proc log entry */
+extern void hysdn_proclog_release(hysdn_card *); /* deinit proc log entry */
+extern void hysdn_addlog(hysdn_card *, char *,...); /* output data to log */
+extern void hysdn_card_errlog(hysdn_card *, tErrLogEntry *, int); /* output card log */
+
+/* boardergo.c */
+extern int ergo_inithardware(hysdn_card * card); /* get hardware -> module init */
+
+/* hysdn_boot.c */
+extern int pof_write_close(hysdn_card *); /* close proc file after writing pof */
+extern int pof_write_open(hysdn_card *, unsigned char **); /* open proc file for writing pof */
+extern int pof_write_buffer(hysdn_card *, int); /* write boot data to card */
+extern int EvalSysrTokData(hysdn_card *, unsigned char *, int); /* Check Sysready Token Data */
+
+/* hysdn_sched.c */
+extern int hysdn_sched_tx(hysdn_card *, unsigned char *,
+ unsigned short volatile *, unsigned short volatile *,
+ unsigned short);
+extern int hysdn_sched_rx(hysdn_card *, unsigned char *, unsigned short,
+ unsigned short);
+extern int hysdn_tx_cfgline(hysdn_card *, unsigned char *,
+ unsigned short); /* send one cfg line */
+
+/* hysdn_net.c */
+extern unsigned int hynet_enable;
+extern char *hysdn_net_revision;
+extern int hysdn_net_create(hysdn_card *); /* create a new net device */
+extern int hysdn_net_release(hysdn_card *); /* delete the device */
+extern char *hysdn_net_getname(hysdn_card *); /* get name of net interface */
+extern void hysdn_tx_netack(hysdn_card *); /* acknowledge a packet tx */
+extern struct sk_buff *hysdn_tx_netget(hysdn_card *); /* get next network packet */
+extern void hysdn_rx_netpkt(hysdn_card *, unsigned char *,
+ unsigned short); /* rxed packet from network */
+
+#ifdef CONFIG_HYSDN_CAPI
+extern unsigned int hycapi_enable;
+extern int hycapi_capi_create(hysdn_card *); /* create a new capi device */
+extern int hycapi_capi_release(hysdn_card *); /* delete the device */
+extern int hycapi_capi_stop(hysdn_card *card); /* suspend */
+extern void hycapi_rx_capipkt(hysdn_card * card, unsigned char * buf,
+ unsigned short len);
+extern void hycapi_tx_capiack(hysdn_card * card);
+extern struct sk_buff *hycapi_tx_capiget(hysdn_card *card);
+extern int hycapi_init(void);
+extern void hycapi_cleanup(void);
+#endif /* CONFIG_HYSDN_CAPI */
+
+#endif /* HYSDN_DEFS_H */
diff --git a/drivers/isdn/hysdn/hysdn_init.c b/drivers/isdn/hysdn/hysdn_init.c
new file mode 100644
index 0000000..b7cc5c2
--- /dev/null
+++ b/drivers/isdn/hysdn/hysdn_init.c
@@ -0,0 +1,237 @@
+/* $Id: hysdn_init.c,v 1.6.6.6 2001/09/23 22:24:54 kai Exp $
+ *
+ * Linux driver for HYSDN cards, init functions.
+ *
+ * Author Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+
+#include "hysdn_defs.h"
+
+static struct pci_device_id hysdn_pci_tbl[] = {
+ { PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX,
+ PCI_ANY_ID, PCI_SUBDEVICE_ID_HYPERCOPE_METRO, 0, 0, BD_METRO },
+ { PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX,
+ PCI_ANY_ID, PCI_SUBDEVICE_ID_HYPERCOPE_CHAMP2, 0, 0, BD_CHAMP2 },
+ { PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX,
+ PCI_ANY_ID, PCI_SUBDEVICE_ID_HYPERCOPE_ERGO, 0, 0, BD_ERGO },
+ { PCI_VENDOR_ID_HYPERCOPE, PCI_DEVICE_ID_HYPERCOPE_PLX,
+ PCI_ANY_ID, PCI_SUBDEVICE_ID_HYPERCOPE_OLD_ERGO, 0, 0, BD_ERGO },
+
+ { } /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(pci, hysdn_pci_tbl);
+MODULE_DESCRIPTION("ISDN4Linux: Driver for HYSDN cards");
+MODULE_AUTHOR("Werner Cornelius");
+MODULE_LICENSE("GPL");
+
+static char *hysdn_init_revision = "$Revision: 1.6.6.6 $";
+static int cardmax; /* number of found cards */
+hysdn_card *card_root = NULL; /* pointer to first card */
+static hysdn_card *card_last = NULL; /* pointer to first card */
+
+
+/****************************************************************************/
+/* The module startup and shutdown code. Only compiled when used as module. */
+/* Using the driver as module is always advisable, because the booting */
+/* image becomes smaller and the driver code is only loaded when needed. */
+/* Additionally newer versions may be activated without rebooting. */
+/****************************************************************************/
+
+/******************************************************/
+/* extract revision number from string for log output */
+/******************************************************/
+char *
+hysdn_getrev(const char *revision)
+{
+ char *rev;
+ char *p;
+
+ if ((p = strchr(revision, ':'))) {
+ rev = p + 2;
+ p = strchr(rev, '$');
+ *--p = 0;
+ } else
+ rev = "???";
+ return rev;
+}
+
+
+/****************************************************************************/
+/* init_module is called once when the module is loaded to do all necessary */
+/* things like autodetect... */
+/* If the return value of this function is 0 the init has been successful */
+/* and the module is added to the list in /proc/modules, otherwise an error */
+/* is assumed and the module will not be kept in memory. */
+/****************************************************************************/
+
+static int __devinit hysdn_pci_init_one(struct pci_dev *akt_pcidev,
+ const struct pci_device_id *ent)
+{
+ hysdn_card *card;
+ int rc;
+
+ rc = pci_enable_device(akt_pcidev);
+ if (rc)
+ return rc;
+
+ if (!(card = kzalloc(sizeof(hysdn_card), GFP_KERNEL))) {
+ printk(KERN_ERR "HYSDN: unable to alloc device mem \n");
+ rc = -ENOMEM;
+ goto err_out;
+ }
+ card->myid = cardmax; /* set own id */
+ card->bus = akt_pcidev->bus->number;
+ card->devfn = akt_pcidev->devfn; /* slot + function */
+ card->subsysid = akt_pcidev->subsystem_device;
+ card->irq = akt_pcidev->irq;
+ card->iobase = pci_resource_start(akt_pcidev, PCI_REG_PLX_IO_BASE);
+ card->plxbase = pci_resource_start(akt_pcidev, PCI_REG_PLX_MEM_BASE);
+ card->membase = pci_resource_start(akt_pcidev, PCI_REG_MEMORY_BASE);
+ card->brdtype = BD_NONE; /* unknown */
+ card->debug_flags = DEF_DEB_FLAGS; /* set default debug */
+ card->faxchans = 0; /* default no fax channels */
+ card->bchans = 2; /* and 2 b-channels */
+ card->brdtype = ent->driver_data;
+
+ if (ergo_inithardware(card)) {
+ printk(KERN_WARNING "HYSDN: card at io 0x%04x already in use\n", card->iobase);
+ rc = -EBUSY;
+ goto err_out_card;
+ }
+
+ cardmax++;
+ card->next = NULL; /*end of chain */
+ if (card_last)
+ card_last->next = card; /* pointer to next card */
+ else
+ card_root = card;
+ card_last = card; /* new chain end */
+
+ pci_set_drvdata(akt_pcidev, card);
+ return 0;
+
+err_out_card:
+ kfree(card);
+err_out:
+ pci_disable_device(akt_pcidev);
+ return rc;
+}
+
+static void __devexit hysdn_pci_remove_one(struct pci_dev *akt_pcidev)
+{
+ hysdn_card *card = pci_get_drvdata(akt_pcidev);
+
+ pci_set_drvdata(akt_pcidev, NULL);
+
+ if (card->stopcard)
+ card->stopcard(card);
+
+#ifdef CONFIG_HYSDN_CAPI
+ hycapi_capi_release(card);
+#endif
+
+ if (card->releasehardware)
+ card->releasehardware(card); /* free all hardware resources */
+
+ if (card == card_root) {
+ card_root = card_root->next;
+ if (!card_root)
+ card_last = NULL;
+ } else {
+ hysdn_card *tmp = card_root;
+ while (tmp) {
+ if (tmp->next == card)
+ tmp->next = card->next;
+ card_last = tmp;
+ tmp = tmp->next;
+ }
+ }
+
+ kfree(card);
+ pci_disable_device(akt_pcidev);
+}
+
+static struct pci_driver hysdn_pci_driver = {
+ .name = "hysdn",
+ .id_table = hysdn_pci_tbl,
+ .probe = hysdn_pci_init_one,
+ .remove = __devexit_p(hysdn_pci_remove_one),
+};
+
+static int hysdn_have_procfs;
+
+static int __init
+hysdn_init(void)
+{
+ char tmp[50];
+ int rc;
+
+ strcpy(tmp, hysdn_init_revision);
+ printk(KERN_NOTICE "HYSDN: module Rev: %s loaded\n", hysdn_getrev(tmp));
+ strcpy(tmp, hysdn_net_revision);
+ printk(KERN_NOTICE "HYSDN: network interface Rev: %s \n", hysdn_getrev(tmp));
+
+ rc = pci_register_driver(&hysdn_pci_driver);
+ if (rc)
+ return rc;
+
+ printk(KERN_INFO "HYSDN: %d card(s) found.\n", cardmax);
+
+ if (!hysdn_procconf_init())
+ hysdn_have_procfs = 1;
+
+#ifdef CONFIG_HYSDN_CAPI
+ if(cardmax > 0) {
+ if(hycapi_init()) {
+ printk(KERN_ERR "HYCAPI: init failed\n");
+
+ if (hysdn_have_procfs)
+ hysdn_procconf_release();
+
+ pci_unregister_driver(&hysdn_pci_driver);
+ return -ESPIPE;
+ }
+ }
+#endif /* CONFIG_HYSDN_CAPI */
+
+ return 0; /* no error */
+} /* init_module */
+
+
+/***********************************************************************/
+/* cleanup_module is called when the module is released by the kernel. */
+/* The routine is only called if init_module has been successful and */
+/* the module counter has a value of 0. Otherwise this function will */
+/* not be called. This function must release all resources still allo- */
+/* cated as after the return from this function the module code will */
+/* be removed from memory. */
+/***********************************************************************/
+static void __exit
+hysdn_exit(void)
+{
+ if (hysdn_have_procfs)
+ hysdn_procconf_release();
+
+ pci_unregister_driver(&hysdn_pci_driver);
+
+#ifdef CONFIG_HYSDN_CAPI
+ hycapi_cleanup();
+#endif /* CONFIG_HYSDN_CAPI */
+
+ printk(KERN_NOTICE "HYSDN: module unloaded\n");
+} /* cleanup_module */
+
+module_init(hysdn_init);
+module_exit(hysdn_exit);
diff --git a/drivers/isdn/hysdn/hysdn_net.c b/drivers/isdn/hysdn/hysdn_net.c
new file mode 100644
index 0000000..3f2a0a2
--- /dev/null
+++ b/drivers/isdn/hysdn/hysdn_net.c
@@ -0,0 +1,344 @@
+/* $Id: hysdn_net.c,v 1.8.6.4 2001/09/23 22:24:54 kai Exp $
+ *
+ * Linux driver for HYSDN cards, net (ethernet type) handling routines.
+ *
+ * Author Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * This net module has been inspired by the skeleton driver from
+ * Donald Becker (becker@CESDIS.gsfc.nasa.gov)
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/signal.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/inetdevice.h>
+
+#include "hysdn_defs.h"
+
+unsigned int hynet_enable = 0xffffffff;
+module_param(hynet_enable, uint, 0);
+
+/* store the actual version for log reporting */
+char *hysdn_net_revision = "$Revision: 1.8.6.4 $";
+
+#define MAX_SKB_BUFFERS 20 /* number of buffers for keeping TX-data */
+
+/****************************************************************************/
+/* structure containing the complete network data. The structure is aligned */
+/* in a way that both, the device and statistics are kept inside it. */
+/* for proper access, the device structure MUST be the first var/struct */
+/* inside the definition. */
+/****************************************************************************/
+struct net_local {
+ struct net_device netdev; /* the network device */
+ struct net_device_stats stats;
+ /* additional vars may be added here */
+ char dev_name[9]; /* our own device name */
+
+ /* Tx control lock. This protects the transmit buffer ring
+ * state along with the "tx full" state of the driver. This
+ * means all netif_queue flow control actions are protected
+ * by this lock as well.
+ */
+ spinlock_t lock;
+ struct sk_buff *skbs[MAX_SKB_BUFFERS]; /* pointers to tx-skbs */
+ int in_idx, out_idx; /* indexes to buffer ring */
+ int sk_count; /* number of buffers currently in ring */
+}; /* net_local */
+
+
+/*****************************************************/
+/* Get the current statistics for this card. */
+/* This may be called with the card open or closed ! */
+/*****************************************************/
+static struct net_device_stats *
+net_get_stats(struct net_device *dev)
+{
+ return (&((struct net_local *) dev)->stats);
+} /* net_device_stats */
+
+/*********************************************************************/
+/* Open/initialize the board. This is called (in the current kernel) */
+/* sometime after booting when the 'ifconfig' program is run. */
+/* This routine should set everything up anew at each open, even */
+/* registers that "should" only need to be set once at boot, so that */
+/* there is non-reboot way to recover if something goes wrong. */
+/*********************************************************************/
+static int
+net_open(struct net_device *dev)
+{
+ struct in_device *in_dev;
+ hysdn_card *card = dev->priv;
+ int i;
+
+ netif_start_queue(dev); /* start tx-queueing */
+
+ /* Fill in the MAC-level header (if not already set) */
+ if (!card->mac_addr[0]) {
+ for (i = 0; i < ETH_ALEN; i++)
+ dev->dev_addr[i] = 0xfc;
+ if ((in_dev = dev->ip_ptr) != NULL) {
+ struct in_ifaddr *ifa = in_dev->ifa_list;
+ if (ifa != NULL)
+ memcpy(dev->dev_addr + (ETH_ALEN - sizeof(ifa->ifa_local)), &ifa->ifa_local, sizeof(ifa->ifa_local));
+ }
+ } else
+ memcpy(dev->dev_addr, card->mac_addr, ETH_ALEN);
+
+ return (0);
+} /* net_open */
+
+/*******************************************/
+/* flush the currently occupied tx-buffers */
+/* must only be called when device closed */
+/*******************************************/
+static void
+flush_tx_buffers(struct net_local *nl)
+{
+
+ while (nl->sk_count) {
+ dev_kfree_skb(nl->skbs[nl->out_idx++]); /* free skb */
+ if (nl->out_idx >= MAX_SKB_BUFFERS)
+ nl->out_idx = 0; /* wrap around */
+ nl->sk_count--;
+ }
+} /* flush_tx_buffers */
+
+
+/*********************************************************************/
+/* close/decativate the device. The device is not removed, but only */
+/* deactivated. */
+/*********************************************************************/
+static int
+net_close(struct net_device *dev)
+{
+
+ netif_stop_queue(dev); /* disable queueing */
+
+ flush_tx_buffers((struct net_local *) dev);
+
+ return (0); /* success */
+} /* net_close */
+
+/************************************/
+/* send a packet on this interface. */
+/* new style for kernel >= 2.3.33 */
+/************************************/
+static int
+net_send_packet(struct sk_buff *skb, struct net_device *dev)
+{
+ struct net_local *lp = (struct net_local *) dev;
+
+ spin_lock_irq(&lp->lock);
+
+ lp->skbs[lp->in_idx++] = skb; /* add to buffer list */
+ if (lp->in_idx >= MAX_SKB_BUFFERS)
+ lp->in_idx = 0; /* wrap around */
+ lp->sk_count++; /* adjust counter */
+ dev->trans_start = jiffies;
+
+ /* If we just used up the very last entry in the
+ * TX ring on this device, tell the queueing
+ * layer to send no more.
+ */
+ if (lp->sk_count >= MAX_SKB_BUFFERS)
+ netif_stop_queue(dev);
+
+ /* When the TX completion hw interrupt arrives, this
+ * is when the transmit statistics are updated.
+ */
+
+ spin_unlock_irq(&lp->lock);
+
+ if (lp->sk_count <= 3) {
+ schedule_work(&((hysdn_card *) dev->priv)->irq_queue);
+ }
+ return (0); /* success */
+} /* net_send_packet */
+
+
+
+/***********************************************************************/
+/* acknowlegde a packet send. The network layer will be informed about */
+/* completion */
+/***********************************************************************/
+void
+hysdn_tx_netack(hysdn_card * card)
+{
+ struct net_local *lp = card->netif;
+
+ if (!lp)
+ return; /* non existing device */
+
+
+ if (!lp->sk_count)
+ return; /* error condition */
+
+ lp->stats.tx_packets++;
+ lp->stats.tx_bytes += lp->skbs[lp->out_idx]->len;
+
+ dev_kfree_skb(lp->skbs[lp->out_idx++]); /* free skb */
+ if (lp->out_idx >= MAX_SKB_BUFFERS)
+ lp->out_idx = 0; /* wrap around */
+
+ if (lp->sk_count-- == MAX_SKB_BUFFERS) /* dec usage count */
+ netif_start_queue((struct net_device *) lp);
+} /* hysdn_tx_netack */
+
+/*****************************************************/
+/* we got a packet from the network, go and queue it */
+/*****************************************************/
+void
+hysdn_rx_netpkt(hysdn_card * card, unsigned char *buf, unsigned short len)
+{
+ struct net_local *lp = card->netif;
+ struct sk_buff *skb;
+
+ if (!lp)
+ return; /* non existing device */
+
+ lp->stats.rx_bytes += len;
+
+ skb = dev_alloc_skb(len);
+ if (skb == NULL) {
+ printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n",
+ lp->netdev.name);
+ lp->stats.rx_dropped++;
+ return;
+ }
+ /* copy the data */
+ memcpy(skb_put(skb, len), buf, len);
+
+ /* determine the used protocol */
+ skb->protocol = eth_type_trans(skb, &lp->netdev);
+
+ netif_rx(skb);
+ lp->stats.rx_packets++; /* adjust packet count */
+
+} /* hysdn_rx_netpkt */
+
+/*****************************************************/
+/* return the pointer to a network packet to be send */
+/*****************************************************/
+struct sk_buff *
+hysdn_tx_netget(hysdn_card * card)
+{
+ struct net_local *lp = card->netif;
+
+ if (!lp)
+ return (NULL); /* non existing device */
+
+ if (!lp->sk_count)
+ return (NULL); /* nothing available */
+
+ return (lp->skbs[lp->out_idx]); /* next packet to send */
+} /* hysdn_tx_netget */
+
+
+/*******************************************/
+/* init function called by register device */
+/*******************************************/
+static int
+net_init(struct net_device *dev)
+{
+ /* setup the function table */
+ dev->open = net_open;
+ dev->stop = net_close;
+ dev->hard_start_xmit = net_send_packet;
+ dev->get_stats = net_get_stats;
+
+ /* Fill in the fields of the device structure with ethernet values. */
+ ether_setup(dev);
+
+ return (0); /* success */
+} /* net_init */
+
+/*****************************************************************************/
+/* hysdn_net_create creates a new net device for the given card. If a device */
+/* already exists, it will be deleted and created a new one. The return value */
+/* 0 announces success, else a negative error code will be returned. */
+/*****************************************************************************/
+int
+hysdn_net_create(hysdn_card * card)
+{
+ struct net_device *dev;
+ int i;
+ if(!card) {
+ printk(KERN_WARNING "No card-pt in hysdn_net_create!\n");
+ return (-ENOMEM);
+ }
+ hysdn_net_release(card); /* release an existing net device */
+ if ((dev = kzalloc(sizeof(struct net_local), GFP_KERNEL)) == NULL) {
+ printk(KERN_WARNING "HYSDN: unable to allocate mem\n");
+ return (-ENOMEM);
+ }
+
+ spin_lock_init(&((struct net_local *) dev)->lock);
+
+ /* initialise necessary or informing fields */
+ dev->base_addr = card->iobase; /* IO address */
+ dev->irq = card->irq; /* irq */
+ dev->init = net_init; /* the init function of the device */
+ if(dev->name) {
+ strcpy(dev->name, ((struct net_local *) dev)->dev_name);
+ }
+ if ((i = register_netdev(dev))) {
+ printk(KERN_WARNING "HYSDN: unable to create network device\n");
+ kfree(dev);
+ return (i);
+ }
+ dev->priv = card; /* remember pointer to own data structure */
+ card->netif = dev; /* setup the local pointer */
+
+ if (card->debug_flags & LOG_NET_INIT)
+ hysdn_addlog(card, "network device created");
+ return (0); /* and return success */
+} /* hysdn_net_create */
+
+/***************************************************************************/
+/* hysdn_net_release deletes the net device for the given card. The return */
+/* value 0 announces success, else a negative error code will be returned. */
+/***************************************************************************/
+int
+hysdn_net_release(hysdn_card * card)
+{
+ struct net_device *dev = card->netif;
+
+ if (!dev)
+ return (0); /* non existing */
+
+ card->netif = NULL; /* clear out pointer */
+ dev->stop(dev); /* close the device */
+
+ flush_tx_buffers((struct net_local *) dev); /* empty buffers */
+
+ unregister_netdev(dev); /* release the device */
+ free_netdev(dev); /* release the memory allocated */
+ if (card->debug_flags & LOG_NET_INIT)
+ hysdn_addlog(card, "network device deleted");
+
+ return (0); /* always successful */
+} /* hysdn_net_release */
+
+/*****************************************************************************/
+/* hysdn_net_getname returns a pointer to the name of the network interface. */
+/* if the interface is not existing, a "-" is returned. */
+/*****************************************************************************/
+char *
+hysdn_net_getname(hysdn_card * card)
+{
+ struct net_device *dev = card->netif;
+
+ if (!dev)
+ return ("-"); /* non existing */
+
+ return (dev->name);
+} /* hysdn_net_getname */
diff --git a/drivers/isdn/hysdn/hysdn_pof.h b/drivers/isdn/hysdn/hysdn_pof.h
new file mode 100644
index 0000000..3a72b90
--- /dev/null
+++ b/drivers/isdn/hysdn/hysdn_pof.h
@@ -0,0 +1,78 @@
+/* $Id: hysdn_pof.h,v 1.2.6.1 2001/09/23 22:24:54 kai Exp $
+ *
+ * Linux driver for HYSDN cards, definitions used for handling pof-files.
+ *
+ * Author Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/************************/
+/* POF specific defines */
+/************************/
+#define BOOT_BUF_SIZE 0x1000 /* =4096, maybe moved to other h file */
+#define CRYPT_FEEDTERM 0x8142
+#define CRYPT_STARTTERM 0x81a5
+ /* max. timeout time in seconds
+ * from end of booting to POF is ready
+ */
+#define POF_READY_TIME_OUT_SEC 10
+
+/**********************************/
+/* defines for 1.stage boot image */
+/**********************************/
+
+/* the POF file record containing the boot loader image
+ * has 2 pages a 16KB:
+ * 1. page contains the high 16-bit part of the 32-bit E1 words
+ * 2. page contains the low 16-bit part of the 32-bit E1 words
+ *
+ * In each 16KB page we assume the start of the boot loader code
+ * in the highest 2KB part (at offset 0x3800);
+ * the rest (0x0000..0x37FF) is assumed to contain 0 bytes.
+ */
+
+#define POF_BOOT_LOADER_PAGE_SIZE 0x4000 /* =16384U */
+#define POF_BOOT_LOADER_TOTAL_SIZE (2U*POF_BOOT_LOADER_PAGE_SIZE)
+
+#define POF_BOOT_LOADER_CODE_SIZE 0x0800 /* =2KB =2048U */
+
+ /* offset in boot page, where loader code may start */
+ /* =0x3800= 14336U */
+#define POF_BOOT_LOADER_OFF_IN_PAGE (POF_BOOT_LOADER_PAGE_SIZE-POF_BOOT_LOADER_CODE_SIZE)
+
+
+/*--------------------------------------POF file record structs------------*/
+typedef struct PofFileHdr_tag { /* Pof file header */
+/*00 */ unsigned long Magic __attribute__((packed));
+/*04 */ unsigned long N_PofRecs __attribute__((packed));
+/*08 */
+} tPofFileHdr;
+
+typedef struct PofRecHdr_tag { /* Pof record header */
+/*00 */ unsigned short PofRecId __attribute__((packed));
+/*02 */ unsigned long PofRecDataLen __attribute__((packed));
+/*06 */
+} tPofRecHdr;
+
+typedef struct PofTimeStamp_tag {
+/*00 */ unsigned long UnixTime __attribute__((packed));
+ /*04 */ unsigned char DateTimeText[0x28];
+ /* =40 */
+/*2C */
+} tPofTimeStamp;
+
+ /* tPofFileHdr.Magic value: */
+#define TAGFILEMAGIC 0x464F501AUL
+ /* tPofRecHdr.PofRecId values: */
+#define TAG_ABSDATA 0x1000 /* abs. data */
+#define TAG_BOOTDTA 0x1001 /* boot data */
+#define TAG_COMMENT 0x0020
+#define TAG_SYSCALL 0x0021
+#define TAG_FLOWCTRL 0x0022
+#define TAG_TIMESTMP 0x0010 /* date/time stamp of version */
+#define TAG_CABSDATA 0x1100 /* crypted abs. data */
+#define TAG_CBOOTDTA 0x1101 /* crypted boot data */
diff --git a/drivers/isdn/hysdn/hysdn_procconf.c b/drivers/isdn/hysdn/hysdn_procconf.c
new file mode 100644
index 0000000..484299b
--- /dev/null
+++ b/drivers/isdn/hysdn/hysdn_procconf.c
@@ -0,0 +1,429 @@
+/* $Id: hysdn_procconf.c,v 1.8.6.4 2001/09/23 22:24:54 kai Exp $
+ *
+ * Linux driver for HYSDN cards, /proc/net filesystem dir and conf functions.
+ *
+ * written by Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ *
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/pci.h>
+#include <linux/smp_lock.h>
+#include <net/net_namespace.h>
+
+#include "hysdn_defs.h"
+
+static char *hysdn_procconf_revision = "$Revision: 1.8.6.4 $";
+
+#define INFO_OUT_LEN 80 /* length of info line including lf */
+
+/********************************************************/
+/* defines and data structure for conf write operations */
+/********************************************************/
+#define CONF_STATE_DETECT 0 /* waiting for detect */
+#define CONF_STATE_CONF 1 /* writing config data */
+#define CONF_STATE_POF 2 /* writing pof data */
+#define CONF_LINE_LEN 255 /* 255 chars max */
+
+struct conf_writedata {
+ hysdn_card *card; /* card the device is connected to */
+ int buf_size; /* actual number of bytes in the buffer */
+ int needed_size; /* needed size when reading pof */
+ int state; /* actual interface states from above constants */
+ unsigned char conf_line[CONF_LINE_LEN]; /* buffered conf line */
+ unsigned short channel; /* active channel number */
+ unsigned char *pof_buffer; /* buffer when writing pof */
+};
+
+/***********************************************************************/
+/* process_line parses one config line and transfers it to the card if */
+/* necessary. */
+/* if the return value is negative an error occurred. */
+/***********************************************************************/
+static int
+process_line(struct conf_writedata *cnf)
+{
+ unsigned char *cp = cnf->conf_line;
+ int i;
+
+ if (cnf->card->debug_flags & LOG_CNF_LINE)
+ hysdn_addlog(cnf->card, "conf line: %s", cp);
+
+ if (*cp == '-') { /* option */
+ cp++; /* point to option char */
+
+ if (*cp++ != 'c')
+ return (0); /* option unknown or used */
+ i = 0; /* start value for channel */
+ while ((*cp <= '9') && (*cp >= '0'))
+ i = i * 10 + *cp++ - '0'; /* get decimal number */
+ if (i > 65535) {
+ if (cnf->card->debug_flags & LOG_CNF_MISC)
+ hysdn_addlog(cnf->card, "conf channel invalid %d", i);
+ return (-ERR_INV_CHAN); /* invalid channel */
+ }
+ cnf->channel = i & 0xFFFF; /* set new channel number */
+ return (0); /* success */
+ } /* option */
+ if (*cp == '*') { /* line to send */
+ if (cnf->card->debug_flags & LOG_CNF_DATA)
+ hysdn_addlog(cnf->card, "conf chan=%d %s", cnf->channel, cp);
+ return (hysdn_tx_cfgline(cnf->card, cnf->conf_line + 1,
+ cnf->channel)); /* send the line without * */
+ } /* line to send */
+ return (0);
+} /* process_line */
+
+/***********************************/
+/* conf file operations and tables */
+/***********************************/
+
+/****************************************************/
+/* write conf file -> boot or send cfg line to card */
+/****************************************************/
+static ssize_t
+hysdn_conf_write(struct file *file, const char __user *buf, size_t count, loff_t * off)
+{
+ struct conf_writedata *cnf;
+ int i;
+ unsigned char ch, *cp;
+
+ if (!count)
+ return (0); /* nothing to handle */
+
+ if (!(cnf = file->private_data))
+ return (-EFAULT); /* should never happen */
+
+ if (cnf->state == CONF_STATE_DETECT) { /* auto detect cnf or pof data */
+ if (copy_from_user(&ch, buf, 1)) /* get first char for detect */
+ return (-EFAULT);
+
+ if (ch == 0x1A) {
+ /* we detected a pof file */
+ if ((cnf->needed_size = pof_write_open(cnf->card, &cnf->pof_buffer)) <= 0)
+ return (cnf->needed_size); /* an error occurred -> exit */
+ cnf->buf_size = 0; /* buffer is empty */
+ cnf->state = CONF_STATE_POF; /* new state */
+ } else {
+ /* conf data has been detected */
+ cnf->buf_size = 0; /* buffer is empty */
+ cnf->state = CONF_STATE_CONF; /* requested conf data write */
+ if (cnf->card->state != CARD_STATE_RUN)
+ return (-ERR_NOT_BOOTED);
+ cnf->conf_line[CONF_LINE_LEN - 1] = 0; /* limit string length */
+ cnf->channel = 4098; /* default channel for output */
+ }
+ } /* state was auto detect */
+ if (cnf->state == CONF_STATE_POF) { /* pof write active */
+ i = cnf->needed_size - cnf->buf_size; /* bytes still missing for write */
+ if (i <= 0)
+ return (-EINVAL); /* size error handling pof */
+
+ if (i < count)
+ count = i; /* limit requested number of bytes */
+ if (copy_from_user(cnf->pof_buffer + cnf->buf_size, buf, count))
+ return (-EFAULT); /* error while copying */
+ cnf->buf_size += count;
+
+ if (cnf->needed_size == cnf->buf_size) {
+ cnf->needed_size = pof_write_buffer(cnf->card, cnf->buf_size); /* write data */
+ if (cnf->needed_size <= 0) {
+ cnf->card->state = CARD_STATE_BOOTERR; /* show boot error */
+ return (cnf->needed_size); /* an error occurred */
+ }
+ cnf->buf_size = 0; /* buffer is empty again */
+ }
+ }
+ /* pof write active */
+ else { /* conf write active */
+
+ if (cnf->card->state != CARD_STATE_RUN) {
+ if (cnf->card->debug_flags & LOG_CNF_MISC)
+ hysdn_addlog(cnf->card, "cnf write denied -> not booted");
+ return (-ERR_NOT_BOOTED);
+ }
+ i = (CONF_LINE_LEN - 1) - cnf->buf_size; /* bytes available in buffer */
+ if (i > 0) {
+ /* copy remaining bytes into buffer */
+
+ if (count > i)
+ count = i; /* limit transfer */
+ if (copy_from_user(cnf->conf_line + cnf->buf_size, buf, count))
+ return (-EFAULT); /* error while copying */
+
+ i = count; /* number of chars in buffer */
+ cp = cnf->conf_line + cnf->buf_size;
+ while (i) {
+ /* search for end of line */
+ if ((*cp < ' ') && (*cp != 9))
+ break; /* end of line found */
+ cp++;
+ i--;
+ } /* search for end of line */
+
+ if (i) {
+ /* delimiter found */
+ *cp++ = 0; /* string termination */
+ count -= (i - 1); /* subtract remaining bytes from count */
+ while ((i) && (*cp < ' ') && (*cp != 9)) {
+ i--; /* discard next char */
+ count++; /* mark as read */
+ cp++; /* next char */
+ }
+ cnf->buf_size = 0; /* buffer is empty after transfer */
+ if ((i = process_line(cnf)) < 0) /* handle the line */
+ count = i; /* return the error */
+ }
+ /* delimiter found */
+ else {
+ cnf->buf_size += count; /* add chars to string */
+ if (cnf->buf_size >= CONF_LINE_LEN - 1) {
+ if (cnf->card->debug_flags & LOG_CNF_MISC)
+ hysdn_addlog(cnf->card, "cnf line too long %d chars pos %d", cnf->buf_size, count);
+ return (-ERR_CONF_LONG);
+ }
+ } /* not delimited */
+
+ }
+ /* copy remaining bytes into buffer */
+ else {
+ if (cnf->card->debug_flags & LOG_CNF_MISC)
+ hysdn_addlog(cnf->card, "cnf line too long");
+ return (-ERR_CONF_LONG);
+ }
+ } /* conf write active */
+
+ return (count);
+} /* hysdn_conf_write */
+
+/*******************************************/
+/* read conf file -> output card info data */
+/*******************************************/
+static ssize_t
+hysdn_conf_read(struct file *file, char __user *buf, size_t count, loff_t *off)
+{
+ char *cp;
+
+ if (!(file->f_mode & FMODE_READ))
+ return -EPERM; /* no permission to read */
+
+ if (!(cp = file->private_data))
+ return -EFAULT; /* should never happen */
+
+ return simple_read_from_buffer(buf, count, off, cp, strlen(cp));
+} /* hysdn_conf_read */
+
+/******************/
+/* open conf file */
+/******************/
+static int
+hysdn_conf_open(struct inode *ino, struct file *filep)
+{
+ hysdn_card *card;
+ struct proc_dir_entry *pd;
+ struct conf_writedata *cnf;
+ char *cp, *tmp;
+
+ /* now search the addressed card */
+ lock_kernel();
+ card = card_root;
+ while (card) {
+ pd = card->procconf;
+ if (pd == PDE(ino))
+ break;
+ card = card->next; /* search next entry */
+ }
+ if (!card) {
+ unlock_kernel();
+ return (-ENODEV); /* device is unknown/invalid */
+ }
+ if (card->debug_flags & (LOG_PROC_OPEN | LOG_PROC_ALL))
+ hysdn_addlog(card, "config open for uid=%d gid=%d mode=0x%x",
+ filep->f_uid, filep->f_gid, filep->f_mode);
+
+ if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
+ /* write only access -> write boot file or conf line */
+
+ if (!(cnf = kmalloc(sizeof(struct conf_writedata), GFP_KERNEL))) {
+ unlock_kernel();
+ return (-EFAULT);
+ }
+ cnf->card = card;
+ cnf->buf_size = 0; /* nothing buffered */
+ cnf->state = CONF_STATE_DETECT; /* start auto detect */
+ filep->private_data = cnf;
+
+ } else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
+ /* read access -> output card info data */
+
+ if (!(tmp = kmalloc(INFO_OUT_LEN * 2 + 2, GFP_KERNEL))) {
+ unlock_kernel();
+ return (-EFAULT); /* out of memory */
+ }
+ filep->private_data = tmp; /* start of string */
+
+ /* first output a headline */
+ sprintf(tmp, "id bus slot type irq iobase dp-mem b-chans fax-chans state device");
+ cp = tmp; /* start of string */
+ while (*cp)
+ cp++;
+ while (((cp - tmp) % (INFO_OUT_LEN + 1)) != INFO_OUT_LEN)
+ *cp++ = ' ';
+ *cp++ = '\n';
+
+ /* and now the data */
+ sprintf(cp, "%d %3d %4d %4d %3d 0x%04x 0x%08lx %7d %9d %3d %s",
+ card->myid,
+ card->bus,
+ PCI_SLOT(card->devfn),
+ card->brdtype,
+ card->irq,
+ card->iobase,
+ card->membase,
+ card->bchans,
+ card->faxchans,
+ card->state,
+ hysdn_net_getname(card));
+ while (*cp)
+ cp++;
+ while (((cp - tmp) % (INFO_OUT_LEN + 1)) != INFO_OUT_LEN)
+ *cp++ = ' ';
+ *cp++ = '\n';
+ *cp = 0; /* end of string */
+ } else { /* simultaneous read/write access forbidden ! */
+ unlock_kernel();
+ return (-EPERM); /* no permission this time */
+ }
+ unlock_kernel();
+ return nonseekable_open(ino, filep);
+} /* hysdn_conf_open */
+
+/***************************/
+/* close a config file. */
+/***************************/
+static int
+hysdn_conf_close(struct inode *ino, struct file *filep)
+{
+ hysdn_card *card;
+ struct conf_writedata *cnf;
+ int retval = 0;
+ struct proc_dir_entry *pd;
+
+ lock_kernel();
+ /* search the addressed card */
+ card = card_root;
+ while (card) {
+ pd = card->procconf;
+ if (pd == PDE(ino))
+ break;
+ card = card->next; /* search next entry */
+ }
+ if (!card) {
+ unlock_kernel();
+ return (-ENODEV); /* device is unknown/invalid */
+ }
+ if (card->debug_flags & (LOG_PROC_OPEN | LOG_PROC_ALL))
+ hysdn_addlog(card, "config close for uid=%d gid=%d mode=0x%x",
+ filep->f_uid, filep->f_gid, filep->f_mode);
+
+ if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
+ /* write only access -> write boot file or conf line */
+ if (filep->private_data) {
+ cnf = filep->private_data;
+
+ if (cnf->state == CONF_STATE_POF)
+ retval = pof_write_close(cnf->card); /* close the pof write */
+ kfree(filep->private_data); /* free allocated memory for buffer */
+
+ } /* handle write private data */
+ } else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
+ /* read access -> output card info data */
+
+ kfree(filep->private_data); /* release memory */
+ }
+ unlock_kernel();
+ return (retval);
+} /* hysdn_conf_close */
+
+/******************************************************/
+/* table for conf filesystem functions defined above. */
+/******************************************************/
+static const struct file_operations conf_fops =
+{
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = hysdn_conf_read,
+ .write = hysdn_conf_write,
+ .open = hysdn_conf_open,
+ .release = hysdn_conf_close,
+};
+
+/*****************************/
+/* hysdn subdir in /proc/net */
+/*****************************/
+struct proc_dir_entry *hysdn_proc_entry = NULL;
+
+/*******************************************************************************/
+/* hysdn_procconf_init is called when the module is loaded and after the cards */
+/* have been detected. The needed proc dir and card config files are created. */
+/* The log init is called at last. */
+/*******************************************************************************/
+int
+hysdn_procconf_init(void)
+{
+ hysdn_card *card;
+ unsigned char conf_name[20];
+
+ hysdn_proc_entry = proc_mkdir(PROC_SUBDIR_NAME, init_net.proc_net);
+ if (!hysdn_proc_entry) {
+ printk(KERN_ERR "HYSDN: unable to create hysdn subdir\n");
+ return (-1);
+ }
+ card = card_root; /* point to first card */
+ while (card) {
+
+ sprintf(conf_name, "%s%d", PROC_CONF_BASENAME, card->myid);
+ if ((card->procconf = (void *) proc_create(conf_name,
+ S_IFREG | S_IRUGO | S_IWUSR,
+ hysdn_proc_entry,
+ &conf_fops)) != NULL) {
+ hysdn_proclog_init(card); /* init the log file entry */
+ }
+ card = card->next; /* next entry */
+ }
+
+ printk(KERN_NOTICE "HYSDN: procfs Rev. %s initialised\n", hysdn_getrev(hysdn_procconf_revision));
+ return (0);
+} /* hysdn_procconf_init */
+
+/*************************************************************************************/
+/* hysdn_procconf_release is called when the module is unloaded and before the cards */
+/* resources are released. The module counter is assumed to be 0 ! */
+/*************************************************************************************/
+void
+hysdn_procconf_release(void)
+{
+ hysdn_card *card;
+ unsigned char conf_name[20];
+
+ card = card_root; /* start with first card */
+ while (card) {
+
+ sprintf(conf_name, "%s%d", PROC_CONF_BASENAME, card->myid);
+ if (card->procconf)
+ remove_proc_entry(conf_name, hysdn_proc_entry);
+
+ hysdn_proclog_release(card); /* init the log file entry */
+
+ card = card->next; /* point to next card */
+ }
+
+ remove_proc_entry(PROC_SUBDIR_NAME, init_net.proc_net);
+}
diff --git a/drivers/isdn/hysdn/hysdn_proclog.c b/drivers/isdn/hysdn/hysdn_proclog.c
new file mode 100644
index 0000000..8991d2c
--- /dev/null
+++ b/drivers/isdn/hysdn/hysdn_proclog.c
@@ -0,0 +1,433 @@
+/* $Id: hysdn_proclog.c,v 1.9.6.3 2001/09/23 22:24:54 kai Exp $
+ *
+ * Linux driver for HYSDN cards, /proc/net filesystem log functions.
+ *
+ * Author Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/smp_lock.h>
+
+#include "hysdn_defs.h"
+
+/* the proc subdir for the interface is defined in the procconf module */
+extern struct proc_dir_entry *hysdn_proc_entry;
+
+static void put_log_buffer(hysdn_card * card, char *cp);
+
+/*************************************************/
+/* structure keeping ascii log for device output */
+/*************************************************/
+struct log_data {
+ struct log_data *next;
+ unsigned long usage_cnt;/* number of files still to work */
+ void *proc_ctrl; /* pointer to own control procdata structure */
+ char log_start[2]; /* log string start (final len aligned by size) */
+};
+
+/**********************************************/
+/* structure holding proc entrys for one card */
+/**********************************************/
+struct procdata {
+ struct proc_dir_entry *log; /* log entry */
+ char log_name[15]; /* log filename */
+ struct log_data *log_head, *log_tail; /* head and tail for queue */
+ int if_used; /* open count for interface */
+ int volatile del_lock; /* lock for delete operations */
+ unsigned char logtmp[LOG_MAX_LINELEN];
+ wait_queue_head_t rd_queue;
+};
+
+
+/**********************************************/
+/* log function for cards error log interface */
+/**********************************************/
+void
+hysdn_card_errlog(hysdn_card * card, tErrLogEntry * logp, int maxsize)
+{
+ char buf[ERRLOG_TEXT_SIZE + 40];
+
+ sprintf(buf, "LOG 0x%08lX 0x%08lX : %s\n", logp->ulErrType, logp->ulErrSubtype, logp->ucText);
+ put_log_buffer(card, buf); /* output the string */
+} /* hysdn_card_errlog */
+
+/***************************************************/
+/* Log function using format specifiers for output */
+/***************************************************/
+void
+hysdn_addlog(hysdn_card * card, char *fmt,...)
+{
+ struct procdata *pd = card->proclog;
+ char *cp;
+ va_list args;
+
+ if (!pd)
+ return; /* log structure non existent */
+
+ cp = pd->logtmp;
+ cp += sprintf(cp, "HYSDN: card %d ", card->myid);
+
+ va_start(args, fmt);
+ cp += vsprintf(cp, fmt, args);
+ va_end(args);
+ *cp++ = '\n';
+ *cp = 0;
+
+ if (card->debug_flags & DEB_OUT_SYSLOG)
+ printk(KERN_INFO "%s", pd->logtmp);
+ else
+ put_log_buffer(card, pd->logtmp);
+
+} /* hysdn_addlog */
+
+/********************************************/
+/* put an log buffer into the log queue. */
+/* This buffer will be kept until all files */
+/* opened for read got the contents. */
+/* Flushes buffers not longer in use. */
+/********************************************/
+static void
+put_log_buffer(hysdn_card * card, char *cp)
+{
+ struct log_data *ib;
+ struct procdata *pd = card->proclog;
+ int i;
+ unsigned long flags;
+
+ if (!pd)
+ return;
+ if (!cp)
+ return;
+ if (!*cp)
+ return;
+ if (pd->if_used <= 0)
+ return; /* no open file for read */
+
+ if (!(ib = kmalloc(sizeof(struct log_data) + strlen(cp), GFP_ATOMIC)))
+ return; /* no memory */
+ strcpy(ib->log_start, cp); /* set output string */
+ ib->next = NULL;
+ ib->proc_ctrl = pd; /* point to own control structure */
+ spin_lock_irqsave(&card->hysdn_lock, flags);
+ ib->usage_cnt = pd->if_used;
+ if (!pd->log_head)
+ pd->log_head = ib; /* new head */
+ else
+ pd->log_tail->next = ib; /* follows existing messages */
+ pd->log_tail = ib; /* new tail */
+ i = pd->del_lock++; /* get lock state */
+ spin_unlock_irqrestore(&card->hysdn_lock, flags);
+
+ /* delete old entrys */
+ if (!i)
+ while (pd->log_head->next) {
+ if ((pd->log_head->usage_cnt <= 0) &&
+ (pd->log_head->next->usage_cnt <= 0)) {
+ ib = pd->log_head;
+ pd->log_head = pd->log_head->next;
+ kfree(ib);
+ } else
+ break;
+ } /* pd->log_head->next */
+ pd->del_lock--; /* release lock level */
+ wake_up_interruptible(&(pd->rd_queue)); /* announce new entry */
+} /* put_log_buffer */
+
+
+/******************************/
+/* file operations and tables */
+/******************************/
+
+/****************************************/
+/* write log file -> set log level bits */
+/****************************************/
+static ssize_t
+hysdn_log_write(struct file *file, const char __user *buf, size_t count, loff_t * off)
+{
+ unsigned long u = 0;
+ int found = 0;
+ unsigned char *cp, valbuf[128];
+ long base = 10;
+ hysdn_card *card = (hysdn_card *) file->private_data;
+
+ if (count > (sizeof(valbuf) - 1))
+ count = sizeof(valbuf) - 1; /* limit length */
+ if (copy_from_user(valbuf, buf, count))
+ return (-EFAULT); /* copy failed */
+
+ valbuf[count] = 0; /* terminating 0 */
+ cp = valbuf;
+ if ((count > 2) && (valbuf[0] == '0') && (valbuf[1] == 'x')) {
+ cp += 2; /* pointer after hex modifier */
+ base = 16;
+ }
+ /* scan the input for debug flags */
+ while (*cp) {
+ if ((*cp >= '0') && (*cp <= '9')) {
+ found = 1;
+ u *= base; /* adjust to next digit */
+ u += *cp++ - '0';
+ continue;
+ }
+ if (base != 16)
+ break; /* end of number */
+
+ if ((*cp >= 'a') && (*cp <= 'f')) {
+ found = 1;
+ u *= base; /* adjust to next digit */
+ u += *cp++ - 'a' + 10;
+ continue;
+ }
+ break; /* terminated */
+ }
+
+ if (found) {
+ card->debug_flags = u; /* remember debug flags */
+ hysdn_addlog(card, "debug set to 0x%lx", card->debug_flags);
+ }
+ return (count);
+} /* hysdn_log_write */
+
+/******************/
+/* read log file */
+/******************/
+static ssize_t
+hysdn_log_read(struct file *file, char __user *buf, size_t count, loff_t * off)
+{
+ struct log_data *inf;
+ int len;
+ struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode);
+ struct procdata *pd = NULL;
+ hysdn_card *card;
+
+ if (!*((struct log_data **) file->private_data)) {
+ if (file->f_flags & O_NONBLOCK)
+ return (-EAGAIN);
+
+ /* sorry, but we need to search the card */
+ card = card_root;
+ while (card) {
+ pd = card->proclog;
+ if (pd->log == pde)
+ break;
+ card = card->next; /* search next entry */
+ }
+ if (card)
+ interruptible_sleep_on(&(pd->rd_queue));
+ else
+ return (-EAGAIN);
+
+ }
+ if (!(inf = *((struct log_data **) file->private_data)))
+ return (0);
+
+ inf->usage_cnt--; /* new usage count */
+ file->private_data = &inf->next; /* next structure */
+ if ((len = strlen(inf->log_start)) <= count) {
+ if (copy_to_user(buf, inf->log_start, len))
+ return -EFAULT;
+ *off += len;
+ return (len);
+ }
+ return (0);
+} /* hysdn_log_read */
+
+/******************/
+/* open log file */
+/******************/
+static int
+hysdn_log_open(struct inode *ino, struct file *filep)
+{
+ hysdn_card *card;
+ struct procdata *pd = NULL;
+ unsigned long flags;
+
+ lock_kernel();
+ card = card_root;
+ while (card) {
+ pd = card->proclog;
+ if (pd->log == PDE(ino))
+ break;
+ card = card->next; /* search next entry */
+ }
+ if (!card) {
+ unlock_kernel();
+ return (-ENODEV); /* device is unknown/invalid */
+ }
+ filep->private_data = card; /* remember our own card */
+
+ if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
+ /* write only access -> write log level only */
+ } else if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
+
+ /* read access -> log/debug read */
+ spin_lock_irqsave(&card->hysdn_lock, flags);
+ pd->if_used++;
+ if (pd->log_head)
+ filep->private_data = &pd->log_tail->next;
+ else
+ filep->private_data = &pd->log_head;
+ spin_unlock_irqrestore(&card->hysdn_lock, flags);
+ } else { /* simultaneous read/write access forbidden ! */
+ unlock_kernel();
+ return (-EPERM); /* no permission this time */
+ }
+ unlock_kernel();
+ return nonseekable_open(ino, filep);
+} /* hysdn_log_open */
+
+/*******************************************************************************/
+/* close a cardlog file. If the file has been opened for exclusive write it is */
+/* assumed as pof data input and the pof loader is noticed about. */
+/* Otherwise file is handled as log output. In this case the interface usage */
+/* count is decremented and all buffers are noticed of closing. If this file */
+/* was the last one to be closed, all buffers are freed. */
+/*******************************************************************************/
+static int
+hysdn_log_close(struct inode *ino, struct file *filep)
+{
+ struct log_data *inf;
+ struct procdata *pd;
+ hysdn_card *card;
+ int retval = 0;
+
+ lock_kernel();
+ if ((filep->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE) {
+ /* write only access -> write debug level written */
+ retval = 0; /* success */
+ } else {
+ /* read access -> log/debug read, mark one further file as closed */
+
+ pd = NULL;
+ inf = *((struct log_data **) filep->private_data); /* get first log entry */
+ if (inf)
+ pd = (struct procdata *) inf->proc_ctrl; /* still entries there */
+ else {
+ /* no info available -> search card */
+ card = card_root;
+ while (card) {
+ pd = card->proclog;
+ if (pd->log == PDE(ino))
+ break;
+ card = card->next; /* search next entry */
+ }
+ if (card)
+ pd = card->proclog; /* pointer to procfs log */
+ }
+ if (pd)
+ pd->if_used--; /* decrement interface usage count by one */
+
+ while (inf) {
+ inf->usage_cnt--; /* decrement usage count for buffers */
+ inf = inf->next;
+ }
+
+ if (pd)
+ if (pd->if_used <= 0) /* delete buffers if last file closed */
+ while (pd->log_head) {
+ inf = pd->log_head;
+ pd->log_head = pd->log_head->next;
+ kfree(inf);
+ }
+ } /* read access */
+ unlock_kernel();
+
+ return (retval);
+} /* hysdn_log_close */
+
+/*************************************************/
+/* select/poll routine to be able using select() */
+/*************************************************/
+static unsigned int
+hysdn_log_poll(struct file *file, poll_table * wait)
+{
+ unsigned int mask = 0;
+ struct proc_dir_entry *pde = PDE(file->f_path.dentry->d_inode);
+ hysdn_card *card;
+ struct procdata *pd = NULL;
+
+ if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_WRITE)
+ return (mask); /* no polling for write supported */
+
+ /* we need to search the card */
+ card = card_root;
+ while (card) {
+ pd = card->proclog;
+ if (pd->log == pde)
+ break;
+ card = card->next; /* search next entry */
+ }
+ if (!card)
+ return (mask); /* card not found */
+
+ poll_wait(file, &(pd->rd_queue), wait);
+
+ if (*((struct log_data **) file->private_data))
+ mask |= POLLIN | POLLRDNORM;
+
+ return mask;
+} /* hysdn_log_poll */
+
+/**************************************************/
+/* table for log filesystem functions defined above. */
+/**************************************************/
+static const struct file_operations log_fops =
+{
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = hysdn_log_read,
+ .write = hysdn_log_write,
+ .poll = hysdn_log_poll,
+ .open = hysdn_log_open,
+ .release = hysdn_log_close,
+};
+
+
+/***********************************************************************************/
+/* hysdn_proclog_init is called when the module is loaded after creating the cards */
+/* conf files. */
+/***********************************************************************************/
+int
+hysdn_proclog_init(hysdn_card * card)
+{
+ struct procdata *pd;
+
+ /* create a cardlog proc entry */
+
+ if ((pd = kzalloc(sizeof(struct procdata), GFP_KERNEL)) != NULL) {
+ sprintf(pd->log_name, "%s%d", PROC_LOG_BASENAME, card->myid);
+ pd->log = proc_create(pd->log_name,
+ S_IFREG | S_IRUGO | S_IWUSR, hysdn_proc_entry,
+ &log_fops);
+
+ init_waitqueue_head(&(pd->rd_queue));
+
+ card->proclog = (void *) pd; /* remember procfs structure */
+ }
+ return (0);
+} /* hysdn_proclog_init */
+
+/************************************************************************************/
+/* hysdn_proclog_release is called when the module is unloaded and before the cards */
+/* conf file is released */
+/* The module counter is assumed to be 0 ! */
+/************************************************************************************/
+void
+hysdn_proclog_release(hysdn_card * card)
+{
+ struct procdata *pd;
+
+ if ((pd = (struct procdata *) card->proclog) != NULL) {
+ if (pd->log)
+ remove_proc_entry(pd->log_name, hysdn_proc_entry);
+ kfree(pd); /* release memory */
+ card->proclog = NULL;
+ }
+} /* hysdn_proclog_release */
diff --git a/drivers/isdn/hysdn/hysdn_sched.c b/drivers/isdn/hysdn/hysdn_sched.c
new file mode 100644
index 0000000..81db4a1
--- /dev/null
+++ b/drivers/isdn/hysdn/hysdn_sched.c
@@ -0,0 +1,197 @@
+/* $Id: hysdn_sched.c,v 1.5.6.4 2001/11/06 21:58:19 kai Exp $
+ *
+ * Linux driver for HYSDN cards
+ * scheduler routines for handling exchange card <-> pc.
+ *
+ * Author Werner Cornelius (werner@titro.de) for Hypercope GmbH
+ * Copyright 1999 by Werner Cornelius (werner@titro.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/signal.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+
+#include "hysdn_defs.h"
+
+/*****************************************************************************/
+/* hysdn_sched_rx is called from the cards handler to announce new data is */
+/* available from the card. The routine has to handle the data and return */
+/* with a nonzero code if the data could be worked (or even thrown away), if */
+/* no room to buffer the data is available a zero return tells the card */
+/* to keep the data until later. */
+/*****************************************************************************/
+int
+hysdn_sched_rx(hysdn_card *card, unsigned char *buf, unsigned short len,
+ unsigned short chan)
+{
+
+ switch (chan) {
+ case CHAN_NDIS_DATA:
+ if (hynet_enable & (1 << card->myid)) {
+ /* give packet to network handler */
+ hysdn_rx_netpkt(card, buf, len);
+ }
+ break;
+
+ case CHAN_ERRLOG:
+ hysdn_card_errlog(card, (tErrLogEntry *) buf, len);
+ if (card->err_log_state == ERRLOG_STATE_ON)
+ card->err_log_state = ERRLOG_STATE_START; /* start new fetch */
+ break;
+#ifdef CONFIG_HYSDN_CAPI
+ case CHAN_CAPI:
+/* give packet to CAPI handler */
+ if (hycapi_enable & (1 << card->myid)) {
+ hycapi_rx_capipkt(card, buf, len);
+ }
+ break;
+#endif /* CONFIG_HYSDN_CAPI */
+ default:
+ printk(KERN_INFO "irq message channel %d len %d unhandled \n", chan, len);
+ break;
+
+ } /* switch rx channel */
+
+ return (1); /* always handled */
+} /* hysdn_sched_rx */
+
+/*****************************************************************************/
+/* hysdn_sched_tx is called from the cards handler to announce that there is */
+/* room in the tx-buffer to the card and data may be sent if needed. */
+/* If the routine wants to send data it must fill buf, len and chan with the */
+/* appropriate data and return a nonzero value. With a zero return no new */
+/* data to send is assumed. maxlen specifies the buffer size available for */
+/* sending. */
+/*****************************************************************************/
+int
+hysdn_sched_tx(hysdn_card *card, unsigned char *buf,
+ unsigned short volatile *len, unsigned short volatile *chan,
+ unsigned short maxlen)
+{
+ struct sk_buff *skb;
+
+ if (card->net_tx_busy) {
+ card->net_tx_busy = 0; /* reset flag */
+ hysdn_tx_netack(card); /* acknowledge packet send */
+ } /* a network packet has completely been transferred */
+ /* first of all async requests are handled */
+ if (card->async_busy) {
+ if (card->async_len <= maxlen) {
+ memcpy(buf, card->async_data, card->async_len);
+ *len = card->async_len;
+ *chan = card->async_channel;
+ card->async_busy = 0; /* reset request */
+ return (1);
+ }
+ card->async_busy = 0; /* in case of length error */
+ } /* async request */
+ if ((card->err_log_state == ERRLOG_STATE_START) &&
+ (maxlen >= ERRLOG_CMD_REQ_SIZE)) {
+ strcpy(buf, ERRLOG_CMD_REQ); /* copy the command */
+ *len = ERRLOG_CMD_REQ_SIZE; /* buffer length */
+ *chan = CHAN_ERRLOG; /* and channel */
+ card->err_log_state = ERRLOG_STATE_ON; /* new state is on */
+ return (1); /* tell that data should be send */
+ } /* error log start and able to send */
+ if ((card->err_log_state == ERRLOG_STATE_STOP) &&
+ (maxlen >= ERRLOG_CMD_STOP_SIZE)) {
+ strcpy(buf, ERRLOG_CMD_STOP); /* copy the command */
+ *len = ERRLOG_CMD_STOP_SIZE; /* buffer length */
+ *chan = CHAN_ERRLOG; /* and channel */
+ card->err_log_state = ERRLOG_STATE_OFF; /* new state is off */
+ return (1); /* tell that data should be send */
+ } /* error log start and able to send */
+ /* now handle network interface packets */
+ if ((hynet_enable & (1 << card->myid)) &&
+ (skb = hysdn_tx_netget(card)) != NULL)
+ {
+ if (skb->len <= maxlen) {
+ /* copy the packet to the buffer */
+ skb_copy_from_linear_data(skb, buf, skb->len);
+ *len = skb->len;
+ *chan = CHAN_NDIS_DATA;
+ card->net_tx_busy = 1; /* we are busy sending network data */
+ return (1); /* go and send the data */
+ } else
+ hysdn_tx_netack(card); /* aknowledge packet -> throw away */
+ } /* send a network packet if available */
+#ifdef CONFIG_HYSDN_CAPI
+ if( ((hycapi_enable & (1 << card->myid))) &&
+ ((skb = hycapi_tx_capiget(card)) != NULL) )
+ {
+ if (skb->len <= maxlen) {
+ skb_copy_from_linear_data(skb, buf, skb->len);
+ *len = skb->len;
+ *chan = CHAN_CAPI;
+ hycapi_tx_capiack(card);
+ return (1); /* go and send the data */
+ }
+ }
+#endif /* CONFIG_HYSDN_CAPI */
+ return (0); /* nothing to send */
+} /* hysdn_sched_tx */
+
+
+/*****************************************************************************/
+/* send one config line to the card and return 0 if successful, otherwise a */
+/* negative error code. */
+/* The function works with timeouts perhaps not giving the greatest speed */
+/* sending the line, but this should be meaningless beacuse only some lines */
+/* are to be sent and this happens very seldom. */
+/*****************************************************************************/
+int
+hysdn_tx_cfgline(hysdn_card *card, unsigned char *line, unsigned short chan)
+{
+ int cnt = 50; /* timeout intervalls */
+ unsigned long flags;
+
+ if (card->debug_flags & LOG_SCHED_ASYN)
+ hysdn_addlog(card, "async tx-cfg chan=%d len=%d", chan, strlen(line) + 1);
+
+ while (card->async_busy) {
+
+ if (card->debug_flags & LOG_SCHED_ASYN)
+ hysdn_addlog(card, "async tx-cfg delayed");
+
+ msleep_interruptible(20); /* Timeout 20ms */
+ if (!--cnt)
+ return (-ERR_ASYNC_TIME); /* timed out */
+ } /* wait for buffer to become free */
+
+ spin_lock_irqsave(&card->hysdn_lock, flags);
+ strcpy(card->async_data, line);
+ card->async_len = strlen(line) + 1;
+ card->async_channel = chan;
+ card->async_busy = 1; /* request transfer */
+
+ /* now queue the task */
+ schedule_work(&card->irq_queue);
+ spin_unlock_irqrestore(&card->hysdn_lock, flags);
+
+ if (card->debug_flags & LOG_SCHED_ASYN)
+ hysdn_addlog(card, "async tx-cfg data queued");
+
+ cnt++; /* short delay */
+
+ while (card->async_busy) {
+
+ if (card->debug_flags & LOG_SCHED_ASYN)
+ hysdn_addlog(card, "async tx-cfg waiting for tx-ready");
+
+ msleep_interruptible(20); /* Timeout 20ms */
+ if (!--cnt)
+ return (-ERR_ASYNC_TIME); /* timed out */
+ } /* wait for buffer to become free again */
+
+ if (card->debug_flags & LOG_SCHED_ASYN)
+ hysdn_addlog(card, "async tx-cfg data send");
+
+ return (0); /* line send correctly */
+} /* hysdn_tx_cfgline */
diff --git a/drivers/isdn/hysdn/ince1pc.h b/drivers/isdn/hysdn/ince1pc.h
new file mode 100644
index 0000000..7a36694
--- /dev/null
+++ b/drivers/isdn/hysdn/ince1pc.h
@@ -0,0 +1,134 @@
+/*
+ * Linux driver for HYSDN cards
+ * common definitions for both sides of the bus:
+ * - conventions both spoolers must know
+ * - channel numbers agreed upon
+ *
+ * Author M. Steinkopf
+ * Copyright 1999 by M. Steinkopf
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef __INCE1PC_H__
+#define __INCE1PC_H__
+
+/* basic scalar definitions have same meanning,
+ * but their declaration location depends on environment
+ */
+
+/*--------------------------------------channel numbers---------------------*/
+#define CHAN_SYSTEM 0x0001 /* system channel (spooler to spooler) */
+#define CHAN_ERRLOG 0x0005 /* error logger */
+#define CHAN_CAPI 0x0064 /* CAPI interface */
+#define CHAN_NDIS_DATA 0x1001 /* NDIS data transfer */
+
+/*--------------------------------------POF ready msg-----------------------*/
+ /* NOTE: after booting POF sends system ready message to PC: */
+#define RDY_MAGIC 0x52535953UL /* 'SYSR' reversed */
+#define RDY_MAGIC_SIZE 4 /* size in bytes */
+
+#define MAX_N_TOK_BYTES 255
+
+#define MIN_RDY_MSG_SIZE RDY_MAGIC_SIZE
+#define MAX_RDY_MSG_SIZE (RDY_MAGIC_SIZE+MAX_N_TOK_BYTES)
+
+#define SYSR_TOK_END 0
+#define SYSR_TOK_B_CHAN 1 /* nr. of B-Channels; DataLen=1; def: 2 */
+#define SYSR_TOK_FAX_CHAN 2 /* nr. of FAX Channels; DataLen=1; def: 0 */
+#define SYSR_TOK_MAC_ADDR 3 /* MAC-Address; DataLen=6; def: auto */
+#define SYSR_TOK_ESC 255 /* undefined data size yet */
+ /* default values, if not corrected by token: */
+#define SYSR_TOK_B_CHAN_DEF 2 /* assume 2 B-Channels */
+#define SYSR_TOK_FAX_CHAN_DEF 1 /* assume 1 FAX Channel */
+
+/* syntax of new SYSR token stream:
+ * channel: CHAN_SYSTEM
+ * msgsize: MIN_RDY_MSG_SIZE <= x <= MAX_RDY_MSG_SIZE
+ * RDY_MAGIC_SIZE <= x <= (RDY_MAGIC_SIZE+MAX_N_TOK_BYTES)
+ * msg : 0 1 2 3 {4 5 6 ..}
+ * S Y S R MAX_N_TOK_BYTES bytes of TokenStream
+ *
+ * TokenStream := empty
+ * | {NonEndTokenChunk} EndToken RotlCRC
+ * NonEndTokenChunk:= NonEndTokenId DataLen [Data]
+ * NonEndTokenId := 0x01 .. 0xFE 1 BYTE
+ * DataLen := 0x00 .. 0xFF 1 BYTE
+ * Data := DataLen bytes
+ * EndToken := 0x00
+ * RotlCRC := special 1 byte CRC over all NonEndTokenChunk bytes
+ * s. RotlCRC algorithm
+ *
+ * RotlCRC algorithm:
+ * ucSum= 0 1 unsigned char
+ * for all NonEndTokenChunk bytes:
+ * ROTL(ucSum,1) rotate left by 1
+ * ucSum += Char; add current byte with swap around
+ * RotlCRC= ~ucSum; invert all bits for result
+ *
+ * note:
+ * - for 16-bit FIFO add padding 0 byte to achieve even token data bytes!
+ */
+
+/*--------------------------------------error logger------------------------*/
+ /* note: pof needs final 0 ! */
+#define ERRLOG_CMD_REQ "ERRLOG ON"
+#define ERRLOG_CMD_REQ_SIZE 10 /* with final 0 byte ! */
+#define ERRLOG_CMD_STOP "ERRLOG OFF"
+#define ERRLOG_CMD_STOP_SIZE 11 /* with final 0 byte ! */
+
+#define ERRLOG_ENTRY_SIZE 64 /* sizeof(tErrLogEntry) */
+ /* remaining text size = 55 */
+#define ERRLOG_TEXT_SIZE (ERRLOG_ENTRY_SIZE-2*4-1)
+
+typedef struct ErrLogEntry_tag {
+
+/*00 */ unsigned long ulErrType;
+
+/*04 */ unsigned long ulErrSubtype;
+
+/*08 */ unsigned char ucTextSize;
+
+ /*09 */ unsigned char ucText[ERRLOG_TEXT_SIZE];
+ /* ASCIIZ of len ucTextSize-1 */
+
+/*40 */
+} tErrLogEntry;
+
+
+#if defined(__TURBOC__)
+#if sizeof(tErrLogEntry) != ERRLOG_ENTRY_SIZE
+#error size of tErrLogEntry != ERRLOG_ENTRY_SIZE
+#endif /* */
+#endif /* */
+
+/*--------------------------------------DPRAM boot spooler------------------*/
+ /* this is the struture used between pc and
+ * hyperstone to exchange boot data
+ */
+#define DPRAM_SPOOLER_DATA_SIZE 0x20
+typedef struct DpramBootSpooler_tag {
+
+/*00 */ unsigned char Len;
+
+/*01 */ volatile unsigned char RdPtr;
+
+/*02 */ unsigned char WrPtr;
+
+/*03 */ unsigned char Data[DPRAM_SPOOLER_DATA_SIZE];
+
+/*23 */
+} tDpramBootSpooler;
+
+
+#define DPRAM_SPOOLER_MIN_SIZE 5 /* Len+RdPtr+Wrptr+2*data */
+#define DPRAM_SPOOLER_DEF_SIZE 0x23 /* current default size */
+
+/*--------------------------------------HYCARD/ERGO DPRAM SoftUart----------*/
+ /* at DPRAM offset 0x1C00: */
+#define SIZE_RSV_SOFT_UART 0x1B0 /* 432 bytes reserved for SoftUart */
+
+
+#endif /* __INCE1PC_H__ */
diff --git a/drivers/isdn/i4l/Kconfig b/drivers/isdn/i4l/Kconfig
new file mode 100644
index 0000000..36778b2
--- /dev/null
+++ b/drivers/isdn/i4l/Kconfig
@@ -0,0 +1,139 @@
+#
+# Old ISDN4Linux config
+#
+
+config ISDN_PPP
+ bool "Support synchronous PPP"
+ depends on INET
+ select SLHC
+ help
+ Over digital connections such as ISDN, there is no need to
+ synchronize sender and recipient's clocks with start and stop bits
+ as is done over analog telephone lines. Instead, one can use
+ "synchronous PPP". Saying Y here will include this protocol. This
+ protocol is used by Cisco and Sun for example. So you want to say Y
+ here if the other end of your ISDN connection supports it. You will
+ need a special version of pppd (called ipppd) for using this
+ feature. See <file:Documentation/isdn/README.syncppp> and
+ <file:Documentation/isdn/syncPPP.FAQ> for more information.
+
+config ISDN_PPP_VJ
+ bool "Use VJ-compression with synchronous PPP"
+ depends on ISDN_PPP
+ help
+ This enables Van Jacobson header compression for synchronous PPP.
+ Say Y if the other end of the connection supports it.
+
+config ISDN_MPP
+ bool "Support generic MP (RFC 1717)"
+ depends on ISDN_PPP
+ help
+ With synchronous PPP enabled, it is possible to increase throughput
+ by bundling several ISDN-connections, using this protocol. See
+ <file:Documentation/isdn/README.syncppp> for more information.
+
+config IPPP_FILTER
+ bool "Filtering for synchronous PPP"
+ depends on ISDN_PPP
+ help
+ Say Y here if you want to be able to filter the packets passing over
+ IPPP interfaces. This allows you to control which packets count as
+ activity (i.e. which packets will reset the idle timer or bring up
+ a demand-dialled link) and which packets are to be dropped entirely.
+ You need to say Y here if you wish to use the pass-filter and
+ active-filter options to ipppd.
+
+config ISDN_PPP_BSDCOMP
+ tristate "Support BSD compression"
+ depends on ISDN_PPP
+ help
+ Support for the BSD-Compress compression method for PPP, which uses
+ the LZW compression method to compress each PPP packet before it is
+ sent over the wire. The machine at the other end of the PPP link
+ (usually your ISP) has to support the BSD-Compress compression
+ method as well for this to be useful. Even if they don't support it,
+ it is safe to say Y here.
+
+config ISDN_AUDIO
+ bool "Support audio via ISDN"
+ help
+ If you say Y here, the modem-emulator will support a subset of the
+ EIA Class 8 Voice commands. Using a getty with voice-support
+ (mgetty+sendfax by <gert@greenie.muc.de> with an extension, available
+ with the ISDN utility package for example), you will be able to use
+ your Linux box as an ISDN-answering machine. Of course, this must be
+ supported by the lowlevel driver also. Currently, the HiSax driver
+ is the only voice-supporting driver. See
+ <file:Documentation/isdn/README.audio> for more information.
+
+config ISDN_TTY_FAX
+ bool "Support AT-Fax Class 1 and 2 commands"
+ depends on ISDN_AUDIO
+ help
+ If you say Y here, the modem-emulator will support a subset of the
+ Fax Class 1 and 2 commands. Using a getty with fax-support
+ (mgetty+sendfax, hylafax), you will be able to use your Linux box as
+ an ISDN-fax-machine. This must be supported by the lowlevel driver
+ also. See <file:Documentation/isdn/README.fax> for more information.
+
+config ISDN_X25
+ bool "X.25 PLP on top of ISDN"
+ depends on X25
+ help
+ This feature provides the X.25 protocol over ISDN connections.
+ See <file:Documentation/isdn/README.x25> for more information
+ if you are thinking about using this.
+
+
+menu "ISDN feature submodules"
+
+config ISDN_DRV_LOOP
+ tristate "isdnloop support"
+ depends on BROKEN_ON_SMP
+ help
+ This driver provides a virtual ISDN card. Its primary purpose is
+ testing of linklevel features or configuration without getting
+ charged by your service-provider for lots of phone calls.
+ You need will need the loopctrl utility from the latest isdn4k-utils
+ package to set up this driver.
+
+config ISDN_DIVERSION
+ tristate "Support isdn diversion services"
+ help
+ This option allows you to use some supplementary diversion
+ services in conjunction with the HiSax driver on an EURO/DSS1
+ line.
+
+ Supported options are CD (call deflection), CFU (Call forward
+ unconditional), CFB (Call forward when busy) and CFNR (call forward
+ not reachable). Additionally the actual CFU, CFB and CFNR state may
+ be interrogated.
+
+ The use of CFU, CFB, CFNR and interrogation may be limited to some
+ countries. The keypad protocol is still not implemented. CD should
+ work in all countries if the service has been subscribed to.
+
+ Please read the file <file:Documentation/isdn/README.diversion>.
+
+endmenu
+
+comment "ISDN4Linux hardware drivers"
+
+source "drivers/isdn/hisax/Kconfig"
+
+
+menu "Active cards"
+
+source "drivers/isdn/icn/Kconfig"
+
+source "drivers/isdn/pcbit/Kconfig"
+
+source "drivers/isdn/sc/Kconfig"
+
+source "drivers/isdn/act2000/Kconfig"
+
+source "drivers/isdn/hysdn/Kconfig"
+
+endmenu
+
+source "drivers/isdn/gigaset/Kconfig"
diff --git a/drivers/isdn/i4l/Makefile b/drivers/isdn/i4l/Makefile
new file mode 100644
index 0000000..49a06c0
--- /dev/null
+++ b/drivers/isdn/i4l/Makefile
@@ -0,0 +1,18 @@
+# Makefile for the kernel ISDN subsystem and device drivers.
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_ISDN_I4L) += isdn.o
+obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o
+
+# Multipart objects.
+
+isdn-y := isdn_net.o isdn_tty.o isdn_v110.o isdn_common.o
+
+# Optional parts of multipart objects.
+
+isdn-$(CONFIG_ISDN_PPP) += isdn_ppp.o
+isdn-$(CONFIG_ISDN_X25) += isdn_concap.o isdn_x25iface.o
+isdn-$(CONFIG_ISDN_AUDIO) += isdn_audio.o
+isdn-$(CONFIG_ISDN_TTY_FAX) += isdn_ttyfax.o
+
diff --git a/drivers/isdn/i4l/isdn_audio.c b/drivers/isdn/i4l/isdn_audio.c
new file mode 100644
index 0000000..fb350c5
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_audio.c
@@ -0,0 +1,710 @@
+/* $Id: isdn_audio.c,v 1.1.2.2 2004/01/12 22:37:18 keil Exp $
+ *
+ * Linux ISDN subsystem, audio conversion and compression (linklevel).
+ *
+ * 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 <linux/isdn.h>
+#include "isdn_audio.h"
+#include "isdn_common.h"
+
+char *isdn_audio_revision = "$Revision: 1.1.2.2 $";
+
+/*
+ * 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);
+}
+
+/*
+ * linear <-> adpcm conversion stuff
+ * Most parts from the mgetty-package.
+ * (C) by Gert Doering and Klaus Weidner
+ * Used by permission of Gert Doering
+ */
+
+
+#define ZEROTRAP /* turn on the trap as per the MIL-STD */
+#undef ZEROTRAP
+#define BIAS 0x84 /* define the add-in bias for 16 bit samples */
+#define CLIP 32635
+
+static unsigned char
+isdn_audio_linear2ulaw(int sample)
+{
+ static int exp_lut[256] =
+ {
+ 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7
+ };
+ int sign,
+ exponent,
+ mantissa;
+ unsigned char ulawbyte;
+
+ /* Get the sample into sign-magnitude. */
+ sign = (sample >> 8) & 0x80; /* set aside the sign */
+ if (sign != 0)
+ sample = -sample; /* get magnitude */
+ if (sample > CLIP)
+ sample = CLIP; /* clip the magnitude */
+
+ /* Convert from 16 bit linear to ulaw. */
+ sample = sample + BIAS;
+ exponent = exp_lut[(sample >> 7) & 0xFF];
+ mantissa = (sample >> (exponent + 3)) & 0x0F;
+ ulawbyte = ~(sign | (exponent << 4) | mantissa);
+#ifdef ZEROTRAP
+ /* optional CCITT trap */
+ if (ulawbyte == 0)
+ ulawbyte = 0x02;
+#endif
+ return (ulawbyte);
+}
+
+
+static int Mx[3][8] =
+{
+ {0x3800, 0x5600, 0, 0, 0, 0, 0, 0},
+ {0x399a, 0x3a9f, 0x4d14, 0x6607, 0, 0, 0, 0},
+ {0x3556, 0x3556, 0x399A, 0x3A9F, 0x4200, 0x4D14, 0x6607, 0x6607},
+};
+
+static int bitmask[9] =
+{
+ 0, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff
+};
+
+static int
+isdn_audio_get_bits(adpcm_state * s, unsigned char **in, int *len)
+{
+ while (s->nleft < s->nbits) {
+ int d = *((*in)++);
+ (*len)--;
+ s->word = (s->word << 8) | d;
+ s->nleft += 8;
+ }
+ s->nleft -= s->nbits;
+ return (s->word >> s->nleft) & bitmask[s->nbits];
+}
+
+static void
+isdn_audio_put_bits(int data, int nbits, adpcm_state * s,
+ unsigned char **out, int *len)
+{
+ s->word = (s->word << nbits) | (data & bitmask[nbits]);
+ s->nleft += nbits;
+ while (s->nleft >= 8) {
+ int d = (s->word >> (s->nleft - 8));
+ *(out[0]++) = d & 255;
+ (*len)++;
+ s->nleft -= 8;
+ }
+}
+
+adpcm_state *
+isdn_audio_adpcm_init(adpcm_state * s, int nbits)
+{
+ if (!s)
+ s = kmalloc(sizeof(adpcm_state), GFP_ATOMIC);
+ if (s) {
+ s->a = 0;
+ s->d = 5;
+ s->word = 0;
+ s->nleft = 0;
+ s->nbits = nbits;
+ }
+ return s;
+}
+
+dtmf_state *
+isdn_audio_dtmf_init(dtmf_state * s)
+{
+ if (!s)
+ s = kmalloc(sizeof(dtmf_state), GFP_ATOMIC);
+ if (s) {
+ s->idx = 0;
+ s->last = ' ';
+ }
+ return s;
+}
+
+/*
+ * Decompression of adpcm data to a/u-law
+ *
+ */
+
+int
+isdn_audio_adpcm2xlaw(adpcm_state * s, int fmt, unsigned char *in,
+ unsigned char *out, int len)
+{
+ int a = s->a;
+ int d = s->d;
+ int nbits = s->nbits;
+ int olen = 0;
+
+ while (len) {
+ int e = isdn_audio_get_bits(s, &in, &len);
+ int sign;
+
+ if (nbits == 4 && e == 0)
+ d = 4;
+ sign = (e >> (nbits - 1)) ? -1 : 1;
+ e &= bitmask[nbits - 1];
+ a += sign * ((e << 1) + 1) * d >> 1;
+ if (d & 1)
+ a++;
+ if (fmt)
+ *out++ = isdn_audio_ulaw_to_alaw[
+ isdn_audio_linear2ulaw(a << 2)];
+ else
+ *out++ = isdn_audio_linear2ulaw(a << 2);
+ olen++;
+ d = (d * Mx[nbits - 2][e] + 0x2000) >> 14;
+ if (d < 5)
+ d = 5;
+ }
+ s->a = a;
+ s->d = d;
+ return olen;
+}
+
+int
+isdn_audio_xlaw2adpcm(adpcm_state * s, int fmt, unsigned char *in,
+ unsigned char *out, int len)
+{
+ int a = s->a;
+ int d = s->d;
+ int nbits = s->nbits;
+ int olen = 0;
+
+ while (len--) {
+ int e = 0,
+ nmax = 1 << (nbits - 1);
+ int sign,
+ delta;
+
+ if (fmt)
+ delta = (isdn_audio_alaw_to_s16[*in++] >> 2) - a;
+ else
+ delta = (isdn_audio_ulaw_to_s16[*in++] >> 2) - a;
+ if (delta < 0) {
+ e = nmax;
+ delta = -delta;
+ }
+ while (--nmax && delta > d) {
+ delta -= d;
+ e++;
+ }
+ if (nbits == 4 && ((e & 0x0f) == 0))
+ e = 8;
+ isdn_audio_put_bits(e, nbits, s, &out, &olen);
+ sign = (e >> (nbits - 1)) ? -1 : 1;
+ e &= bitmask[nbits - 1];
+
+ a += sign * ((e << 1) + 1) * d >> 1;
+ if (d & 1)
+ a++;
+ d = (d * Mx[nbits - 2][e] + 0x2000) >> 14;
+ if (d < 5)
+ d = 5;
+ }
+ s->a = a;
+ s->d = d;
+ return olen;
+}
+
+/*
+ * Goertzel algorithm.
+ * See http://ptolemy.eecs.berkeley.edu/~pino/Ptolemy/papers/96/dtmf_ict/
+ * for more info.
+ * Result is stored into an sk_buff and queued up for later
+ * evaluation.
+ */
+static void
+isdn_audio_goertzel(int *sample, modem_info * info)
+{
+ int sk,
+ sk1,
+ sk2;
+ int k,
+ n;
+ struct sk_buff *skb;
+ int *result;
+
+ skb = dev_alloc_skb(sizeof(int) * NCOEFF);
+ if (!skb) {
+ printk(KERN_WARNING
+ "isdn_audio: Could not alloc DTMF result for ttyI%d\n",
+ info->line);
+ return;
+ }
+ result = (int *) skb_put(skb, sizeof(int) * NCOEFF);
+ for (k = 0; k < NCOEFF; k++) {
+ sk = sk1 = sk2 = 0;
+ for (n = 0; n < DTMF_NPOINTS; n++) {
+ sk = sample[n] + ((cos2pik[k] * sk1) >> 15) - sk2;
+ sk2 = sk1;
+ sk1 = sk;
+ }
+ /* Avoid overflows */
+ sk >>= 1;
+ sk2 >>= 1;
+ /* compute |X(k)|**2 */
+ /* report overflows. This should not happen. */
+ /* Comment this out if desired */
+ if (sk < -32768 || sk > 32767)
+ printk(KERN_DEBUG
+ "isdn_audio: dtmf goertzel overflow, sk=%d\n", sk);
+ if (sk2 < -32768 || sk2 > 32767)
+ printk(KERN_DEBUG
+ "isdn_audio: dtmf goertzel overflow, sk2=%d\n", sk2);
+ result[k] =
+ ((sk * sk) >> AMP_BITS) -
+ ((((cos2pik[k] * sk) >> 15) * sk2) >> AMP_BITS) +
+ ((sk2 * sk2) >> AMP_BITS);
+ }
+ skb_queue_tail(&info->dtmf_queue, skb);
+ isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
+}
+
+void
+isdn_audio_eval_dtmf(modem_info * info)
+{
+ struct sk_buff *skb;
+ int *result;
+ dtmf_state *s;
+ int silence;
+ int i;
+ int di;
+ int ch;
+ int grp[2];
+ char what;
+ char *p;
+ int thresh;
+
+ while ((skb = skb_dequeue(&info->dtmf_queue))) {
+ result = (int *) skb->data;
+ s = info->dtmf_state;
+ grp[LOGRP] = grp[HIGRP] = -1;
+ silence = 0;
+ thresh = 0;
+ for (i = 0; i < NCOEFF; i++) {
+ if (result[i] > DTMF_TRESH) {
+ if (result[i] > thresh)
+ thresh = result[i];
+ }
+ else if (result[i] < SILENCE_TRESH)
+ silence++;
+ }
+ if (silence == NCOEFF)
+ what = ' ';
+ else {
+ if (thresh > 0) {
+ thresh = thresh >> 4; /* touchtones must match within 12 dB */
+ for (i = 0; i < NCOEFF; i++) {
+ if (result[i] < thresh)
+ continue; /* ignore */
+ /* good level found. This is allowed only one time per group */
+ if (i < NCOEFF / 2) {
+ /* lowgroup*/
+ if (grp[LOGRP] >= 0) {
+ // Bad. Another tone found. */
+ grp[LOGRP] = -1;
+ break;
+ }
+ else
+ grp[LOGRP] = i;
+ }
+ else { /* higroup */
+ if (grp[HIGRP] >= 0) { // Bad. Another tone found. */
+ grp[HIGRP] = -1;
+ break;
+ }
+ else
+ grp[HIGRP] = i - NCOEFF/2;
+ }
+ }
+ if ((grp[LOGRP] >= 0) && (grp[HIGRP] >= 0)) {
+ what = dtmf_matrix[grp[LOGRP]][grp[HIGRP]];
+ if (s->last != ' ' && s->last != '.')
+ s->last = what; /* min. 1 non-DTMF between DTMF */
+ } else
+ what = '.';
+ }
+ else
+ what = '.';
+ }
+ if ((what != s->last) && (what != ' ') && (what != '.')) {
+ printk(KERN_DEBUG "dtmf: tt='%c'\n", what);
+ p = skb->data;
+ *p++ = 0x10;
+ *p = what;
+ skb_trim(skb, 2);
+ ISDN_AUDIO_SKB_DLECOUNT(skb) = 0;
+ ISDN_AUDIO_SKB_LOCK(skb) = 0;
+ di = info->isdn_driver;
+ ch = info->isdn_channel;
+ __skb_queue_tail(&dev->drv[di]->rpqueue[ch], skb);
+ dev->drv[di]->rcvcount[ch] += 2;
+ /* Schedule dequeuing */
+ if ((dev->modempoll) && (info->rcvsched))
+ isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
+ wake_up_interruptible(&dev->drv[di]->rcv_waitq[ch]);
+ } else
+ kfree_skb(skb);
+ s->last = what;
+ }
+}
+
+/*
+ * Decode DTMF tones, queue result in separate sk_buf for
+ * later examination.
+ * Parameters:
+ * s = pointer to state-struct.
+ * buf = input audio data
+ * len = size of audio data.
+ * fmt = audio data format (0 = ulaw, 1 = alaw)
+ */
+void
+isdn_audio_calc_dtmf(modem_info * info, unsigned char *buf, int len, int fmt)
+{
+ dtmf_state *s = info->dtmf_state;
+ int i;
+ int c;
+
+ while (len) {
+ c = DTMF_NPOINTS - s->idx;
+ if (c > len)
+ c = len;
+ if (c <= 0)
+ break;
+ for (i = 0; i < c; i++) {
+ if (fmt)
+ s->buf[s->idx++] =
+ isdn_audio_alaw_to_s16[*buf++] >> (15 - AMP_BITS);
+ else
+ s->buf[s->idx++] =
+ isdn_audio_ulaw_to_s16[*buf++] >> (15 - AMP_BITS);
+ }
+ if (s->idx == DTMF_NPOINTS) {
+ isdn_audio_goertzel(s->buf, info);
+ s->idx = 0;
+ }
+ len -= c;
+ }
+}
+
+silence_state *
+isdn_audio_silence_init(silence_state * s)
+{
+ if (!s)
+ s = kmalloc(sizeof(silence_state), GFP_ATOMIC);
+ if (s) {
+ s->idx = 0;
+ s->state = 0;
+ }
+ return s;
+}
+
+void
+isdn_audio_calc_silence(modem_info * info, unsigned char *buf, int len, int fmt)
+{
+ silence_state *s = info->silence_state;
+ int i;
+ signed char c;
+
+ if (!info->emu.vpar[1]) return;
+
+ for (i = 0; i < len; i++) {
+ if (fmt)
+ c = isdn_audio_alaw_to_ulaw[*buf++];
+ else
+ c = *buf++;
+
+ if (c > 0) c -= 128;
+ c = abs(c);
+
+ if (c > (info->emu.vpar[1] * 4)) {
+ s->idx = 0;
+ s->state = 1;
+ } else {
+ if (s->idx < 210000) s->idx++;
+ }
+ }
+}
+
+void
+isdn_audio_put_dle_code(modem_info * info, u_char code)
+{
+ struct sk_buff *skb;
+ int di;
+ int ch;
+ char *p;
+
+ skb = dev_alloc_skb(2);
+ if (!skb) {
+ printk(KERN_WARNING
+ "isdn_audio: Could not alloc skb for ttyI%d\n",
+ info->line);
+ return;
+ }
+ p = (char *) skb_put(skb, 2);
+ p[0] = 0x10;
+ p[1] = code;
+ ISDN_AUDIO_SKB_DLECOUNT(skb) = 0;
+ ISDN_AUDIO_SKB_LOCK(skb) = 0;
+ di = info->isdn_driver;
+ ch = info->isdn_channel;
+ __skb_queue_tail(&dev->drv[di]->rpqueue[ch], skb);
+ dev->drv[di]->rcvcount[ch] += 2;
+ /* Schedule dequeuing */
+ if ((dev->modempoll) && (info->rcvsched))
+ isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
+ wake_up_interruptible(&dev->drv[di]->rcv_waitq[ch]);
+}
+
+void
+isdn_audio_eval_silence(modem_info * info)
+{
+ silence_state *s = info->silence_state;
+ char what;
+
+ what = ' ';
+
+ if (s->idx > (info->emu.vpar[2] * 800)) {
+ s->idx = 0;
+ if (!s->state) { /* silence from beginning of rec */
+ what = 's';
+ } else {
+ what = 'q';
+ }
+ }
+ if ((what == 's') || (what == 'q')) {
+ printk(KERN_DEBUG "ttyI%d: %s\n", info->line,
+ (what=='s') ? "silence":"quiet");
+ isdn_audio_put_dle_code(info, what);
+ }
+}
diff --git a/drivers/isdn/i4l/isdn_audio.h b/drivers/isdn/i4l/isdn_audio.h
new file mode 100644
index 0000000..013c358
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_audio.h
@@ -0,0 +1,44 @@
+/* $Id: isdn_audio.h,v 1.1.2.2 2004/01/12 22:37:18 keil Exp $
+ *
+ * Linux ISDN subsystem, audio conversion and compression (linklevel).
+ *
+ * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#define DTMF_NPOINTS 205 /* Number of samples for DTMF recognition */
+typedef struct adpcm_state {
+ int a;
+ int d;
+ int word;
+ int nleft;
+ int nbits;
+} adpcm_state;
+
+typedef struct dtmf_state {
+ char last;
+ char llast;
+ int idx;
+ int buf[DTMF_NPOINTS];
+} dtmf_state;
+
+typedef struct silence_state {
+ int state;
+ unsigned int idx;
+} silence_state;
+
+extern void isdn_audio_ulaw2alaw(unsigned char *, unsigned long);
+extern void isdn_audio_alaw2ulaw(unsigned char *, unsigned long);
+extern adpcm_state *isdn_audio_adpcm_init(adpcm_state *, int);
+extern int isdn_audio_adpcm2xlaw(adpcm_state *, int, unsigned char *, unsigned char *, int);
+extern int isdn_audio_xlaw2adpcm(adpcm_state *, int, unsigned char *, unsigned char *, int);
+extern void isdn_audio_calc_dtmf(modem_info *, unsigned char *, int, int);
+extern void isdn_audio_eval_dtmf(modem_info *);
+dtmf_state *isdn_audio_dtmf_init(dtmf_state *);
+extern void isdn_audio_calc_silence(modem_info *, unsigned char *, int, int);
+extern void isdn_audio_eval_silence(modem_info *);
+silence_state *isdn_audio_silence_init(silence_state *);
+extern void isdn_audio_put_dle_code(modem_info *, u_char);
diff --git a/drivers/isdn/i4l/isdn_bsdcomp.c b/drivers/isdn/i4l/isdn_bsdcomp.c
new file mode 100644
index 0000000..02d9918
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_bsdcomp.c
@@ -0,0 +1,929 @@
+/*
+ * BSD compression module
+ *
+ * Patched version for ISDN syncPPP written 1997/1998 by Michael Hipp
+ * The whole module is now SKB based.
+ *
+ */
+
+/*
+ * Update: The Berkeley copyright was changed, and the change
+ * is retroactive to all "true" BSD software (ie everything
+ * from UCB as opposed to other peoples code that just carried
+ * the same license). The new copyright doesn't clash with the
+ * GPL, so the module-only restriction has been removed..
+ */
+
+/*
+ * Original copyright notice:
+ *
+ * Copyright (c) 1985, 1986 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * James A. Woods, derived from original work by Spencer Thomas
+ * and Joseph Orost.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/string.h> /* used in new tty drivers */
+#include <linux/signal.h> /* used in new tty drivers */
+#include <linux/bitops.h>
+
+#include <asm/system.h>
+#include <asm/byteorder.h>
+#include <asm/types.h>
+
+#include <linux/if.h>
+
+#include <linux/if_ether.h>
+#include <linux/netdevice.h>
+#include <linux/skbuff.h>
+#include <linux/inet.h>
+#include <linux/ioctl.h>
+#include <linux/vmalloc.h>
+
+#include <linux/ppp_defs.h>
+
+#include <linux/isdn.h>
+#include <linux/isdn_ppp.h>
+#include <linux/ip.h>
+#include <linux/tcp.h>
+#include <linux/if_arp.h>
+#include <linux/ppp-comp.h>
+
+#include "isdn_ppp.h"
+
+MODULE_DESCRIPTION("ISDN4Linux: BSD Compression for PPP over ISDN");
+MODULE_LICENSE("Dual BSD/GPL");
+
+#define BSD_VERSION(x) ((x) >> 5)
+#define BSD_NBITS(x) ((x) & 0x1F)
+
+#define BSD_CURRENT_VERSION 1
+
+#define DEBUG 1
+
+/*
+ * A dictionary for doing BSD compress.
+ */
+
+struct bsd_dict {
+ u32 fcode;
+ u16 codem1; /* output of hash table -1 */
+ u16 cptr; /* map code to hash table entry */
+};
+
+struct bsd_db {
+ int totlen; /* length of this structure */
+ unsigned int hsize; /* size of the hash table */
+ unsigned char hshift; /* used in hash function */
+ unsigned char n_bits; /* current bits/code */
+ unsigned char maxbits; /* maximum bits/code */
+ unsigned char debug; /* non-zero if debug desired */
+ unsigned char unit; /* ppp unit number */
+ u16 seqno; /* sequence # of next packet */
+ unsigned int mru; /* size of receive (decompress) bufr */
+ unsigned int maxmaxcode; /* largest valid code */
+ unsigned int max_ent; /* largest code in use */
+ unsigned int in_count; /* uncompressed bytes, aged */
+ unsigned int bytes_out; /* compressed bytes, aged */
+ unsigned int ratio; /* recent compression ratio */
+ unsigned int checkpoint; /* when to next check the ratio */
+ unsigned int clear_count; /* times dictionary cleared */
+ unsigned int incomp_count; /* incompressible packets */
+ unsigned int incomp_bytes; /* incompressible bytes */
+ unsigned int uncomp_count; /* uncompressed packets */
+ unsigned int uncomp_bytes; /* uncompressed bytes */
+ unsigned int comp_count; /* compressed packets */
+ unsigned int comp_bytes; /* compressed bytes */
+ unsigned short *lens; /* array of lengths of codes */
+ struct bsd_dict *dict; /* dictionary */
+ int xmit;
+};
+
+#define BSD_OVHD 2 /* BSD compress overhead/packet */
+#define MIN_BSD_BITS 9
+#define BSD_INIT_BITS MIN_BSD_BITS
+#define MAX_BSD_BITS 15
+
+/*
+ * the next two codes should not be changed lightly, as they must not
+ * lie within the contiguous general code space.
+ */
+#define CLEAR 256 /* table clear output code */
+#define FIRST 257 /* first free entry */
+#define LAST 255
+
+#define MAXCODE(b) ((1 << (b)) - 1)
+#define BADCODEM1 MAXCODE(MAX_BSD_BITS);
+
+#define BSD_HASH(prefix,suffix,hshift) ((((unsigned long)(suffix))<<(hshift)) \
+ ^ (unsigned long)(prefix))
+#define BSD_KEY(prefix,suffix) ((((unsigned long)(suffix)) << 16) \
+ + (unsigned long)(prefix))
+
+#define CHECK_GAP 10000 /* Ratio check interval */
+
+#define RATIO_SCALE_LOG 8
+#define RATIO_SCALE (1<<RATIO_SCALE_LOG)
+#define RATIO_MAX (0x7fffffff>>RATIO_SCALE_LOG)
+
+/*
+ * clear the dictionary
+ */
+
+static void bsd_clear(struct bsd_db *db)
+{
+ db->clear_count++;
+ db->max_ent = FIRST-1;
+ db->n_bits = BSD_INIT_BITS;
+ db->bytes_out = 0;
+ db->in_count = 0;
+ db->incomp_count = 0;
+ db->ratio = 0;
+ db->checkpoint = CHECK_GAP;
+}
+
+/*
+ * If the dictionary is full, then see if it is time to reset it.
+ *
+ * Compute the compression ratio using fixed-point arithmetic
+ * with 8 fractional bits.
+ *
+ * Since we have an infinite stream instead of a single file,
+ * watch only the local compression ratio.
+ *
+ * Since both peers must reset the dictionary at the same time even in
+ * the absence of CLEAR codes (while packets are incompressible), they
+ * must compute the same ratio.
+ */
+static int bsd_check (struct bsd_db *db) /* 1=output CLEAR */
+{
+ unsigned int new_ratio;
+
+ if (db->in_count >= db->checkpoint)
+ {
+ /* age the ratio by limiting the size of the counts */
+ if (db->in_count >= RATIO_MAX || db->bytes_out >= RATIO_MAX)
+ {
+ db->in_count -= (db->in_count >> 2);
+ db->bytes_out -= (db->bytes_out >> 2);
+ }
+
+ db->checkpoint = db->in_count + CHECK_GAP;
+
+ if (db->max_ent >= db->maxmaxcode)
+ {
+ /* Reset the dictionary only if the ratio is worse,
+ * or if it looks as if it has been poisoned
+ * by incompressible data.
+ *
+ * This does not overflow, because
+ * db->in_count <= RATIO_MAX.
+ */
+
+ new_ratio = db->in_count << RATIO_SCALE_LOG;
+ if (db->bytes_out != 0)
+ {
+ new_ratio /= db->bytes_out;
+ }
+
+ if (new_ratio < db->ratio || new_ratio < 1 * RATIO_SCALE)
+ {
+ bsd_clear (db);
+ return 1;
+ }
+ db->ratio = new_ratio;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Return statistics.
+ */
+
+static void bsd_stats (void *state, struct compstat *stats)
+{
+ struct bsd_db *db = (struct bsd_db *) state;
+
+ stats->unc_bytes = db->uncomp_bytes;
+ stats->unc_packets = db->uncomp_count;
+ stats->comp_bytes = db->comp_bytes;
+ stats->comp_packets = db->comp_count;
+ stats->inc_bytes = db->incomp_bytes;
+ stats->inc_packets = db->incomp_count;
+ stats->in_count = db->in_count;
+ stats->bytes_out = db->bytes_out;
+}
+
+/*
+ * Reset state, as on a CCP ResetReq.
+ */
+static void bsd_reset (void *state,unsigned char code, unsigned char id,
+ unsigned char *data, unsigned len,
+ struct isdn_ppp_resetparams *rsparm)
+{
+ struct bsd_db *db = (struct bsd_db *) state;
+
+ bsd_clear(db);
+ db->seqno = 0;
+ db->clear_count = 0;
+}
+
+/*
+ * Release the compression structure
+ */
+static void bsd_free (void *state)
+{
+ struct bsd_db *db = (struct bsd_db *) state;
+
+ if (db) {
+ /*
+ * Release the dictionary
+ */
+ vfree(db->dict);
+ db->dict = NULL;
+
+ /*
+ * Release the string buffer
+ */
+ vfree(db->lens);
+ db->lens = NULL;
+
+ /*
+ * Finally release the structure itself.
+ */
+ kfree(db);
+ }
+}
+
+
+/*
+ * Allocate space for a (de) compressor.
+ */
+static void *bsd_alloc (struct isdn_ppp_comp_data *data)
+{
+ int bits;
+ unsigned int hsize, hshift, maxmaxcode;
+ struct bsd_db *db;
+ int decomp;
+
+ static unsigned int htab[][2] = {
+ { 5003 , 4 } , { 5003 , 4 } , { 5003 , 4 } , { 5003 , 4 } ,
+ { 9001 , 5 } , { 18013 , 6 } , { 35023 , 7 } , { 69001 , 8 }
+ };
+
+ if (data->optlen != 1 || data->num != CI_BSD_COMPRESS
+ || BSD_VERSION(data->options[0]) != BSD_CURRENT_VERSION)
+ return NULL;
+
+ bits = BSD_NBITS(data->options[0]);
+
+ if(bits < 9 || bits > 15)
+ return NULL;
+
+ hsize = htab[bits-9][0];
+ hshift = htab[bits-9][1];
+
+ /*
+ * Allocate the main control structure for this instance.
+ */
+ maxmaxcode = MAXCODE(bits);
+ db = kzalloc (sizeof (struct bsd_db),GFP_KERNEL);
+ if (!db)
+ return NULL;
+
+ db->xmit = data->flags & IPPP_COMP_FLAG_XMIT;
+ decomp = db->xmit ? 0 : 1;
+
+ /*
+ * Allocate space for the dictionary. This may be more than one page in
+ * length.
+ */
+ db->dict = vmalloc(hsize * sizeof(struct bsd_dict));
+ if (!db->dict) {
+ bsd_free (db);
+ return NULL;
+ }
+
+ /*
+ * If this is the compression buffer then there is no length data.
+ * For decompression, the length information is needed as well.
+ */
+ if (!decomp)
+ db->lens = NULL;
+ else {
+ db->lens = vmalloc((maxmaxcode + 1) * sizeof(db->lens[0]));
+ if (!db->lens) {
+ bsd_free (db);
+ return (NULL);
+ }
+ }
+
+ /*
+ * Initialize the data information for the compression code
+ */
+ db->totlen = sizeof (struct bsd_db) + (sizeof (struct bsd_dict) * hsize);
+ db->hsize = hsize;
+ db->hshift = hshift;
+ db->maxmaxcode = maxmaxcode;
+ db->maxbits = bits;
+
+ return (void *) db;
+}
+
+/*
+ * Initialize the database.
+ */
+static int bsd_init (void *state, struct isdn_ppp_comp_data *data, int unit, int debug)
+{
+ struct bsd_db *db = state;
+ int indx;
+ int decomp;
+
+ if(!state || !data) {
+ printk(KERN_ERR "isdn_bsd_init: [%d] ERR, state %lx data %lx\n",unit,(long)state,(long)data);
+ return 0;
+ }
+
+ decomp = db->xmit ? 0 : 1;
+
+ if (data->optlen != 1 || data->num != CI_BSD_COMPRESS
+ || (BSD_VERSION(data->options[0]) != BSD_CURRENT_VERSION)
+ || (BSD_NBITS(data->options[0]) != db->maxbits)
+ || (decomp && db->lens == NULL)) {
+ printk(KERN_ERR "isdn_bsd: %d %d %d %d %lx\n",data->optlen,data->num,data->options[0],decomp,(unsigned long)db->lens);
+ return 0;
+ }
+
+ if (decomp)
+ for(indx=LAST;indx>=0;indx--)
+ db->lens[indx] = 1;
+
+ indx = db->hsize;
+ while (indx-- != 0) {
+ db->dict[indx].codem1 = BADCODEM1;
+ db->dict[indx].cptr = 0;
+ }
+
+ db->unit = unit;
+ db->mru = 0;
+
+ db->debug = 1;
+
+ bsd_reset(db,0,0,NULL,0,NULL);
+
+ return 1;
+}
+
+/*
+ * Obtain pointers to the various structures in the compression tables
+ */
+
+#define dict_ptrx(p,idx) &(p->dict[idx])
+#define lens_ptrx(p,idx) &(p->lens[idx])
+
+#ifdef DEBUG
+static unsigned short *lens_ptr(struct bsd_db *db, int idx)
+{
+ if ((unsigned int) idx > (unsigned int) db->maxmaxcode) {
+ printk (KERN_DEBUG "<9>ppp: lens_ptr(%d) > max\n", idx);
+ idx = 0;
+ }
+ return lens_ptrx (db, idx);
+}
+
+static struct bsd_dict *dict_ptr(struct bsd_db *db, int idx)
+{
+ if ((unsigned int) idx >= (unsigned int) db->hsize) {
+ printk (KERN_DEBUG "<9>ppp: dict_ptr(%d) > max\n", idx);
+ idx = 0;
+ }
+ return dict_ptrx (db, idx);
+}
+
+#else
+#define lens_ptr(db,idx) lens_ptrx(db,idx)
+#define dict_ptr(db,idx) dict_ptrx(db,idx)
+#endif
+
+/*
+ * compress a packet
+ */
+static int bsd_compress (void *state, struct sk_buff *skb_in, struct sk_buff *skb_out,int proto)
+{
+ struct bsd_db *db;
+ int hshift;
+ unsigned int max_ent;
+ unsigned int n_bits;
+ unsigned int bitno;
+ unsigned long accm;
+ int ent;
+ unsigned long fcode;
+ struct bsd_dict *dictp;
+ unsigned char c;
+ int hval,disp,ilen,mxcode;
+ unsigned char *rptr = skb_in->data;
+ int isize = skb_in->len;
+
+#define OUTPUT(ent) \
+ { \
+ bitno -= n_bits; \
+ accm |= ((ent) << bitno); \
+ do { \
+ if(skb_out && skb_tailroom(skb_out) > 0) \
+ *(skb_put(skb_out,1)) = (unsigned char) (accm>>24); \
+ accm <<= 8; \
+ bitno += 8; \
+ } while (bitno <= 24); \
+ }
+
+ /*
+ * If the protocol is not in the range we're interested in,
+ * just return without compressing the packet. If it is,
+ * the protocol becomes the first byte to compress.
+ */
+ printk(KERN_DEBUG "bsd_compress called with %x\n",proto);
+
+ ent = proto;
+ if (proto < 0x21 || proto > 0xf9 || !(proto & 0x1) )
+ return 0;
+
+ db = (struct bsd_db *) state;
+ hshift = db->hshift;
+ max_ent = db->max_ent;
+ n_bits = db->n_bits;
+ bitno = 32;
+ accm = 0;
+ mxcode = MAXCODE (n_bits);
+
+ /* This is the PPP header information */
+ if(skb_out && skb_tailroom(skb_out) >= 2) {
+ char *v = skb_put(skb_out,2);
+ /* we only push our own data on the header,
+ AC,PC and protos is pushed by caller */
+ v[0] = db->seqno >> 8;
+ v[1] = db->seqno;
+ }
+
+ ilen = ++isize; /* This is off by one, but that is what is in draft! */
+
+ while (--ilen > 0) {
+ c = *rptr++;
+ fcode = BSD_KEY (ent, c);
+ hval = BSD_HASH (ent, c, hshift);
+ dictp = dict_ptr (db, hval);
+
+ /* Validate and then check the entry. */
+ if (dictp->codem1 >= max_ent)
+ goto nomatch;
+
+ if (dictp->fcode == fcode) {
+ ent = dictp->codem1 + 1;
+ continue; /* found (prefix,suffix) */
+ }
+
+ /* continue probing until a match or invalid entry */
+ disp = (hval == 0) ? 1 : hval;
+
+ do {
+ hval += disp;
+ if (hval >= db->hsize)
+ hval -= db->hsize;
+ dictp = dict_ptr (db, hval);
+ if (dictp->codem1 >= max_ent)
+ goto nomatch;
+ } while (dictp->fcode != fcode);
+
+ ent = dictp->codem1 + 1; /* finally found (prefix,suffix) */
+ continue;
+
+nomatch:
+ OUTPUT(ent); /* output the prefix */
+
+ /* code -> hashtable */
+ if (max_ent < db->maxmaxcode) {
+ struct bsd_dict *dictp2;
+ struct bsd_dict *dictp3;
+ int indx;
+
+ /* expand code size if needed */
+ if (max_ent >= mxcode) {
+ db->n_bits = ++n_bits;
+ mxcode = MAXCODE (n_bits);
+ }
+
+ /*
+ * Invalidate old hash table entry using
+ * this code, and then take it over.
+ */
+ dictp2 = dict_ptr (db, max_ent + 1);
+ indx = dictp2->cptr;
+ dictp3 = dict_ptr (db, indx);
+
+ if (dictp3->codem1 == max_ent)
+ dictp3->codem1 = BADCODEM1;
+
+ dictp2->cptr = hval;
+ dictp->codem1 = max_ent;
+ dictp->fcode = fcode;
+ db->max_ent = ++max_ent;
+
+ if (db->lens) {
+ unsigned short *len1 = lens_ptr (db, max_ent);
+ unsigned short *len2 = lens_ptr (db, ent);
+ *len1 = *len2 + 1;
+ }
+ }
+ ent = c;
+ }
+
+ OUTPUT(ent); /* output the last code */
+
+ if(skb_out)
+ db->bytes_out += skb_out->len; /* Do not count bytes from here */
+ db->uncomp_bytes += isize;
+ db->in_count += isize;
+ ++db->uncomp_count;
+ ++db->seqno;
+
+ if (bitno < 32)
+ ++db->bytes_out; /* must be set before calling bsd_check */
+
+ /*
+ * Generate the clear command if needed
+ */
+
+ if (bsd_check(db))
+ OUTPUT (CLEAR);
+
+ /*
+ * Pad dribble bits of last code with ones.
+ * Do not emit a completely useless byte of ones.
+ */
+ if (bitno < 32 && skb_out && skb_tailroom(skb_out) > 0)
+ *(skb_put(skb_out,1)) = (unsigned char) ((accm | (0xff << (bitno-8))) >> 24);
+
+ /*
+ * Increase code size if we would have without the packet
+ * boundary because the decompressor will do so.
+ */
+ if (max_ent >= mxcode && max_ent < db->maxmaxcode)
+ db->n_bits++;
+
+ /* If output length is too large then this is an incompressible frame. */
+ if (!skb_out || (skb_out && skb_out->len >= skb_in->len) ) {
+ ++db->incomp_count;
+ db->incomp_bytes += isize;
+ return 0;
+ }
+
+ /* Count the number of compressed frames */
+ ++db->comp_count;
+ db->comp_bytes += skb_out->len;
+ return skb_out->len;
+
+#undef OUTPUT
+}
+
+/*
+ * Update the "BSD Compress" dictionary on the receiver for
+ * incompressible data by pretending to compress the incoming data.
+ */
+static void bsd_incomp (void *state, struct sk_buff *skb_in,int proto)
+{
+ bsd_compress (state, skb_in, NULL, proto);
+}
+
+/*
+ * Decompress "BSD Compress".
+ */
+static int bsd_decompress (void *state, struct sk_buff *skb_in, struct sk_buff *skb_out,
+ struct isdn_ppp_resetparams *rsparm)
+{
+ struct bsd_db *db;
+ unsigned int max_ent;
+ unsigned long accm;
+ unsigned int bitno; /* 1st valid bit in accm */
+ unsigned int n_bits;
+ unsigned int tgtbitno; /* bitno when we have a code */
+ struct bsd_dict *dictp;
+ int seq;
+ unsigned int incode;
+ unsigned int oldcode;
+ unsigned int finchar;
+ unsigned char *p,*ibuf;
+ int ilen;
+ int codelen;
+ int extra;
+
+ db = (struct bsd_db *) state;
+ max_ent = db->max_ent;
+ accm = 0;
+ bitno = 32; /* 1st valid bit in accm */
+ n_bits = db->n_bits;
+ tgtbitno = 32 - n_bits; /* bitno when we have a code */
+
+ printk(KERN_DEBUG "bsd_decompress called\n");
+
+ if(!skb_in || !skb_out) {
+ printk(KERN_ERR "bsd_decompress called with NULL parameter\n");
+ return DECOMP_ERROR;
+ }
+
+ /*
+ * Get the sequence number.
+ */
+ if( (p = skb_pull(skb_in,2)) == NULL) {
+ return DECOMP_ERROR;
+ }
+ p-=2;
+ seq = (p[0] << 8) + p[1];
+ ilen = skb_in->len;
+ ibuf = skb_in->data;
+
+ /*
+ * Check the sequence number and give up if it differs from
+ * the value we're expecting.
+ */
+ if (seq != db->seqno) {
+ if (db->debug) {
+ printk(KERN_DEBUG "bsd_decomp%d: bad sequence # %d, expected %d\n",
+ db->unit, seq, db->seqno - 1);
+ }
+ return DECOMP_ERROR;
+ }
+
+ ++db->seqno;
+ db->bytes_out += ilen;
+
+ if(skb_tailroom(skb_out) > 0)
+ *(skb_put(skb_out,1)) = 0;
+ else
+ return DECOMP_ERR_NOMEM;
+
+ oldcode = CLEAR;
+
+ /*
+ * Keep the checkpoint correctly so that incompressible packets
+ * clear the dictionary at the proper times.
+ */
+
+ for (;;) {
+ if (ilen-- <= 0) {
+ db->in_count += (skb_out->len - 1); /* don't count the header */
+ break;
+ }
+
+ /*
+ * Accumulate bytes until we have a complete code.
+ * Then get the next code, relying on the 32-bit,
+ * unsigned accm to mask the result.
+ */
+
+ bitno -= 8;
+ accm |= *ibuf++ << bitno;
+ if (tgtbitno < bitno)
+ continue;
+
+ incode = accm >> tgtbitno;
+ accm <<= n_bits;
+ bitno += n_bits;
+
+ /*
+ * The dictionary must only be cleared at the end of a packet.
+ */
+
+ if (incode == CLEAR) {
+ if (ilen > 0) {
+ if (db->debug)
+ printk(KERN_DEBUG "bsd_decomp%d: bad CLEAR\n", db->unit);
+ return DECOMP_FATALERROR; /* probably a bug */
+ }
+ bsd_clear(db);
+ break;
+ }
+
+ if ((incode > max_ent + 2) || (incode > db->maxmaxcode)
+ || (incode > max_ent && oldcode == CLEAR)) {
+ if (db->debug) {
+ printk(KERN_DEBUG "bsd_decomp%d: bad code 0x%x oldcode=0x%x ",
+ db->unit, incode, oldcode);
+ printk(KERN_DEBUG "max_ent=0x%x skb->Len=%d seqno=%d\n",
+ max_ent, skb_out->len, db->seqno);
+ }
+ return DECOMP_FATALERROR; /* probably a bug */
+ }
+
+ /* Special case for KwKwK string. */
+ if (incode > max_ent) {
+ finchar = oldcode;
+ extra = 1;
+ } else {
+ finchar = incode;
+ extra = 0;
+ }
+
+ codelen = *(lens_ptr (db, finchar));
+ if( skb_tailroom(skb_out) < codelen + extra) {
+ if (db->debug) {
+ printk(KERN_DEBUG "bsd_decomp%d: ran out of mru\n", db->unit);
+#ifdef DEBUG
+ printk(KERN_DEBUG " len=%d, finchar=0x%x, codelen=%d,skblen=%d\n",
+ ilen, finchar, codelen, skb_out->len);
+#endif
+ }
+ return DECOMP_FATALERROR;
+ }
+
+ /*
+ * Decode this code and install it in the decompressed buffer.
+ */
+
+ p = skb_put(skb_out,codelen);
+ p += codelen;
+ while (finchar > LAST) {
+ struct bsd_dict *dictp2 = dict_ptr (db, finchar);
+
+ dictp = dict_ptr (db, dictp2->cptr);
+
+#ifdef DEBUG
+ if (--codelen <= 0 || dictp->codem1 != finchar-1) {
+ if (codelen <= 0) {
+ printk(KERN_ERR "bsd_decomp%d: fell off end of chain ", db->unit);
+ printk(KERN_ERR "0x%x at 0x%x by 0x%x, max_ent=0x%x\n", incode, finchar, dictp2->cptr, max_ent);
+ } else {
+ if (dictp->codem1 != finchar-1) {
+ printk(KERN_ERR "bsd_decomp%d: bad code chain 0x%x finchar=0x%x ",db->unit, incode, finchar);
+ printk(KERN_ERR "oldcode=0x%x cptr=0x%x codem1=0x%x\n", oldcode, dictp2->cptr, dictp->codem1);
+ }
+ }
+ return DECOMP_FATALERROR;
+ }
+#endif
+
+ {
+ u32 fcode = dictp->fcode;
+ *--p = (fcode >> 16) & 0xff;
+ finchar = fcode & 0xffff;
+ }
+ }
+ *--p = finchar;
+
+#ifdef DEBUG
+ if (--codelen != 0)
+ printk(KERN_ERR "bsd_decomp%d: short by %d after code 0x%x, max_ent=0x%x\n", db->unit, codelen, incode, max_ent);
+#endif
+
+ if (extra) /* the KwKwK case again */
+ *(skb_put(skb_out,1)) = finchar;
+
+ /*
+ * If not first code in a packet, and
+ * if not out of code space, then allocate a new code.
+ *
+ * Keep the hash table correct so it can be used
+ * with uncompressed packets.
+ */
+ if (oldcode != CLEAR && max_ent < db->maxmaxcode) {
+ struct bsd_dict *dictp2, *dictp3;
+ u16 *lens1, *lens2;
+ unsigned long fcode;
+ int hval, disp, indx;
+
+ fcode = BSD_KEY(oldcode,finchar);
+ hval = BSD_HASH(oldcode,finchar,db->hshift);
+ dictp = dict_ptr (db, hval);
+
+ /* look for a free hash table entry */
+ if (dictp->codem1 < max_ent) {
+ disp = (hval == 0) ? 1 : hval;
+ do {
+ hval += disp;
+ if (hval >= db->hsize)
+ hval -= db->hsize;
+ dictp = dict_ptr (db, hval);
+ } while (dictp->codem1 < max_ent);
+ }
+
+ /*
+ * Invalidate previous hash table entry
+ * assigned this code, and then take it over
+ */
+
+ dictp2 = dict_ptr (db, max_ent + 1);
+ indx = dictp2->cptr;
+ dictp3 = dict_ptr (db, indx);
+
+ if (dictp3->codem1 == max_ent)
+ dictp3->codem1 = BADCODEM1;
+
+ dictp2->cptr = hval;
+ dictp->codem1 = max_ent;
+ dictp->fcode = fcode;
+ db->max_ent = ++max_ent;
+
+ /* Update the length of this string. */
+ lens1 = lens_ptr (db, max_ent);
+ lens2 = lens_ptr (db, oldcode);
+ *lens1 = *lens2 + 1;
+
+ /* Expand code size if needed. */
+ if (max_ent >= MAXCODE(n_bits) && max_ent < db->maxmaxcode) {
+ db->n_bits = ++n_bits;
+ tgtbitno = 32-n_bits;
+ }
+ }
+ oldcode = incode;
+ }
+
+ ++db->comp_count;
+ ++db->uncomp_count;
+ db->comp_bytes += skb_in->len - BSD_OVHD;
+ db->uncomp_bytes += skb_out->len;
+
+ if (bsd_check(db)) {
+ if (db->debug)
+ printk(KERN_DEBUG "bsd_decomp%d: peer should have cleared dictionary on %d\n",
+ db->unit, db->seqno - 1);
+ }
+ return skb_out->len;
+}
+
+/*************************************************************
+ * Table of addresses for the BSD compression module
+ *************************************************************/
+
+static struct isdn_ppp_compressor ippp_bsd_compress = {
+ .owner = THIS_MODULE,
+ .num = CI_BSD_COMPRESS,
+ .alloc = bsd_alloc,
+ .free = bsd_free,
+ .init = bsd_init,
+ .reset = bsd_reset,
+ .compress = bsd_compress,
+ .decompress = bsd_decompress,
+ .incomp = bsd_incomp,
+ .stat = bsd_stats,
+};
+
+/*************************************************************
+ * Module support routines
+ *************************************************************/
+
+static int __init isdn_bsdcomp_init(void)
+{
+ int answer = isdn_ppp_register_compressor (&ippp_bsd_compress);
+ if (answer == 0)
+ printk (KERN_INFO "PPP BSD Compression module registered\n");
+ return answer;
+}
+
+static void __exit isdn_bsdcomp_exit(void)
+{
+ isdn_ppp_unregister_compressor (&ippp_bsd_compress);
+}
+
+module_init(isdn_bsdcomp_init);
+module_exit(isdn_bsdcomp_exit);
diff --git a/drivers/isdn/i4l/isdn_common.c b/drivers/isdn/i4l/isdn_common.c
new file mode 100644
index 0000000..7188c59
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_common.c
@@ -0,0 +1,2390 @@
+/* $Id: isdn_common.c,v 1.1.2.3 2004/02/10 01:07:13 keil Exp $
+ *
+ * Linux ISDN subsystem, common used functions (linklevel).
+ *
+ * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de)
+ * Copyright 1995,96 Thinking Objects Software GmbH Wuerzburg
+ * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/vmalloc.h>
+#include <linux/isdn.h>
+#include <linux/smp_lock.h>
+#include "isdn_common.h"
+#include "isdn_tty.h"
+#include "isdn_net.h"
+#include "isdn_ppp.h"
+#ifdef CONFIG_ISDN_AUDIO
+#include "isdn_audio.h"
+#endif
+#ifdef CONFIG_ISDN_DIVERSION_MODULE
+#define CONFIG_ISDN_DIVERSION
+#endif
+#ifdef CONFIG_ISDN_DIVERSION
+#include <linux/isdn_divertif.h>
+#endif /* CONFIG_ISDN_DIVERSION */
+#include "isdn_v110.h"
+
+/* Debugflags */
+#undef ISDN_DEBUG_STATCALLB
+
+MODULE_DESCRIPTION("ISDN4Linux: link layer");
+MODULE_AUTHOR("Fritz Elfert");
+MODULE_LICENSE("GPL");
+
+isdn_dev *dev;
+
+static char *isdn_revision = "$Revision: 1.1.2.3 $";
+
+extern char *isdn_net_revision;
+extern char *isdn_tty_revision;
+#ifdef CONFIG_ISDN_PPP
+extern char *isdn_ppp_revision;
+#else
+static char *isdn_ppp_revision = ": none $";
+#endif
+#ifdef CONFIG_ISDN_AUDIO
+extern char *isdn_audio_revision;
+#else
+static char *isdn_audio_revision = ": none $";
+#endif
+extern char *isdn_v110_revision;
+
+#ifdef CONFIG_ISDN_DIVERSION
+static isdn_divert_if *divert_if; /* = NULL */
+#endif /* CONFIG_ISDN_DIVERSION */
+
+
+static int isdn_writebuf_stub(int, int, const u_char __user *, int);
+static void set_global_features(void);
+static int isdn_wildmat(char *s, char *p);
+static int isdn_add_channels(isdn_driver_t *d, int drvidx, int n, int adding);
+
+static inline void
+isdn_lock_driver(isdn_driver_t *drv)
+{
+ try_module_get(drv->interface->owner);
+ drv->locks++;
+}
+
+void
+isdn_lock_drivers(void)
+{
+ int i;
+
+ for (i = 0; i < ISDN_MAX_DRIVERS; i++) {
+ if (!dev->drv[i])
+ continue;
+ isdn_lock_driver(dev->drv[i]);
+ }
+}
+
+static inline void
+isdn_unlock_driver(isdn_driver_t *drv)
+{
+ if (drv->locks > 0) {
+ drv->locks--;
+ module_put(drv->interface->owner);
+ }
+}
+
+void
+isdn_unlock_drivers(void)
+{
+ int i;
+
+ for (i = 0; i < ISDN_MAX_DRIVERS; i++) {
+ if (!dev->drv[i])
+ continue;
+ isdn_unlock_driver(dev->drv[i]);
+ }
+}
+
+#if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP)
+void
+isdn_dumppkt(char *s, u_char * p, int len, int dumplen)
+{
+ int dumpc;
+
+ printk(KERN_DEBUG "%s(%d) ", s, len);
+ for (dumpc = 0; (dumpc < dumplen) && (len); len--, dumpc++)
+ printk(" %02x", *p++);
+ printk("\n");
+}
+#endif
+
+/*
+ * I picked the pattern-matching-functions from an old GNU-tar version (1.10)
+ * It was originally written and put to PD by rs@mirror.TMC.COM (Rich Salz)
+ */
+static int
+isdn_star(char *s, char *p)
+{
+ while (isdn_wildmat(s, p)) {
+ if (*++s == '\0')
+ return (2);
+ }
+ return (0);
+}
+
+/*
+ * Shell-type Pattern-matching for incoming caller-Ids
+ * This function gets a string in s and checks, if it matches the pattern
+ * given in p.
+ *
+ * Return:
+ * 0 = match.
+ * 1 = no match.
+ * 2 = no match. Would eventually match, if s would be longer.
+ *
+ * Possible Patterns:
+ *
+ * '?' matches one character
+ * '*' matches zero or more characters
+ * [xyz] matches the set of characters in brackets.
+ * [^xyz] matches any single character not in the set of characters
+ */
+
+static int
+isdn_wildmat(char *s, char *p)
+{
+ register int last;
+ register int matched;
+ register int reverse;
+ register int nostar = 1;
+
+ if (!(*s) && !(*p))
+ return(1);
+ for (; *p; s++, p++)
+ switch (*p) {
+ case '\\':
+ /*
+ * Literal match with following character,
+ * fall through.
+ */
+ p++;
+ default:
+ if (*s != *p)
+ return (*s == '\0')?2:1;
+ continue;
+ case '?':
+ /* Match anything. */
+ if (*s == '\0')
+ return (2);
+ continue;
+ case '*':
+ nostar = 0;
+ /* Trailing star matches everything. */
+ return (*++p ? isdn_star(s, p) : 0);
+ case '[':
+ /* [^....] means inverse character class. */
+ if ((reverse = (p[1] == '^')))
+ p++;
+ for (last = 0, matched = 0; *++p && (*p != ']'); last = *p)
+ /* This next line requires a good C compiler. */
+ if (*p == '-' ? *s <= *++p && *s >= last : *s == *p)
+ matched = 1;
+ if (matched == reverse)
+ return (1);
+ continue;
+ }
+ return (*s == '\0')?0:nostar;
+}
+
+int isdn_msncmp( const char * msn1, const char * msn2 )
+{
+ char TmpMsn1[ ISDN_MSNLEN ];
+ char TmpMsn2[ ISDN_MSNLEN ];
+ char *p;
+
+ for ( p = TmpMsn1; *msn1 && *msn1 != ':'; ) // Strip off a SPID
+ *p++ = *msn1++;
+ *p = '\0';
+
+ for ( p = TmpMsn2; *msn2 && *msn2 != ':'; ) // Strip off a SPID
+ *p++ = *msn2++;
+ *p = '\0';
+
+ return isdn_wildmat( TmpMsn1, TmpMsn2 );
+}
+
+int
+isdn_dc2minor(int di, int ch)
+{
+ int i;
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+ if (dev->chanmap[i] == ch && dev->drvmap[i] == di)
+ return i;
+ return -1;
+}
+
+static int isdn_timer_cnt1 = 0;
+static int isdn_timer_cnt2 = 0;
+static int isdn_timer_cnt3 = 0;
+
+static void
+isdn_timer_funct(ulong dummy)
+{
+ int tf = dev->tflags;
+ if (tf & ISDN_TIMER_FAST) {
+ if (tf & ISDN_TIMER_MODEMREAD)
+ isdn_tty_readmodem();
+ if (tf & ISDN_TIMER_MODEMPLUS)
+ isdn_tty_modem_escape();
+ if (tf & ISDN_TIMER_MODEMXMIT)
+ isdn_tty_modem_xmit();
+ }
+ if (tf & ISDN_TIMER_SLOW) {
+ if (++isdn_timer_cnt1 >= ISDN_TIMER_02SEC) {
+ isdn_timer_cnt1 = 0;
+ if (tf & ISDN_TIMER_NETDIAL)
+ isdn_net_dial();
+ }
+ if (++isdn_timer_cnt2 >= ISDN_TIMER_1SEC) {
+ isdn_timer_cnt2 = 0;
+ if (tf & ISDN_TIMER_NETHANGUP)
+ isdn_net_autohup();
+ if (++isdn_timer_cnt3 >= ISDN_TIMER_RINGING) {
+ isdn_timer_cnt3 = 0;
+ if (tf & ISDN_TIMER_MODEMRING)
+ isdn_tty_modem_ring();
+ }
+ if (tf & ISDN_TIMER_CARRIER)
+ isdn_tty_carrier_timeout();
+ }
+ }
+ if (tf)
+ mod_timer(&dev->timer, jiffies+ISDN_TIMER_RES);
+}
+
+void
+isdn_timer_ctrl(int tf, int onoff)
+{
+ unsigned long flags;
+ int old_tflags;
+
+ spin_lock_irqsave(&dev->timerlock, flags);
+ if ((tf & ISDN_TIMER_SLOW) && (!(dev->tflags & ISDN_TIMER_SLOW))) {
+ /* If the slow-timer wasn't activated until now */
+ isdn_timer_cnt1 = 0;
+ isdn_timer_cnt2 = 0;
+ }
+ old_tflags = dev->tflags;
+ if (onoff)
+ dev->tflags |= tf;
+ else
+ dev->tflags &= ~tf;
+ if (dev->tflags && !old_tflags)
+ mod_timer(&dev->timer, jiffies+ISDN_TIMER_RES);
+ spin_unlock_irqrestore(&dev->timerlock, flags);
+}
+
+/*
+ * Receive a packet from B-Channel. (Called from low-level-module)
+ */
+static void
+isdn_receive_skb_callback(int di, int channel, struct sk_buff *skb)
+{
+ int i;
+
+ if ((i = isdn_dc2minor(di, channel)) == -1) {
+ dev_kfree_skb(skb);
+ return;
+ }
+ /* Update statistics */
+ dev->ibytes[i] += skb->len;
+
+ /* First, try to deliver data to network-device */
+ if (isdn_net_rcv_skb(i, skb))
+ return;
+
+ /* V.110 handling
+ * makes sense for async streams only, so it is
+ * called after possible net-device delivery.
+ */
+ if (dev->v110[i]) {
+ atomic_inc(&dev->v110use[i]);
+ skb = isdn_v110_decode(dev->v110[i], skb);
+ atomic_dec(&dev->v110use[i]);
+ if (!skb)
+ return;
+ }
+
+ /* No network-device found, deliver to tty or raw-channel */
+ if (skb->len) {
+ if (isdn_tty_rcv_skb(i, di, channel, skb))
+ return;
+ wake_up_interruptible(&dev->drv[di]->rcv_waitq[channel]);
+ } else
+ dev_kfree_skb(skb);
+}
+
+/*
+ * Intercept command from Linklevel to Lowlevel.
+ * If layer 2 protocol is V.110 and this is not supported by current
+ * lowlevel-driver, use driver's transparent mode and handle V.110 in
+ * linklevel instead.
+ */
+int
+isdn_command(isdn_ctrl *cmd)
+{
+ if (cmd->driver == -1) {
+ printk(KERN_WARNING "isdn_command command(%x) driver -1\n", cmd->command);
+ return(1);
+ }
+ if (!dev->drv[cmd->driver]) {
+ printk(KERN_WARNING "isdn_command command(%x) dev->drv[%d] NULL\n",
+ cmd->command, cmd->driver);
+ return(1);
+ }
+ if (!dev->drv[cmd->driver]->interface) {
+ printk(KERN_WARNING "isdn_command command(%x) dev->drv[%d]->interface NULL\n",
+ cmd->command, cmd->driver);
+ return(1);
+ }
+ if (cmd->command == ISDN_CMD_SETL2) {
+ int idx = isdn_dc2minor(cmd->driver, cmd->arg & 255);
+ unsigned long l2prot = (cmd->arg >> 8) & 255;
+ unsigned long features = (dev->drv[cmd->driver]->interface->features
+ >> ISDN_FEATURE_L2_SHIFT) &
+ ISDN_FEATURE_L2_MASK;
+ unsigned long l2_feature = (1 << l2prot);
+
+ switch (l2prot) {
+ case ISDN_PROTO_L2_V11096:
+ case ISDN_PROTO_L2_V11019:
+ case ISDN_PROTO_L2_V11038:
+ /* If V.110 requested, but not supported by
+ * HL-driver, set emulator-flag and change
+ * Layer-2 to transparent
+ */
+ if (!(features & l2_feature)) {
+ dev->v110emu[idx] = l2prot;
+ cmd->arg = (cmd->arg & 255) |
+ (ISDN_PROTO_L2_TRANS << 8);
+ } else
+ dev->v110emu[idx] = 0;
+ }
+ }
+ return dev->drv[cmd->driver]->interface->command(cmd);
+}
+
+void
+isdn_all_eaz(int di, int ch)
+{
+ isdn_ctrl cmd;
+
+ if (di < 0)
+ return;
+ cmd.driver = di;
+ cmd.arg = ch;
+ cmd.command = ISDN_CMD_SETEAZ;
+ cmd.parm.num[0] = '\0';
+ isdn_command(&cmd);
+}
+
+/*
+ * Begin of a CAPI like LL<->HL interface, currently used only for
+ * supplementary service (CAPI 2.0 part III)
+ */
+#include <linux/isdn/capicmd.h>
+
+static int
+isdn_capi_rec_hl_msg(capi_msg *cm) {
+
+ int di;
+ int ch;
+
+ di = (cm->adr.Controller & 0x7f) -1;
+ ch = isdn_dc2minor(di, (cm->adr.Controller>>8)& 0x7f);
+ switch(cm->Command) {
+ case CAPI_FACILITY:
+ /* in the moment only handled in tty */
+ return(isdn_tty_capi_facility(cm));
+ default:
+ return(-1);
+ }
+}
+
+static int
+isdn_status_callback(isdn_ctrl * c)
+{
+ int di;
+ u_long flags;
+ int i;
+ int r;
+ int retval = 0;
+ isdn_ctrl cmd;
+ isdn_net_dev *p;
+
+ di = c->driver;
+ i = isdn_dc2minor(di, c->arg);
+ switch (c->command) {
+ case ISDN_STAT_BSENT:
+ if (i < 0)
+ return -1;
+ if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+ return 0;
+ if (isdn_net_stat_callback(i, c))
+ return 0;
+ if (isdn_v110_stat_callback(i, c))
+ return 0;
+ if (isdn_tty_stat_callback(i, c))
+ return 0;
+ wake_up_interruptible(&dev->drv[di]->snd_waitq[c->arg]);
+ break;
+ case ISDN_STAT_STAVAIL:
+ dev->drv[di]->stavail += c->arg;
+ wake_up_interruptible(&dev->drv[di]->st_waitq);
+ break;
+ case ISDN_STAT_RUN:
+ dev->drv[di]->flags |= DRV_FLAG_RUNNING;
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+ if (dev->drvmap[i] == di)
+ isdn_all_eaz(di, dev->chanmap[i]);
+ set_global_features();
+ break;
+ case ISDN_STAT_STOP:
+ dev->drv[di]->flags &= ~DRV_FLAG_RUNNING;
+ break;
+ case ISDN_STAT_ICALL:
+ if (i < 0)
+ return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+ printk(KERN_DEBUG "ICALL (net): %d %ld %s\n", di, c->arg, c->parm.num);
+#endif
+ if (dev->global_flags & ISDN_GLOBAL_STOPPED) {
+ cmd.driver = di;
+ cmd.arg = c->arg;
+ cmd.command = ISDN_CMD_HANGUP;
+ isdn_command(&cmd);
+ return 0;
+ }
+ /* Try to find a network-interface which will accept incoming call */
+ r = ((c->command == ISDN_STAT_ICALLW) ? 0 : isdn_net_find_icall(di, c->arg, i, &c->parm.setup));
+ switch (r) {
+ case 0:
+ /* No network-device replies.
+ * Try ttyI's.
+ * These return 0 on no match, 1 on match and
+ * 3 on eventually match, if CID is longer.
+ */
+ if (c->command == ISDN_STAT_ICALL)
+ if ((retval = isdn_tty_find_icall(di, c->arg, &c->parm.setup))) return(retval);
+#ifdef CONFIG_ISDN_DIVERSION
+ if (divert_if)
+ if ((retval = divert_if->stat_callback(c)))
+ return(retval); /* processed */
+#endif /* CONFIG_ISDN_DIVERSION */
+ if ((!retval) && (dev->drv[di]->flags & DRV_FLAG_REJBUS)) {
+ /* No tty responding */
+ cmd.driver = di;
+ cmd.arg = c->arg;
+ cmd.command = ISDN_CMD_HANGUP;
+ isdn_command(&cmd);
+ retval = 2;
+ }
+ break;
+ case 1:
+ /* Schedule connection-setup */
+ isdn_net_dial();
+ cmd.driver = di;
+ cmd.arg = c->arg;
+ cmd.command = ISDN_CMD_ACCEPTD;
+ for ( p = dev->netdev; p; p = p->next )
+ if ( p->local->isdn_channel == cmd.arg )
+ {
+ strcpy( cmd.parm.setup.eazmsn, p->local->msn );
+ isdn_command(&cmd);
+ retval = 1;
+ break;
+ }
+ break;
+
+ case 2: /* For calling back, first reject incoming call ... */
+ case 3: /* Interface found, but down, reject call actively */
+ retval = 2;
+ printk(KERN_INFO "isdn: Rejecting Call\n");
+ cmd.driver = di;
+ cmd.arg = c->arg;
+ cmd.command = ISDN_CMD_HANGUP;
+ isdn_command(&cmd);
+ if (r == 3)
+ break;
+ /* Fall through */
+ case 4:
+ /* ... then start callback. */
+ isdn_net_dial();
+ break;
+ case 5:
+ /* Number would eventually match, if longer */
+ retval = 3;
+ break;
+ }
+#ifdef ISDN_DEBUG_STATCALLB
+ printk(KERN_DEBUG "ICALL: ret=%d\n", retval);
+#endif
+ return retval;
+ break;
+ case ISDN_STAT_CINF:
+ if (i < 0)
+ return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+ printk(KERN_DEBUG "CINF: %ld %s\n", c->arg, c->parm.num);
+#endif
+ if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+ return 0;
+ if (strcmp(c->parm.num, "0"))
+ isdn_net_stat_callback(i, c);
+ isdn_tty_stat_callback(i, c);
+ break;
+ case ISDN_STAT_CAUSE:
+#ifdef ISDN_DEBUG_STATCALLB
+ printk(KERN_DEBUG "CAUSE: %ld %s\n", c->arg, c->parm.num);
+#endif
+ printk(KERN_INFO "isdn: %s,ch%ld cause: %s\n",
+ dev->drvid[di], c->arg, c->parm.num);
+ isdn_tty_stat_callback(i, c);
+#ifdef CONFIG_ISDN_DIVERSION
+ if (divert_if)
+ divert_if->stat_callback(c);
+#endif /* CONFIG_ISDN_DIVERSION */
+ break;
+ case ISDN_STAT_DISPLAY:
+#ifdef ISDN_DEBUG_STATCALLB
+ printk(KERN_DEBUG "DISPLAY: %ld %s\n", c->arg, c->parm.display);
+#endif
+ isdn_tty_stat_callback(i, c);
+#ifdef CONFIG_ISDN_DIVERSION
+ if (divert_if)
+ divert_if->stat_callback(c);
+#endif /* CONFIG_ISDN_DIVERSION */
+ break;
+ case ISDN_STAT_DCONN:
+ if (i < 0)
+ return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+ printk(KERN_DEBUG "DCONN: %ld\n", c->arg);
+#endif
+ if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+ return 0;
+ /* Find any net-device, waiting for D-channel setup */
+ if (isdn_net_stat_callback(i, c))
+ break;
+ isdn_v110_stat_callback(i, c);
+ /* Find any ttyI, waiting for D-channel setup */
+ if (isdn_tty_stat_callback(i, c)) {
+ cmd.driver = di;
+ cmd.arg = c->arg;
+ cmd.command = ISDN_CMD_ACCEPTB;
+ isdn_command(&cmd);
+ break;
+ }
+ break;
+ case ISDN_STAT_DHUP:
+ if (i < 0)
+ return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+ printk(KERN_DEBUG "DHUP: %ld\n", c->arg);
+#endif
+ if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+ return 0;
+ dev->drv[di]->online &= ~(1 << (c->arg));
+ isdn_info_update();
+ /* Signal hangup to network-devices */
+ if (isdn_net_stat_callback(i, c))
+ break;
+ isdn_v110_stat_callback(i, c);
+ if (isdn_tty_stat_callback(i, c))
+ break;
+#ifdef CONFIG_ISDN_DIVERSION
+ if (divert_if)
+ divert_if->stat_callback(c);
+#endif /* CONFIG_ISDN_DIVERSION */
+ break;
+ break;
+ case ISDN_STAT_BCONN:
+ if (i < 0)
+ return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+ printk(KERN_DEBUG "BCONN: %ld\n", c->arg);
+#endif
+ /* Signal B-channel-connect to network-devices */
+ if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+ return 0;
+ dev->drv[di]->online |= (1 << (c->arg));
+ isdn_info_update();
+ if (isdn_net_stat_callback(i, c))
+ break;
+ isdn_v110_stat_callback(i, c);
+ if (isdn_tty_stat_callback(i, c))
+ break;
+ break;
+ case ISDN_STAT_BHUP:
+ if (i < 0)
+ return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+ printk(KERN_DEBUG "BHUP: %ld\n", c->arg);
+#endif
+ if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+ return 0;
+ dev->drv[di]->online &= ~(1 << (c->arg));
+ isdn_info_update();
+#ifdef CONFIG_ISDN_X25
+ /* Signal hangup to network-devices */
+ if (isdn_net_stat_callback(i, c))
+ break;
+#endif
+ isdn_v110_stat_callback(i, c);
+ if (isdn_tty_stat_callback(i, c))
+ break;
+ break;
+ case ISDN_STAT_NODCH:
+ if (i < 0)
+ return -1;
+#ifdef ISDN_DEBUG_STATCALLB
+ printk(KERN_DEBUG "NODCH: %ld\n", c->arg);
+#endif
+ if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+ return 0;
+ if (isdn_net_stat_callback(i, c))
+ break;
+ if (isdn_tty_stat_callback(i, c))
+ break;
+ break;
+ case ISDN_STAT_ADDCH:
+ spin_lock_irqsave(&dev->lock, flags);
+ if (isdn_add_channels(dev->drv[di], di, c->arg, 1)) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -1;
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+ isdn_info_update();
+ break;
+ case ISDN_STAT_DISCH:
+ spin_lock_irqsave(&dev->lock, flags);
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+ if ((dev->drvmap[i] == di) &&
+ (dev->chanmap[i] == c->arg)) {
+ if (c->parm.num[0])
+ dev->usage[i] &= ~ISDN_USAGE_DISABLED;
+ else
+ if (USG_NONE(dev->usage[i])) {
+ dev->usage[i] |= ISDN_USAGE_DISABLED;
+ }
+ else
+ retval = -1;
+ break;
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+ isdn_info_update();
+ break;
+ case ISDN_STAT_UNLOAD:
+ while (dev->drv[di]->locks > 0) {
+ isdn_unlock_driver(dev->drv[di]);
+ }
+ spin_lock_irqsave(&dev->lock, flags);
+ isdn_tty_stat_callback(i, c);
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+ if (dev->drvmap[i] == di) {
+ dev->drvmap[i] = -1;
+ dev->chanmap[i] = -1;
+ dev->usage[i] &= ~ISDN_USAGE_DISABLED;
+ }
+ dev->drivers--;
+ dev->channels -= dev->drv[di]->channels;
+ kfree(dev->drv[di]->rcverr);
+ kfree(dev->drv[di]->rcvcount);
+ for (i = 0; i < dev->drv[di]->channels; i++)
+ skb_queue_purge(&dev->drv[di]->rpqueue[i]);
+ kfree(dev->drv[di]->rpqueue);
+ kfree(dev->drv[di]->rcv_waitq);
+ kfree(dev->drv[di]);
+ dev->drv[di] = NULL;
+ dev->drvid[di][0] = '\0';
+ isdn_info_update();
+ set_global_features();
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return 0;
+ case ISDN_STAT_L1ERR:
+ break;
+ case CAPI_PUT_MESSAGE:
+ return(isdn_capi_rec_hl_msg(&c->parm.cmsg));
+#ifdef CONFIG_ISDN_TTY_FAX
+ case ISDN_STAT_FAXIND:
+ isdn_tty_stat_callback(i, c);
+ break;
+#endif
+#ifdef CONFIG_ISDN_AUDIO
+ case ISDN_STAT_AUDIO:
+ isdn_tty_stat_callback(i, c);
+ break;
+#endif
+#ifdef CONFIG_ISDN_DIVERSION
+ case ISDN_STAT_PROT:
+ case ISDN_STAT_REDIR:
+ if (divert_if)
+ return(divert_if->stat_callback(c));
+#endif /* CONFIG_ISDN_DIVERSION */
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Get integer from char-pointer, set pointer to end of number
+ */
+int
+isdn_getnum(char **p)
+{
+ int v = -1;
+
+ while (*p[0] >= '0' && *p[0] <= '9')
+ v = ((v < 0) ? 0 : (v * 10)) + (int) ((*p[0]++) - '0');
+ return v;
+}
+
+#define DLE 0x10
+
+/*
+ * isdn_readbchan() tries to get data from the read-queue.
+ * It MUST be called with interrupts off.
+ *
+ * Be aware that this is not an atomic operation when sleep != 0, even though
+ * interrupts are turned off! Well, like that we are currently only called
+ * on behalf of a read system call on raw device files (which are documented
+ * to be dangerous and for for debugging purpose only). The inode semaphore
+ * takes care that this is not called for the same minor device number while
+ * we are sleeping, but access is not serialized against simultaneous read()
+ * from the corresponding ttyI device. Can other ugly events, like changes
+ * of the mapping (di,ch)<->minor, happen during the sleep? --he
+ */
+int
+isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, wait_queue_head_t *sleep)
+{
+ int count;
+ int count_pull;
+ int count_put;
+ int dflag;
+ struct sk_buff *skb;
+ u_char *cp;
+
+ if (!dev->drv[di])
+ return 0;
+ if (skb_queue_empty(&dev->drv[di]->rpqueue[channel])) {
+ if (sleep)
+ interruptible_sleep_on(sleep);
+ else
+ return 0;
+ }
+ if (len > dev->drv[di]->rcvcount[channel])
+ len = dev->drv[di]->rcvcount[channel];
+ cp = buf;
+ count = 0;
+ while (len) {
+ if (!(skb = skb_peek(&dev->drv[di]->rpqueue[channel])))
+ break;
+#ifdef CONFIG_ISDN_AUDIO
+ if (ISDN_AUDIO_SKB_LOCK(skb))
+ break;
+ ISDN_AUDIO_SKB_LOCK(skb) = 1;
+ if ((ISDN_AUDIO_SKB_DLECOUNT(skb)) || (dev->drv[di]->DLEflag & (1 << channel))) {
+ char *p = skb->data;
+ unsigned long DLEmask = (1 << channel);
+
+ dflag = 0;
+ count_pull = count_put = 0;
+ while ((count_pull < skb->len) && (len > 0)) {
+ len--;
+ if (dev->drv[di]->DLEflag & DLEmask) {
+ *cp++ = DLE;
+ dev->drv[di]->DLEflag &= ~DLEmask;
+ } else {
+ *cp++ = *p;
+ if (*p == DLE) {
+ dev->drv[di]->DLEflag |= DLEmask;
+ (ISDN_AUDIO_SKB_DLECOUNT(skb))--;
+ }
+ p++;
+ count_pull++;
+ }
+ count_put++;
+ }
+ if (count_pull >= skb->len)
+ dflag = 1;
+ } else {
+#endif
+ /* No DLE's in buff, so simply copy it */
+ dflag = 1;
+ if ((count_pull = skb->len) > len) {
+ count_pull = len;
+ dflag = 0;
+ }
+ count_put = count_pull;
+ skb_copy_from_linear_data(skb, cp, count_put);
+ cp += count_put;
+ len -= count_put;
+#ifdef CONFIG_ISDN_AUDIO
+ }
+#endif
+ count += count_put;
+ if (fp) {
+ memset(fp, 0, count_put);
+ fp += count_put;
+ }
+ if (dflag) {
+ /* We got all the data in this buff.
+ * Now we can dequeue it.
+ */
+ if (fp)
+ *(fp - 1) = 0xff;
+#ifdef CONFIG_ISDN_AUDIO
+ ISDN_AUDIO_SKB_LOCK(skb) = 0;
+#endif
+ skb = skb_dequeue(&dev->drv[di]->rpqueue[channel]);
+ dev_kfree_skb(skb);
+ } else {
+ /* Not yet emptied this buff, so it
+ * must stay in the queue, for further calls
+ * but we pull off the data we got until now.
+ */
+ skb_pull(skb, count_pull);
+#ifdef CONFIG_ISDN_AUDIO
+ ISDN_AUDIO_SKB_LOCK(skb) = 0;
+#endif
+ }
+ dev->drv[di]->rcvcount[channel] -= count_put;
+ }
+ return count;
+}
+
+/*
+ * isdn_readbchan_tty() tries to get data from the read-queue.
+ * It MUST be called with interrupts off.
+ *
+ * Be aware that this is not an atomic operation when sleep != 0, even though
+ * interrupts are turned off! Well, like that we are currently only called
+ * on behalf of a read system call on raw device files (which are documented
+ * to be dangerous and for for debugging purpose only). The inode semaphore
+ * takes care that this is not called for the same minor device number while
+ * we are sleeping, but access is not serialized against simultaneous read()
+ * from the corresponding ttyI device. Can other ugly events, like changes
+ * of the mapping (di,ch)<->minor, happen during the sleep? --he
+ */
+int
+isdn_readbchan_tty(int di, int channel, struct tty_struct *tty, int cisco_hack)
+{
+ int count;
+ int count_pull;
+ int count_put;
+ int dflag;
+ struct sk_buff *skb;
+ char last = 0;
+ int len;
+
+ if (!dev->drv[di])
+ return 0;
+ if (skb_queue_empty(&dev->drv[di]->rpqueue[channel]))
+ return 0;
+
+ len = tty_buffer_request_room(tty, dev->drv[di]->rcvcount[channel]);
+ if(len == 0)
+ return len;
+
+ count = 0;
+ while (len) {
+ if (!(skb = skb_peek(&dev->drv[di]->rpqueue[channel])))
+ break;
+#ifdef CONFIG_ISDN_AUDIO
+ if (ISDN_AUDIO_SKB_LOCK(skb))
+ break;
+ ISDN_AUDIO_SKB_LOCK(skb) = 1;
+ if ((ISDN_AUDIO_SKB_DLECOUNT(skb)) || (dev->drv[di]->DLEflag & (1 << channel))) {
+ char *p = skb->data;
+ unsigned long DLEmask = (1 << channel);
+
+ dflag = 0;
+ count_pull = count_put = 0;
+ while ((count_pull < skb->len) && (len > 0)) {
+ /* push every character but the last to the tty buffer directly */
+ if ( count_put )
+ tty_insert_flip_char(tty, last, TTY_NORMAL);
+ len--;
+ if (dev->drv[di]->DLEflag & DLEmask) {
+ last = DLE;
+ dev->drv[di]->DLEflag &= ~DLEmask;
+ } else {
+ last = *p;
+ if (last == DLE) {
+ dev->drv[di]->DLEflag |= DLEmask;
+ (ISDN_AUDIO_SKB_DLECOUNT(skb))--;
+ }
+ p++;
+ count_pull++;
+ }
+ count_put++;
+ }
+ if (count_pull >= skb->len)
+ dflag = 1;
+ } else {
+#endif
+ /* No DLE's in buff, so simply copy it */
+ dflag = 1;
+ if ((count_pull = skb->len) > len) {
+ count_pull = len;
+ dflag = 0;
+ }
+ count_put = count_pull;
+ if(count_put > 1)
+ tty_insert_flip_string(tty, skb->data, count_put - 1);
+ last = skb->data[count_put - 1];
+ len -= count_put;
+#ifdef CONFIG_ISDN_AUDIO
+ }
+#endif
+ count += count_put;
+ if (dflag) {
+ /* We got all the data in this buff.
+ * Now we can dequeue it.
+ */
+ if(cisco_hack)
+ tty_insert_flip_char(tty, last, 0xFF);
+ else
+ tty_insert_flip_char(tty, last, TTY_NORMAL);
+#ifdef CONFIG_ISDN_AUDIO
+ ISDN_AUDIO_SKB_LOCK(skb) = 0;
+#endif
+ skb = skb_dequeue(&dev->drv[di]->rpqueue[channel]);
+ dev_kfree_skb(skb);
+ } else {
+ tty_insert_flip_char(tty, last, TTY_NORMAL);
+ /* Not yet emptied this buff, so it
+ * must stay in the queue, for further calls
+ * but we pull off the data we got until now.
+ */
+ skb_pull(skb, count_pull);
+#ifdef CONFIG_ISDN_AUDIO
+ ISDN_AUDIO_SKB_LOCK(skb) = 0;
+#endif
+ }
+ dev->drv[di]->rcvcount[channel] -= count_put;
+ }
+ return count;
+}
+
+
+static inline int
+isdn_minor2drv(int minor)
+{
+ return (dev->drvmap[minor]);
+}
+
+static inline int
+isdn_minor2chan(int minor)
+{
+ return (dev->chanmap[minor]);
+}
+
+static char *
+isdn_statstr(void)
+{
+ static char istatbuf[2048];
+ char *p;
+ int i;
+
+ sprintf(istatbuf, "idmap:\t");
+ p = istatbuf + strlen(istatbuf);
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ sprintf(p, "%s ", (dev->drvmap[i] < 0) ? "-" : dev->drvid[dev->drvmap[i]]);
+ p = istatbuf + strlen(istatbuf);
+ }
+ sprintf(p, "\nchmap:\t");
+ p = istatbuf + strlen(istatbuf);
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ sprintf(p, "%d ", dev->chanmap[i]);
+ p = istatbuf + strlen(istatbuf);
+ }
+ sprintf(p, "\ndrmap:\t");
+ p = istatbuf + strlen(istatbuf);
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ sprintf(p, "%d ", dev->drvmap[i]);
+ p = istatbuf + strlen(istatbuf);
+ }
+ sprintf(p, "\nusage:\t");
+ p = istatbuf + strlen(istatbuf);
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ sprintf(p, "%d ", dev->usage[i]);
+ p = istatbuf + strlen(istatbuf);
+ }
+ sprintf(p, "\nflags:\t");
+ p = istatbuf + strlen(istatbuf);
+ for (i = 0; i < ISDN_MAX_DRIVERS; i++) {
+ if (dev->drv[i]) {
+ sprintf(p, "%ld ", dev->drv[i]->online);
+ p = istatbuf + strlen(istatbuf);
+ } else {
+ sprintf(p, "? ");
+ p = istatbuf + strlen(istatbuf);
+ }
+ }
+ sprintf(p, "\nphone:\t");
+ p = istatbuf + strlen(istatbuf);
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ sprintf(p, "%s ", dev->num[i]);
+ p = istatbuf + strlen(istatbuf);
+ }
+ sprintf(p, "\n");
+ return istatbuf;
+}
+
+/* Module interface-code */
+
+void
+isdn_info_update(void)
+{
+ infostruct *p = dev->infochain;
+
+ while (p) {
+ *(p->private) = 1;
+ p = (infostruct *) p->next;
+ }
+ wake_up_interruptible(&(dev->info_waitq));
+}
+
+static ssize_t
+isdn_read(struct file *file, char __user *buf, size_t count, loff_t * off)
+{
+ uint minor = iminor(file->f_path.dentry->d_inode);
+ int len = 0;
+ int drvidx;
+ int chidx;
+ int retval;
+ char *p;
+
+ lock_kernel();
+ if (minor == ISDN_MINOR_STATUS) {
+ if (!file->private_data) {
+ if (file->f_flags & O_NONBLOCK) {
+ retval = -EAGAIN;
+ goto out;
+ }
+ interruptible_sleep_on(&(dev->info_waitq));
+ }
+ p = isdn_statstr();
+ file->private_data = NULL;
+ if ((len = strlen(p)) <= count) {
+ if (copy_to_user(buf, p, len)) {
+ retval = -EFAULT;
+ goto out;
+ }
+ *off += len;
+ retval = len;
+ goto out;
+ }
+ retval = 0;
+ goto out;
+ }
+ if (!dev->drivers) {
+ retval = -ENODEV;
+ goto out;
+ }
+ if (minor <= ISDN_MINOR_BMAX) {
+ printk(KERN_WARNING "isdn_read minor %d obsolete!\n", minor);
+ drvidx = isdn_minor2drv(minor);
+ if (drvidx < 0) {
+ retval = -ENODEV;
+ goto out;
+ }
+ if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING)) {
+ retval = -ENODEV;
+ goto out;
+ }
+ chidx = isdn_minor2chan(minor);
+ if (!(p = kmalloc(count, GFP_KERNEL))) {
+ retval = -ENOMEM;
+ goto out;
+ }
+ len = isdn_readbchan(drvidx, chidx, p, NULL, count,
+ &dev->drv[drvidx]->rcv_waitq[chidx]);
+ *off += len;
+ if (copy_to_user(buf,p,len))
+ len = -EFAULT;
+ kfree(p);
+ retval = len;
+ goto out;
+ }
+ if (minor <= ISDN_MINOR_CTRLMAX) {
+ drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
+ if (drvidx < 0) {
+ retval = -ENODEV;
+ goto out;
+ }
+ if (!dev->drv[drvidx]->stavail) {
+ if (file->f_flags & O_NONBLOCK) {
+ retval = -EAGAIN;
+ goto out;
+ }
+ interruptible_sleep_on(&(dev->drv[drvidx]->st_waitq));
+ }
+ if (dev->drv[drvidx]->interface->readstat) {
+ if (count > dev->drv[drvidx]->stavail)
+ count = dev->drv[drvidx]->stavail;
+ len = dev->drv[drvidx]->interface->readstat(buf, count,
+ drvidx, isdn_minor2chan(minor - ISDN_MINOR_CTRL));
+ if (len < 0) {
+ retval = len;
+ goto out;
+ }
+ } else {
+ len = 0;
+ }
+ if (len)
+ dev->drv[drvidx]->stavail -= len;
+ else
+ dev->drv[drvidx]->stavail = 0;
+ *off += len;
+ retval = len;
+ goto out;
+ }
+#ifdef CONFIG_ISDN_PPP
+ if (minor <= ISDN_MINOR_PPPMAX) {
+ retval = isdn_ppp_read(minor - ISDN_MINOR_PPP, file, buf, count);
+ goto out;
+ }
+#endif
+ retval = -ENODEV;
+ out:
+ unlock_kernel();
+ return retval;
+}
+
+static ssize_t
+isdn_write(struct file *file, const char __user *buf, size_t count, loff_t * off)
+{
+ uint minor = iminor(file->f_path.dentry->d_inode);
+ int drvidx;
+ int chidx;
+ int retval;
+
+ if (minor == ISDN_MINOR_STATUS)
+ return -EPERM;
+ if (!dev->drivers)
+ return -ENODEV;
+
+ lock_kernel();
+ if (minor <= ISDN_MINOR_BMAX) {
+ printk(KERN_WARNING "isdn_write minor %d obsolete!\n", minor);
+ drvidx = isdn_minor2drv(minor);
+ if (drvidx < 0) {
+ retval = -ENODEV;
+ goto out;
+ }
+ if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING)) {
+ retval = -ENODEV;
+ goto out;
+ }
+ chidx = isdn_minor2chan(minor);
+ while ((retval = isdn_writebuf_stub(drvidx, chidx, buf, count)) == 0)
+ interruptible_sleep_on(&dev->drv[drvidx]->snd_waitq[chidx]);
+ goto out;
+ }
+ if (minor <= ISDN_MINOR_CTRLMAX) {
+ drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
+ if (drvidx < 0) {
+ retval = -ENODEV;
+ goto out;
+ }
+ /*
+ * We want to use the isdnctrl device to load the firmware
+ *
+ if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING))
+ return -ENODEV;
+ */
+ if (dev->drv[drvidx]->interface->writecmd)
+ retval = dev->drv[drvidx]->interface->
+ writecmd(buf, count, drvidx,
+ isdn_minor2chan(minor - ISDN_MINOR_CTRL));
+ else
+ retval = count;
+ goto out;
+ }
+#ifdef CONFIG_ISDN_PPP
+ if (minor <= ISDN_MINOR_PPPMAX) {
+ retval = isdn_ppp_write(minor - ISDN_MINOR_PPP, file, buf, count);
+ goto out;
+ }
+#endif
+ retval = -ENODEV;
+ out:
+ unlock_kernel();
+ return retval;
+}
+
+static unsigned int
+isdn_poll(struct file *file, poll_table * wait)
+{
+ unsigned int mask = 0;
+ unsigned int minor = iminor(file->f_path.dentry->d_inode);
+ int drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
+
+ lock_kernel();
+ if (minor == ISDN_MINOR_STATUS) {
+ poll_wait(file, &(dev->info_waitq), wait);
+ /* mask = POLLOUT | POLLWRNORM; */
+ if (file->private_data) {
+ mask |= POLLIN | POLLRDNORM;
+ }
+ goto out;
+ }
+ if (minor >= ISDN_MINOR_CTRL && minor <= ISDN_MINOR_CTRLMAX) {
+ if (drvidx < 0) {
+ /* driver deregistered while file open */
+ mask = POLLHUP;
+ goto out;
+ }
+ poll_wait(file, &(dev->drv[drvidx]->st_waitq), wait);
+ mask = POLLOUT | POLLWRNORM;
+ if (dev->drv[drvidx]->stavail) {
+ mask |= POLLIN | POLLRDNORM;
+ }
+ goto out;
+ }
+#ifdef CONFIG_ISDN_PPP
+ if (minor <= ISDN_MINOR_PPPMAX) {
+ mask = isdn_ppp_poll(file, wait);
+ goto out;
+ }
+#endif
+ mask = POLLERR;
+ out:
+ unlock_kernel();
+ return mask;
+}
+
+
+static int
+isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
+{
+ uint minor = iminor(inode);
+ isdn_ctrl c;
+ int drvidx;
+ int chidx;
+ int ret;
+ int i;
+ char __user *p;
+ char *s;
+ union iocpar {
+ char name[10];
+ char bname[22];
+ isdn_ioctl_struct iocts;
+ isdn_net_ioctl_phone phone;
+ isdn_net_ioctl_cfg cfg;
+ } iocpar;
+ void __user *argp = (void __user *)arg;
+
+#define name iocpar.name
+#define bname iocpar.bname
+#define iocts iocpar.iocts
+#define phone iocpar.phone
+#define cfg iocpar.cfg
+
+ if (minor == ISDN_MINOR_STATUS) {
+ switch (cmd) {
+ case IIOCGETDVR:
+ return (TTY_DV +
+ (NET_DV << 8) +
+ (INF_DV << 16));
+ case IIOCGETCPS:
+ if (arg) {
+ ulong __user *p = argp;
+ int i;
+ if (!access_ok(VERIFY_WRITE, p,
+ sizeof(ulong) * ISDN_MAX_CHANNELS * 2))
+ return -EFAULT;
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ put_user(dev->ibytes[i], p++);
+ put_user(dev->obytes[i], p++);
+ }
+ return 0;
+ } else
+ return -EINVAL;
+ break;
+#ifdef CONFIG_NETDEVICES
+ case IIOCNETGPN:
+ /* Get peer phone number of a connected
+ * isdn network interface */
+ if (arg) {
+ if (copy_from_user(&phone, argp, sizeof(phone)))
+ return -EFAULT;
+ return isdn_net_getpeer(&phone, argp);
+ } else
+ return -EINVAL;
+#endif
+ default:
+ return -EINVAL;
+ }
+ }
+ if (!dev->drivers)
+ return -ENODEV;
+ if (minor <= ISDN_MINOR_BMAX) {
+ drvidx = isdn_minor2drv(minor);
+ if (drvidx < 0)
+ return -ENODEV;
+ chidx = isdn_minor2chan(minor);
+ if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING))
+ return -ENODEV;
+ return 0;
+ }
+ if (minor <= ISDN_MINOR_CTRLMAX) {
+/*
+ * isdn net devices manage lots of configuration variables as linked lists.
+ * Those lists must only be manipulated from user space. Some of the ioctl's
+ * service routines access user space and are not atomic. Therefor, ioctl's
+ * manipulating the lists and ioctl's sleeping while accessing the lists
+ * are serialized by means of a semaphore.
+ */
+ switch (cmd) {
+ case IIOCNETDWRSET:
+ printk(KERN_INFO "INFO: ISDN_DW_ABC_EXTENSION not enabled\n");
+ return(-EINVAL);
+ case IIOCNETLCR:
+ printk(KERN_INFO "INFO: ISDN_ABC_LCR_SUPPORT not enabled\n");
+ return -ENODEV;
+#ifdef CONFIG_NETDEVICES
+ case IIOCNETAIF:
+ /* Add a network-interface */
+ if (arg) {
+ if (copy_from_user(name, argp, sizeof(name)))
+ return -EFAULT;
+ s = name;
+ } else {
+ s = NULL;
+ }
+ ret = mutex_lock_interruptible(&dev->mtx);
+ if( ret ) return ret;
+ if ((s = isdn_net_new(s, NULL))) {
+ if (copy_to_user(argp, s, strlen(s) + 1)){
+ ret = -EFAULT;
+ } else {
+ ret = 0;
+ }
+ } else
+ ret = -ENODEV;
+ mutex_unlock(&dev->mtx);
+ return ret;
+ case IIOCNETASL:
+ /* Add a slave to a network-interface */
+ if (arg) {
+ if (copy_from_user(bname, argp, sizeof(bname) - 1))
+ return -EFAULT;
+ } else
+ return -EINVAL;
+ ret = mutex_lock_interruptible(&dev->mtx);
+ if( ret ) return ret;
+ if ((s = isdn_net_newslave(bname))) {
+ if (copy_to_user(argp, s, strlen(s) + 1)){
+ ret = -EFAULT;
+ } else {
+ ret = 0;
+ }
+ } else
+ ret = -ENODEV;
+ mutex_unlock(&dev->mtx);
+ return ret;
+ case IIOCNETDIF:
+ /* Delete a network-interface */
+ if (arg) {
+ if (copy_from_user(name, argp, sizeof(name)))
+ return -EFAULT;
+ ret = mutex_lock_interruptible(&dev->mtx);
+ if( ret ) return ret;
+ ret = isdn_net_rm(name);
+ mutex_unlock(&dev->mtx);
+ return ret;
+ } else
+ return -EINVAL;
+ case IIOCNETSCF:
+ /* Set configurable parameters of a network-interface */
+ if (arg) {
+ if (copy_from_user(&cfg, argp, sizeof(cfg)))
+ return -EFAULT;
+ return isdn_net_setcfg(&cfg);
+ } else
+ return -EINVAL;
+ case IIOCNETGCF:
+ /* Get configurable parameters of a network-interface */
+ if (arg) {
+ if (copy_from_user(&cfg, argp, sizeof(cfg)))
+ return -EFAULT;
+ if (!(ret = isdn_net_getcfg(&cfg))) {
+ if (copy_to_user(argp, &cfg, sizeof(cfg)))
+ return -EFAULT;
+ }
+ return ret;
+ } else
+ return -EINVAL;
+ case IIOCNETANM:
+ /* Add a phone-number to a network-interface */
+ if (arg) {
+ if (copy_from_user(&phone, argp, sizeof(phone)))
+ return -EFAULT;
+ ret = mutex_lock_interruptible(&dev->mtx);
+ if( ret ) return ret;
+ ret = isdn_net_addphone(&phone);
+ mutex_unlock(&dev->mtx);
+ return ret;
+ } else
+ return -EINVAL;
+ case IIOCNETGNM:
+ /* Get list of phone-numbers of a network-interface */
+ if (arg) {
+ if (copy_from_user(&phone, argp, sizeof(phone)))
+ return -EFAULT;
+ ret = mutex_lock_interruptible(&dev->mtx);
+ if( ret ) return ret;
+ ret = isdn_net_getphones(&phone, argp);
+ mutex_unlock(&dev->mtx);
+ return ret;
+ } else
+ return -EINVAL;
+ case IIOCNETDNM:
+ /* Delete a phone-number of a network-interface */
+ if (arg) {
+ if (copy_from_user(&phone, argp, sizeof(phone)))
+ return -EFAULT;
+ ret = mutex_lock_interruptible(&dev->mtx);
+ if( ret ) return ret;
+ ret = isdn_net_delphone(&phone);
+ mutex_unlock(&dev->mtx);
+ return ret;
+ } else
+ return -EINVAL;
+ case IIOCNETDIL:
+ /* Force dialing of a network-interface */
+ if (arg) {
+ if (copy_from_user(name, argp, sizeof(name)))
+ return -EFAULT;
+ return isdn_net_force_dial(name);
+ } else
+ return -EINVAL;
+#ifdef CONFIG_ISDN_PPP
+ case IIOCNETALN:
+ if (!arg)
+ return -EINVAL;
+ if (copy_from_user(name, argp, sizeof(name)))
+ return -EFAULT;
+ return isdn_ppp_dial_slave(name);
+ case IIOCNETDLN:
+ if (!arg)
+ return -EINVAL;
+ if (copy_from_user(name, argp, sizeof(name)))
+ return -EFAULT;
+ return isdn_ppp_hangup_slave(name);
+#endif
+ case IIOCNETHUP:
+ /* Force hangup of a network-interface */
+ if (!arg)
+ return -EINVAL;
+ if (copy_from_user(name, argp, sizeof(name)))
+ return -EFAULT;
+ return isdn_net_force_hangup(name);
+ break;
+#endif /* CONFIG_NETDEVICES */
+ case IIOCSETVER:
+ dev->net_verbose = arg;
+ printk(KERN_INFO "isdn: Verbose-Level is %d\n", dev->net_verbose);
+ return 0;
+ case IIOCSETGST:
+ if (arg)
+ dev->global_flags |= ISDN_GLOBAL_STOPPED;
+ else
+ dev->global_flags &= ~ISDN_GLOBAL_STOPPED;
+ printk(KERN_INFO "isdn: Global Mode %s\n",
+ (dev->global_flags & ISDN_GLOBAL_STOPPED) ? "stopped" : "running");
+ return 0;
+ case IIOCSETBRJ:
+ drvidx = -1;
+ if (arg) {
+ int i;
+ char *p;
+ if (copy_from_user(&iocts, argp,
+ sizeof(isdn_ioctl_struct)))
+ return -EFAULT;
+ iocts.drvid[sizeof(iocts.drvid)-1] = 0;
+ if (strlen(iocts.drvid)) {
+ if ((p = strchr(iocts.drvid, ',')))
+ *p = 0;
+ drvidx = -1;
+ for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+ if (!(strcmp(dev->drvid[i], iocts.drvid))) {
+ drvidx = i;
+ break;
+ }
+ }
+ }
+ if (drvidx == -1)
+ return -ENODEV;
+ if (iocts.arg)
+ dev->drv[drvidx]->flags |= DRV_FLAG_REJBUS;
+ else
+ dev->drv[drvidx]->flags &= ~DRV_FLAG_REJBUS;
+ return 0;
+ case IIOCSIGPRF:
+ dev->profd = current;
+ return 0;
+ break;
+ case IIOCGETPRF:
+ /* Get all Modem-Profiles */
+ if (arg) {
+ char __user *p = argp;
+ int i;
+
+ if (!access_ok(VERIFY_WRITE, argp,
+ (ISDN_MODEM_NUMREG + ISDN_MSNLEN + ISDN_LMSNLEN)
+ * ISDN_MAX_CHANNELS))
+ return -EFAULT;
+
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ if (copy_to_user(p, dev->mdm.info[i].emu.profile,
+ ISDN_MODEM_NUMREG))
+ return -EFAULT;
+ p += ISDN_MODEM_NUMREG;
+ if (copy_to_user(p, dev->mdm.info[i].emu.pmsn, ISDN_MSNLEN))
+ return -EFAULT;
+ p += ISDN_MSNLEN;
+ if (copy_to_user(p, dev->mdm.info[i].emu.plmsn, ISDN_LMSNLEN))
+ return -EFAULT;
+ p += ISDN_LMSNLEN;
+ }
+ return (ISDN_MODEM_NUMREG + ISDN_MSNLEN + ISDN_LMSNLEN) * ISDN_MAX_CHANNELS;
+ } else
+ return -EINVAL;
+ break;
+ case IIOCSETPRF:
+ /* Set all Modem-Profiles */
+ if (arg) {
+ char __user *p = argp;
+ int i;
+
+ if (!access_ok(VERIFY_READ, argp,
+ (ISDN_MODEM_NUMREG + ISDN_MSNLEN + ISDN_LMSNLEN)
+ * ISDN_MAX_CHANNELS))
+ return -EFAULT;
+
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ if (copy_from_user(dev->mdm.info[i].emu.profile, p,
+ ISDN_MODEM_NUMREG))
+ return -EFAULT;
+ p += ISDN_MODEM_NUMREG;
+ if (copy_from_user(dev->mdm.info[i].emu.plmsn, p, ISDN_LMSNLEN))
+ return -EFAULT;
+ p += ISDN_LMSNLEN;
+ if (copy_from_user(dev->mdm.info[i].emu.pmsn, p, ISDN_MSNLEN))
+ return -EFAULT;
+ p += ISDN_MSNLEN;
+ }
+ return 0;
+ } else
+ return -EINVAL;
+ break;
+ case IIOCSETMAP:
+ case IIOCGETMAP:
+ /* Set/Get MSN->EAZ-Mapping for a driver */
+ if (arg) {
+
+ if (copy_from_user(&iocts, argp,
+ sizeof(isdn_ioctl_struct)))
+ return -EFAULT;
+ iocts.drvid[sizeof(iocts.drvid)-1] = 0;
+ if (strlen(iocts.drvid)) {
+ drvidx = -1;
+ for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+ if (!(strcmp(dev->drvid[i], iocts.drvid))) {
+ drvidx = i;
+ break;
+ }
+ } else
+ drvidx = 0;
+ if (drvidx == -1)
+ return -ENODEV;
+ if (cmd == IIOCSETMAP) {
+ int loop = 1;
+
+ p = (char __user *) iocts.arg;
+ i = 0;
+ while (loop) {
+ int j = 0;
+
+ while (1) {
+ if (!access_ok(VERIFY_READ, p, 1))
+ return -EFAULT;
+ get_user(bname[j], p++);
+ switch (bname[j]) {
+ case '\0':
+ loop = 0;
+ /* Fall through */
+ case ',':
+ bname[j] = '\0';
+ strcpy(dev->drv[drvidx]->msn2eaz[i], bname);
+ j = ISDN_MSNLEN;
+ break;
+ default:
+ j++;
+ }
+ if (j >= ISDN_MSNLEN)
+ break;
+ }
+ if (++i > 9)
+ break;
+ }
+ } else {
+ p = (char __user *) iocts.arg;
+ for (i = 0; i < 10; i++) {
+ snprintf(bname, sizeof(bname), "%s%s",
+ strlen(dev->drv[drvidx]->msn2eaz[i]) ?
+ dev->drv[drvidx]->msn2eaz[i] : "_",
+ (i < 9) ? "," : "\0");
+ if (copy_to_user(p, bname, strlen(bname) + 1))
+ return -EFAULT;
+ p += strlen(bname);
+ }
+ }
+ return 0;
+ } else
+ return -EINVAL;
+ case IIOCDBGVAR:
+ if (arg) {
+ if (copy_to_user(argp, &dev, sizeof(ulong)))
+ return -EFAULT;
+ return 0;
+ } else
+ return -EINVAL;
+ break;
+ default:
+ if ((cmd & IIOCDRVCTL) == IIOCDRVCTL)
+ cmd = ((cmd >> _IOC_NRSHIFT) & _IOC_NRMASK) & ISDN_DRVIOCTL_MASK;
+ else
+ return -EINVAL;
+ if (arg) {
+ int i;
+ char *p;
+ if (copy_from_user(&iocts, argp, sizeof(isdn_ioctl_struct)))
+ return -EFAULT;
+ iocts.drvid[sizeof(iocts.drvid)-1] = 0;
+ if (strlen(iocts.drvid)) {
+ if ((p = strchr(iocts.drvid, ',')))
+ *p = 0;
+ drvidx = -1;
+ for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+ if (!(strcmp(dev->drvid[i], iocts.drvid))) {
+ drvidx = i;
+ break;
+ }
+ } else
+ drvidx = 0;
+ if (drvidx == -1)
+ return -ENODEV;
+ if (!access_ok(VERIFY_WRITE, argp,
+ sizeof(isdn_ioctl_struct)))
+ return -EFAULT;
+ c.driver = drvidx;
+ c.command = ISDN_CMD_IOCTL;
+ c.arg = cmd;
+ memcpy(c.parm.num, &iocts.arg, sizeof(ulong));
+ ret = isdn_command(&c);
+ memcpy(&iocts.arg, c.parm.num, sizeof(ulong));
+ if (copy_to_user(argp, &iocts, sizeof(isdn_ioctl_struct)))
+ return -EFAULT;
+ return ret;
+ } else
+ return -EINVAL;
+ }
+ }
+#ifdef CONFIG_ISDN_PPP
+ if (minor <= ISDN_MINOR_PPPMAX)
+ return (isdn_ppp_ioctl(minor - ISDN_MINOR_PPP, file, cmd, arg));
+#endif
+ return -ENODEV;
+
+#undef name
+#undef bname
+#undef iocts
+#undef phone
+#undef cfg
+}
+
+/*
+ * Open the device code.
+ */
+static int
+isdn_open(struct inode *ino, struct file *filep)
+{
+ uint minor = iminor(ino);
+ int drvidx;
+ int chidx;
+ int retval = -ENODEV;
+
+ lock_kernel();
+ if (minor == ISDN_MINOR_STATUS) {
+ infostruct *p;
+
+ if ((p = kmalloc(sizeof(infostruct), GFP_KERNEL))) {
+ p->next = (char *) dev->infochain;
+ p->private = (char *) &(filep->private_data);
+ dev->infochain = p;
+ /* At opening we allow a single update */
+ filep->private_data = (char *) 1;
+ retval = 0;
+ goto out;
+ } else {
+ retval = -ENOMEM;
+ goto out;
+ }
+ }
+ if (!dev->channels)
+ goto out;
+ if (minor <= ISDN_MINOR_BMAX) {
+ printk(KERN_WARNING "isdn_open minor %d obsolete!\n", minor);
+ drvidx = isdn_minor2drv(minor);
+ if (drvidx < 0)
+ goto out;
+ chidx = isdn_minor2chan(minor);
+ if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING))
+ goto out;
+ if (!(dev->drv[drvidx]->online & (1 << chidx)))
+ goto out;
+ isdn_lock_drivers();
+ retval = 0;
+ goto out;
+ }
+ if (minor <= ISDN_MINOR_CTRLMAX) {
+ drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL);
+ if (drvidx < 0)
+ goto out;
+ isdn_lock_drivers();
+ retval = 0;
+ goto out;
+ }
+#ifdef CONFIG_ISDN_PPP
+ if (minor <= ISDN_MINOR_PPPMAX) {
+ retval = isdn_ppp_open(minor - ISDN_MINOR_PPP, filep);
+ if (retval == 0)
+ isdn_lock_drivers();
+ goto out;
+ }
+#endif
+ out:
+ nonseekable_open(ino, filep);
+ unlock_kernel();
+ return retval;
+}
+
+static int
+isdn_close(struct inode *ino, struct file *filep)
+{
+ uint minor = iminor(ino);
+
+ lock_kernel();
+ if (minor == ISDN_MINOR_STATUS) {
+ infostruct *p = dev->infochain;
+ infostruct *q = NULL;
+
+ while (p) {
+ if (p->private == (char *) &(filep->private_data)) {
+ if (q)
+ q->next = p->next;
+ else
+ dev->infochain = (infostruct *) (p->next);
+ kfree(p);
+ goto out;
+ }
+ q = p;
+ p = (infostruct *) (p->next);
+ }
+ printk(KERN_WARNING "isdn: No private data while closing isdnctrl\n");
+ goto out;
+ }
+ isdn_unlock_drivers();
+ if (minor <= ISDN_MINOR_BMAX)
+ goto out;
+ if (minor <= ISDN_MINOR_CTRLMAX) {
+ if (dev->profd == current)
+ dev->profd = NULL;
+ goto out;
+ }
+#ifdef CONFIG_ISDN_PPP
+ if (minor <= ISDN_MINOR_PPPMAX)
+ isdn_ppp_release(minor - ISDN_MINOR_PPP, filep);
+#endif
+
+ out:
+ unlock_kernel();
+ return 0;
+}
+
+static const struct file_operations isdn_fops =
+{
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = isdn_read,
+ .write = isdn_write,
+ .poll = isdn_poll,
+ .ioctl = isdn_ioctl,
+ .open = isdn_open,
+ .release = isdn_close,
+};
+
+char *
+isdn_map_eaz2msn(char *msn, int di)
+{
+ isdn_driver_t *this = dev->drv[di];
+ int i;
+
+ if (strlen(msn) == 1) {
+ i = msn[0] - '0';
+ if ((i >= 0) && (i <= 9))
+ if (strlen(this->msn2eaz[i]))
+ return (this->msn2eaz[i]);
+ }
+ return (msn);
+}
+
+/*
+ * Find an unused ISDN-channel, whose feature-flags match the
+ * given L2- and L3-protocols.
+ */
+#define L2V (~(ISDN_FEATURE_L2_V11096|ISDN_FEATURE_L2_V11019|ISDN_FEATURE_L2_V11038))
+
+/*
+ * This function must be called with holding the dev->lock.
+ */
+int
+isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev
+ ,int pre_chan, char *msn)
+{
+ int i;
+ ulong features;
+ ulong vfeatures;
+
+ features = ((1 << l2_proto) | (0x10000 << l3_proto));
+ vfeatures = (((1 << l2_proto) | (0x10000 << l3_proto)) &
+ ~(ISDN_FEATURE_L2_V11096|ISDN_FEATURE_L2_V11019|ISDN_FEATURE_L2_V11038));
+ /* If Layer-2 protocol is V.110, accept drivers with
+ * transparent feature even if these don't support V.110
+ * because we can emulate this in linklevel.
+ */
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+ if (USG_NONE(dev->usage[i]) &&
+ (dev->drvmap[i] != -1)) {
+ int d = dev->drvmap[i];
+ if ((dev->usage[i] & ISDN_USAGE_EXCLUSIVE) &&
+ ((pre_dev != d) || (pre_chan != dev->chanmap[i])))
+ continue;
+ if (!strcmp(isdn_map_eaz2msn(msn, d), "-"))
+ continue;
+ if (dev->usage[i] & ISDN_USAGE_DISABLED)
+ continue; /* usage not allowed */
+ if (dev->drv[d]->flags & DRV_FLAG_RUNNING) {
+ if (((dev->drv[d]->interface->features & features) == features) ||
+ (((dev->drv[d]->interface->features & vfeatures) == vfeatures) &&
+ (dev->drv[d]->interface->features & ISDN_FEATURE_L2_TRANS))) {
+ if ((pre_dev < 0) || (pre_chan < 0)) {
+ dev->usage[i] &= ISDN_USAGE_EXCLUSIVE;
+ dev->usage[i] |= usage;
+ isdn_info_update();
+ return i;
+ } else {
+ if ((pre_dev == d) && (pre_chan == dev->chanmap[i])) {
+ dev->usage[i] &= ISDN_USAGE_EXCLUSIVE;
+ dev->usage[i] |= usage;
+ isdn_info_update();
+ return i;
+ }
+ }
+ }
+ }
+ }
+ return -1;
+}
+
+/*
+ * Set state of ISDN-channel to 'unused'
+ */
+void
+isdn_free_channel(int di, int ch, int usage)
+{
+ int i;
+
+ if ((di < 0) || (ch < 0)) {
+ printk(KERN_WARNING "%s: called with invalid drv(%d) or channel(%d)\n",
+ __func__, di, ch);
+ return;
+ }
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+ if (((!usage) || ((dev->usage[i] & ISDN_USAGE_MASK) == usage)) &&
+ (dev->drvmap[i] == di) &&
+ (dev->chanmap[i] == ch)) {
+ dev->usage[i] &= (ISDN_USAGE_NONE | ISDN_USAGE_EXCLUSIVE);
+ strcpy(dev->num[i], "???");
+ dev->ibytes[i] = 0;
+ dev->obytes[i] = 0;
+// 20.10.99 JIM, try to reinitialize v110 !
+ dev->v110emu[i] = 0;
+ atomic_set(&(dev->v110use[i]), 0);
+ isdn_v110_close(dev->v110[i]);
+ dev->v110[i] = NULL;
+// 20.10.99 JIM, try to reinitialize v110 !
+ isdn_info_update();
+ if (dev->drv[di])
+ skb_queue_purge(&dev->drv[di]->rpqueue[ch]);
+ }
+}
+
+/*
+ * Cancel Exclusive-Flag for ISDN-channel
+ */
+void
+isdn_unexclusive_channel(int di, int ch)
+{
+ int i;
+
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+ if ((dev->drvmap[i] == di) &&
+ (dev->chanmap[i] == ch)) {
+ dev->usage[i] &= ~ISDN_USAGE_EXCLUSIVE;
+ isdn_info_update();
+ return;
+ }
+}
+
+/*
+ * writebuf replacement for SKB_ABLE drivers
+ */
+static int
+isdn_writebuf_stub(int drvidx, int chan, const u_char __user * buf, int len)
+{
+ int ret;
+ int hl = dev->drv[drvidx]->interface->hl_hdrlen;
+ struct sk_buff *skb = alloc_skb(hl + len, GFP_ATOMIC);
+
+ if (!skb)
+ return -ENOMEM;
+ skb_reserve(skb, hl);
+ if (copy_from_user(skb_put(skb, len), buf, len)) {
+ dev_kfree_skb(skb);
+ return -EFAULT;
+ }
+ ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, 1, skb);
+ if (ret <= 0)
+ dev_kfree_skb(skb);
+ if (ret > 0)
+ dev->obytes[isdn_dc2minor(drvidx, chan)] += ret;
+ return ret;
+}
+
+/*
+ * Return: length of data on success, -ERRcode on failure.
+ */
+int
+isdn_writebuf_skb_stub(int drvidx, int chan, int ack, struct sk_buff *skb)
+{
+ int ret;
+ struct sk_buff *nskb = NULL;
+ int v110_ret = skb->len;
+ int idx = isdn_dc2minor(drvidx, chan);
+
+ if (dev->v110[idx]) {
+ atomic_inc(&dev->v110use[idx]);
+ nskb = isdn_v110_encode(dev->v110[idx], skb);
+ atomic_dec(&dev->v110use[idx]);
+ if (!nskb)
+ return 0;
+ v110_ret = *((int *)nskb->data);
+ skb_pull(nskb, sizeof(int));
+ if (!nskb->len) {
+ dev_kfree_skb(nskb);
+ return v110_ret;
+ }
+ /* V.110 must always be acknowledged */
+ ack = 1;
+ ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, nskb);
+ } else {
+ int hl = dev->drv[drvidx]->interface->hl_hdrlen;
+
+ if( skb_headroom(skb) < hl ){
+ /*
+ * This should only occur when new HL driver with
+ * increased hl_hdrlen was loaded after netdevice
+ * was created and connected to the new driver.
+ *
+ * The V.110 branch (re-allocates on its own) does
+ * not need this
+ */
+ struct sk_buff * skb_tmp;
+
+ skb_tmp = skb_realloc_headroom(skb, hl);
+ printk(KERN_DEBUG "isdn_writebuf_skb_stub: reallocating headroom%s\n", skb_tmp ? "" : " failed");
+ if (!skb_tmp) return -ENOMEM; /* 0 better? */
+ ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, skb_tmp);
+ if( ret > 0 ){
+ dev_kfree_skb(skb);
+ } else {
+ dev_kfree_skb(skb_tmp);
+ }
+ } else {
+ ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, skb);
+ }
+ }
+ if (ret > 0) {
+ dev->obytes[idx] += ret;
+ if (dev->v110[idx]) {
+ atomic_inc(&dev->v110use[idx]);
+ dev->v110[idx]->skbuser++;
+ atomic_dec(&dev->v110use[idx]);
+ /* For V.110 return unencoded data length */
+ ret = v110_ret;
+ /* if the complete frame was send we free the skb;
+ if not upper function will requeue the skb */
+ if (ret == skb->len)
+ dev_kfree_skb(skb);
+ }
+ } else
+ if (dev->v110[idx])
+ dev_kfree_skb(nskb);
+ return ret;
+}
+
+static int
+isdn_add_channels(isdn_driver_t *d, int drvidx, int n, int adding)
+{
+ int j, k, m;
+
+ init_waitqueue_head(&d->st_waitq);
+ if (d->flags & DRV_FLAG_RUNNING)
+ return -1;
+ if (n < 1) return 0;
+
+ m = (adding) ? d->channels + n : n;
+
+ if (dev->channels + n > ISDN_MAX_CHANNELS) {
+ printk(KERN_WARNING "register_isdn: Max. %d channels supported\n",
+ ISDN_MAX_CHANNELS);
+ return -1;
+ }
+
+ if ((adding) && (d->rcverr))
+ kfree(d->rcverr);
+ if (!(d->rcverr = kzalloc(sizeof(int) * m, GFP_ATOMIC))) {
+ printk(KERN_WARNING "register_isdn: Could not alloc rcverr\n");
+ return -1;
+ }
+
+ if ((adding) && (d->rcvcount))
+ kfree(d->rcvcount);
+ if (!(d->rcvcount = kzalloc(sizeof(int) * m, GFP_ATOMIC))) {
+ printk(KERN_WARNING "register_isdn: Could not alloc rcvcount\n");
+ if (!adding)
+ kfree(d->rcverr);
+ return -1;
+ }
+
+ if ((adding) && (d->rpqueue)) {
+ for (j = 0; j < d->channels; j++)
+ skb_queue_purge(&d->rpqueue[j]);
+ kfree(d->rpqueue);
+ }
+ if (!(d->rpqueue = kmalloc(sizeof(struct sk_buff_head) * m, GFP_ATOMIC))) {
+ printk(KERN_WARNING "register_isdn: Could not alloc rpqueue\n");
+ if (!adding) {
+ kfree(d->rcvcount);
+ kfree(d->rcverr);
+ }
+ return -1;
+ }
+ for (j = 0; j < m; j++) {
+ skb_queue_head_init(&d->rpqueue[j]);
+ }
+
+ if ((adding) && (d->rcv_waitq))
+ kfree(d->rcv_waitq);
+ d->rcv_waitq = kmalloc(sizeof(wait_queue_head_t) * 2 * m, GFP_ATOMIC);
+ if (!d->rcv_waitq) {
+ printk(KERN_WARNING "register_isdn: Could not alloc rcv_waitq\n");
+ if (!adding) {
+ kfree(d->rpqueue);
+ kfree(d->rcvcount);
+ kfree(d->rcverr);
+ }
+ return -1;
+ }
+ d->snd_waitq = d->rcv_waitq + m;
+ for (j = 0; j < m; j++) {
+ init_waitqueue_head(&d->rcv_waitq[j]);
+ init_waitqueue_head(&d->snd_waitq[j]);
+ }
+
+ dev->channels += n;
+ for (j = d->channels; j < m; j++)
+ for (k = 0; k < ISDN_MAX_CHANNELS; k++)
+ if (dev->chanmap[k] < 0) {
+ dev->chanmap[k] = j;
+ dev->drvmap[k] = drvidx;
+ break;
+ }
+ d->channels = m;
+ return 0;
+}
+
+/*
+ * Low-level-driver registration
+ */
+
+static void
+set_global_features(void)
+{
+ int drvidx;
+
+ dev->global_features = 0;
+ for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++) {
+ if (!dev->drv[drvidx])
+ continue;
+ if (dev->drv[drvidx]->interface)
+ dev->global_features |= dev->drv[drvidx]->interface->features;
+ }
+}
+
+#ifdef CONFIG_ISDN_DIVERSION
+
+static char *map_drvname(int di)
+{
+ if ((di < 0) || (di >= ISDN_MAX_DRIVERS))
+ return(NULL);
+ return(dev->drvid[di]); /* driver name */
+} /* map_drvname */
+
+static int map_namedrv(char *id)
+{ int i;
+
+ for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+ { if (!strcmp(dev->drvid[i],id))
+ return(i);
+ }
+ return(-1);
+} /* map_namedrv */
+
+int DIVERT_REG_NAME(isdn_divert_if *i_div)
+{
+ if (i_div->if_magic != DIVERT_IF_MAGIC)
+ return(DIVERT_VER_ERR);
+ switch (i_div->cmd)
+ {
+ case DIVERT_CMD_REL:
+ if (divert_if != i_div)
+ return(DIVERT_REL_ERR);
+ divert_if = NULL; /* free interface */
+ return(DIVERT_NO_ERR);
+
+ case DIVERT_CMD_REG:
+ if (divert_if)
+ return(DIVERT_REG_ERR);
+ i_div->ll_cmd = isdn_command; /* set command function */
+ i_div->drv_to_name = map_drvname;
+ i_div->name_to_drv = map_namedrv;
+ divert_if = i_div; /* remember interface */
+ return(DIVERT_NO_ERR);
+
+ default:
+ return(DIVERT_CMD_ERR);
+ }
+} /* DIVERT_REG_NAME */
+
+EXPORT_SYMBOL(DIVERT_REG_NAME);
+
+#endif /* CONFIG_ISDN_DIVERSION */
+
+
+EXPORT_SYMBOL(register_isdn);
+#ifdef CONFIG_ISDN_PPP
+EXPORT_SYMBOL(isdn_ppp_register_compressor);
+EXPORT_SYMBOL(isdn_ppp_unregister_compressor);
+#endif
+
+int
+register_isdn(isdn_if * i)
+{
+ isdn_driver_t *d;
+ int j;
+ ulong flags;
+ int drvidx;
+
+ if (dev->drivers >= ISDN_MAX_DRIVERS) {
+ printk(KERN_WARNING "register_isdn: Max. %d drivers supported\n",
+ ISDN_MAX_DRIVERS);
+ return 0;
+ }
+ if (!i->writebuf_skb) {
+ printk(KERN_WARNING "register_isdn: No write routine given.\n");
+ return 0;
+ }
+ if (!(d = kzalloc(sizeof(isdn_driver_t), GFP_KERNEL))) {
+ printk(KERN_WARNING "register_isdn: Could not alloc driver-struct\n");
+ return 0;
+ }
+
+ d->maxbufsize = i->maxbufsize;
+ d->pktcount = 0;
+ d->stavail = 0;
+ d->flags = DRV_FLAG_LOADED;
+ d->online = 0;
+ d->interface = i;
+ d->channels = 0;
+ spin_lock_irqsave(&dev->lock, flags);
+ for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++)
+ if (!dev->drv[drvidx])
+ break;
+ if (isdn_add_channels(d, drvidx, i->channels, 0)) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ kfree(d);
+ return 0;
+ }
+ i->channels = drvidx;
+ i->rcvcallb_skb = isdn_receive_skb_callback;
+ i->statcallb = isdn_status_callback;
+ if (!strlen(i->id))
+ sprintf(i->id, "line%d", drvidx);
+ for (j = 0; j < drvidx; j++)
+ if (!strcmp(i->id, dev->drvid[j]))
+ sprintf(i->id, "line%d", drvidx);
+ dev->drv[drvidx] = d;
+ strcpy(dev->drvid[drvidx], i->id);
+ isdn_info_update();
+ dev->drivers++;
+ set_global_features();
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return 1;
+}
+
+/*
+ *****************************************************************************
+ * And now the modules code.
+ *****************************************************************************
+ */
+
+static char *
+isdn_getrev(const char *revision)
+{
+ char *rev;
+ char *p;
+
+ if ((p = strchr(revision, ':'))) {
+ rev = p + 2;
+ p = strchr(rev, '$');
+ *--p = 0;
+ } else
+ rev = "???";
+ return rev;
+}
+
+/*
+ * Allocate and initialize all data, register modem-devices
+ */
+static int __init isdn_init(void)
+{
+ int i;
+ char tmprev[50];
+
+ if (!(dev = vmalloc(sizeof(isdn_dev)))) {
+ printk(KERN_WARNING "isdn: Could not allocate device-struct.\n");
+ return -EIO;
+ }
+ memset((char *) dev, 0, sizeof(isdn_dev));
+ init_timer(&dev->timer);
+ dev->timer.function = isdn_timer_funct;
+ spin_lock_init(&dev->lock);
+ spin_lock_init(&dev->timerlock);
+#ifdef MODULE
+ dev->owner = THIS_MODULE;
+#endif
+ mutex_init(&dev->mtx);
+ init_waitqueue_head(&dev->info_waitq);
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ dev->drvmap[i] = -1;
+ dev->chanmap[i] = -1;
+ dev->m_idx[i] = -1;
+ strcpy(dev->num[i], "???");
+ init_waitqueue_head(&dev->mdm.info[i].open_wait);
+ init_waitqueue_head(&dev->mdm.info[i].close_wait);
+ }
+ if (register_chrdev(ISDN_MAJOR, "isdn", &isdn_fops)) {
+ printk(KERN_WARNING "isdn: Could not register control devices\n");
+ vfree(dev);
+ return -EIO;
+ }
+ if ((isdn_tty_modem_init()) < 0) {
+ printk(KERN_WARNING "isdn: Could not register tty devices\n");
+ vfree(dev);
+ unregister_chrdev(ISDN_MAJOR, "isdn");
+ return -EIO;
+ }
+#ifdef CONFIG_ISDN_PPP
+ if (isdn_ppp_init() < 0) {
+ printk(KERN_WARNING "isdn: Could not create PPP-device-structs\n");
+ isdn_tty_exit();
+ unregister_chrdev(ISDN_MAJOR, "isdn");
+ vfree(dev);
+ return -EIO;
+ }
+#endif /* CONFIG_ISDN_PPP */
+
+ strcpy(tmprev, isdn_revision);
+ printk(KERN_NOTICE "ISDN subsystem Rev: %s/", isdn_getrev(tmprev));
+ strcpy(tmprev, isdn_tty_revision);
+ printk("%s/", isdn_getrev(tmprev));
+ strcpy(tmprev, isdn_net_revision);
+ printk("%s/", isdn_getrev(tmprev));
+ strcpy(tmprev, isdn_ppp_revision);
+ printk("%s/", isdn_getrev(tmprev));
+ strcpy(tmprev, isdn_audio_revision);
+ printk("%s/", isdn_getrev(tmprev));
+ strcpy(tmprev, isdn_v110_revision);
+ printk("%s", isdn_getrev(tmprev));
+
+#ifdef MODULE
+ printk(" loaded\n");
+#else
+ printk("\n");
+#endif
+ isdn_info_update();
+ return 0;
+}
+
+/*
+ * Unload module
+ */
+static void __exit isdn_exit(void)
+{
+#ifdef CONFIG_ISDN_PPP
+ isdn_ppp_cleanup();
+#endif
+ if (isdn_net_rmall() < 0) {
+ printk(KERN_WARNING "isdn: net-device busy, remove cancelled\n");
+ return;
+ }
+ isdn_tty_exit();
+ unregister_chrdev(ISDN_MAJOR, "isdn");
+ del_timer(&dev->timer);
+ /* call vfree with interrupts enabled, else it will hang */
+ vfree(dev);
+ printk(KERN_NOTICE "ISDN-subsystem unloaded\n");
+}
+
+module_init(isdn_init);
+module_exit(isdn_exit);
diff --git a/drivers/isdn/i4l/isdn_common.h b/drivers/isdn/i4l/isdn_common.h
new file mode 100644
index 0000000..082735d
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_common.h
@@ -0,0 +1,47 @@
+/* $Id: isdn_common.h,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $
+ *
+ * header for Linux ISDN subsystem
+ * common used functions and debugging-switches (linklevel).
+ *
+ * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de)
+ * Copyright 1995,96 by Thinking Objects Software GmbH Wuerzburg
+ * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#undef ISDN_DEBUG_MODEM_OPEN
+#undef ISDN_DEBUG_MODEM_IOCTL
+#undef ISDN_DEBUG_MODEM_WAITSENT
+#undef ISDN_DEBUG_MODEM_HUP
+#undef ISDN_DEBUG_MODEM_ICALL
+#undef ISDN_DEBUG_MODEM_DUMP
+#undef ISDN_DEBUG_MODEM_VOICE
+#undef ISDN_DEBUG_AT
+#undef ISDN_DEBUG_NET_DUMP
+#undef ISDN_DEBUG_NET_DIAL
+#undef ISDN_DEBUG_NET_ICALL
+
+/* Prototypes */
+extern void isdn_lock_drivers(void);
+extern void isdn_unlock_drivers(void);
+extern void isdn_free_channel(int di, int ch, int usage);
+extern void isdn_all_eaz(int di, int ch);
+extern int isdn_command(isdn_ctrl *);
+extern int isdn_dc2minor(int di, int ch);
+extern void isdn_info_update(void);
+extern char *isdn_map_eaz2msn(char *msn, int di);
+extern void isdn_timer_ctrl(int tf, int onoff);
+extern void isdn_unexclusive_channel(int di, int ch);
+extern int isdn_getnum(char **);
+extern int isdn_readbchan(int, int, u_char *, u_char *, int, wait_queue_head_t *);
+extern int isdn_readbchan_tty(int, int, struct tty_struct *, int);
+extern int isdn_get_free_channel(int, int, int, int, int, char *);
+extern int isdn_writebuf_skb_stub(int, int, int, struct sk_buff *);
+extern int register_isdn(isdn_if * i);
+extern int isdn_msncmp( const char *, const char *);
+#if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP)
+extern void isdn_dumppkt(char *, u_char *, int, int);
+#endif
diff --git a/drivers/isdn/i4l/isdn_concap.c b/drivers/isdn/i4l/isdn_concap.c
new file mode 100644
index 0000000..0193b6f
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_concap.c
@@ -0,0 +1,99 @@
+/* $Id: isdn_concap.c,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $
+ *
+ * Linux ISDN subsystem, protocol encapsulation
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+/* Stuff to support the concap_proto by isdn4linux. isdn4linux - specific
+ * stuff goes here. Stuff that depends only on the concap protocol goes to
+ * another -- protocol specific -- source file.
+ *
+ */
+
+
+#include <linux/isdn.h>
+#include "isdn_x25iface.h"
+#include "isdn_net.h"
+#include <linux/concap.h>
+#include "isdn_concap.h"
+
+
+/* The following set of device service operations are for encapsulation
+ protocols that require for reliable datalink semantics. That means:
+
+ - before any data is to be submitted the connection must explicitly
+ be set up.
+ - after the successful set up of the connection is signalled the
+ connection is considered to be reliably up.
+
+ Auto-dialing ist not compatible with this requirements. Thus, auto-dialing
+ is completely bypassed.
+
+ It might be possible to implement a (non standardized) datalink protocol
+ that provides a reliable data link service while using some auto dialing
+ mechanism. Such a protocol would need an auxiliary channel (i.e. user-user-
+ signaling on the D-channel) while the B-channel is down.
+ */
+
+
+static int isdn_concap_dl_data_req(struct concap_proto *concap, struct sk_buff *skb)
+{
+ struct net_device *ndev = concap -> net_dev;
+ isdn_net_dev *nd = ((isdn_net_local *) ndev->priv)->netdev;
+ isdn_net_local *lp = isdn_net_get_locked_lp(nd);
+
+ IX25DEBUG( "isdn_concap_dl_data_req: %s \n", concap->net_dev->name);
+ if (!lp) {
+ IX25DEBUG( "isdn_concap_dl_data_req: %s : isdn_net_send_skb returned %d\n", concap -> net_dev -> name, 1);
+ return 1;
+ }
+ lp->huptimer = 0;
+ isdn_net_writebuf_skb(lp, skb);
+ spin_unlock_bh(&lp->xmit_lock);
+ IX25DEBUG( "isdn_concap_dl_data_req: %s : isdn_net_send_skb returned %d\n", concap -> net_dev -> name, 0);
+ return 0;
+}
+
+
+static int isdn_concap_dl_connect_req(struct concap_proto *concap)
+{
+ struct net_device *ndev = concap -> net_dev;
+ isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+ int ret;
+ IX25DEBUG( "isdn_concap_dl_connect_req: %s \n", ndev -> name);
+
+ /* dial ... */
+ ret = isdn_net_dial_req( lp );
+ if ( ret ) IX25DEBUG("dialing failed\n");
+ return ret;
+}
+
+static int isdn_concap_dl_disconn_req(struct concap_proto *concap)
+{
+ IX25DEBUG( "isdn_concap_dl_disconn_req: %s \n", concap -> net_dev -> name);
+
+ isdn_net_hangup( concap -> net_dev );
+ return 0;
+}
+
+struct concap_device_ops isdn_concap_reliable_dl_dops = {
+ &isdn_concap_dl_data_req,
+ &isdn_concap_dl_connect_req,
+ &isdn_concap_dl_disconn_req
+};
+
+/* The following should better go into a dedicated source file such that
+ this sourcefile does not need to include any protocol specific header
+ files. For now:
+ */
+struct concap_proto * isdn_concap_new( int encap )
+{
+ switch ( encap ) {
+ case ISDN_NET_ENCAP_X25IFACE:
+ return isdn_x25iface_proto_new();
+ }
+ return NULL;
+}
diff --git a/drivers/isdn/i4l/isdn_concap.h b/drivers/isdn/i4l/isdn_concap.h
new file mode 100644
index 0000000..6ac7e04
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_concap.h
@@ -0,0 +1,13 @@
+/* $Id: isdn_concap.h,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $
+ *
+ * Linux ISDN subsystem, protocol encapsulation
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+extern struct concap_device_ops isdn_concap_reliable_dl_dops;
+extern struct concap_proto * isdn_concap_new( int );
+
+
diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c
new file mode 100644
index 0000000..1bfc55d
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_net.c
@@ -0,0 +1,3210 @@
+/* $Id: isdn_net.c,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $
+ *
+ * Linux ISDN subsystem, network interfaces and related functions (linklevel).
+ *
+ * Copyright 1994-1998 by Fritz Elfert (fritz@isdn4linux.de)
+ * Copyright 1995,96 by Thinking Objects Software GmbH Wuerzburg
+ * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Data Over Voice (DOV) support added - Guy Ellis 23-Mar-02
+ * guy@traverse.com.au
+ * Outgoing calls - looks for a 'V' in first char of dialed number
+ * Incoming calls - checks first character of eaz as follows:
+ * Numeric - accept DATA only - original functionality
+ * 'V' - accept VOICE (DOV) only
+ * 'B' - accept BOTH DATA and DOV types
+ *
+ * Jan 2001: fix CISCO HDLC Bjoern A. Zeeb <i4l@zabbadoz.net>
+ * for info on the protocol, see
+ * http://i4l.zabbadoz.net/i4l/cisco-hdlc.txt
+ */
+
+#include <linux/isdn.h>
+#include <net/arp.h>
+#include <net/dst.h>
+#include <net/pkt_sched.h>
+#include <linux/inetdevice.h>
+#include "isdn_common.h"
+#include "isdn_net.h"
+#ifdef CONFIG_ISDN_PPP
+#include "isdn_ppp.h"
+#endif
+#ifdef CONFIG_ISDN_X25
+#include <linux/concap.h>
+#include "isdn_concap.h"
+#endif
+
+
+/*
+ * Outline of new tbusy handling:
+ *
+ * Old method, roughly spoken, consisted of setting tbusy when entering
+ * isdn_net_start_xmit() and at several other locations and clearing
+ * it from isdn_net_start_xmit() thread when sending was successful.
+ *
+ * With 2.3.x multithreaded network core, to prevent problems, tbusy should
+ * only be set by the isdn_net_start_xmit() thread and only when a tx-busy
+ * condition is detected. Other threads (in particular isdn_net_stat_callb())
+ * are only allowed to clear tbusy.
+ *
+ * -HE
+ */
+
+/*
+ * About SOFTNET:
+ * Most of the changes were pretty obvious and basically done by HE already.
+ *
+ * One problem of the isdn net device code is that is uses struct net_device
+ * for masters and slaves. However, only master interface are registered to
+ * the network layer, and therefore, it only makes sense to call netif_*
+ * functions on them.
+ *
+ * --KG
+ */
+
+/*
+ * Find out if the netdevice has been ifup-ed yet.
+ * For slaves, look at the corresponding master.
+ */
+static __inline__ int isdn_net_device_started(isdn_net_dev *n)
+{
+ isdn_net_local *lp = n->local;
+ struct net_device *dev;
+
+ if (lp->master)
+ dev = lp->master;
+ else
+ dev = n->dev;
+ return netif_running(dev);
+}
+
+/*
+ * wake up the network -> net_device queue.
+ * For slaves, wake the corresponding master interface.
+ */
+static __inline__ void isdn_net_device_wake_queue(isdn_net_local *lp)
+{
+ if (lp->master)
+ netif_wake_queue(lp->master);
+ else
+ netif_wake_queue(lp->netdev->dev);
+}
+
+/*
+ * stop the network -> net_device queue.
+ * For slaves, stop the corresponding master interface.
+ */
+static __inline__ void isdn_net_device_stop_queue(isdn_net_local *lp)
+{
+ if (lp->master)
+ netif_stop_queue(lp->master);
+ else
+ netif_stop_queue(lp->netdev->dev);
+}
+
+/*
+ * find out if the net_device which this lp belongs to (lp can be
+ * master or slave) is busy. It's busy iff all (master and slave)
+ * queues are busy
+ */
+static __inline__ int isdn_net_device_busy(isdn_net_local *lp)
+{
+ isdn_net_local *nlp;
+ isdn_net_dev *nd;
+ unsigned long flags;
+
+ if (!isdn_net_lp_busy(lp))
+ return 0;
+
+ if (lp->master)
+ nd = ((isdn_net_local *) lp->master->priv)->netdev;
+ else
+ nd = lp->netdev;
+
+ spin_lock_irqsave(&nd->queue_lock, flags);
+ nlp = lp->next;
+ while (nlp != lp) {
+ if (!isdn_net_lp_busy(nlp)) {
+ spin_unlock_irqrestore(&nd->queue_lock, flags);
+ return 0;
+ }
+ nlp = nlp->next;
+ }
+ spin_unlock_irqrestore(&nd->queue_lock, flags);
+ return 1;
+}
+
+static __inline__ void isdn_net_inc_frame_cnt(isdn_net_local *lp)
+{
+ atomic_inc(&lp->frame_cnt);
+ if (isdn_net_device_busy(lp))
+ isdn_net_device_stop_queue(lp);
+}
+
+static __inline__ void isdn_net_dec_frame_cnt(isdn_net_local *lp)
+{
+ atomic_dec(&lp->frame_cnt);
+
+ if (!(isdn_net_device_busy(lp))) {
+ if (!skb_queue_empty(&lp->super_tx_queue)) {
+ schedule_work(&lp->tqueue);
+ } else {
+ isdn_net_device_wake_queue(lp);
+ }
+ }
+}
+
+static __inline__ void isdn_net_zero_frame_cnt(isdn_net_local *lp)
+{
+ atomic_set(&lp->frame_cnt, 0);
+}
+
+/* For 2.2.x we leave the transmitter busy timeout at 2 secs, just
+ * to be safe.
+ * For 2.3.x we push it up to 20 secs, because call establishment
+ * (in particular callback) may take such a long time, and we
+ * don't want confusing messages in the log. However, there is a slight
+ * possibility that this large timeout will break other things like MPPP,
+ * which might rely on the tx timeout. If so, we'll find out this way...
+ */
+
+#define ISDN_NET_TX_TIMEOUT (20*HZ)
+
+/* Prototypes */
+
+static int isdn_net_force_dial_lp(isdn_net_local *);
+static int isdn_net_start_xmit(struct sk_buff *, struct net_device *);
+
+static void isdn_net_ciscohdlck_connected(isdn_net_local *lp);
+static void isdn_net_ciscohdlck_disconnected(isdn_net_local *lp);
+
+char *isdn_net_revision = "$Revision: 1.1.2.2 $";
+
+ /*
+ * Code for raw-networking over ISDN
+ */
+
+static void
+isdn_net_unreachable(struct net_device *dev, struct sk_buff *skb, char *reason)
+{
+ if(skb) {
+
+ u_short proto = ntohs(skb->protocol);
+
+ printk(KERN_DEBUG "isdn_net: %s: %s, signalling dst_link_failure %s\n",
+ dev->name,
+ (reason != NULL) ? reason : "unknown",
+ (proto != ETH_P_IP) ? "Protocol != ETH_P_IP" : "");
+
+ dst_link_failure(skb);
+ }
+ else { /* dial not triggered by rawIP packet */
+ printk(KERN_DEBUG "isdn_net: %s: %s\n",
+ dev->name,
+ (reason != NULL) ? reason : "reason unknown");
+ }
+}
+
+static void
+isdn_net_reset(struct net_device *dev)
+{
+#ifdef CONFIG_ISDN_X25
+ struct concap_device_ops * dops =
+ ( (isdn_net_local *) dev->priv ) -> dops;
+ struct concap_proto * cprot =
+ ( (isdn_net_local *) dev->priv ) -> netdev -> cprot;
+#endif
+#ifdef CONFIG_ISDN_X25
+ if( cprot && cprot -> pops && dops )
+ cprot -> pops -> restart ( cprot, dev, dops );
+#endif
+}
+
+/* Open/initialize the board. */
+static int
+isdn_net_open(struct net_device *dev)
+{
+ int i;
+ struct net_device *p;
+ struct in_device *in_dev;
+
+ /* moved here from isdn_net_reset, because only the master has an
+ interface associated which is supposed to be started. BTW:
+ we need to call netif_start_queue, not netif_wake_queue here */
+ netif_start_queue(dev);
+
+ isdn_net_reset(dev);
+ /* Fill in the MAC-level header (not needed, but for compatibility... */
+ for (i = 0; i < ETH_ALEN - sizeof(u32); i++)
+ dev->dev_addr[i] = 0xfc;
+ if ((in_dev = dev->ip_ptr) != NULL) {
+ /*
+ * Any address will do - we take the first
+ */
+ struct in_ifaddr *ifa = in_dev->ifa_list;
+ if (ifa != NULL)
+ memcpy(dev->dev_addr+2, &ifa->ifa_local, 4);
+ }
+
+ /* If this interface has slaves, start them also */
+
+ if ((p = (((isdn_net_local *) dev->priv)->slave))) {
+ while (p) {
+ isdn_net_reset(p);
+ p = (((isdn_net_local *) p->priv)->slave);
+ }
+ }
+ isdn_lock_drivers();
+ return 0;
+}
+
+/*
+ * Assign an ISDN-channel to a net-interface
+ */
+static void
+isdn_net_bind_channel(isdn_net_local * lp, int idx)
+{
+ lp->flags |= ISDN_NET_CONNECTED;
+ lp->isdn_device = dev->drvmap[idx];
+ lp->isdn_channel = dev->chanmap[idx];
+ dev->rx_netdev[idx] = lp->netdev;
+ dev->st_netdev[idx] = lp->netdev;
+}
+
+/*
+ * unbind a net-interface (resets interface after an error)
+ */
+static void
+isdn_net_unbind_channel(isdn_net_local * lp)
+{
+ skb_queue_purge(&lp->super_tx_queue);
+
+ if (!lp->master) { /* reset only master device */
+ /* Moral equivalent of dev_purge_queues():
+ BEWARE! This chunk of code cannot be called from hardware
+ interrupt handler. I hope it is true. --ANK
+ */
+ qdisc_reset_all_tx(lp->netdev->dev);
+ }
+ lp->dialstate = 0;
+ dev->rx_netdev[isdn_dc2minor(lp->isdn_device, lp->isdn_channel)] = NULL;
+ dev->st_netdev[isdn_dc2minor(lp->isdn_device, lp->isdn_channel)] = NULL;
+ isdn_free_channel(lp->isdn_device, lp->isdn_channel, ISDN_USAGE_NET);
+ lp->flags &= ~ISDN_NET_CONNECTED;
+ lp->isdn_device = -1;
+ lp->isdn_channel = -1;
+}
+
+/*
+ * Perform auto-hangup and cps-calculation for net-interfaces.
+ *
+ * auto-hangup:
+ * Increment idle-counter (this counter is reset on any incoming or
+ * outgoing packet), if counter exceeds configured limit either do a
+ * hangup immediately or - if configured - wait until just before the next
+ * charge-info.
+ *
+ * cps-calculation (needed for dynamic channel-bundling):
+ * Since this function is called every second, simply reset the
+ * byte-counter of the interface after copying it to the cps-variable.
+ */
+static unsigned long last_jiffies = -HZ;
+
+void
+isdn_net_autohup(void)
+{
+ isdn_net_dev *p = dev->netdev;
+ int anymore;
+
+ anymore = 0;
+ while (p) {
+ isdn_net_local *l = p->local;
+ if (jiffies == last_jiffies)
+ l->cps = l->transcount;
+ else
+ l->cps = (l->transcount * HZ) / (jiffies - last_jiffies);
+ l->transcount = 0;
+ if (dev->net_verbose > 3)
+ printk(KERN_DEBUG "%s: %d bogocps\n", p->dev->name, l->cps);
+ if ((l->flags & ISDN_NET_CONNECTED) && (!l->dialstate)) {
+ anymore = 1;
+ l->huptimer++;
+ /*
+ * if there is some dialmode where timeout-hangup
+ * should _not_ be done, check for that here
+ */
+ if ((l->onhtime) &&
+ (l->huptimer > l->onhtime))
+ {
+ if (l->hupflags & ISDN_MANCHARGE &&
+ l->hupflags & ISDN_CHARGEHUP) {
+ while (time_after(jiffies, l->chargetime + l->chargeint))
+ l->chargetime += l->chargeint;
+ if (time_after(jiffies, l->chargetime + l->chargeint - 2 * HZ))
+ if (l->outgoing || l->hupflags & ISDN_INHUP)
+ isdn_net_hangup(p->dev);
+ } else if (l->outgoing) {
+ if (l->hupflags & ISDN_CHARGEHUP) {
+ if (l->hupflags & ISDN_WAITCHARGE) {
+ printk(KERN_DEBUG "isdn_net: Hupflags of %s are %X\n",
+ p->dev->name, l->hupflags);
+ isdn_net_hangup(p->dev);
+ } else if (time_after(jiffies, l->chargetime + l->chargeint)) {
+ printk(KERN_DEBUG
+ "isdn_net: %s: chtime = %lu, chint = %d\n",
+ p->dev->name, l->chargetime, l->chargeint);
+ isdn_net_hangup(p->dev);
+ }
+ } else
+ isdn_net_hangup(p->dev);
+ } else if (l->hupflags & ISDN_INHUP)
+ isdn_net_hangup(p->dev);
+ }
+
+ if(dev->global_flags & ISDN_GLOBAL_STOPPED || (ISDN_NET_DIALMODE(*l) == ISDN_NET_DM_OFF)) {
+ isdn_net_hangup(p->dev);
+ break;
+ }
+ }
+ p = (isdn_net_dev *) p->next;
+ }
+ last_jiffies = jiffies;
+ isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, anymore);
+}
+
+static void isdn_net_lp_disconnected(isdn_net_local *lp)
+{
+ isdn_net_rm_from_bundle(lp);
+}
+
+/*
+ * Handle status-messages from ISDN-interfacecard.
+ * This function is called from within the main-status-dispatcher
+ * isdn_status_callback, which itself is called from the low-level driver.
+ * Return: 1 = Event handled, 0 = not for us or unknown Event.
+ */
+int
+isdn_net_stat_callback(int idx, isdn_ctrl *c)
+{
+ isdn_net_dev *p = dev->st_netdev[idx];
+ int cmd = c->command;
+
+ if (p) {
+ isdn_net_local *lp = p->local;
+#ifdef CONFIG_ISDN_X25
+ struct concap_proto *cprot = lp->netdev->cprot;
+ struct concap_proto_ops *pops = cprot ? cprot->pops : NULL;
+#endif
+ switch (cmd) {
+ case ISDN_STAT_BSENT:
+ /* A packet has successfully been sent out */
+ if ((lp->flags & ISDN_NET_CONNECTED) &&
+ (!lp->dialstate)) {
+ isdn_net_dec_frame_cnt(lp);
+ lp->stats.tx_packets++;
+ lp->stats.tx_bytes += c->parm.length;
+ }
+ return 1;
+ case ISDN_STAT_DCONN:
+ /* D-Channel is up */
+ switch (lp->dialstate) {
+ case 4:
+ case 7:
+ case 8:
+ lp->dialstate++;
+ return 1;
+ case 12:
+ lp->dialstate = 5;
+ return 1;
+ }
+ break;
+ case ISDN_STAT_DHUP:
+ /* Either D-Channel-hangup or error during dialout */
+#ifdef CONFIG_ISDN_X25
+ /* If we are not connencted then dialing had
+ failed. If there are generic encap protocol
+ receiver routines signal the closure of
+ the link*/
+
+ if( !(lp->flags & ISDN_NET_CONNECTED)
+ && pops && pops -> disconn_ind )
+ pops -> disconn_ind(cprot);
+#endif /* CONFIG_ISDN_X25 */
+ if ((!lp->dialstate) && (lp->flags & ISDN_NET_CONNECTED)) {
+ if (lp->p_encap == ISDN_NET_ENCAP_CISCOHDLCK)
+ isdn_net_ciscohdlck_disconnected(lp);
+#ifdef CONFIG_ISDN_PPP
+ if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+ isdn_ppp_free(lp);
+#endif
+ isdn_net_lp_disconnected(lp);
+ isdn_all_eaz(lp->isdn_device, lp->isdn_channel);
+ printk(KERN_INFO "%s: remote hangup\n", p->dev->name);
+ printk(KERN_INFO "%s: Chargesum is %d\n", p->dev->name,
+ lp->charge);
+ isdn_net_unbind_channel(lp);
+ return 1;
+ }
+ break;
+#ifdef CONFIG_ISDN_X25
+ case ISDN_STAT_BHUP:
+ /* B-Channel-hangup */
+ /* try if there are generic encap protocol
+ receiver routines and signal the closure of
+ the link */
+ if( pops && pops -> disconn_ind ){
+ pops -> disconn_ind(cprot);
+ return 1;
+ }
+ break;
+#endif /* CONFIG_ISDN_X25 */
+ case ISDN_STAT_BCONN:
+ /* B-Channel is up */
+ isdn_net_zero_frame_cnt(lp);
+ switch (lp->dialstate) {
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 12:
+ if (lp->dialstate <= 6) {
+ dev->usage[idx] |= ISDN_USAGE_OUTGOING;
+ isdn_info_update();
+ } else
+ dev->rx_netdev[idx] = p;
+ lp->dialstate = 0;
+ isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 1);
+ if (lp->p_encap == ISDN_NET_ENCAP_CISCOHDLCK)
+ isdn_net_ciscohdlck_connected(lp);
+ if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP) {
+ if (lp->master) { /* is lp a slave? */
+ isdn_net_dev *nd = ((isdn_net_local *)lp->master->priv)->netdev;
+ isdn_net_add_to_bundle(nd, lp);
+ }
+ }
+ printk(KERN_INFO "isdn_net: %s connected\n", p->dev->name);
+ /* If first Chargeinfo comes before B-Channel connect,
+ * we correct the timestamp here.
+ */
+ lp->chargetime = jiffies;
+
+ /* reset dial-timeout */
+ lp->dialstarted = 0;
+ lp->dialwait_timer = 0;
+
+#ifdef CONFIG_ISDN_PPP
+ if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+ isdn_ppp_wakeup_daemon(lp);
+#endif
+#ifdef CONFIG_ISDN_X25
+ /* try if there are generic concap receiver routines */
+ if( pops )
+ if( pops->connect_ind)
+ pops->connect_ind(cprot);
+#endif /* CONFIG_ISDN_X25 */
+ /* ppp needs to do negotiations first */
+ if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP)
+ isdn_net_device_wake_queue(lp);
+ return 1;
+ }
+ break;
+ case ISDN_STAT_NODCH:
+ /* No D-Channel avail. */
+ if (lp->dialstate == 4) {
+ lp->dialstate--;
+ return 1;
+ }
+ break;
+ case ISDN_STAT_CINF:
+ /* Charge-info from TelCo. Calculate interval between
+ * charge-infos and set timestamp for last info for
+ * usage by isdn_net_autohup()
+ */
+ lp->charge++;
+ if (lp->hupflags & ISDN_HAVECHARGE) {
+ lp->hupflags &= ~ISDN_WAITCHARGE;
+ lp->chargeint = jiffies - lp->chargetime - (2 * HZ);
+ }
+ if (lp->hupflags & ISDN_WAITCHARGE)
+ lp->hupflags |= ISDN_HAVECHARGE;
+ lp->chargetime = jiffies;
+ printk(KERN_DEBUG "isdn_net: Got CINF chargetime of %s now %lu\n",
+ p->dev->name, lp->chargetime);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Perform dialout for net-interfaces and timeout-handling for
+ * D-Channel-up and B-Channel-up Messages.
+ * This function is initially called from within isdn_net_start_xmit() or
+ * or isdn_net_find_icall() after initializing the dialstate for an
+ * interface. If further calls are needed, the function schedules itself
+ * for a timer-callback via isdn_timer_function().
+ * The dialstate is also affected by incoming status-messages from
+ * the ISDN-Channel which are handled in isdn_net_stat_callback() above.
+ */
+void
+isdn_net_dial(void)
+{
+ isdn_net_dev *p = dev->netdev;
+ int anymore = 0;
+ int i;
+ isdn_ctrl cmd;
+ u_char *phone_number;
+
+ while (p) {
+ isdn_net_local *lp = p->local;
+
+#ifdef ISDN_DEBUG_NET_DIAL
+ if (lp->dialstate)
+ printk(KERN_DEBUG "%s: dialstate=%d\n", p->dev->name, lp->dialstate);
+#endif
+ switch (lp->dialstate) {
+ case 0:
+ /* Nothing to do for this interface */
+ break;
+ case 1:
+ /* Initiate dialout. Set phone-number-pointer to first number
+ * of interface.
+ */
+ lp->dial = lp->phone[1];
+ if (!lp->dial) {
+ printk(KERN_WARNING "%s: phone number deleted?\n",
+ p->dev->name);
+ isdn_net_hangup(p->dev);
+ break;
+ }
+ anymore = 1;
+
+ if(lp->dialtimeout > 0)
+ if(lp->dialstarted == 0 || time_after(jiffies, lp->dialstarted + lp->dialtimeout + lp->dialwait)) {
+ lp->dialstarted = jiffies;
+ lp->dialwait_timer = 0;
+ }
+
+ lp->dialstate++;
+ /* Fall through */
+ case 2:
+ /* Prepare dialing. Clear EAZ, then set EAZ. */
+ cmd.driver = lp->isdn_device;
+ cmd.arg = lp->isdn_channel;
+ cmd.command = ISDN_CMD_CLREAZ;
+ isdn_command(&cmd);
+ sprintf(cmd.parm.num, "%s", isdn_map_eaz2msn(lp->msn, cmd.driver));
+ cmd.command = ISDN_CMD_SETEAZ;
+ isdn_command(&cmd);
+ lp->dialretry = 0;
+ anymore = 1;
+ lp->dialstate++;
+ /* Fall through */
+ case 3:
+ /* Setup interface, dial current phone-number, switch to next number.
+ * If list of phone-numbers is exhausted, increment
+ * retry-counter.
+ */
+ if(dev->global_flags & ISDN_GLOBAL_STOPPED || (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF)) {
+ char *s;
+ if (dev->global_flags & ISDN_GLOBAL_STOPPED)
+ s = "dial suppressed: isdn system stopped";
+ else
+ s = "dial suppressed: dialmode `off'";
+ isdn_net_unreachable(p->dev, NULL, s);
+ isdn_net_hangup(p->dev);
+ break;
+ }
+ cmd.driver = lp->isdn_device;
+ cmd.command = ISDN_CMD_SETL2;
+ cmd.arg = lp->isdn_channel + (lp->l2_proto << 8);
+ isdn_command(&cmd);
+ cmd.driver = lp->isdn_device;
+ cmd.command = ISDN_CMD_SETL3;
+ cmd.arg = lp->isdn_channel + (lp->l3_proto << 8);
+ isdn_command(&cmd);
+ cmd.driver = lp->isdn_device;
+ cmd.arg = lp->isdn_channel;
+ if (!lp->dial) {
+ printk(KERN_WARNING "%s: phone number deleted?\n",
+ p->dev->name);
+ isdn_net_hangup(p->dev);
+ break;
+ }
+ if (!strncmp(lp->dial->num, "LEASED", strlen("LEASED"))) {
+ lp->dialstate = 4;
+ printk(KERN_INFO "%s: Open leased line ...\n", p->dev->name);
+ } else {
+ if(lp->dialtimeout > 0)
+ if (time_after(jiffies, lp->dialstarted + lp->dialtimeout)) {
+ lp->dialwait_timer = jiffies + lp->dialwait;
+ lp->dialstarted = 0;
+ isdn_net_unreachable(p->dev, NULL, "dial: timed out");
+ isdn_net_hangup(p->dev);
+ break;
+ }
+
+ cmd.driver = lp->isdn_device;
+ cmd.command = ISDN_CMD_DIAL;
+ cmd.parm.setup.si2 = 0;
+
+ /* check for DOV */
+ phone_number = lp->dial->num;
+ if ((*phone_number == 'v') ||
+ (*phone_number == 'V')) { /* DOV call */
+ cmd.parm.setup.si1 = 1;
+ } else { /* DATA call */
+ cmd.parm.setup.si1 = 7;
+ }
+
+ strcpy(cmd.parm.setup.phone, phone_number);
+ /*
+ * Switch to next number or back to start if at end of list.
+ */
+ if (!(lp->dial = (isdn_net_phone *) lp->dial->next)) {
+ lp->dial = lp->phone[1];
+ lp->dialretry++;
+
+ if (lp->dialretry > lp->dialmax) {
+ if (lp->dialtimeout == 0) {
+ lp->dialwait_timer = jiffies + lp->dialwait;
+ lp->dialstarted = 0;
+ isdn_net_unreachable(p->dev, NULL, "dial: tried all numbers dialmax times");
+ }
+ isdn_net_hangup(p->dev);
+ break;
+ }
+ }
+ sprintf(cmd.parm.setup.eazmsn, "%s",
+ isdn_map_eaz2msn(lp->msn, cmd.driver));
+ i = isdn_dc2minor(lp->isdn_device, lp->isdn_channel);
+ if (i >= 0) {
+ strcpy(dev->num[i], cmd.parm.setup.phone);
+ dev->usage[i] |= ISDN_USAGE_OUTGOING;
+ isdn_info_update();
+ }
+ printk(KERN_INFO "%s: dialing %d %s... %s\n", p->dev->name,
+ lp->dialretry, cmd.parm.setup.phone,
+ (cmd.parm.setup.si1 == 1) ? "DOV" : "");
+ lp->dtimer = 0;
+#ifdef ISDN_DEBUG_NET_DIAL
+ printk(KERN_DEBUG "dial: d=%d c=%d\n", lp->isdn_device,
+ lp->isdn_channel);
+#endif
+ isdn_command(&cmd);
+ }
+ lp->huptimer = 0;
+ lp->outgoing = 1;
+ if (lp->chargeint) {
+ lp->hupflags |= ISDN_HAVECHARGE;
+ lp->hupflags &= ~ISDN_WAITCHARGE;
+ } else {
+ lp->hupflags |= ISDN_WAITCHARGE;
+ lp->hupflags &= ~ISDN_HAVECHARGE;
+ }
+ anymore = 1;
+ lp->dialstate =
+ (lp->cbdelay &&
+ (lp->flags & ISDN_NET_CBOUT)) ? 12 : 4;
+ break;
+ case 4:
+ /* Wait for D-Channel-connect.
+ * If timeout, switch back to state 3.
+ * Dialmax-handling moved to state 3.
+ */
+ if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT10)
+ lp->dialstate = 3;
+ anymore = 1;
+ break;
+ case 5:
+ /* Got D-Channel-Connect, send B-Channel-request */
+ cmd.driver = lp->isdn_device;
+ cmd.arg = lp->isdn_channel;
+ cmd.command = ISDN_CMD_ACCEPTB;
+ anymore = 1;
+ lp->dtimer = 0;
+ lp->dialstate++;
+ isdn_command(&cmd);
+ break;
+ case 6:
+ /* Wait for B- or D-Channel-connect. If timeout,
+ * switch back to state 3.
+ */
+#ifdef ISDN_DEBUG_NET_DIAL
+ printk(KERN_DEBUG "dialtimer2: %d\n", lp->dtimer);
+#endif
+ if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT10)
+ lp->dialstate = 3;
+ anymore = 1;
+ break;
+ case 7:
+ /* Got incoming Call, setup L2 and L3 protocols,
+ * then wait for D-Channel-connect
+ */
+#ifdef ISDN_DEBUG_NET_DIAL
+ printk(KERN_DEBUG "dialtimer4: %d\n", lp->dtimer);
+#endif
+ cmd.driver = lp->isdn_device;
+ cmd.command = ISDN_CMD_SETL2;
+ cmd.arg = lp->isdn_channel + (lp->l2_proto << 8);
+ isdn_command(&cmd);
+ cmd.driver = lp->isdn_device;
+ cmd.command = ISDN_CMD_SETL3;
+ cmd.arg = lp->isdn_channel + (lp->l3_proto << 8);
+ isdn_command(&cmd);
+ if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT15)
+ isdn_net_hangup(p->dev);
+ else {
+ anymore = 1;
+ lp->dialstate++;
+ }
+ break;
+ case 9:
+ /* Got incoming D-Channel-Connect, send B-Channel-request */
+ cmd.driver = lp->isdn_device;
+ cmd.arg = lp->isdn_channel;
+ cmd.command = ISDN_CMD_ACCEPTB;
+ isdn_command(&cmd);
+ anymore = 1;
+ lp->dtimer = 0;
+ lp->dialstate++;
+ break;
+ case 8:
+ case 10:
+ /* Wait for B- or D-channel-connect */
+#ifdef ISDN_DEBUG_NET_DIAL
+ printk(KERN_DEBUG "dialtimer4: %d\n", lp->dtimer);
+#endif
+ if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT10)
+ isdn_net_hangup(p->dev);
+ else
+ anymore = 1;
+ break;
+ case 11:
+ /* Callback Delay */
+ if (lp->dtimer++ > lp->cbdelay)
+ lp->dialstate = 1;
+ anymore = 1;
+ break;
+ case 12:
+ /* Remote does callback. Hangup after cbdelay, then wait for incoming
+ * call (in state 4).
+ */
+ if (lp->dtimer++ > lp->cbdelay)
+ {
+ printk(KERN_INFO "%s: hangup waiting for callback ...\n", p->dev->name);
+ lp->dtimer = 0;
+ lp->dialstate = 4;
+ cmd.driver = lp->isdn_device;
+ cmd.command = ISDN_CMD_HANGUP;
+ cmd.arg = lp->isdn_channel;
+ isdn_command(&cmd);
+ isdn_all_eaz(lp->isdn_device, lp->isdn_channel);
+ }
+ anymore = 1;
+ break;
+ default:
+ printk(KERN_WARNING "isdn_net: Illegal dialstate %d for device %s\n",
+ lp->dialstate, p->dev->name);
+ }
+ p = (isdn_net_dev *) p->next;
+ }
+ isdn_timer_ctrl(ISDN_TIMER_NETDIAL, anymore);
+}
+
+/*
+ * Perform hangup for a net-interface.
+ */
+void
+isdn_net_hangup(struct net_device *d)
+{
+ isdn_net_local *lp = (isdn_net_local *) d->priv;
+ isdn_ctrl cmd;
+#ifdef CONFIG_ISDN_X25
+ struct concap_proto *cprot = lp->netdev->cprot;
+ struct concap_proto_ops *pops = cprot ? cprot->pops : NULL;
+#endif
+
+ if (lp->flags & ISDN_NET_CONNECTED) {
+ if (lp->slave != NULL) {
+ isdn_net_local *slp = (isdn_net_local *)lp->slave->priv;
+ if (slp->flags & ISDN_NET_CONNECTED) {
+ printk(KERN_INFO
+ "isdn_net: hang up slave %s before %s\n",
+ lp->slave->name, d->name);
+ isdn_net_hangup(lp->slave);
+ }
+ }
+ printk(KERN_INFO "isdn_net: local hangup %s\n", d->name);
+#ifdef CONFIG_ISDN_PPP
+ if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+ isdn_ppp_free(lp);
+#endif
+ isdn_net_lp_disconnected(lp);
+#ifdef CONFIG_ISDN_X25
+ /* try if there are generic encap protocol
+ receiver routines and signal the closure of
+ the link */
+ if( pops && pops -> disconn_ind )
+ pops -> disconn_ind(cprot);
+#endif /* CONFIG_ISDN_X25 */
+
+ cmd.driver = lp->isdn_device;
+ cmd.command = ISDN_CMD_HANGUP;
+ cmd.arg = lp->isdn_channel;
+ isdn_command(&cmd);
+ printk(KERN_INFO "%s: Chargesum is %d\n", d->name, lp->charge);
+ isdn_all_eaz(lp->isdn_device, lp->isdn_channel);
+ }
+ isdn_net_unbind_channel(lp);
+}
+
+typedef struct {
+ unsigned short source;
+ unsigned short dest;
+} ip_ports;
+
+static void
+isdn_net_log_skb(struct sk_buff * skb, isdn_net_local * lp)
+{
+ /* hopefully, this was set correctly */
+ const u_char *p = skb_network_header(skb);
+ unsigned short proto = ntohs(skb->protocol);
+ int data_ofs;
+ ip_ports *ipp;
+ char addinfo[100];
+
+ addinfo[0] = '\0';
+ /* This check stolen from 2.1.72 dev_queue_xmit_nit() */
+ if (p < skb->data || skb->network_header >= skb->tail) {
+ /* fall back to old isdn_net_log_packet method() */
+ char * buf = skb->data;
+
+ printk(KERN_DEBUG "isdn_net: protocol %04x is buggy, dev %s\n", skb->protocol, lp->netdev->dev->name);
+ p = buf;
+ proto = ETH_P_IP;
+ switch (lp->p_encap) {
+ case ISDN_NET_ENCAP_IPTYP:
+ proto = ntohs(*(unsigned short *) &buf[0]);
+ p = &buf[2];
+ break;
+ case ISDN_NET_ENCAP_ETHER:
+ proto = ntohs(*(unsigned short *) &buf[12]);
+ p = &buf[14];
+ break;
+ case ISDN_NET_ENCAP_CISCOHDLC:
+ proto = ntohs(*(unsigned short *) &buf[2]);
+ p = &buf[4];
+ break;
+#ifdef CONFIG_ISDN_PPP
+ case ISDN_NET_ENCAP_SYNCPPP:
+ proto = ntohs(skb->protocol);
+ p = &buf[IPPP_MAX_HEADER];
+ break;
+#endif
+ }
+ }
+ data_ofs = ((p[0] & 15) * 4);
+ switch (proto) {
+ case ETH_P_IP:
+ switch (p[9]) {
+ case 1:
+ strcpy(addinfo, " ICMP");
+ break;
+ case 2:
+ strcpy(addinfo, " IGMP");
+ break;
+ case 4:
+ strcpy(addinfo, " IPIP");
+ break;
+ case 6:
+ ipp = (ip_ports *) (&p[data_ofs]);
+ sprintf(addinfo, " TCP, port: %d -> %d", ntohs(ipp->source),
+ ntohs(ipp->dest));
+ break;
+ case 8:
+ strcpy(addinfo, " EGP");
+ break;
+ case 12:
+ strcpy(addinfo, " PUP");
+ break;
+ case 17:
+ ipp = (ip_ports *) (&p[data_ofs]);
+ sprintf(addinfo, " UDP, port: %d -> %d", ntohs(ipp->source),
+ ntohs(ipp->dest));
+ break;
+ case 22:
+ strcpy(addinfo, " IDP");
+ break;
+ }
+ printk(KERN_INFO
+ "OPEN: %d.%d.%d.%d -> %d.%d.%d.%d%s\n",
+
+ p[12], p[13], p[14], p[15],
+ p[16], p[17], p[18], p[19],
+ addinfo);
+ break;
+ case ETH_P_ARP:
+ printk(KERN_INFO
+ "OPEN: ARP %d.%d.%d.%d -> *.*.*.* ?%d.%d.%d.%d\n",
+ p[14], p[15], p[16], p[17],
+ p[24], p[25], p[26], p[27]);
+ break;
+ }
+}
+
+/*
+ * this function is used to send supervisory data, i.e. data which was
+ * not received from the network layer, but e.g. frames from ipppd, CCP
+ * reset frames etc.
+ */
+void isdn_net_write_super(isdn_net_local *lp, struct sk_buff *skb)
+{
+ if (in_irq()) {
+ // we can't grab the lock from irq context,
+ // so we just queue the packet
+ skb_queue_tail(&lp->super_tx_queue, skb);
+ schedule_work(&lp->tqueue);
+ return;
+ }
+
+ spin_lock_bh(&lp->xmit_lock);
+ if (!isdn_net_lp_busy(lp)) {
+ isdn_net_writebuf_skb(lp, skb);
+ } else {
+ skb_queue_tail(&lp->super_tx_queue, skb);
+ }
+ spin_unlock_bh(&lp->xmit_lock);
+}
+
+/*
+ * called from tq_immediate
+ */
+static void isdn_net_softint(struct work_struct *work)
+{
+ isdn_net_local *lp = container_of(work, isdn_net_local, tqueue);
+ struct sk_buff *skb;
+
+ spin_lock_bh(&lp->xmit_lock);
+ while (!isdn_net_lp_busy(lp)) {
+ skb = skb_dequeue(&lp->super_tx_queue);
+ if (!skb)
+ break;
+ isdn_net_writebuf_skb(lp, skb);
+ }
+ spin_unlock_bh(&lp->xmit_lock);
+}
+
+/*
+ * all frames sent from the (net) LL to a HL driver should go via this function
+ * it's serialized by the caller holding the lp->xmit_lock spinlock
+ */
+void isdn_net_writebuf_skb(isdn_net_local *lp, struct sk_buff *skb)
+{
+ int ret;
+ int len = skb->len; /* save len */
+
+ /* before obtaining the lock the caller should have checked that
+ the lp isn't busy */
+ if (isdn_net_lp_busy(lp)) {
+ printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__);
+ goto error;
+ }
+
+ if (!(lp->flags & ISDN_NET_CONNECTED)) {
+ printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__);
+ goto error;
+ }
+ ret = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, 1, skb);
+ if (ret != len) {
+ /* we should never get here */
+ printk(KERN_WARNING "%s: HL driver queue full\n", lp->netdev->dev->name);
+ goto error;
+ }
+
+ lp->transcount += len;
+ isdn_net_inc_frame_cnt(lp);
+ return;
+
+ error:
+ dev_kfree_skb(skb);
+ lp->stats.tx_errors++;
+
+}
+
+
+/*
+ * Helper function for isdn_net_start_xmit.
+ * When called, the connection is already established.
+ * Based on cps-calculation, check if device is overloaded.
+ * If so, and if a slave exists, trigger dialing for it.
+ * If any slave is online, deliver packets using a simple round robin
+ * scheme.
+ *
+ * Return: 0 on success, !0 on failure.
+ */
+
+static int
+isdn_net_xmit(struct net_device *ndev, struct sk_buff *skb)
+{
+ isdn_net_dev *nd;
+ isdn_net_local *slp;
+ isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+ int retv = 0;
+
+ if (((isdn_net_local *) (ndev->priv))->master) {
+ printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__);
+ dev_kfree_skb(skb);
+ return 0;
+ }
+
+ /* For the other encaps the header has already been built */
+#ifdef CONFIG_ISDN_PPP
+ if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) {
+ return isdn_ppp_xmit(skb, ndev);
+ }
+#endif
+ nd = ((isdn_net_local *) ndev->priv)->netdev;
+ lp = isdn_net_get_locked_lp(nd);
+ if (!lp) {
+ printk(KERN_WARNING "%s: all channels busy - requeuing!\n", ndev->name);
+ return 1;
+ }
+ /* we have our lp locked from now on */
+
+ /* Reset hangup-timeout */
+ lp->huptimer = 0; // FIXME?
+ isdn_net_writebuf_skb(lp, skb);
+ spin_unlock_bh(&lp->xmit_lock);
+
+ /* the following stuff is here for backwards compatibility.
+ * in future, start-up and hangup of slaves (based on current load)
+ * should move to userspace and get based on an overall cps
+ * calculation
+ */
+ if (lp->cps > lp->triggercps) {
+ if (lp->slave) {
+ if (!lp->sqfull) {
+ /* First time overload: set timestamp only */
+ lp->sqfull = 1;
+ lp->sqfull_stamp = jiffies;
+ } else {
+ /* subsequent overload: if slavedelay exceeded, start dialing */
+ if (time_after(jiffies, lp->sqfull_stamp + lp->slavedelay)) {
+ slp = lp->slave->priv;
+ if (!(slp->flags & ISDN_NET_CONNECTED)) {
+ isdn_net_force_dial_lp((isdn_net_local *) lp->slave->priv);
+ }
+ }
+ }
+ }
+ } else {
+ if (lp->sqfull && time_after(jiffies, lp->sqfull_stamp + lp->slavedelay + (10 * HZ))) {
+ lp->sqfull = 0;
+ }
+ /* this is a hack to allow auto-hangup for slaves on moderate loads */
+ nd->queue = nd->local;
+ }
+
+ return retv;
+
+}
+
+static void
+isdn_net_adjust_hdr(struct sk_buff *skb, struct net_device *dev)
+{
+ isdn_net_local *lp = (isdn_net_local *) dev->priv;
+ if (!skb)
+ return;
+ if (lp->p_encap == ISDN_NET_ENCAP_ETHER) {
+ const int pullsize = skb_network_offset(skb) - ETH_HLEN;
+ if (pullsize > 0) {
+ printk(KERN_DEBUG "isdn_net: Pull junk %d\n", pullsize);
+ skb_pull(skb, pullsize);
+ }
+ }
+}
+
+
+static void isdn_net_tx_timeout(struct net_device * ndev)
+{
+ isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+
+ printk(KERN_WARNING "isdn_tx_timeout dev %s dialstate %d\n", ndev->name, lp->dialstate);
+ if (!lp->dialstate){
+ lp->stats.tx_errors++;
+ /*
+ * There is a certain probability that this currently
+ * works at all because if we always wake up the interface,
+ * then upper layer will try to send the next packet
+ * immediately. And then, the old clean_up logic in the
+ * driver will hopefully continue to work as it used to do.
+ *
+ * This is rather primitive right know, we better should
+ * clean internal queues here, in particular for multilink and
+ * ppp, and reset HL driver's channel, too. --HE
+ *
+ * actually, this may not matter at all, because ISDN hardware
+ * should not see transmitter hangs at all IMO
+ * changed KERN_DEBUG to KERN_WARNING to find out if this is
+ * ever called --KG
+ */
+ }
+ ndev->trans_start = jiffies;
+ netif_wake_queue(ndev);
+}
+
+/*
+ * Try sending a packet.
+ * If this interface isn't connected to a ISDN-Channel, find a free channel,
+ * and start dialing.
+ */
+static int
+isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+#ifdef CONFIG_ISDN_X25
+ struct concap_proto * cprot = lp -> netdev -> cprot;
+/* At this point hard_start_xmit() passes control to the encapsulation
+ protocol (if present).
+ For X.25 auto-dialing is completly bypassed because:
+ - It does not conform with the semantics of a reliable datalink
+ service as needed by X.25 PLP.
+ - I don't want that the interface starts dialing when the network layer
+ sends a message which requests to disconnect the lapb link (or if it
+ sends any other message not resulting in data transmission).
+ Instead, dialing will be initiated by the encapsulation protocol entity
+ when a dl_establish request is received from the upper layer.
+*/
+ if (cprot && cprot -> pops) {
+ int ret = cprot -> pops -> encap_and_xmit ( cprot , skb);
+
+ if (ret)
+ netif_stop_queue(ndev);
+ return ret;
+ } else
+#endif
+ /* auto-dialing xmit function */
+ {
+#ifdef ISDN_DEBUG_NET_DUMP
+ u_char *buf;
+#endif
+ isdn_net_adjust_hdr(skb, ndev);
+#ifdef ISDN_DEBUG_NET_DUMP
+ buf = skb->data;
+ isdn_dumppkt("S:", buf, skb->len, 40);
+#endif
+
+ if (!(lp->flags & ISDN_NET_CONNECTED)) {
+ int chi;
+ /* only do autodial if allowed by config */
+ if (!(ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_AUTO)) {
+ isdn_net_unreachable(ndev, skb, "dial rejected: interface not in dialmode `auto'");
+ dev_kfree_skb(skb);
+ return 0;
+ }
+ if (lp->phone[1]) {
+ ulong flags;
+
+ if(lp->dialwait_timer <= 0)
+ if(lp->dialstarted > 0 && lp->dialtimeout > 0 && time_before(jiffies, lp->dialstarted + lp->dialtimeout + lp->dialwait))
+ lp->dialwait_timer = lp->dialstarted + lp->dialtimeout + lp->dialwait;
+
+ if(lp->dialwait_timer > 0) {
+ if(time_before(jiffies, lp->dialwait_timer)) {
+ isdn_net_unreachable(ndev, skb, "dial rejected: retry-time not reached");
+ dev_kfree_skb(skb);
+ return 0;
+ } else
+ lp->dialwait_timer = 0;
+ }
+ /* Grab a free ISDN-Channel */
+ spin_lock_irqsave(&dev->lock, flags);
+ if (((chi =
+ isdn_get_free_channel(
+ ISDN_USAGE_NET,
+ lp->l2_proto,
+ lp->l3_proto,
+ lp->pre_device,
+ lp->pre_channel,
+ lp->msn)
+ ) < 0) &&
+ ((chi =
+ isdn_get_free_channel(
+ ISDN_USAGE_NET,
+ lp->l2_proto,
+ lp->l3_proto,
+ lp->pre_device,
+ lp->pre_channel^1,
+ lp->msn)
+ ) < 0)) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ isdn_net_unreachable(ndev, skb,
+ "No channel");
+ dev_kfree_skb(skb);
+ return 0;
+ }
+ /* Log packet, which triggered dialing */
+ if (dev->net_verbose)
+ isdn_net_log_skb(skb, lp);
+ lp->dialstate = 1;
+ /* Connect interface with channel */
+ isdn_net_bind_channel(lp, chi);
+#ifdef CONFIG_ISDN_PPP
+ if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) {
+ /* no 'first_skb' handling for syncPPP */
+ if (isdn_ppp_bind(lp) < 0) {
+ dev_kfree_skb(skb);
+ isdn_net_unbind_channel(lp);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return 0; /* STN (skb to nirvana) ;) */
+ }
+#ifdef CONFIG_IPPP_FILTER
+ if (isdn_ppp_autodial_filter(skb, lp)) {
+ isdn_ppp_free(lp);
+ isdn_net_unbind_channel(lp);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ isdn_net_unreachable(ndev, skb, "dial rejected: packet filtered");
+ dev_kfree_skb(skb);
+ return 0;
+ }
+#endif
+ spin_unlock_irqrestore(&dev->lock, flags);
+ isdn_net_dial(); /* Initiate dialing */
+ netif_stop_queue(ndev);
+ return 1; /* let upper layer requeue skb packet */
+ }
+#endif
+ /* Initiate dialing */
+ spin_unlock_irqrestore(&dev->lock, flags);
+ isdn_net_dial();
+ isdn_net_device_stop_queue(lp);
+ return 1;
+ } else {
+ isdn_net_unreachable(ndev, skb,
+ "No phone number");
+ dev_kfree_skb(skb);
+ return 0;
+ }
+ } else {
+ /* Device is connected to an ISDN channel */
+ ndev->trans_start = jiffies;
+ if (!lp->dialstate) {
+ /* ISDN connection is established, try sending */
+ int ret;
+ ret = (isdn_net_xmit(ndev, skb));
+ if(ret) netif_stop_queue(ndev);
+ return ret;
+ } else
+ netif_stop_queue(ndev);
+ }
+ }
+ return 1;
+}
+
+/*
+ * Shutdown a net-interface.
+ */
+static int
+isdn_net_close(struct net_device *dev)
+{
+ struct net_device *p;
+#ifdef CONFIG_ISDN_X25
+ struct concap_proto * cprot =
+ ( (isdn_net_local *) dev->priv ) -> netdev -> cprot;
+ /* printk(KERN_DEBUG "isdn_net_close %s\n" , dev-> name ); */
+#endif
+
+#ifdef CONFIG_ISDN_X25
+ if( cprot && cprot -> pops ) cprot -> pops -> close( cprot );
+#endif
+ netif_stop_queue(dev);
+ if ((p = (((isdn_net_local *) dev->priv)->slave))) {
+ /* If this interface has slaves, stop them also */
+ while (p) {
+#ifdef CONFIG_ISDN_X25
+ cprot = ( (isdn_net_local *) p->priv )
+ -> netdev -> cprot;
+ if( cprot && cprot -> pops )
+ cprot -> pops -> close( cprot );
+#endif
+ isdn_net_hangup(p);
+ p = (((isdn_net_local *) p->priv)->slave);
+ }
+ }
+ isdn_net_hangup(dev);
+ isdn_unlock_drivers();
+ return 0;
+}
+
+/*
+ * Get statistics
+ */
+static struct net_device_stats *
+isdn_net_get_stats(struct net_device *dev)
+{
+ isdn_net_local *lp = (isdn_net_local *) dev->priv;
+ return &lp->stats;
+}
+
+/* This is simply a copy from std. eth.c EXCEPT we pull ETH_HLEN
+ * instead of dev->hard_header_len off. This is done because the
+ * lowlevel-driver has already pulled off its stuff when we get
+ * here and this routine only gets called with p_encap == ETHER.
+ * Determine the packet's protocol ID. The rule here is that we
+ * assume 802.3 if the type field is short enough to be a length.
+ * This is normal practice and works for any 'now in use' protocol.
+ */
+
+static unsigned short
+isdn_net_type_trans(struct sk_buff *skb, struct net_device *dev)
+{
+ struct ethhdr *eth;
+ unsigned char *rawp;
+
+ skb_reset_mac_header(skb);
+ skb_pull(skb, ETH_HLEN);
+ eth = eth_hdr(skb);
+
+ if (*eth->h_dest & 1) {
+ if (memcmp(eth->h_dest, dev->broadcast, ETH_ALEN) == 0)
+ skb->pkt_type = PACKET_BROADCAST;
+ else
+ skb->pkt_type = PACKET_MULTICAST;
+ }
+ /*
+ * This ALLMULTI check should be redundant by 1.4
+ * so don't forget to remove it.
+ */
+
+ else if (dev->flags & (IFF_PROMISC /*| IFF_ALLMULTI*/)) {
+ if (memcmp(eth->h_dest, dev->dev_addr, ETH_ALEN))
+ skb->pkt_type = PACKET_OTHERHOST;
+ }
+ if (ntohs(eth->h_proto) >= 1536)
+ return eth->h_proto;
+
+ rawp = skb->data;
+
+ /*
+ * This is a magic hack to spot IPX packets. Older Novell breaks
+ * the protocol design and runs IPX over 802.3 without an 802.2 LLC
+ * layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
+ * won't work for fault tolerant netware but does for the rest.
+ */
+ if (*(unsigned short *) rawp == 0xFFFF)
+ return htons(ETH_P_802_3);
+ /*
+ * Real 802.2 LLC
+ */
+ return htons(ETH_P_802_2);
+}
+
+
+/*
+ * CISCO HDLC keepalive specific stuff
+ */
+static struct sk_buff*
+isdn_net_ciscohdlck_alloc_skb(isdn_net_local *lp, int len)
+{
+ unsigned short hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen;
+ struct sk_buff *skb;
+
+ skb = alloc_skb(hl + len, GFP_ATOMIC);
+ if (skb)
+ skb_reserve(skb, hl);
+ else
+ printk("isdn out of mem at %s:%d!\n", __FILE__, __LINE__);
+ return skb;
+}
+
+/* cisco hdlck device private ioctls */
+static int
+isdn_ciscohdlck_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ isdn_net_local *lp = (isdn_net_local *) dev->priv;
+ unsigned long len = 0;
+ unsigned long expires = 0;
+ int tmp = 0;
+ int period = lp->cisco_keepalive_period;
+ s8 debserint = lp->cisco_debserint;
+ int rc = 0;
+
+ if (lp->p_encap != ISDN_NET_ENCAP_CISCOHDLCK)
+ return -EINVAL;
+
+ switch (cmd) {
+ /* get/set keepalive period */
+ case SIOCGKEEPPERIOD:
+ len = (unsigned long)sizeof(lp->cisco_keepalive_period);
+ if (copy_to_user(ifr->ifr_data,
+ &lp->cisco_keepalive_period, len))
+ rc = -EFAULT;
+ break;
+ case SIOCSKEEPPERIOD:
+ tmp = lp->cisco_keepalive_period;
+ len = (unsigned long)sizeof(lp->cisco_keepalive_period);
+ if (copy_from_user(&period, ifr->ifr_data, len))
+ rc = -EFAULT;
+ if ((period > 0) && (period <= 32767))
+ lp->cisco_keepalive_period = period;
+ else
+ rc = -EINVAL;
+ if (!rc && (tmp != lp->cisco_keepalive_period)) {
+ expires = (unsigned long)(jiffies +
+ lp->cisco_keepalive_period * HZ);
+ mod_timer(&lp->cisco_timer, expires);
+ printk(KERN_INFO "%s: Keepalive period set "
+ "to %d seconds.\n",
+ dev->name, lp->cisco_keepalive_period);
+ }
+ break;
+
+ /* get/set debugging */
+ case SIOCGDEBSERINT:
+ len = (unsigned long)sizeof(lp->cisco_debserint);
+ if (copy_to_user(ifr->ifr_data,
+ &lp->cisco_debserint, len))
+ rc = -EFAULT;
+ break;
+ case SIOCSDEBSERINT:
+ len = (unsigned long)sizeof(lp->cisco_debserint);
+ if (copy_from_user(&debserint,
+ ifr->ifr_data, len))
+ rc = -EFAULT;
+ if ((debserint >= 0) && (debserint <= 64))
+ lp->cisco_debserint = debserint;
+ else
+ rc = -EINVAL;
+ break;
+
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ return (rc);
+}
+
+/* called via cisco_timer.function */
+static void
+isdn_net_ciscohdlck_slarp_send_keepalive(unsigned long data)
+{
+ isdn_net_local *lp = (isdn_net_local *) data;
+ struct sk_buff *skb;
+ unsigned char *p;
+ unsigned long last_cisco_myseq = lp->cisco_myseq;
+ int myseq_diff = 0;
+
+ if (!(lp->flags & ISDN_NET_CONNECTED) || lp->dialstate) {
+ printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__);
+ return;
+ }
+ lp->cisco_myseq++;
+
+ myseq_diff = (lp->cisco_myseq - lp->cisco_mineseen);
+ if ((lp->cisco_line_state) && ((myseq_diff >= 3)||(myseq_diff <= -3))) {
+ /* line up -> down */
+ lp->cisco_line_state = 0;
+ printk (KERN_WARNING
+ "UPDOWN: Line protocol on Interface %s,"
+ " changed state to down\n", lp->netdev->dev->name);
+ /* should stop routing higher-level data accross */
+ } else if ((!lp->cisco_line_state) &&
+ (myseq_diff >= 0) && (myseq_diff <= 2)) {
+ /* line down -> up */
+ lp->cisco_line_state = 1;
+ printk (KERN_WARNING
+ "UPDOWN: Line protocol on Interface %s,"
+ " changed state to up\n", lp->netdev->dev->name);
+ /* restart routing higher-level data accross */
+ }
+
+ if (lp->cisco_debserint)
+ printk (KERN_DEBUG "%s: HDLC "
+ "myseq %lu, mineseen %lu%c, yourseen %lu, %s\n",
+ lp->netdev->dev->name, last_cisco_myseq, lp->cisco_mineseen,
+ ((last_cisco_myseq == lp->cisco_mineseen) ? '*' : 040),
+ lp->cisco_yourseq,
+ ((lp->cisco_line_state) ? "line up" : "line down"));
+
+ skb = isdn_net_ciscohdlck_alloc_skb(lp, 4 + 14);
+ if (!skb)
+ return;
+
+ p = skb_put(skb, 4 + 14);
+
+ /* cisco header */
+ p += put_u8 (p, CISCO_ADDR_UNICAST);
+ p += put_u8 (p, CISCO_CTRL);
+ p += put_u16(p, CISCO_TYPE_SLARP);
+
+ /* slarp keepalive */
+ p += put_u32(p, CISCO_SLARP_KEEPALIVE);
+ p += put_u32(p, lp->cisco_myseq);
+ p += put_u32(p, lp->cisco_yourseq);
+ p += put_u16(p, 0xffff); // reliablity, always 0xffff
+
+ isdn_net_write_super(lp, skb);
+
+ lp->cisco_timer.expires = jiffies + lp->cisco_keepalive_period * HZ;
+
+ add_timer(&lp->cisco_timer);
+}
+
+static void
+isdn_net_ciscohdlck_slarp_send_request(isdn_net_local *lp)
+{
+ struct sk_buff *skb;
+ unsigned char *p;
+
+ skb = isdn_net_ciscohdlck_alloc_skb(lp, 4 + 14);
+ if (!skb)
+ return;
+
+ p = skb_put(skb, 4 + 14);
+
+ /* cisco header */
+ p += put_u8 (p, CISCO_ADDR_UNICAST);
+ p += put_u8 (p, CISCO_CTRL);
+ p += put_u16(p, CISCO_TYPE_SLARP);
+
+ /* slarp request */
+ p += put_u32(p, CISCO_SLARP_REQUEST);
+ p += put_u32(p, 0); // address
+ p += put_u32(p, 0); // netmask
+ p += put_u16(p, 0); // unused
+
+ isdn_net_write_super(lp, skb);
+}
+
+static void
+isdn_net_ciscohdlck_connected(isdn_net_local *lp)
+{
+ lp->cisco_myseq = 0;
+ lp->cisco_mineseen = 0;
+ lp->cisco_yourseq = 0;
+ lp->cisco_keepalive_period = ISDN_TIMER_KEEPINT;
+ lp->cisco_last_slarp_in = 0;
+ lp->cisco_line_state = 0;
+ lp->cisco_debserint = 0;
+
+ /* send slarp request because interface/seq.no.s reset */
+ isdn_net_ciscohdlck_slarp_send_request(lp);
+
+ init_timer(&lp->cisco_timer);
+ lp->cisco_timer.data = (unsigned long) lp;
+ lp->cisco_timer.function = isdn_net_ciscohdlck_slarp_send_keepalive;
+ lp->cisco_timer.expires = jiffies + lp->cisco_keepalive_period * HZ;
+ add_timer(&lp->cisco_timer);
+}
+
+static void
+isdn_net_ciscohdlck_disconnected(isdn_net_local *lp)
+{
+ del_timer(&lp->cisco_timer);
+}
+
+static void
+isdn_net_ciscohdlck_slarp_send_reply(isdn_net_local *lp)
+{
+ struct sk_buff *skb;
+ unsigned char *p;
+ struct in_device *in_dev = NULL;
+ __be32 addr = 0; /* local ipv4 address */
+ __be32 mask = 0; /* local netmask */
+
+ if ((in_dev = lp->netdev->dev->ip_ptr) != NULL) {
+ /* take primary(first) address of interface */
+ struct in_ifaddr *ifa = in_dev->ifa_list;
+ if (ifa != NULL) {
+ addr = ifa->ifa_local;
+ mask = ifa->ifa_mask;
+ }
+ }
+
+ skb = isdn_net_ciscohdlck_alloc_skb(lp, 4 + 14);
+ if (!skb)
+ return;
+
+ p = skb_put(skb, 4 + 14);
+
+ /* cisco header */
+ p += put_u8 (p, CISCO_ADDR_UNICAST);
+ p += put_u8 (p, CISCO_CTRL);
+ p += put_u16(p, CISCO_TYPE_SLARP);
+
+ /* slarp reply, send own ip/netmask; if values are nonsense remote
+ * should think we are unable to provide it with an address via SLARP */
+ p += put_u32(p, CISCO_SLARP_REPLY);
+ *(__be32 *)p = addr; // address
+ p += 4;
+ *(__be32 *)p = mask; // netmask
+ p += 4;
+ p += put_u16(p, 0); // unused
+
+ isdn_net_write_super(lp, skb);
+}
+
+static void
+isdn_net_ciscohdlck_slarp_in(isdn_net_local *lp, struct sk_buff *skb)
+{
+ unsigned char *p;
+ int period;
+ u32 code;
+ u32 my_seq, addr;
+ u32 your_seq, mask;
+ u32 local;
+ u16 unused;
+
+ if (skb->len < 14)
+ return;
+
+ p = skb->data;
+ p += get_u32(p, &code);
+
+ switch (code) {
+ case CISCO_SLARP_REQUEST:
+ lp->cisco_yourseq = 0;
+ isdn_net_ciscohdlck_slarp_send_reply(lp);
+ break;
+ case CISCO_SLARP_REPLY:
+ addr = ntohl(*(u32 *)p);
+ mask = ntohl(*(u32 *)(p+4));
+ if (mask != 0xfffffffc)
+ goto slarp_reply_out;
+ if ((addr & 3) == 0 || (addr & 3) == 3)
+ goto slarp_reply_out;
+ local = addr ^ 3;
+ printk(KERN_INFO "%s: got slarp reply: "
+ "remote ip: %d.%d.%d.%d, "
+ "local ip: %d.%d.%d.%d "
+ "mask: %d.%d.%d.%d\n",
+ lp->netdev->dev->name,
+ HIPQUAD(addr),
+ HIPQUAD(local),
+ HIPQUAD(mask));
+ break;
+ slarp_reply_out:
+ printk(KERN_INFO "%s: got invalid slarp "
+ "reply (%d.%d.%d.%d/%d.%d.%d.%d) "
+ "- ignored\n", lp->netdev->dev->name,
+ HIPQUAD(addr), HIPQUAD(mask));
+ break;
+ case CISCO_SLARP_KEEPALIVE:
+ period = (int)((jiffies - lp->cisco_last_slarp_in
+ + HZ/2 - 1) / HZ);
+ if (lp->cisco_debserint &&
+ (period != lp->cisco_keepalive_period) &&
+ lp->cisco_last_slarp_in) {
+ printk(KERN_DEBUG "%s: Keepalive period mismatch - "
+ "is %d but should be %d.\n",
+ lp->netdev->dev->name, period,
+ lp->cisco_keepalive_period);
+ }
+ lp->cisco_last_slarp_in = jiffies;
+ p += get_u32(p, &my_seq);
+ p += get_u32(p, &your_seq);
+ p += get_u16(p, &unused);
+ lp->cisco_yourseq = my_seq;
+ lp->cisco_mineseen = your_seq;
+ break;
+ }
+}
+
+static void
+isdn_net_ciscohdlck_receive(isdn_net_local *lp, struct sk_buff *skb)
+{
+ unsigned char *p;
+ u8 addr;
+ u8 ctrl;
+ u16 type;
+
+ if (skb->len < 4)
+ goto out_free;
+
+ p = skb->data;
+ p += get_u8 (p, &addr);
+ p += get_u8 (p, &ctrl);
+ p += get_u16(p, &type);
+ skb_pull(skb, 4);
+
+ if (addr != CISCO_ADDR_UNICAST && addr != CISCO_ADDR_BROADCAST) {
+ printk(KERN_WARNING "%s: Unknown Cisco addr 0x%02x\n",
+ lp->netdev->dev->name, addr);
+ goto out_free;
+ }
+ if (ctrl != CISCO_CTRL) {
+ printk(KERN_WARNING "%s: Unknown Cisco ctrl 0x%02x\n",
+ lp->netdev->dev->name, ctrl);
+ goto out_free;
+ }
+
+ switch (type) {
+ case CISCO_TYPE_SLARP:
+ isdn_net_ciscohdlck_slarp_in(lp, skb);
+ goto out_free;
+ case CISCO_TYPE_CDP:
+ if (lp->cisco_debserint)
+ printk(KERN_DEBUG "%s: Received CDP packet. use "
+ "\"no cdp enable\" on cisco.\n",
+ lp->netdev->dev->name);
+ goto out_free;
+ default:
+ /* no special cisco protocol */
+ skb->protocol = htons(type);
+ netif_rx(skb);
+ return;
+ }
+
+ out_free:
+ kfree_skb(skb);
+}
+
+/*
+ * Got a packet from ISDN-Channel.
+ */
+static void
+isdn_net_receive(struct net_device *ndev, struct sk_buff *skb)
+{
+ isdn_net_local *lp = (isdn_net_local *) ndev->priv;
+ isdn_net_local *olp = lp; /* original 'lp' */
+#ifdef CONFIG_ISDN_X25
+ struct concap_proto *cprot = lp -> netdev -> cprot;
+#endif
+ lp->transcount += skb->len;
+
+ lp->stats.rx_packets++;
+ lp->stats.rx_bytes += skb->len;
+ if (lp->master) {
+ /* Bundling: If device is a slave-device, deliver to master, also
+ * handle master's statistics and hangup-timeout
+ */
+ ndev = lp->master;
+ lp = (isdn_net_local *) ndev->priv;
+ lp->stats.rx_packets++;
+ lp->stats.rx_bytes += skb->len;
+ }
+ skb->dev = ndev;
+ skb->pkt_type = PACKET_HOST;
+ skb_reset_mac_header(skb);
+#ifdef ISDN_DEBUG_NET_DUMP
+ isdn_dumppkt("R:", skb->data, skb->len, 40);
+#endif
+ switch (lp->p_encap) {
+ case ISDN_NET_ENCAP_ETHER:
+ /* Ethernet over ISDN */
+ olp->huptimer = 0;
+ lp->huptimer = 0;
+ skb->protocol = isdn_net_type_trans(skb, ndev);
+ break;
+ case ISDN_NET_ENCAP_UIHDLC:
+ /* HDLC with UI-frame (for ispa with -h1 option) */
+ olp->huptimer = 0;
+ lp->huptimer = 0;
+ skb_pull(skb, 2);
+ /* Fall through */
+ case ISDN_NET_ENCAP_RAWIP:
+ /* RAW-IP without MAC-Header */
+ olp->huptimer = 0;
+ lp->huptimer = 0;
+ skb->protocol = htons(ETH_P_IP);
+ break;
+ case ISDN_NET_ENCAP_CISCOHDLCK:
+ isdn_net_ciscohdlck_receive(lp, skb);
+ return;
+ case ISDN_NET_ENCAP_CISCOHDLC:
+ /* CISCO-HDLC IP with type field and fake I-frame-header */
+ skb_pull(skb, 2);
+ /* Fall through */
+ case ISDN_NET_ENCAP_IPTYP:
+ /* IP with type field */
+ olp->huptimer = 0;
+ lp->huptimer = 0;
+ skb->protocol = *(unsigned short *) &(skb->data[0]);
+ skb_pull(skb, 2);
+ if (*(unsigned short *) skb->data == 0xFFFF)
+ skb->protocol = htons(ETH_P_802_3);
+ break;
+#ifdef CONFIG_ISDN_PPP
+ case ISDN_NET_ENCAP_SYNCPPP:
+ /* huptimer is done in isdn_ppp_push_higher */
+ isdn_ppp_receive(lp->netdev, olp, skb);
+ return;
+#endif
+
+ default:
+#ifdef CONFIG_ISDN_X25
+ /* try if there are generic sync_device receiver routines */
+ if(cprot) if(cprot -> pops)
+ if( cprot -> pops -> data_ind){
+ cprot -> pops -> data_ind(cprot,skb);
+ return;
+ };
+#endif /* CONFIG_ISDN_X25 */
+ printk(KERN_WARNING "%s: unknown encapsulation, dropping\n",
+ lp->netdev->dev->name);
+ kfree_skb(skb);
+ return;
+ }
+
+ netif_rx(skb);
+ return;
+}
+
+/*
+ * A packet arrived via ISDN. Search interface-chain for a corresponding
+ * interface. If found, deliver packet to receiver-function and return 1,
+ * else return 0.
+ */
+int
+isdn_net_rcv_skb(int idx, struct sk_buff *skb)
+{
+ isdn_net_dev *p = dev->rx_netdev[idx];
+
+ if (p) {
+ isdn_net_local *lp = p->local;
+ if ((lp->flags & ISDN_NET_CONNECTED) &&
+ (!lp->dialstate)) {
+ isdn_net_receive(p->dev, skb);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * build an header
+ * depends on encaps that is being used.
+ */
+
+static int isdn_net_header(struct sk_buff *skb, struct net_device *dev,
+ unsigned short type,
+ const void *daddr, const void *saddr, unsigned plen)
+{
+ isdn_net_local *lp = dev->priv;
+ unsigned char *p;
+ ushort len = 0;
+
+ switch (lp->p_encap) {
+ case ISDN_NET_ENCAP_ETHER:
+ len = eth_header(skb, dev, type, daddr, saddr, plen);
+ break;
+#ifdef CONFIG_ISDN_PPP
+ case ISDN_NET_ENCAP_SYNCPPP:
+ /* stick on a fake header to keep fragmentation code happy. */
+ len = IPPP_MAX_HEADER;
+ skb_push(skb,len);
+ break;
+#endif
+ case ISDN_NET_ENCAP_RAWIP:
+ printk(KERN_WARNING "isdn_net_header called with RAW_IP!\n");
+ len = 0;
+ break;
+ case ISDN_NET_ENCAP_IPTYP:
+ /* ethernet type field */
+ *((ushort *) skb_push(skb, 2)) = htons(type);
+ len = 2;
+ break;
+ case ISDN_NET_ENCAP_UIHDLC:
+ /* HDLC with UI-Frames (for ispa with -h1 option) */
+ *((ushort *) skb_push(skb, 2)) = htons(0x0103);
+ len = 2;
+ break;
+ case ISDN_NET_ENCAP_CISCOHDLC:
+ case ISDN_NET_ENCAP_CISCOHDLCK:
+ p = skb_push(skb, 4);
+ p += put_u8 (p, CISCO_ADDR_UNICAST);
+ p += put_u8 (p, CISCO_CTRL);
+ p += put_u16(p, type);
+ len = 4;
+ break;
+#ifdef CONFIG_ISDN_X25
+ default:
+ /* try if there are generic concap protocol routines */
+ if( lp-> netdev -> cprot ){
+ printk(KERN_WARNING "isdn_net_header called with concap_proto!\n");
+ len = 0;
+ break;
+ }
+ break;
+#endif /* CONFIG_ISDN_X25 */
+ }
+ return len;
+}
+
+/* We don't need to send arp, because we have point-to-point connections. */
+static int
+isdn_net_rebuild_header(struct sk_buff *skb)
+{
+ struct net_device *dev = skb->dev;
+ isdn_net_local *lp = dev->priv;
+ int ret = 0;
+
+ if (lp->p_encap == ISDN_NET_ENCAP_ETHER) {
+ struct ethhdr *eth = (struct ethhdr *) skb->data;
+
+ /*
+ * Only ARP/IP is currently supported
+ */
+
+ if (eth->h_proto != htons(ETH_P_IP)) {
+ printk(KERN_WARNING
+ "isdn_net: %s don't know how to resolve type %d addresses?\n",
+ dev->name, (int) eth->h_proto);
+ memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
+ return 0;
+ }
+ /*
+ * Try to get ARP to resolve the header.
+ */
+#ifdef CONFIG_INET
+ ret = arp_find(eth->h_dest, skb);
+#endif
+ }
+ return ret;
+}
+
+static int isdn_header_cache(const struct neighbour *neigh, struct hh_cache *hh)
+{
+ const struct net_device *dev = neigh->dev;
+ isdn_net_local *lp = dev->priv;
+
+ if (lp->p_encap == ISDN_NET_ENCAP_ETHER)
+ return eth_header_cache(neigh, hh);
+ return -1;
+}
+
+static void isdn_header_cache_update(struct hh_cache *hh,
+ const struct net_device *dev,
+ const unsigned char *haddr)
+{
+ isdn_net_local *lp = dev->priv;
+ if (lp->p_encap == ISDN_NET_ENCAP_ETHER)
+ return eth_header_cache_update(hh, dev, haddr);
+}
+
+static const struct header_ops isdn_header_ops = {
+ .create = isdn_net_header,
+ .rebuild = isdn_net_rebuild_header,
+ .cache = isdn_header_cache,
+ .cache_update = isdn_header_cache_update,
+};
+
+/*
+ * Interface-setup. (just after registering a new interface)
+ */
+static int
+isdn_net_init(struct net_device *ndev)
+{
+ ushort max_hlhdr_len = 0;
+ int drvidx;
+
+ ether_setup(ndev);
+ ndev->header_ops = NULL;
+
+ /* Setup the generic properties */
+ ndev->mtu = 1500;
+ ndev->flags = IFF_NOARP|IFF_POINTOPOINT;
+ ndev->type = ARPHRD_ETHER;
+ ndev->addr_len = ETH_ALEN;
+ ndev->validate_addr = NULL;
+
+ /* for clients with MPPP maybe higher values better */
+ ndev->tx_queue_len = 30;
+
+ /* The ISDN-specific entries in the device structure. */
+ ndev->open = &isdn_net_open;
+ ndev->hard_start_xmit = &isdn_net_start_xmit;
+
+ /*
+ * up till binding we ask the protocol layer to reserve as much
+ * as we might need for HL layer
+ */
+
+ for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++)
+ if (dev->drv[drvidx])
+ if (max_hlhdr_len < dev->drv[drvidx]->interface->hl_hdrlen)
+ max_hlhdr_len = dev->drv[drvidx]->interface->hl_hdrlen;
+
+ ndev->hard_header_len = ETH_HLEN + max_hlhdr_len;
+ ndev->stop = &isdn_net_close;
+ ndev->get_stats = &isdn_net_get_stats;
+ ndev->do_ioctl = NULL;
+ return 0;
+}
+
+static void
+isdn_net_swapbind(int drvidx)
+{
+ isdn_net_dev *p;
+
+#ifdef ISDN_DEBUG_NET_ICALL
+ printk(KERN_DEBUG "n_fi: swapping ch of %d\n", drvidx);
+#endif
+ p = dev->netdev;
+ while (p) {
+ if (p->local->pre_device == drvidx)
+ switch (p->local->pre_channel) {
+ case 0:
+ p->local->pre_channel = 1;
+ break;
+ case 1:
+ p->local->pre_channel = 0;
+ break;
+ }
+ p = (isdn_net_dev *) p->next;
+ }
+}
+
+static void
+isdn_net_swap_usage(int i1, int i2)
+{
+ int u1 = dev->usage[i1] & ISDN_USAGE_EXCLUSIVE;
+ int u2 = dev->usage[i2] & ISDN_USAGE_EXCLUSIVE;
+
+#ifdef ISDN_DEBUG_NET_ICALL
+ printk(KERN_DEBUG "n_fi: usage of %d and %d\n", i1, i2);
+#endif
+ dev->usage[i1] &= ~ISDN_USAGE_EXCLUSIVE;
+ dev->usage[i1] |= u2;
+ dev->usage[i2] &= ~ISDN_USAGE_EXCLUSIVE;
+ dev->usage[i2] |= u1;
+ isdn_info_update();
+}
+
+/*
+ * An incoming call-request has arrived.
+ * Search the interface-chain for an appropriate interface.
+ * If found, connect the interface to the ISDN-channel and initiate
+ * D- and B-Channel-setup. If secure-flag is set, accept only
+ * configured phone-numbers. If callback-flag is set, initiate
+ * callback-dialing.
+ *
+ * Return-Value: 0 = No appropriate interface for this call.
+ * 1 = Call accepted
+ * 2 = Reject call, wait cbdelay, then call back
+ * 3 = Reject call
+ * 4 = Wait cbdelay, then call back
+ * 5 = No appropriate interface for this call,
+ * would eventually match if CID was longer.
+ */
+
+int
+isdn_net_find_icall(int di, int ch, int idx, setup_parm *setup)
+{
+ char *eaz;
+ int si1;
+ int si2;
+ int ematch;
+ int wret;
+ int swapped;
+ int sidx = 0;
+ u_long flags;
+ isdn_net_dev *p;
+ isdn_net_phone *n;
+ char nr[ISDN_MSNLEN];
+ char *my_eaz;
+
+ /* Search name in netdev-chain */
+ if (!setup->phone[0]) {
+ nr[0] = '0';
+ nr[1] = '\0';
+ printk(KERN_INFO "isdn_net: Incoming call without OAD, assuming '0'\n");
+ } else
+ strlcpy(nr, setup->phone, ISDN_MSNLEN);
+ si1 = (int) setup->si1;
+ si2 = (int) setup->si2;
+ if (!setup->eazmsn[0]) {
+ printk(KERN_WARNING "isdn_net: Incoming call without CPN, assuming '0'\n");
+ eaz = "0";
+ } else
+ eaz = setup->eazmsn;
+ if (dev->net_verbose > 1)
+ printk(KERN_INFO "isdn_net: call from %s,%d,%d -> %s\n", nr, si1, si2, eaz);
+ /* Accept DATA and VOICE calls at this stage
+ * local eaz is checked later for allowed call types
+ */
+ if ((si1 != 7) && (si1 != 1)) {
+ if (dev->net_verbose > 1)
+ printk(KERN_INFO "isdn_net: Service-Indicator not 1 or 7, ignored\n");
+ return 0;
+ }
+ n = (isdn_net_phone *) 0;
+ p = dev->netdev;
+ ematch = wret = swapped = 0;
+#ifdef ISDN_DEBUG_NET_ICALL
+ printk(KERN_DEBUG "n_fi: di=%d ch=%d idx=%d usg=%d\n", di, ch, idx,
+ dev->usage[idx]);
+#endif
+ while (p) {
+ int matchret;
+ isdn_net_local *lp = p->local;
+
+ /* If last check has triggered as binding-swap, revert it */
+ switch (swapped) {
+ case 2:
+ isdn_net_swap_usage(idx, sidx);
+ /* fall through */
+ case 1:
+ isdn_net_swapbind(di);
+ break;
+ }
+ swapped = 0;
+ /* check acceptable call types for DOV */
+ my_eaz = isdn_map_eaz2msn(lp->msn, di);
+ if (si1 == 1) { /* it's a DOV call, check if we allow it */
+ if (*my_eaz == 'v' || *my_eaz == 'V' ||
+ *my_eaz == 'b' || *my_eaz == 'B')
+ my_eaz++; /* skip to allow a match */
+ else
+ my_eaz = NULL; /* force non match */
+ } else { /* it's a DATA call, check if we allow it */
+ if (*my_eaz == 'b' || *my_eaz == 'B')
+ my_eaz++; /* skip to allow a match */
+ }
+ if (my_eaz)
+ matchret = isdn_msncmp(eaz, my_eaz);
+ else
+ matchret = 1;
+ if (!matchret)
+ ematch = 1;
+
+ /* Remember if more numbers eventually can match */
+ if (matchret > wret)
+ wret = matchret;
+#ifdef ISDN_DEBUG_NET_ICALL
+ printk(KERN_DEBUG "n_fi: if='%s', l.msn=%s, l.flags=%d, l.dstate=%d\n",
+ p->dev->name, lp->msn, lp->flags, lp->dialstate);
+#endif
+ if ((!matchret) && /* EAZ is matching */
+ (((!(lp->flags & ISDN_NET_CONNECTED)) && /* but not connected */
+ (USG_NONE(dev->usage[idx]))) || /* and ch. unused or */
+ ((((lp->dialstate == 4) || (lp->dialstate == 12)) && /* if dialing */
+ (!(lp->flags & ISDN_NET_CALLBACK))) /* but no callback */
+ )))
+ {
+#ifdef ISDN_DEBUG_NET_ICALL
+ printk(KERN_DEBUG "n_fi: match1, pdev=%d pch=%d\n",
+ lp->pre_device, lp->pre_channel);
+#endif
+ if (dev->usage[idx] & ISDN_USAGE_EXCLUSIVE) {
+ if ((lp->pre_channel != ch) ||
+ (lp->pre_device != di)) {
+ /* Here we got a problem:
+ * If using an ICN-Card, an incoming call is always signaled on
+ * on the first channel of the card, if both channels are
+ * down. However this channel may be bound exclusive. If the
+ * second channel is free, this call should be accepted.
+ * The solution is horribly but it runs, so what:
+ * We exchange the exclusive bindings of the two channels, the
+ * corresponding variables in the interface-structs.
+ */
+ if (ch == 0) {
+ sidx = isdn_dc2minor(di, 1);
+#ifdef ISDN_DEBUG_NET_ICALL
+ printk(KERN_DEBUG "n_fi: ch is 0\n");
+#endif
+ if (USG_NONE(dev->usage[sidx])) {
+ /* Second Channel is free, now see if it is bound
+ * exclusive too. */
+ if (dev->usage[sidx] & ISDN_USAGE_EXCLUSIVE) {
+#ifdef ISDN_DEBUG_NET_ICALL
+ printk(KERN_DEBUG "n_fi: 2nd channel is down and bound\n");
+#endif
+ /* Yes, swap bindings only, if the original
+ * binding is bound to channel 1 of this driver */
+ if ((lp->pre_device == di) &&
+ (lp->pre_channel == 1)) {
+ isdn_net_swapbind(di);
+ swapped = 1;
+ } else {
+ /* ... else iterate next device */
+ p = (isdn_net_dev *) p->next;
+ continue;
+ }
+ } else {
+#ifdef ISDN_DEBUG_NET_ICALL
+ printk(KERN_DEBUG "n_fi: 2nd channel is down and unbound\n");
+#endif
+ /* No, swap always and swap excl-usage also */
+ isdn_net_swap_usage(idx, sidx);
+ isdn_net_swapbind(di);
+ swapped = 2;
+ }
+ /* Now check for exclusive binding again */
+#ifdef ISDN_DEBUG_NET_ICALL
+ printk(KERN_DEBUG "n_fi: final check\n");
+#endif
+ if ((dev->usage[idx] & ISDN_USAGE_EXCLUSIVE) &&
+ ((lp->pre_channel != ch) ||
+ (lp->pre_device != di))) {
+#ifdef ISDN_DEBUG_NET_ICALL
+ printk(KERN_DEBUG "n_fi: final check failed\n");
+#endif
+ p = (isdn_net_dev *) p->next;
+ continue;
+ }
+ }
+ } else {
+ /* We are already on the second channel, so nothing to do */
+#ifdef ISDN_DEBUG_NET_ICALL
+ printk(KERN_DEBUG "n_fi: already on 2nd channel\n");
+#endif
+ }
+ }
+ }
+#ifdef ISDN_DEBUG_NET_ICALL
+ printk(KERN_DEBUG "n_fi: match2\n");
+#endif
+ n = lp->phone[0];
+ if (lp->flags & ISDN_NET_SECURE) {
+ while (n) {
+ if (!isdn_msncmp(nr, n->num))
+ break;
+ n = (isdn_net_phone *) n->next;
+ }
+ }
+ if (n || (!(lp->flags & ISDN_NET_SECURE))) {
+#ifdef ISDN_DEBUG_NET_ICALL
+ printk(KERN_DEBUG "n_fi: match3\n");
+#endif
+ /* matching interface found */
+
+ /*
+ * Is the state STOPPED?
+ * If so, no dialin is allowed,
+ * so reject actively.
+ * */
+ if (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF) {
+ printk(KERN_INFO "incoming call, interface %s `stopped' -> rejected\n",
+ p->dev->name);
+ return 3;
+ }
+ /*
+ * Is the interface up?
+ * If not, reject the call actively.
+ */
+ if (!isdn_net_device_started(p)) {
+ printk(KERN_INFO "%s: incoming call, interface down -> rejected\n",
+ p->dev->name);
+ return 3;
+ }
+ /* Interface is up, now see if it's a slave. If so, see if
+ * it's master and parent slave is online. If not, reject the call.
+ */
+ if (lp->master) {
+ isdn_net_local *mlp = (isdn_net_local *) lp->master->priv;
+ printk(KERN_DEBUG "ICALLslv: %s\n", p->dev->name);
+ printk(KERN_DEBUG "master=%s\n", lp->master->name);
+ if (mlp->flags & ISDN_NET_CONNECTED) {
+ printk(KERN_DEBUG "master online\n");
+ /* Master is online, find parent-slave (master if first slave) */
+ while (mlp->slave) {
+ if ((isdn_net_local *) mlp->slave->priv == lp)
+ break;
+ mlp = (isdn_net_local *) mlp->slave->priv;
+ }
+ } else
+ printk(KERN_DEBUG "master offline\n");
+ /* Found parent, if it's offline iterate next device */
+ printk(KERN_DEBUG "mlpf: %d\n", mlp->flags & ISDN_NET_CONNECTED);
+ if (!(mlp->flags & ISDN_NET_CONNECTED)) {
+ p = (isdn_net_dev *) p->next;
+ continue;
+ }
+ }
+ if (lp->flags & ISDN_NET_CALLBACK) {
+ int chi;
+ /*
+ * Is the state MANUAL?
+ * If so, no callback can be made,
+ * so reject actively.
+ * */
+ if (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF) {
+ printk(KERN_INFO "incoming call for callback, interface %s `off' -> rejected\n",
+ p->dev->name);
+ return 3;
+ }
+ printk(KERN_DEBUG "%s: call from %s -> %s, start callback\n",
+ p->dev->name, nr, eaz);
+ if (lp->phone[1]) {
+ /* Grab a free ISDN-Channel */
+ spin_lock_irqsave(&dev->lock, flags);
+ if ((chi =
+ isdn_get_free_channel(
+ ISDN_USAGE_NET,
+ lp->l2_proto,
+ lp->l3_proto,
+ lp->pre_device,
+ lp->pre_channel,
+ lp->msn)
+ ) < 0) {
+
+ printk(KERN_WARNING "isdn_net_find_icall: No channel for %s\n",
+ p->dev->name);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return 0;
+ }
+ /* Setup dialstate. */
+ lp->dtimer = 0;
+ lp->dialstate = 11;
+ /* Connect interface with channel */
+ isdn_net_bind_channel(lp, chi);
+#ifdef CONFIG_ISDN_PPP
+ if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+ if (isdn_ppp_bind(lp) < 0) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ isdn_net_unbind_channel(lp);
+ return 0;
+ }
+#endif
+ spin_unlock_irqrestore(&dev->lock, flags);
+ /* Initiate dialing by returning 2 or 4 */
+ return (lp->flags & ISDN_NET_CBHUP) ? 2 : 4;
+ } else
+ printk(KERN_WARNING "isdn_net: %s: No phone number\n",
+ p->dev->name);
+ return 0;
+ } else {
+ printk(KERN_DEBUG "%s: call from %s -> %s accepted\n",
+ p->dev->name, nr, eaz);
+ /* if this interface is dialing, it does it probably on a different
+ device, so free this device */
+ if ((lp->dialstate == 4) || (lp->dialstate == 12)) {
+#ifdef CONFIG_ISDN_PPP
+ if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+ isdn_ppp_free(lp);
+#endif
+ isdn_net_lp_disconnected(lp);
+ isdn_free_channel(lp->isdn_device, lp->isdn_channel,
+ ISDN_USAGE_NET);
+ }
+ spin_lock_irqsave(&dev->lock, flags);
+ dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE;
+ dev->usage[idx] |= ISDN_USAGE_NET;
+ strcpy(dev->num[idx], nr);
+ isdn_info_update();
+ dev->st_netdev[idx] = lp->netdev;
+ lp->isdn_device = di;
+ lp->isdn_channel = ch;
+ lp->ppp_slot = -1;
+ lp->flags |= ISDN_NET_CONNECTED;
+ lp->dialstate = 7;
+ lp->dtimer = 0;
+ lp->outgoing = 0;
+ lp->huptimer = 0;
+ lp->hupflags |= ISDN_WAITCHARGE;
+ lp->hupflags &= ~ISDN_HAVECHARGE;
+#ifdef CONFIG_ISDN_PPP
+ if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) {
+ if (isdn_ppp_bind(lp) < 0) {
+ isdn_net_unbind_channel(lp);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return 0;
+ }
+ }
+#endif
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return 1;
+ }
+ }
+ }
+ p = (isdn_net_dev *) p->next;
+ }
+ /* If none of configured EAZ/MSN matched and not verbose, be silent */
+ if (!ematch || dev->net_verbose)
+ printk(KERN_INFO "isdn_net: call from %s -> %d %s ignored\n", nr, di, eaz);
+ return (wret == 2)?5:0;
+}
+
+/*
+ * Search list of net-interfaces for an interface with given name.
+ */
+isdn_net_dev *
+isdn_net_findif(char *name)
+{
+ isdn_net_dev *p = dev->netdev;
+
+ while (p) {
+ if (!strcmp(p->dev->name, name))
+ return p;
+ p = (isdn_net_dev *) p->next;
+ }
+ return (isdn_net_dev *) NULL;
+}
+
+/*
+ * Force a net-interface to dial out.
+ * This is called from the userlevel-routine below or
+ * from isdn_net_start_xmit().
+ */
+static int
+isdn_net_force_dial_lp(isdn_net_local * lp)
+{
+ if ((!(lp->flags & ISDN_NET_CONNECTED)) && !lp->dialstate) {
+ int chi;
+ if (lp->phone[1]) {
+ ulong flags;
+
+ /* Grab a free ISDN-Channel */
+ spin_lock_irqsave(&dev->lock, flags);
+ if ((chi = isdn_get_free_channel(
+ ISDN_USAGE_NET,
+ lp->l2_proto,
+ lp->l3_proto,
+ lp->pre_device,
+ lp->pre_channel,
+ lp->msn)) < 0) {
+ printk(KERN_WARNING "isdn_net_force_dial: No channel for %s\n",
+ lp->netdev->dev->name);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -EAGAIN;
+ }
+ lp->dialstate = 1;
+ /* Connect interface with channel */
+ isdn_net_bind_channel(lp, chi);
+#ifdef CONFIG_ISDN_PPP
+ if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP)
+ if (isdn_ppp_bind(lp) < 0) {
+ isdn_net_unbind_channel(lp);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -EAGAIN;
+ }
+#endif
+ /* Initiate dialing */
+ spin_unlock_irqrestore(&dev->lock, flags);
+ isdn_net_dial();
+ return 0;
+ } else
+ return -EINVAL;
+ } else
+ return -EBUSY;
+}
+
+/*
+ * This is called from certain upper protocol layers (multilink ppp
+ * and x25iface encapsulation module) that want to initiate dialing
+ * themselves.
+ */
+int
+isdn_net_dial_req(isdn_net_local * lp)
+{
+ /* is there a better error code? */
+ if (!(ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_AUTO)) return -EBUSY;
+
+ return isdn_net_force_dial_lp(lp);
+}
+
+/*
+ * Force a net-interface to dial out.
+ * This is always called from within userspace (ISDN_IOCTL_NET_DIAL).
+ */
+int
+isdn_net_force_dial(char *name)
+{
+ isdn_net_dev *p = isdn_net_findif(name);
+
+ if (!p)
+ return -ENODEV;
+ return (isdn_net_force_dial_lp(p->local));
+}
+
+/*
+ * Helper for alloc_netdev()
+ */
+static void _isdn_setup(struct net_device *dev)
+{
+ isdn_net_local *lp = dev->priv;
+
+ dev->flags = IFF_NOARP | IFF_POINTOPOINT;
+ lp->p_encap = ISDN_NET_ENCAP_RAWIP;
+ lp->magic = ISDN_NET_MAGIC;
+ lp->last = lp;
+ lp->next = lp;
+ lp->isdn_device = -1;
+ lp->isdn_channel = -1;
+ lp->pre_device = -1;
+ lp->pre_channel = -1;
+ lp->exclusive = -1;
+ lp->ppp_slot = -1;
+ lp->pppbind = -1;
+ skb_queue_head_init(&lp->super_tx_queue);
+ lp->l2_proto = ISDN_PROTO_L2_X75I;
+ lp->l3_proto = ISDN_PROTO_L3_TRANS;
+ lp->triggercps = 6000;
+ lp->slavedelay = 10 * HZ;
+ lp->hupflags = ISDN_INHUP; /* Do hangup even on incoming calls */
+ lp->onhtime = 10; /* Default hangup-time for saving costs */
+ lp->dialmax = 1;
+ /* Hangup before Callback, manual dial */
+ lp->flags = ISDN_NET_CBHUP | ISDN_NET_DM_MANUAL;
+ lp->cbdelay = 25; /* Wait 5 secs before Callback */
+ lp->dialtimeout = -1; /* Infinite Dial-Timeout */
+ lp->dialwait = 5 * HZ; /* Wait 5 sec. after failed dial */
+ lp->dialstarted = 0; /* Jiffies of last dial-start */
+ lp->dialwait_timer = 0; /* Jiffies of earliest next dial-start */
+}
+
+/*
+ * Allocate a new network-interface and initialize its data structures.
+ */
+char *
+isdn_net_new(char *name, struct net_device *master)
+{
+ isdn_net_dev *netdev;
+
+ /* Avoid creating an existing interface */
+ if (isdn_net_findif(name)) {
+ printk(KERN_WARNING "isdn_net: interface %s already exists\n", name);
+ return NULL;
+ }
+ if (name == NULL)
+ return NULL;
+ if (!(netdev = kzalloc(sizeof(isdn_net_dev), GFP_KERNEL))) {
+ printk(KERN_WARNING "isdn_net: Could not allocate net-device\n");
+ return NULL;
+ }
+ netdev->dev = alloc_netdev(sizeof(isdn_net_local), name, _isdn_setup);
+ if (!netdev->dev) {
+ printk(KERN_WARNING "isdn_net: Could not allocate network device\n");
+ kfree(netdev);
+ return NULL;
+ }
+ netdev->local = netdev->dev->priv;
+ netdev->dev->init = isdn_net_init;
+ if (master) {
+ /* Device shall be a slave */
+ struct net_device *p = (((isdn_net_local *) master->priv)->slave);
+ struct net_device *q = master;
+
+ netdev->local->master = master;
+ /* Put device at end of slave-chain */
+ while (p) {
+ q = p;
+ p = (((isdn_net_local *) p->priv)->slave);
+ }
+ ((isdn_net_local *) q->priv)->slave = netdev->dev;
+ } else {
+ /* Device shall be a master */
+ /*
+ * Watchdog timer (currently) for master only.
+ */
+ netdev->dev->tx_timeout = isdn_net_tx_timeout;
+ netdev->dev->watchdog_timeo = ISDN_NET_TX_TIMEOUT;
+ if (register_netdev(netdev->dev) != 0) {
+ printk(KERN_WARNING "isdn_net: Could not register net-device\n");
+ free_netdev(netdev->dev);
+ kfree(netdev);
+ return NULL;
+ }
+ }
+ netdev->queue = netdev->local;
+ spin_lock_init(&netdev->queue_lock);
+
+ netdev->local->netdev = netdev;
+
+ INIT_WORK(&netdev->local->tqueue, isdn_net_softint);
+ spin_lock_init(&netdev->local->xmit_lock);
+
+ /* Put into to netdev-chain */
+ netdev->next = (void *) dev->netdev;
+ dev->netdev = netdev;
+ return netdev->dev->name;
+}
+
+char *
+isdn_net_newslave(char *parm)
+{
+ char *p = strchr(parm, ',');
+ isdn_net_dev *n;
+ char newname[10];
+
+ if (p) {
+ /* Slave-Name MUST not be empty */
+ if (!strlen(p + 1))
+ return NULL;
+ strcpy(newname, p + 1);
+ *p = 0;
+ /* Master must already exist */
+ if (!(n = isdn_net_findif(parm)))
+ return NULL;
+ /* Master must be a real interface, not a slave */
+ if (n->local->master)
+ return NULL;
+ /* Master must not be started yet */
+ if (isdn_net_device_started(n))
+ return NULL;
+ return (isdn_net_new(newname, n->dev));
+ }
+ return NULL;
+}
+
+/*
+ * Set interface-parameters.
+ * Always set all parameters, so the user-level application is responsible
+ * for not overwriting existing setups. It has to get the current
+ * setup first, if only selected parameters are to be changed.
+ */
+int
+isdn_net_setcfg(isdn_net_ioctl_cfg * cfg)
+{
+ isdn_net_dev *p = isdn_net_findif(cfg->name);
+ ulong features;
+ int i;
+ int drvidx;
+ int chidx;
+ char drvid[25];
+
+ if (p) {
+ isdn_net_local *lp = p->local;
+
+ /* See if any registered driver supports the features we want */
+ features = ((1 << cfg->l2_proto) << ISDN_FEATURE_L2_SHIFT) |
+ ((1 << cfg->l3_proto) << ISDN_FEATURE_L3_SHIFT);
+ for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+ if (dev->drv[i])
+ if ((dev->drv[i]->interface->features & features) == features)
+ break;
+ if (i == ISDN_MAX_DRIVERS) {
+ printk(KERN_WARNING "isdn_net: No driver with selected features\n");
+ return -ENODEV;
+ }
+ if (lp->p_encap != cfg->p_encap){
+#ifdef CONFIG_ISDN_X25
+ struct concap_proto * cprot = p -> cprot;
+#endif
+ if (isdn_net_device_started(p)) {
+ printk(KERN_WARNING "%s: cannot change encap when if is up\n",
+ p->dev->name);
+ return -EBUSY;
+ }
+#ifdef CONFIG_ISDN_X25
+ if( cprot && cprot -> pops )
+ cprot -> pops -> proto_del ( cprot );
+ p -> cprot = NULL;
+ lp -> dops = NULL;
+ /* ... , prepare for configuration of new one ... */
+ switch ( cfg -> p_encap ){
+ case ISDN_NET_ENCAP_X25IFACE:
+ lp -> dops = &isdn_concap_reliable_dl_dops;
+ }
+ /* ... and allocate new one ... */
+ p -> cprot = isdn_concap_new( cfg -> p_encap );
+ /* p -> cprot == NULL now if p_encap is not supported
+ by means of the concap_proto mechanism */
+ /* the protocol is not configured yet; this will
+ happen later when isdn_net_reset() is called */
+#endif
+ }
+ switch ( cfg->p_encap ) {
+ case ISDN_NET_ENCAP_SYNCPPP:
+#ifndef CONFIG_ISDN_PPP
+ printk(KERN_WARNING "%s: SyncPPP support not configured\n",
+ p->dev->name);
+ return -EINVAL;
+#else
+ p->dev->type = ARPHRD_PPP; /* change ARP type */
+ p->dev->addr_len = 0;
+ p->dev->do_ioctl = isdn_ppp_dev_ioctl;
+#endif
+ break;
+ case ISDN_NET_ENCAP_X25IFACE:
+#ifndef CONFIG_ISDN_X25
+ printk(KERN_WARNING "%s: isdn-x25 support not configured\n",
+ p->dev->name);
+ return -EINVAL;
+#else
+ p->dev->type = ARPHRD_X25; /* change ARP type */
+ p->dev->addr_len = 0;
+#endif
+ break;
+ case ISDN_NET_ENCAP_CISCOHDLCK:
+ p->dev->do_ioctl = isdn_ciscohdlck_dev_ioctl;
+ break;
+ default:
+ if( cfg->p_encap >= 0 &&
+ cfg->p_encap <= ISDN_NET_ENCAP_MAX_ENCAP )
+ break;
+ printk(KERN_WARNING
+ "%s: encapsulation protocol %d not supported\n",
+ p->dev->name, cfg->p_encap);
+ return -EINVAL;
+ }
+ if (strlen(cfg->drvid)) {
+ /* A bind has been requested ... */
+ char *c,
+ *e;
+
+ drvidx = -1;
+ chidx = -1;
+ strcpy(drvid, cfg->drvid);
+ if ((c = strchr(drvid, ','))) {
+ /* The channel-number is appended to the driver-Id with a comma */
+ chidx = (int) simple_strtoul(c + 1, &e, 10);
+ if (e == c)
+ chidx = -1;
+ *c = '\0';
+ }
+ for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+ /* Lookup driver-Id in array */
+ if (!(strcmp(dev->drvid[i], drvid))) {
+ drvidx = i;
+ break;
+ }
+ if ((drvidx == -1) || (chidx == -1))
+ /* Either driver-Id or channel-number invalid */
+ return -ENODEV;
+ } else {
+ /* Parameters are valid, so get them */
+ drvidx = lp->pre_device;
+ chidx = lp->pre_channel;
+ }
+ if (cfg->exclusive > 0) {
+ unsigned long flags;
+
+ /* If binding is exclusive, try to grab the channel */
+ spin_lock_irqsave(&dev->lock, flags);
+ if ((i = isdn_get_free_channel(ISDN_USAGE_NET,
+ lp->l2_proto, lp->l3_proto, drvidx,
+ chidx, lp->msn)) < 0) {
+ /* Grab failed, because desired channel is in use */
+ lp->exclusive = -1;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -EBUSY;
+ }
+ /* All went ok, so update isdninfo */
+ dev->usage[i] = ISDN_USAGE_EXCLUSIVE;
+ isdn_info_update();
+ spin_unlock_irqrestore(&dev->lock, flags);
+ lp->exclusive = i;
+ } else {
+ /* Non-exclusive binding or unbind. */
+ lp->exclusive = -1;
+ if ((lp->pre_device != -1) && (cfg->exclusive == -1)) {
+ isdn_unexclusive_channel(lp->pre_device, lp->pre_channel);
+ isdn_free_channel(lp->pre_device, lp->pre_channel, ISDN_USAGE_NET);
+ drvidx = -1;
+ chidx = -1;
+ }
+ }
+ strlcpy(lp->msn, cfg->eaz, sizeof(lp->msn));
+ lp->pre_device = drvidx;
+ lp->pre_channel = chidx;
+ lp->onhtime = cfg->onhtime;
+ lp->charge = cfg->charge;
+ lp->l2_proto = cfg->l2_proto;
+ lp->l3_proto = cfg->l3_proto;
+ lp->cbdelay = cfg->cbdelay;
+ lp->dialmax = cfg->dialmax;
+ lp->triggercps = cfg->triggercps;
+ lp->slavedelay = cfg->slavedelay * HZ;
+ lp->pppbind = cfg->pppbind;
+ lp->dialtimeout = cfg->dialtimeout >= 0 ? cfg->dialtimeout * HZ : -1;
+ lp->dialwait = cfg->dialwait * HZ;
+ if (cfg->secure)
+ lp->flags |= ISDN_NET_SECURE;
+ else
+ lp->flags &= ~ISDN_NET_SECURE;
+ if (cfg->cbhup)
+ lp->flags |= ISDN_NET_CBHUP;
+ else
+ lp->flags &= ~ISDN_NET_CBHUP;
+ switch (cfg->callback) {
+ case 0:
+ lp->flags &= ~(ISDN_NET_CALLBACK | ISDN_NET_CBOUT);
+ break;
+ case 1:
+ lp->flags |= ISDN_NET_CALLBACK;
+ lp->flags &= ~ISDN_NET_CBOUT;
+ break;
+ case 2:
+ lp->flags |= ISDN_NET_CBOUT;
+ lp->flags &= ~ISDN_NET_CALLBACK;
+ break;
+ }
+ lp->flags &= ~ISDN_NET_DIALMODE_MASK; /* first all bits off */
+ if (cfg->dialmode && !(cfg->dialmode & ISDN_NET_DIALMODE_MASK)) {
+ /* old isdnctrl version, where only 0 or 1 is given */
+ printk(KERN_WARNING
+ "Old isdnctrl version detected! Please update.\n");
+ lp->flags |= ISDN_NET_DM_OFF; /* turn on `off' bit */
+ }
+ else {
+ lp->flags |= cfg->dialmode; /* turn on selected bits */
+ }
+ if (cfg->chargehup)
+ lp->hupflags |= ISDN_CHARGEHUP;
+ else
+ lp->hupflags &= ~ISDN_CHARGEHUP;
+ if (cfg->ihup)
+ lp->hupflags |= ISDN_INHUP;
+ else
+ lp->hupflags &= ~ISDN_INHUP;
+ if (cfg->chargeint > 10) {
+ lp->hupflags |= ISDN_CHARGEHUP | ISDN_HAVECHARGE | ISDN_MANCHARGE;
+ lp->chargeint = cfg->chargeint * HZ;
+ }
+ if (cfg->p_encap != lp->p_encap) {
+ if (cfg->p_encap == ISDN_NET_ENCAP_RAWIP) {
+ p->dev->header_ops = NULL;
+ p->dev->flags = IFF_NOARP|IFF_POINTOPOINT;
+ } else {
+ p->dev->header_ops = &isdn_header_ops;
+ if (cfg->p_encap == ISDN_NET_ENCAP_ETHER)
+ p->dev->flags = IFF_BROADCAST | IFF_MULTICAST;
+ else
+ p->dev->flags = IFF_NOARP|IFF_POINTOPOINT;
+ }
+ }
+ lp->p_encap = cfg->p_encap;
+ return 0;
+ }
+ return -ENODEV;
+}
+
+/*
+ * Perform get-interface-parameters.ioctl
+ */
+int
+isdn_net_getcfg(isdn_net_ioctl_cfg * cfg)
+{
+ isdn_net_dev *p = isdn_net_findif(cfg->name);
+
+ if (p) {
+ isdn_net_local *lp = p->local;
+
+ strcpy(cfg->eaz, lp->msn);
+ cfg->exclusive = lp->exclusive;
+ if (lp->pre_device >= 0) {
+ sprintf(cfg->drvid, "%s,%d", dev->drvid[lp->pre_device],
+ lp->pre_channel);
+ } else
+ cfg->drvid[0] = '\0';
+ cfg->onhtime = lp->onhtime;
+ cfg->charge = lp->charge;
+ cfg->l2_proto = lp->l2_proto;
+ cfg->l3_proto = lp->l3_proto;
+ cfg->p_encap = lp->p_encap;
+ cfg->secure = (lp->flags & ISDN_NET_SECURE) ? 1 : 0;
+ cfg->callback = 0;
+ if (lp->flags & ISDN_NET_CALLBACK)
+ cfg->callback = 1;
+ if (lp->flags & ISDN_NET_CBOUT)
+ cfg->callback = 2;
+ cfg->cbhup = (lp->flags & ISDN_NET_CBHUP) ? 1 : 0;
+ cfg->dialmode = lp->flags & ISDN_NET_DIALMODE_MASK;
+ cfg->chargehup = (lp->hupflags & 4) ? 1 : 0;
+ cfg->ihup = (lp->hupflags & 8) ? 1 : 0;
+ cfg->cbdelay = lp->cbdelay;
+ cfg->dialmax = lp->dialmax;
+ cfg->triggercps = lp->triggercps;
+ cfg->slavedelay = lp->slavedelay / HZ;
+ cfg->chargeint = (lp->hupflags & ISDN_CHARGEHUP) ?
+ (lp->chargeint / HZ) : 0;
+ cfg->pppbind = lp->pppbind;
+ cfg->dialtimeout = lp->dialtimeout >= 0 ? lp->dialtimeout / HZ : -1;
+ cfg->dialwait = lp->dialwait / HZ;
+ if (lp->slave) {
+ if (strlen(lp->slave->name) > 8)
+ strcpy(cfg->slave, "too-long");
+ else
+ strcpy(cfg->slave, lp->slave->name);
+ } else
+ cfg->slave[0] = '\0';
+ if (lp->master) {
+ if (strlen(lp->master->name) > 8)
+ strcpy(cfg->master, "too-long");
+ strcpy(cfg->master, lp->master->name);
+ } else
+ cfg->master[0] = '\0';
+ return 0;
+ }
+ return -ENODEV;
+}
+
+/*
+ * Add a phone-number to an interface.
+ */
+int
+isdn_net_addphone(isdn_net_ioctl_phone * phone)
+{
+ isdn_net_dev *p = isdn_net_findif(phone->name);
+ isdn_net_phone *n;
+
+ if (p) {
+ if (!(n = kmalloc(sizeof(isdn_net_phone), GFP_KERNEL)))
+ return -ENOMEM;
+ strlcpy(n->num, phone->phone, sizeof(n->num));
+ n->next = p->local->phone[phone->outgoing & 1];
+ p->local->phone[phone->outgoing & 1] = n;
+ return 0;
+ }
+ return -ENODEV;
+}
+
+/*
+ * Copy a string of all phone-numbers of an interface to user space.
+ * This might sleep and must be called with the isdn semaphore down.
+ */
+int
+isdn_net_getphones(isdn_net_ioctl_phone * phone, char __user *phones)
+{
+ isdn_net_dev *p = isdn_net_findif(phone->name);
+ int inout = phone->outgoing & 1;
+ int more = 0;
+ int count = 0;
+ isdn_net_phone *n;
+
+ if (!p)
+ return -ENODEV;
+ inout &= 1;
+ for (n = p->local->phone[inout]; n; n = n->next) {
+ if (more) {
+ put_user(' ', phones++);
+ count++;
+ }
+ if (copy_to_user(phones, n->num, strlen(n->num) + 1)) {
+ return -EFAULT;
+ }
+ phones += strlen(n->num);
+ count += strlen(n->num);
+ more = 1;
+ }
+ put_user(0, phones);
+ count++;
+ return count;
+}
+
+/*
+ * Copy a string containing the peer's phone number of a connected interface
+ * to user space.
+ */
+int
+isdn_net_getpeer(isdn_net_ioctl_phone *phone, isdn_net_ioctl_phone __user *peer)
+{
+ isdn_net_dev *p = isdn_net_findif(phone->name);
+ int ch, dv, idx;
+
+ if (!p)
+ return -ENODEV;
+ /*
+ * Theoretical race: while this executes, the remote number might
+ * become invalid (hang up) or change (new connection), resulting
+ * in (partially) wrong number copied to user. This race
+ * currently ignored.
+ */
+ ch = p->local->isdn_channel;
+ dv = p->local->isdn_device;
+ if(ch < 0 && dv < 0)
+ return -ENOTCONN;
+ idx = isdn_dc2minor(dv, ch);
+ if (idx <0 )
+ return -ENODEV;
+ /* for pre-bound channels, we need this extra check */
+ if (strncmp(dev->num[idx], "???", 3) == 0)
+ return -ENOTCONN;
+ strncpy(phone->phone, dev->num[idx], ISDN_MSNLEN);
+ phone->outgoing = USG_OUTGOING(dev->usage[idx]);
+ if (copy_to_user(peer, phone, sizeof(*peer)))
+ return -EFAULT;
+ return 0;
+}
+/*
+ * Delete a phone-number from an interface.
+ */
+int
+isdn_net_delphone(isdn_net_ioctl_phone * phone)
+{
+ isdn_net_dev *p = isdn_net_findif(phone->name);
+ int inout = phone->outgoing & 1;
+ isdn_net_phone *n;
+ isdn_net_phone *m;
+
+ if (p) {
+ n = p->local->phone[inout];
+ m = NULL;
+ while (n) {
+ if (!strcmp(n->num, phone->phone)) {
+ if (p->local->dial == n)
+ p->local->dial = n->next;
+ if (m)
+ m->next = n->next;
+ else
+ p->local->phone[inout] = n->next;
+ kfree(n);
+ return 0;
+ }
+ m = n;
+ n = (isdn_net_phone *) n->next;
+ }
+ return -EINVAL;
+ }
+ return -ENODEV;
+}
+
+/*
+ * Delete all phone-numbers of an interface.
+ */
+static int
+isdn_net_rmallphone(isdn_net_dev * p)
+{
+ isdn_net_phone *n;
+ isdn_net_phone *m;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ n = p->local->phone[i];
+ while (n) {
+ m = n->next;
+ kfree(n);
+ n = m;
+ }
+ p->local->phone[i] = NULL;
+ }
+ p->local->dial = NULL;
+ return 0;
+}
+
+/*
+ * Force a hangup of a network-interface.
+ */
+int
+isdn_net_force_hangup(char *name)
+{
+ isdn_net_dev *p = isdn_net_findif(name);
+ struct net_device *q;
+
+ if (p) {
+ if (p->local->isdn_device < 0)
+ return 1;
+ q = p->local->slave;
+ /* If this interface has slaves, do a hangup for them also. */
+ while (q) {
+ isdn_net_hangup(q);
+ q = (((isdn_net_local *) q->priv)->slave);
+ }
+ isdn_net_hangup(p->dev);
+ return 0;
+ }
+ return -ENODEV;
+}
+
+/*
+ * Helper-function for isdn_net_rm: Do the real work.
+ */
+static int
+isdn_net_realrm(isdn_net_dev * p, isdn_net_dev * q)
+{
+ u_long flags;
+
+ if (isdn_net_device_started(p)) {
+ return -EBUSY;
+ }
+#ifdef CONFIG_ISDN_X25
+ if( p -> cprot && p -> cprot -> pops )
+ p -> cprot -> pops -> proto_del ( p -> cprot );
+#endif
+ /* Free all phone-entries */
+ isdn_net_rmallphone(p);
+ /* If interface is bound exclusive, free channel-usage */
+ if (p->local->exclusive != -1)
+ isdn_unexclusive_channel(p->local->pre_device, p->local->pre_channel);
+ if (p->local->master) {
+ /* It's a slave-device, so update master's slave-pointer if necessary */
+ if (((isdn_net_local *) (p->local->master->priv))->slave == p->dev)
+ ((isdn_net_local *) (p->local->master->priv))->slave = p->local->slave;
+ } else {
+ /* Unregister only if it's a master-device */
+ unregister_netdev(p->dev);
+ }
+ /* Unlink device from chain */
+ spin_lock_irqsave(&dev->lock, flags);
+ if (q)
+ q->next = p->next;
+ else
+ dev->netdev = p->next;
+ if (p->local->slave) {
+ /* If this interface has a slave, remove it also */
+ char *slavename = p->local->slave->name;
+ isdn_net_dev *n = dev->netdev;
+ q = NULL;
+ while (n) {
+ if (!strcmp(n->dev->name, slavename)) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ isdn_net_realrm(n, q);
+ spin_lock_irqsave(&dev->lock, flags);
+ break;
+ }
+ q = n;
+ n = (isdn_net_dev *)n->next;
+ }
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+ /* If no more net-devices remain, disable auto-hangup timer */
+ if (dev->netdev == NULL)
+ isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 0);
+ free_netdev(p->dev);
+ kfree(p);
+
+ return 0;
+}
+
+/*
+ * Remove a single network-interface.
+ */
+int
+isdn_net_rm(char *name)
+{
+ u_long flags;
+ isdn_net_dev *p;
+ isdn_net_dev *q;
+
+ /* Search name in netdev-chain */
+ spin_lock_irqsave(&dev->lock, flags);
+ p = dev->netdev;
+ q = NULL;
+ while (p) {
+ if (!strcmp(p->dev->name, name)) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return (isdn_net_realrm(p, q));
+ }
+ q = p;
+ p = (isdn_net_dev *) p->next;
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+ /* If no more net-devices remain, disable auto-hangup timer */
+ if (dev->netdev == NULL)
+ isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 0);
+ return -ENODEV;
+}
+
+/*
+ * Remove all network-interfaces
+ */
+int
+isdn_net_rmall(void)
+{
+ u_long flags;
+ int ret;
+
+ /* Walk through netdev-chain */
+ spin_lock_irqsave(&dev->lock, flags);
+ while (dev->netdev) {
+ if (!dev->netdev->local->master) {
+ /* Remove master-devices only, slaves get removed with their master */
+ spin_unlock_irqrestore(&dev->lock, flags);
+ if ((ret = isdn_net_realrm(dev->netdev, NULL))) {
+ return ret;
+ }
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+ }
+ dev->netdev = NULL;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return 0;
+}
diff --git a/drivers/isdn/i4l/isdn_net.h b/drivers/isdn/i4l/isdn_net.h
new file mode 100644
index 0000000..be49497
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_net.h
@@ -0,0 +1,190 @@
+/* $Id: isdn_net.h,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $
+ *
+ * header for Linux ISDN subsystem, network related functions (linklevel).
+ *
+ * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de)
+ * Copyright 1995,96 by Thinking Objects Software GmbH Wuerzburg
+ * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+ /* Definitions for hupflags: */
+#define ISDN_WAITCHARGE 1 /* did not get a charge info yet */
+#define ISDN_HAVECHARGE 2 /* We know a charge info */
+#define ISDN_CHARGEHUP 4 /* We want to use the charge mechanism */
+#define ISDN_INHUP 8 /* Even if incoming, close after huptimeout */
+#define ISDN_MANCHARGE 16 /* Charge Interval manually set */
+
+/*
+ * Definitions for Cisco-HDLC header.
+ */
+
+#define CISCO_ADDR_UNICAST 0x0f
+#define CISCO_ADDR_BROADCAST 0x8f
+#define CISCO_CTRL 0x00
+#define CISCO_TYPE_CDP 0x2000
+#define CISCO_TYPE_SLARP 0x8035
+#define CISCO_SLARP_REQUEST 0
+#define CISCO_SLARP_REPLY 1
+#define CISCO_SLARP_KEEPALIVE 2
+
+extern char *isdn_net_new(char *, struct net_device *);
+extern char *isdn_net_newslave(char *);
+extern int isdn_net_rm(char *);
+extern int isdn_net_rmall(void);
+extern int isdn_net_stat_callback(int, isdn_ctrl *);
+extern int isdn_net_setcfg(isdn_net_ioctl_cfg *);
+extern int isdn_net_getcfg(isdn_net_ioctl_cfg *);
+extern int isdn_net_addphone(isdn_net_ioctl_phone *);
+extern int isdn_net_getphones(isdn_net_ioctl_phone *, char __user *);
+extern int isdn_net_getpeer(isdn_net_ioctl_phone *, isdn_net_ioctl_phone __user *);
+extern int isdn_net_delphone(isdn_net_ioctl_phone *);
+extern int isdn_net_find_icall(int, int, int, setup_parm *);
+extern void isdn_net_hangup(struct net_device *);
+extern void isdn_net_dial(void);
+extern void isdn_net_autohup(void);
+extern int isdn_net_force_hangup(char *);
+extern int isdn_net_force_dial(char *);
+extern isdn_net_dev *isdn_net_findif(char *);
+extern int isdn_net_rcv_skb(int, struct sk_buff *);
+extern int isdn_net_dial_req(isdn_net_local *);
+extern void isdn_net_writebuf_skb(isdn_net_local *lp, struct sk_buff *skb);
+extern void isdn_net_write_super(isdn_net_local *lp, struct sk_buff *skb);
+
+#define ISDN_NET_MAX_QUEUE_LENGTH 2
+
+/*
+ * is this particular channel busy?
+ */
+static __inline__ int isdn_net_lp_busy(isdn_net_local *lp)
+{
+ if (atomic_read(&lp->frame_cnt) < ISDN_NET_MAX_QUEUE_LENGTH)
+ return 0;
+ else
+ return 1;
+}
+
+/*
+ * For the given net device, this will get a non-busy channel out of the
+ * corresponding bundle. The returned channel is locked.
+ */
+static __inline__ isdn_net_local * isdn_net_get_locked_lp(isdn_net_dev *nd)
+{
+ unsigned long flags;
+ isdn_net_local *lp;
+
+ spin_lock_irqsave(&nd->queue_lock, flags);
+ lp = nd->queue; /* get lp on top of queue */
+ spin_lock(&nd->queue->xmit_lock);
+ while (isdn_net_lp_busy(nd->queue)) {
+ spin_unlock(&nd->queue->xmit_lock);
+ nd->queue = nd->queue->next;
+ if (nd->queue == lp) { /* not found -- should never happen */
+ lp = NULL;
+ goto errout;
+ }
+ spin_lock(&nd->queue->xmit_lock);
+ }
+ lp = nd->queue;
+ nd->queue = nd->queue->next;
+ local_bh_disable();
+errout:
+ spin_unlock_irqrestore(&nd->queue_lock, flags);
+ return lp;
+}
+
+/*
+ * add a channel to a bundle
+ */
+static __inline__ void isdn_net_add_to_bundle(isdn_net_dev *nd, isdn_net_local *nlp)
+{
+ isdn_net_local *lp;
+ unsigned long flags;
+
+ spin_lock_irqsave(&nd->queue_lock, flags);
+
+ lp = nd->queue;
+// printk(KERN_DEBUG "%s: lp:%s(%p) nlp:%s(%p) last(%p)\n",
+// __func__, lp->name, lp, nlp->name, nlp, lp->last);
+ nlp->last = lp->last;
+ lp->last->next = nlp;
+ lp->last = nlp;
+ nlp->next = lp;
+ nd->queue = nlp;
+
+ spin_unlock_irqrestore(&nd->queue_lock, flags);
+}
+/*
+ * remove a channel from the bundle it belongs to
+ */
+static __inline__ void isdn_net_rm_from_bundle(isdn_net_local *lp)
+{
+ isdn_net_local *master_lp = lp;
+ unsigned long flags;
+
+ if (lp->master)
+ master_lp = (isdn_net_local *) lp->master->priv;
+
+// printk(KERN_DEBUG "%s: lp:%s(%p) mlp:%s(%p) last(%p) next(%p) mndq(%p)\n",
+// __func__, lp->name, lp, master_lp->name, master_lp, lp->last, lp->next, master_lp->netdev->queue);
+ spin_lock_irqsave(&master_lp->netdev->queue_lock, flags);
+ lp->last->next = lp->next;
+ lp->next->last = lp->last;
+ if (master_lp->netdev->queue == lp) {
+ master_lp->netdev->queue = lp->next;
+ if (lp->next == lp) { /* last in queue */
+ master_lp->netdev->queue = master_lp->netdev->local;
+ }
+ }
+ lp->next = lp->last = lp; /* (re)set own pointers */
+// printk(KERN_DEBUG "%s: mndq(%p)\n",
+// __func__, master_lp->netdev->queue);
+ spin_unlock_irqrestore(&master_lp->netdev->queue_lock, flags);
+}
+
+static inline int
+put_u8(unsigned char *p, u8 x)
+{
+ *p = x;
+ return 1;
+}
+
+static inline int
+put_u16(unsigned char *p, u16 x)
+{
+ *((u16 *)p) = htons(x);
+ return 2;
+}
+
+static inline int
+put_u32(unsigned char *p, u32 x)
+{
+ *((u32 *)p) = htonl(x);
+ return 4;
+}
+
+static inline int
+get_u8(unsigned char *p, u8 *x)
+{
+ *x = *p;
+ return 1;
+}
+
+static inline int
+get_u16(unsigned char *p, u16 *x)
+{
+ *x = ntohs(*((u16 *)p));
+ return 2;
+}
+
+static inline int
+get_u32(unsigned char *p, u32 *x)
+{
+ *x = ntohl(*((u32 *)p));
+ return 4;
+}
+
+
diff --git a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c
new file mode 100644
index 0000000..77c280e
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_ppp.c
@@ -0,0 +1,3042 @@
+/* $Id: isdn_ppp.c,v 1.1.2.3 2004/02/10 01:07:13 keil Exp $
+ *
+ * Linux ISDN subsystem, functions for synchronous PPP (linklevel).
+ *
+ * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/isdn.h>
+#include <linux/poll.h>
+#include <linux/ppp-comp.h>
+#ifdef CONFIG_IPPP_FILTER
+#include <linux/filter.h>
+#endif
+
+#include "isdn_common.h"
+#include "isdn_ppp.h"
+#include "isdn_net.h"
+
+#ifndef PPP_IPX
+#define PPP_IPX 0x002b
+#endif
+
+/* Prototypes */
+static int isdn_ppp_fill_rq(unsigned char *buf, int len, int proto, int slot);
+static int isdn_ppp_closewait(int slot);
+static void isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp,
+ struct sk_buff *skb, int proto);
+static int isdn_ppp_if_get_unit(char *namebuf);
+static int isdn_ppp_set_compressor(struct ippp_struct *is,struct isdn_ppp_comp_data *);
+static struct sk_buff *isdn_ppp_decompress(struct sk_buff *,
+ struct ippp_struct *,struct ippp_struct *,int *proto);
+static void isdn_ppp_receive_ccp(isdn_net_dev * net_dev, isdn_net_local * lp,
+ struct sk_buff *skb,int proto);
+static struct sk_buff *isdn_ppp_compress(struct sk_buff *skb_in,int *proto,
+ struct ippp_struct *is,struct ippp_struct *master,int type);
+static void isdn_ppp_send_ccp(isdn_net_dev *net_dev, isdn_net_local *lp,
+ struct sk_buff *skb);
+
+/* New CCP stuff */
+static void isdn_ppp_ccp_kickup(struct ippp_struct *is);
+static void isdn_ppp_ccp_xmit_reset(struct ippp_struct *is, int proto,
+ unsigned char code, unsigned char id,
+ unsigned char *data, int len);
+static struct ippp_ccp_reset *isdn_ppp_ccp_reset_alloc(struct ippp_struct *is);
+static void isdn_ppp_ccp_reset_free(struct ippp_struct *is);
+static void isdn_ppp_ccp_reset_free_state(struct ippp_struct *is,
+ unsigned char id);
+static void isdn_ppp_ccp_timer_callback(unsigned long closure);
+static struct ippp_ccp_reset_state *isdn_ppp_ccp_reset_alloc_state(struct ippp_struct *is,
+ unsigned char id);
+static void isdn_ppp_ccp_reset_trans(struct ippp_struct *is,
+ struct isdn_ppp_resetparams *rp);
+static void isdn_ppp_ccp_reset_ack_rcvd(struct ippp_struct *is,
+ unsigned char id);
+
+
+
+#ifdef CONFIG_ISDN_MPP
+static ippp_bundle * isdn_ppp_bundle_arr = NULL;
+
+static int isdn_ppp_mp_bundle_array_init(void);
+static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to );
+static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp,
+ struct sk_buff *skb);
+static void isdn_ppp_mp_cleanup( isdn_net_local * lp );
+
+static int isdn_ppp_bundle(struct ippp_struct *, int unit);
+#endif /* CONFIG_ISDN_MPP */
+
+char *isdn_ppp_revision = "$Revision: 1.1.2.3 $";
+
+static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS];
+
+static struct isdn_ppp_compressor *ipc_head = NULL;
+
+/*
+ * frame log (debug)
+ */
+static void
+isdn_ppp_frame_log(char *info, char *data, int len, int maxlen,int unit,int slot)
+{
+ int cnt,
+ j,
+ i;
+ char buf[80];
+
+ if (len < maxlen)
+ maxlen = len;
+
+ for (i = 0, cnt = 0; cnt < maxlen; i++) {
+ for (j = 0; j < 16 && cnt < maxlen; j++, cnt++)
+ sprintf(buf + j * 3, "%02x ", (unsigned char) data[cnt]);
+ printk(KERN_DEBUG "[%d/%d].%s[%d]: %s\n",unit,slot, info, i, buf);
+ }
+}
+
+/*
+ * unbind isdn_net_local <=> ippp-device
+ * note: it can happen, that we hangup/free the master before the slaves
+ * in this case we bind another lp to the master device
+ */
+int
+isdn_ppp_free(isdn_net_local * lp)
+{
+ struct ippp_struct *is;
+
+ if (lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS) {
+ printk(KERN_ERR "%s: ppp_slot(%d) out of range\n",
+ __func__, lp->ppp_slot);
+ return 0;
+ }
+
+#ifdef CONFIG_ISDN_MPP
+ spin_lock(&lp->netdev->pb->lock);
+#endif
+ isdn_net_rm_from_bundle(lp);
+#ifdef CONFIG_ISDN_MPP
+ if (lp->netdev->pb->ref_ct == 1) /* last link in queue? */
+ isdn_ppp_mp_cleanup(lp);
+
+ lp->netdev->pb->ref_ct--;
+ spin_unlock(&lp->netdev->pb->lock);
+#endif /* CONFIG_ISDN_MPP */
+ if (lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS) {
+ printk(KERN_ERR "%s: ppp_slot(%d) now invalid\n",
+ __func__, lp->ppp_slot);
+ return 0;
+ }
+ is = ippp_table[lp->ppp_slot];
+ if ((is->state & IPPP_CONNECT))
+ isdn_ppp_closewait(lp->ppp_slot); /* force wakeup on ippp device */
+ else if (is->state & IPPP_ASSIGNED)
+ is->state = IPPP_OPEN; /* fallback to 'OPEN but not ASSIGNED' state */
+
+ if (is->debug & 0x1)
+ printk(KERN_DEBUG "isdn_ppp_free %d %lx %lx\n", lp->ppp_slot, (long) lp, (long) is->lp);
+
+ is->lp = NULL; /* link is down .. set lp to NULL */
+ lp->ppp_slot = -1; /* is this OK ?? */
+
+ return 0;
+}
+
+/*
+ * bind isdn_net_local <=> ippp-device
+ *
+ * This function is allways called with holding dev->lock so
+ * no additional lock is needed
+ */
+int
+isdn_ppp_bind(isdn_net_local * lp)
+{
+ int i;
+ int unit = 0;
+ struct ippp_struct *is;
+ int retval;
+
+ if (lp->pppbind < 0) { /* device bounded to ippp device ? */
+ isdn_net_dev *net_dev = dev->netdev;
+ char exclusive[ISDN_MAX_CHANNELS]; /* exclusive flags */
+ memset(exclusive, 0, ISDN_MAX_CHANNELS);
+ while (net_dev) { /* step through net devices to find exclusive minors */
+ isdn_net_local *lp = net_dev->local;
+ if (lp->pppbind >= 0)
+ exclusive[lp->pppbind] = 1;
+ net_dev = net_dev->next;
+ }
+ /*
+ * search a free device / slot
+ */
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ if (ippp_table[i]->state == IPPP_OPEN && !exclusive[ippp_table[i]->minor]) { /* OPEN, but not connected! */
+ break;
+ }
+ }
+ } else {
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ if (ippp_table[i]->minor == lp->pppbind &&
+ (ippp_table[i]->state & IPPP_OPEN) == IPPP_OPEN)
+ break;
+ }
+ }
+
+ if (i >= ISDN_MAX_CHANNELS) {
+ printk(KERN_WARNING "isdn_ppp_bind: Can't find a (free) connection to the ipppd daemon.\n");
+ retval = -1;
+ goto out;
+ }
+ /* get unit number from interface name .. ugly! */
+ unit = isdn_ppp_if_get_unit(lp->netdev->dev->name);
+ if (unit < 0) {
+ printk(KERN_ERR "isdn_ppp_bind: illegal interface name %s.\n",
+ lp->netdev->dev->name);
+ retval = -1;
+ goto out;
+ }
+
+ lp->ppp_slot = i;
+ is = ippp_table[i];
+ is->lp = lp;
+ is->unit = unit;
+ is->state = IPPP_OPEN | IPPP_ASSIGNED; /* assigned to a netdevice but not connected */
+#ifdef CONFIG_ISDN_MPP
+ retval = isdn_ppp_mp_init(lp, NULL);
+ if (retval < 0)
+ goto out;
+#endif /* CONFIG_ISDN_MPP */
+
+ retval = lp->ppp_slot;
+
+ out:
+ return retval;
+}
+
+/*
+ * kick the ipppd on the device
+ * (wakes up daemon after B-channel connect)
+ */
+
+void
+isdn_ppp_wakeup_daemon(isdn_net_local * lp)
+{
+ if (lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS) {
+ printk(KERN_ERR "%s: ppp_slot(%d) out of range\n",
+ __func__, lp->ppp_slot);
+ return;
+ }
+ ippp_table[lp->ppp_slot]->state = IPPP_OPEN | IPPP_CONNECT | IPPP_NOBLOCK;
+ wake_up_interruptible(&ippp_table[lp->ppp_slot]->wq);
+}
+
+/*
+ * there was a hangup on the netdevice
+ * force wakeup of the ippp device
+ * go into 'device waits for release' state
+ */
+static int
+isdn_ppp_closewait(int slot)
+{
+ struct ippp_struct *is;
+
+ if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
+ printk(KERN_ERR "%s: slot(%d) out of range\n",
+ __func__, slot);
+ return 0;
+ }
+ is = ippp_table[slot];
+ if (is->state)
+ wake_up_interruptible(&is->wq);
+ is->state = IPPP_CLOSEWAIT;
+ return 1;
+}
+
+/*
+ * isdn_ppp_find_slot / isdn_ppp_free_slot
+ */
+
+static int
+isdn_ppp_get_slot(void)
+{
+ int i;
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ if (!ippp_table[i]->state)
+ return i;
+ }
+ return -1;
+}
+
+/*
+ * isdn_ppp_open
+ */
+
+int
+isdn_ppp_open(int min, struct file *file)
+{
+ int slot;
+ struct ippp_struct *is;
+
+ if (min < 0 || min >= ISDN_MAX_CHANNELS)
+ return -ENODEV;
+
+ slot = isdn_ppp_get_slot();
+ if (slot < 0) {
+ return -EBUSY;
+ }
+ is = file->private_data = ippp_table[slot];
+
+ printk(KERN_DEBUG "ippp, open, slot: %d, minor: %d, state: %04x\n",
+ slot, min, is->state);
+
+ /* compression stuff */
+ is->link_compressor = is->compressor = NULL;
+ is->link_decompressor = is->decompressor = NULL;
+ is->link_comp_stat = is->comp_stat = NULL;
+ is->link_decomp_stat = is->decomp_stat = NULL;
+ is->compflags = 0;
+
+ is->reset = isdn_ppp_ccp_reset_alloc(is);
+
+ is->lp = NULL;
+ is->mp_seqno = 0; /* MP sequence number */
+ is->pppcfg = 0; /* ppp configuration */
+ is->mpppcfg = 0; /* mppp configuration */
+ is->last_link_seqno = -1; /* MP: maybe set to Bundle-MIN, when joining a bundle ?? */
+ is->unit = -1; /* set, when we have our interface */
+ is->mru = 1524; /* MRU, default 1524 */
+ is->maxcid = 16; /* VJ: maxcid */
+ is->tk = current;
+ init_waitqueue_head(&is->wq);
+ is->first = is->rq + NUM_RCV_BUFFS - 1; /* receive queue */
+ is->last = is->rq;
+ is->minor = min;
+#ifdef CONFIG_ISDN_PPP_VJ
+ /*
+ * VJ header compression init
+ */
+ is->slcomp = slhc_init(16, 16); /* not necessary for 2. link in bundle */
+#endif
+#ifdef CONFIG_IPPP_FILTER
+ is->pass_filter = NULL;
+ is->active_filter = NULL;
+#endif
+ is->state = IPPP_OPEN;
+
+ return 0;
+}
+
+/*
+ * release ippp device
+ */
+void
+isdn_ppp_release(int min, struct file *file)
+{
+ int i;
+ struct ippp_struct *is;
+
+ if (min < 0 || min >= ISDN_MAX_CHANNELS)
+ return;
+ is = file->private_data;
+
+ if (!is) {
+ printk(KERN_ERR "%s: no file->private_data\n", __func__);
+ return;
+ }
+ if (is->debug & 0x1)
+ printk(KERN_DEBUG "ippp: release, minor: %d %lx\n", min, (long) is->lp);
+
+ if (is->lp) { /* a lp address says: this link is still up */
+ isdn_net_dev *p = is->lp->netdev;
+
+ if (!p) {
+ printk(KERN_ERR "%s: no lp->netdev\n", __func__);
+ return;
+ }
+ is->state &= ~IPPP_CONNECT; /* -> effect: no call of wakeup */
+ /*
+ * isdn_net_hangup() calls isdn_ppp_free()
+ * isdn_ppp_free() sets is->lp to NULL and lp->ppp_slot to -1
+ * removing the IPPP_CONNECT flag omits calling of isdn_ppp_wakeup_daemon()
+ */
+ isdn_net_hangup(p->dev);
+ }
+ for (i = 0; i < NUM_RCV_BUFFS; i++) {
+ kfree(is->rq[i].buf);
+ is->rq[i].buf = NULL;
+ }
+ is->first = is->rq + NUM_RCV_BUFFS - 1; /* receive queue */
+ is->last = is->rq;
+
+#ifdef CONFIG_ISDN_PPP_VJ
+/* TODO: if this was the previous master: link the slcomp to the new master */
+ slhc_free(is->slcomp);
+ is->slcomp = NULL;
+#endif
+#ifdef CONFIG_IPPP_FILTER
+ kfree(is->pass_filter);
+ is->pass_filter = NULL;
+ kfree(is->active_filter);
+ is->active_filter = NULL;
+#endif
+
+/* TODO: if this was the previous master: link the stuff to the new master */
+ if(is->comp_stat)
+ is->compressor->free(is->comp_stat);
+ if(is->link_comp_stat)
+ is->link_compressor->free(is->link_comp_stat);
+ if(is->link_decomp_stat)
+ is->link_decompressor->free(is->link_decomp_stat);
+ if(is->decomp_stat)
+ is->decompressor->free(is->decomp_stat);
+ is->compressor = is->link_compressor = NULL;
+ is->decompressor = is->link_decompressor = NULL;
+ is->comp_stat = is->link_comp_stat = NULL;
+ is->decomp_stat = is->link_decomp_stat = NULL;
+
+ /* Clean up if necessary */
+ if(is->reset)
+ isdn_ppp_ccp_reset_free(is);
+
+ /* this slot is ready for new connections */
+ is->state = 0;
+}
+
+/*
+ * get_arg .. ioctl helper
+ */
+static int
+get_arg(void __user *b, void *val, int len)
+{
+ if (len <= 0)
+ len = sizeof(void *);
+ if (copy_from_user(val, b, len))
+ return -EFAULT;
+ return 0;
+}
+
+/*
+ * set arg .. ioctl helper
+ */
+static int
+set_arg(void __user *b, void *val,int len)
+{
+ if(len <= 0)
+ len = sizeof(void *);
+ if (copy_to_user(b, val, len))
+ return -EFAULT;
+ return 0;
+}
+
+static int get_filter(void __user *arg, struct sock_filter **p)
+{
+ struct sock_fprog uprog;
+ struct sock_filter *code = NULL;
+ int len, err;
+
+ if (copy_from_user(&uprog, arg, sizeof(uprog)))
+ return -EFAULT;
+
+ if (!uprog.len) {
+ *p = NULL;
+ return 0;
+ }
+
+ /* uprog.len is unsigned short, so no overflow here */
+ len = uprog.len * sizeof(struct sock_filter);
+ code = kmalloc(len, GFP_KERNEL);
+ if (code == NULL)
+ return -ENOMEM;
+
+ if (copy_from_user(code, uprog.filter, len)) {
+ kfree(code);
+ return -EFAULT;
+ }
+
+ err = sk_chk_filter(code, uprog.len);
+ if (err) {
+ kfree(code);
+ return err;
+ }
+
+ *p = code;
+ return uprog.len;
+}
+
+/*
+ * ippp device ioctl
+ */
+int
+isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg)
+{
+ unsigned long val;
+ int r,i,j;
+ struct ippp_struct *is;
+ isdn_net_local *lp;
+ struct isdn_ppp_comp_data data;
+ void __user *argp = (void __user *)arg;
+
+ is = (struct ippp_struct *) file->private_data;
+ lp = is->lp;
+
+ if (is->debug & 0x1)
+ printk(KERN_DEBUG "isdn_ppp_ioctl: minor: %d cmd: %x state: %x\n", min, cmd, is->state);
+
+ if (!(is->state & IPPP_OPEN))
+ return -EINVAL;
+
+ switch (cmd) {
+ case PPPIOCBUNDLE:
+#ifdef CONFIG_ISDN_MPP
+ if (!(is->state & IPPP_CONNECT))
+ return -EINVAL;
+ if ((r = get_arg(argp, &val, sizeof(val) )))
+ return r;
+ printk(KERN_DEBUG "iPPP-bundle: minor: %d, slave unit: %d, master unit: %d\n",
+ (int) min, (int) is->unit, (int) val);
+ return isdn_ppp_bundle(is, val);
+#else
+ return -1;
+#endif
+ break;
+ case PPPIOCGUNIT: /* get ppp/isdn unit number */
+ if ((r = set_arg(argp, &is->unit, sizeof(is->unit) )))
+ return r;
+ break;
+ case PPPIOCGIFNAME:
+ if(!lp)
+ return -EINVAL;
+ if ((r = set_arg(argp, lp->netdev->dev->name,
+ strlen(lp->netdev->dev->name))))
+ return r;
+ break;
+ case PPPIOCGMPFLAGS: /* get configuration flags */
+ if ((r = set_arg(argp, &is->mpppcfg, sizeof(is->mpppcfg) )))
+ return r;
+ break;
+ case PPPIOCSMPFLAGS: /* set configuration flags */
+ if ((r = get_arg(argp, &val, sizeof(val) )))
+ return r;
+ is->mpppcfg = val;
+ break;
+ case PPPIOCGFLAGS: /* get configuration flags */
+ if ((r = set_arg(argp, &is->pppcfg,sizeof(is->pppcfg) )))
+ return r;
+ break;
+ case PPPIOCSFLAGS: /* set configuration flags */
+ if ((r = get_arg(argp, &val, sizeof(val) ))) {
+ return r;
+ }
+ if (val & SC_ENABLE_IP && !(is->pppcfg & SC_ENABLE_IP) && (is->state & IPPP_CONNECT)) {
+ if (lp) {
+ /* OK .. we are ready to send buffers */
+ is->pppcfg = val; /* isdn_ppp_xmit test for SC_ENABLE_IP !!! */
+ netif_wake_queue(lp->netdev->dev);
+ break;
+ }
+ }
+ is->pppcfg = val;
+ break;
+ case PPPIOCGIDLE: /* get idle time information */
+ if (lp) {
+ struct ppp_idle pidle;
+ pidle.xmit_idle = pidle.recv_idle = lp->huptimer;
+ if ((r = set_arg(argp, &pidle,sizeof(struct ppp_idle))))
+ return r;
+ }
+ break;
+ case PPPIOCSMRU: /* set receive unit size for PPP */
+ if ((r = get_arg(argp, &val, sizeof(val) )))
+ return r;
+ is->mru = val;
+ break;
+ case PPPIOCSMPMRU:
+ break;
+ case PPPIOCSMPMTU:
+ break;
+ case PPPIOCSMAXCID: /* set the maximum compression slot id */
+ if ((r = get_arg(argp, &val, sizeof(val) )))
+ return r;
+ val++;
+ if (is->maxcid != val) {
+#ifdef CONFIG_ISDN_PPP_VJ
+ struct slcompress *sltmp;
+#endif
+ if (is->debug & 0x1)
+ printk(KERN_DEBUG "ippp, ioctl: changed MAXCID to %ld\n", val);
+ is->maxcid = val;
+#ifdef CONFIG_ISDN_PPP_VJ
+ sltmp = slhc_init(16, val);
+ if (!sltmp) {
+ printk(KERN_ERR "ippp, can't realloc slhc struct\n");
+ return -ENOMEM;
+ }
+ if (is->slcomp)
+ slhc_free(is->slcomp);
+ is->slcomp = sltmp;
+#endif
+ }
+ break;
+ case PPPIOCGDEBUG:
+ if ((r = set_arg(argp, &is->debug, sizeof(is->debug) )))
+ return r;
+ break;
+ case PPPIOCSDEBUG:
+ if ((r = get_arg(argp, &val, sizeof(val) )))
+ return r;
+ is->debug = val;
+ break;
+ case PPPIOCGCOMPRESSORS:
+ {
+ unsigned long protos[8] = {0,};
+ struct isdn_ppp_compressor *ipc = ipc_head;
+ while(ipc) {
+ j = ipc->num / (sizeof(long)*8);
+ i = ipc->num % (sizeof(long)*8);
+ if(j < 8)
+ protos[j] |= (0x1<<i);
+ ipc = ipc->next;
+ }
+ if ((r = set_arg(argp,protos,8*sizeof(long) )))
+ return r;
+ }
+ break;
+ case PPPIOCSCOMPRESSOR:
+ if ((r = get_arg(argp, &data, sizeof(struct isdn_ppp_comp_data))))
+ return r;
+ return isdn_ppp_set_compressor(is, &data);
+ case PPPIOCGCALLINFO:
+ {
+ struct pppcallinfo pci;
+ memset((char *) &pci,0,sizeof(struct pppcallinfo));
+ if(lp)
+ {
+ strncpy(pci.local_num,lp->msn,63);
+ if(lp->dial) {
+ strncpy(pci.remote_num,lp->dial->num,63);
+ }
+ pci.charge_units = lp->charge;
+ if(lp->outgoing)
+ pci.calltype = CALLTYPE_OUTGOING;
+ else
+ pci.calltype = CALLTYPE_INCOMING;
+ if(lp->flags & ISDN_NET_CALLBACK)
+ pci.calltype |= CALLTYPE_CALLBACK;
+ }
+ return set_arg(argp,&pci,sizeof(struct pppcallinfo));
+ }
+#ifdef CONFIG_IPPP_FILTER
+ case PPPIOCSPASS:
+ {
+ struct sock_filter *code;
+ int len = get_filter(argp, &code);
+ if (len < 0)
+ return len;
+ kfree(is->pass_filter);
+ is->pass_filter = code;
+ is->pass_len = len;
+ break;
+ }
+ case PPPIOCSACTIVE:
+ {
+ struct sock_filter *code;
+ int len = get_filter(argp, &code);
+ if (len < 0)
+ return len;
+ kfree(is->active_filter);
+ is->active_filter = code;
+ is->active_len = len;
+ break;
+ }
+#endif /* CONFIG_IPPP_FILTER */
+ default:
+ break;
+ }
+ return 0;
+}
+
+unsigned int
+isdn_ppp_poll(struct file *file, poll_table * wait)
+{
+ u_int mask;
+ struct ippp_buf_queue *bf, *bl;
+ u_long flags;
+ struct ippp_struct *is;
+
+ is = file->private_data;
+
+ if (is->debug & 0x2)
+ printk(KERN_DEBUG "isdn_ppp_poll: minor: %d\n",
+ iminor(file->f_path.dentry->d_inode));
+
+ /* just registers wait_queue hook. This doesn't really wait. */
+ poll_wait(file, &is->wq, wait);
+
+ if (!(is->state & IPPP_OPEN)) {
+ if(is->state == IPPP_CLOSEWAIT)
+ return POLLHUP;
+ printk(KERN_DEBUG "isdn_ppp: device not open\n");
+ return POLLERR;
+ }
+ /* we're always ready to send .. */
+ mask = POLLOUT | POLLWRNORM;
+
+ spin_lock_irqsave(&is->buflock, flags);
+ bl = is->last;
+ bf = is->first;
+ /*
+ * if IPPP_NOBLOCK is set we return even if we have nothing to read
+ */
+ if (bf->next != bl || (is->state & IPPP_NOBLOCK)) {
+ is->state &= ~IPPP_NOBLOCK;
+ mask |= POLLIN | POLLRDNORM;
+ }
+ spin_unlock_irqrestore(&is->buflock, flags);
+ return mask;
+}
+
+/*
+ * fill up isdn_ppp_read() queue ..
+ */
+
+static int
+isdn_ppp_fill_rq(unsigned char *buf, int len, int proto, int slot)
+{
+ struct ippp_buf_queue *bf, *bl;
+ u_long flags;
+ u_char *nbuf;
+ struct ippp_struct *is;
+
+ if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
+ printk(KERN_WARNING "ippp: illegal slot(%d).\n", slot);
+ return 0;
+ }
+ is = ippp_table[slot];
+
+ if (!(is->state & IPPP_CONNECT)) {
+ printk(KERN_DEBUG "ippp: device not activated.\n");
+ return 0;
+ }
+ nbuf = kmalloc(len + 4, GFP_ATOMIC);
+ if (!nbuf) {
+ printk(KERN_WARNING "ippp: Can't alloc buf\n");
+ return 0;
+ }
+ nbuf[0] = PPP_ALLSTATIONS;
+ nbuf[1] = PPP_UI;
+ nbuf[2] = proto >> 8;
+ nbuf[3] = proto & 0xff;
+ memcpy(nbuf + 4, buf, len);
+
+ spin_lock_irqsave(&is->buflock, flags);
+ bf = is->first;
+ bl = is->last;
+
+ if (bf == bl) {
+ printk(KERN_WARNING "ippp: Queue is full; discarding first buffer\n");
+ bf = bf->next;
+ kfree(bf->buf);
+ is->first = bf;
+ }
+ bl->buf = (char *) nbuf;
+ bl->len = len + 4;
+
+ is->last = bl->next;
+ spin_unlock_irqrestore(&is->buflock, flags);
+ wake_up_interruptible(&is->wq);
+ return len;
+}
+
+/*
+ * read() .. non-blocking: ipppd calls it only after select()
+ * reports, that there is data
+ */
+
+int
+isdn_ppp_read(int min, struct file *file, char __user *buf, int count)
+{
+ struct ippp_struct *is;
+ struct ippp_buf_queue *b;
+ u_long flags;
+ u_char *save_buf;
+
+ is = file->private_data;
+
+ if (!(is->state & IPPP_OPEN))
+ return 0;
+
+ if (!access_ok(VERIFY_WRITE, buf, count))
+ return -EFAULT;
+
+ spin_lock_irqsave(&is->buflock, flags);
+ b = is->first->next;
+ save_buf = b->buf;
+ if (!save_buf) {
+ spin_unlock_irqrestore(&is->buflock, flags);
+ return -EAGAIN;
+ }
+ if (b->len < count)
+ count = b->len;
+ b->buf = NULL;
+ is->first = b;
+
+ spin_unlock_irqrestore(&is->buflock, flags);
+ if (copy_to_user(buf, save_buf, count))
+ count = -EFAULT;
+ kfree(save_buf);
+
+ return count;
+}
+
+/*
+ * ipppd wanna write a packet to the card .. non-blocking
+ */
+
+int
+isdn_ppp_write(int min, struct file *file, const char __user *buf, int count)
+{
+ isdn_net_local *lp;
+ struct ippp_struct *is;
+ int proto;
+ unsigned char protobuf[4];
+
+ is = file->private_data;
+
+ if (!(is->state & IPPP_CONNECT))
+ return 0;
+
+ lp = is->lp;
+
+ /* -> push it directly to the lowlevel interface */
+
+ if (!lp)
+ printk(KERN_DEBUG "isdn_ppp_write: lp == NULL\n");
+ else {
+ /*
+ * Don't reset huptimer for
+ * LCP packets. (Echo requests).
+ */
+ if (copy_from_user(protobuf, buf, 4))
+ return -EFAULT;
+ proto = PPP_PROTOCOL(protobuf);
+ if (proto != PPP_LCP)
+ lp->huptimer = 0;
+
+ if (lp->isdn_device < 0 || lp->isdn_channel < 0)
+ return 0;
+
+ if ((dev->drv[lp->isdn_device]->flags & DRV_FLAG_RUNNING) &&
+ lp->dialstate == 0 &&
+ (lp->flags & ISDN_NET_CONNECTED)) {
+ unsigned short hl;
+ struct sk_buff *skb;
+ /*
+ * we need to reserve enought space in front of
+ * sk_buff. old call to dev_alloc_skb only reserved
+ * 16 bytes, now we are looking what the driver want
+ */
+ hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen;
+ skb = alloc_skb(hl+count, GFP_ATOMIC);
+ if (!skb) {
+ printk(KERN_WARNING "isdn_ppp_write: out of memory!\n");
+ return count;
+ }
+ skb_reserve(skb, hl);
+ if (copy_from_user(skb_put(skb, count), buf, count))
+ {
+ kfree_skb(skb);
+ return -EFAULT;
+ }
+ if (is->debug & 0x40) {
+ printk(KERN_DEBUG "ppp xmit: len %d\n", (int) skb->len);
+ isdn_ppp_frame_log("xmit", skb->data, skb->len, 32,is->unit,lp->ppp_slot);
+ }
+
+ isdn_ppp_send_ccp(lp->netdev,lp,skb); /* keeps CCP/compression states in sync */
+
+ isdn_net_write_super(lp, skb);
+ }
+ }
+ return count;
+}
+
+/*
+ * init memory, structures etc.
+ */
+
+int
+isdn_ppp_init(void)
+{
+ int i,
+ j;
+
+#ifdef CONFIG_ISDN_MPP
+ if( isdn_ppp_mp_bundle_array_init() < 0 )
+ return -ENOMEM;
+#endif /* CONFIG_ISDN_MPP */
+
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ if (!(ippp_table[i] = kzalloc(sizeof(struct ippp_struct), GFP_KERNEL))) {
+ printk(KERN_WARNING "isdn_ppp_init: Could not alloc ippp_table\n");
+ for (j = 0; j < i; j++)
+ kfree(ippp_table[j]);
+ return -1;
+ }
+ spin_lock_init(&ippp_table[i]->buflock);
+ ippp_table[i]->state = 0;
+ ippp_table[i]->first = ippp_table[i]->rq + NUM_RCV_BUFFS - 1;
+ ippp_table[i]->last = ippp_table[i]->rq;
+
+ for (j = 0; j < NUM_RCV_BUFFS; j++) {
+ ippp_table[i]->rq[j].buf = NULL;
+ ippp_table[i]->rq[j].last = ippp_table[i]->rq +
+ (NUM_RCV_BUFFS + j - 1) % NUM_RCV_BUFFS;
+ ippp_table[i]->rq[j].next = ippp_table[i]->rq + (j + 1) % NUM_RCV_BUFFS;
+ }
+ }
+ return 0;
+}
+
+void
+isdn_ppp_cleanup(void)
+{
+ int i;
+
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+ kfree(ippp_table[i]);
+
+#ifdef CONFIG_ISDN_MPP
+ kfree(isdn_ppp_bundle_arr);
+#endif /* CONFIG_ISDN_MPP */
+
+}
+
+/*
+ * check for address/control field and skip if allowed
+ * retval != 0 -> discard packet silently
+ */
+static int isdn_ppp_skip_ac(struct ippp_struct *is, struct sk_buff *skb)
+{
+ if (skb->len < 1)
+ return -1;
+
+ if (skb->data[0] == 0xff) {
+ if (skb->len < 2)
+ return -1;
+
+ if (skb->data[1] != 0x03)
+ return -1;
+
+ // skip address/control (AC) field
+ skb_pull(skb, 2);
+ } else {
+ if (is->pppcfg & SC_REJ_COMP_AC)
+ // if AC compression was not negotiated, but used, discard packet
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * get the PPP protocol header and pull skb
+ * retval < 0 -> discard packet silently
+ */
+static int isdn_ppp_strip_proto(struct sk_buff *skb)
+{
+ int proto;
+
+ if (skb->len < 1)
+ return -1;
+
+ if (skb->data[0] & 0x1) {
+ // protocol field is compressed
+ proto = skb->data[0];
+ skb_pull(skb, 1);
+ } else {
+ if (skb->len < 2)
+ return -1;
+ proto = ((int) skb->data[0] << 8) + skb->data[1];
+ skb_pull(skb, 2);
+ }
+ return proto;
+}
+
+
+/*
+ * handler for incoming packets on a syncPPP interface
+ */
+void isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb)
+{
+ struct ippp_struct *is;
+ int slot;
+ int proto;
+
+ BUG_ON(net_dev->local->master); // we're called with the master device always
+
+ slot = lp->ppp_slot;
+ if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
+ printk(KERN_ERR "isdn_ppp_receive: lp->ppp_slot(%d)\n",
+ lp->ppp_slot);
+ kfree_skb(skb);
+ return;
+ }
+ is = ippp_table[slot];
+
+ if (is->debug & 0x4) {
+ printk(KERN_DEBUG "ippp_receive: is:%08lx lp:%08lx slot:%d unit:%d len:%d\n",
+ (long)is,(long)lp,lp->ppp_slot,is->unit,(int) skb->len);
+ isdn_ppp_frame_log("receive", skb->data, skb->len, 32,is->unit,lp->ppp_slot);
+ }
+
+ if (isdn_ppp_skip_ac(is, skb) < 0) {
+ kfree_skb(skb);
+ return;
+ }
+ proto = isdn_ppp_strip_proto(skb);
+ if (proto < 0) {
+ kfree_skb(skb);
+ return;
+ }
+
+#ifdef CONFIG_ISDN_MPP
+ if (is->compflags & SC_LINK_DECOMP_ON) {
+ skb = isdn_ppp_decompress(skb, is, NULL, &proto);
+ if (!skb) // decompression error
+ return;
+ }
+
+ if (!(is->mpppcfg & SC_REJ_MP_PROT)) { // we agreed to receive MPPP
+ if (proto == PPP_MP) {
+ isdn_ppp_mp_receive(net_dev, lp, skb);
+ return;
+ }
+ }
+#endif
+ isdn_ppp_push_higher(net_dev, lp, skb, proto);
+}
+
+/*
+ * we receive a reassembled frame, MPPP has been taken care of before.
+ * address/control and protocol have been stripped from the skb
+ * note: net_dev has to be master net_dev
+ */
+static void
+isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb, int proto)
+{
+ struct net_device *dev = net_dev->dev;
+ struct ippp_struct *is, *mis;
+ isdn_net_local *mlp = NULL;
+ int slot;
+
+ slot = lp->ppp_slot;
+ if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
+ printk(KERN_ERR "isdn_ppp_push_higher: lp->ppp_slot(%d)\n",
+ lp->ppp_slot);
+ goto drop_packet;
+ }
+ is = ippp_table[slot];
+
+ if (lp->master) { // FIXME?
+ mlp = (isdn_net_local *) lp->master->priv;
+ slot = mlp->ppp_slot;
+ if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
+ printk(KERN_ERR "isdn_ppp_push_higher: master->ppp_slot(%d)\n",
+ lp->ppp_slot);
+ goto drop_packet;
+ }
+ }
+ mis = ippp_table[slot];
+
+ if (is->debug & 0x10) {
+ printk(KERN_DEBUG "push, skb %d %04x\n", (int) skb->len, proto);
+ isdn_ppp_frame_log("rpush", skb->data, skb->len, 32,is->unit,lp->ppp_slot);
+ }
+ if (mis->compflags & SC_DECOMP_ON) {
+ skb = isdn_ppp_decompress(skb, is, mis, &proto);
+ if (!skb) // decompression error
+ return;
+ }
+ switch (proto) {
+ case PPP_IPX: /* untested */
+ if (is->debug & 0x20)
+ printk(KERN_DEBUG "isdn_ppp: IPX\n");
+ skb->protocol = htons(ETH_P_IPX);
+ break;
+ case PPP_IP:
+ if (is->debug & 0x20)
+ printk(KERN_DEBUG "isdn_ppp: IP\n");
+ skb->protocol = htons(ETH_P_IP);
+ break;
+ case PPP_COMP:
+ case PPP_COMPFRAG:
+ printk(KERN_INFO "isdn_ppp: unexpected compressed frame dropped\n");
+ goto drop_packet;
+#ifdef CONFIG_ISDN_PPP_VJ
+ case PPP_VJC_UNCOMP:
+ if (is->debug & 0x20)
+ printk(KERN_DEBUG "isdn_ppp: VJC_UNCOMP\n");
+ if (net_dev->local->ppp_slot < 0) {
+ printk(KERN_ERR "%s: net_dev->local->ppp_slot(%d) out of range\n",
+ __func__, net_dev->local->ppp_slot);
+ goto drop_packet;
+ }
+ if (slhc_remember(ippp_table[net_dev->local->ppp_slot]->slcomp, skb->data, skb->len) <= 0) {
+ printk(KERN_WARNING "isdn_ppp: received illegal VJC_UNCOMP frame!\n");
+ goto drop_packet;
+ }
+ skb->protocol = htons(ETH_P_IP);
+ break;
+ case PPP_VJC_COMP:
+ if (is->debug & 0x20)
+ printk(KERN_DEBUG "isdn_ppp: VJC_COMP\n");
+ {
+ struct sk_buff *skb_old = skb;
+ int pkt_len;
+ skb = dev_alloc_skb(skb_old->len + 128);
+
+ if (!skb) {
+ printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name);
+ skb = skb_old;
+ goto drop_packet;
+ }
+ skb_put(skb, skb_old->len + 128);
+ skb_copy_from_linear_data(skb_old, skb->data,
+ skb_old->len);
+ if (net_dev->local->ppp_slot < 0) {
+ printk(KERN_ERR "%s: net_dev->local->ppp_slot(%d) out of range\n",
+ __func__, net_dev->local->ppp_slot);
+ goto drop_packet;
+ }
+ pkt_len = slhc_uncompress(ippp_table[net_dev->local->ppp_slot]->slcomp,
+ skb->data, skb_old->len);
+ kfree_skb(skb_old);
+ if (pkt_len < 0)
+ goto drop_packet;
+
+ skb_trim(skb, pkt_len);
+ skb->protocol = htons(ETH_P_IP);
+ }
+ break;
+#endif
+ case PPP_CCP:
+ case PPP_CCPFRAG:
+ isdn_ppp_receive_ccp(net_dev,lp,skb,proto);
+ /* Dont pop up ResetReq/Ack stuff to the daemon any
+ longer - the job is done already */
+ if(skb->data[0] == CCP_RESETREQ ||
+ skb->data[0] == CCP_RESETACK)
+ break;
+ /* fall through */
+ default:
+ isdn_ppp_fill_rq(skb->data, skb->len, proto, lp->ppp_slot); /* push data to pppd device */
+ kfree_skb(skb);
+ return;
+ }
+
+#ifdef CONFIG_IPPP_FILTER
+ /* check if the packet passes the pass and active filters
+ * the filter instructions are constructed assuming
+ * a four-byte PPP header on each packet (which is still present) */
+ skb_push(skb, 4);
+
+ {
+ u_int16_t *p = (u_int16_t *) skb->data;
+
+ *p = 0; /* indicate inbound */
+ }
+
+ if (is->pass_filter
+ && sk_run_filter(skb, is->pass_filter, is->pass_len) == 0) {
+ if (is->debug & 0x2)
+ printk(KERN_DEBUG "IPPP: inbound frame filtered.\n");
+ kfree_skb(skb);
+ return;
+ }
+ if (!(is->active_filter
+ && sk_run_filter(skb, is->active_filter,
+ is->active_len) == 0)) {
+ if (is->debug & 0x2)
+ printk(KERN_DEBUG "IPPP: link-active filter: reseting huptimer.\n");
+ lp->huptimer = 0;
+ if (mlp)
+ mlp->huptimer = 0;
+ }
+ skb_pull(skb, 4);
+#else /* CONFIG_IPPP_FILTER */
+ lp->huptimer = 0;
+ if (mlp)
+ mlp->huptimer = 0;
+#endif /* CONFIG_IPPP_FILTER */
+ skb->dev = dev;
+ skb_reset_mac_header(skb);
+ netif_rx(skb);
+ /* net_dev->local->stats.rx_packets++; done in isdn_net.c */
+ return;
+
+ drop_packet:
+ net_dev->local->stats.rx_dropped++;
+ kfree_skb(skb);
+}
+
+/*
+ * isdn_ppp_skb_push ..
+ * checks whether we have enough space at the beginning of the skb
+ * and allocs a new SKB if necessary
+ */
+static unsigned char *isdn_ppp_skb_push(struct sk_buff **skb_p,int len)
+{
+ struct sk_buff *skb = *skb_p;
+
+ if(skb_headroom(skb) < len) {
+ struct sk_buff *nskb = skb_realloc_headroom(skb, len);
+
+ if (!nskb) {
+ printk(KERN_ERR "isdn_ppp_skb_push: can't realloc headroom!\n");
+ dev_kfree_skb(skb);
+ return NULL;
+ }
+ printk(KERN_DEBUG "isdn_ppp_skb_push:under %d %d\n",skb_headroom(skb),len);
+ dev_kfree_skb(skb);
+ *skb_p = nskb;
+ return skb_push(nskb, len);
+ }
+ return skb_push(skb,len);
+}
+
+/*
+ * send ppp frame .. we expect a PIDCOMPressable proto --
+ * (here: currently always PPP_IP,PPP_VJC_COMP,PPP_VJC_UNCOMP)
+ *
+ * VJ compression may change skb pointer!!! .. requeue with old
+ * skb isn't allowed!!
+ */
+
+int
+isdn_ppp_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+ isdn_net_local *lp,*mlp;
+ isdn_net_dev *nd;
+ unsigned int proto = PPP_IP; /* 0x21 */
+ struct ippp_struct *ipt,*ipts;
+ int slot, retval = 0;
+
+ mlp = (isdn_net_local *) (netdev->priv);
+ nd = mlp->netdev; /* get master lp */
+
+ slot = mlp->ppp_slot;
+ if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
+ printk(KERN_ERR "isdn_ppp_xmit: lp->ppp_slot(%d)\n",
+ mlp->ppp_slot);
+ kfree_skb(skb);
+ goto out;
+ }
+ ipts = ippp_table[slot];
+
+ if (!(ipts->pppcfg & SC_ENABLE_IP)) { /* PPP connected ? */
+ if (ipts->debug & 0x1)
+ printk(KERN_INFO "%s: IP frame delayed.\n", netdev->name);
+ retval = 1;
+ goto out;
+ }
+
+ switch (ntohs(skb->protocol)) {
+ case ETH_P_IP:
+ proto = PPP_IP;
+ break;
+ case ETH_P_IPX:
+ proto = PPP_IPX; /* untested */
+ break;
+ default:
+ printk(KERN_ERR "isdn_ppp: skipped unsupported protocol: %#x.\n",
+ skb->protocol);
+ dev_kfree_skb(skb);
+ goto out;
+ }
+
+ lp = isdn_net_get_locked_lp(nd);
+ if (!lp) {
+ printk(KERN_WARNING "%s: all channels busy - requeuing!\n", netdev->name);
+ retval = 1;
+ goto out;
+ }
+ /* we have our lp locked from now on */
+
+ slot = lp->ppp_slot;
+ if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
+ printk(KERN_ERR "isdn_ppp_xmit: lp->ppp_slot(%d)\n",
+ lp->ppp_slot);
+ kfree_skb(skb);
+ goto unlock;
+ }
+ ipt = ippp_table[slot];
+
+ /*
+ * after this line .. requeueing in the device queue is no longer allowed!!!
+ */
+
+ /* Pull off the fake header we stuck on earlier to keep
+ * the fragmentation code happy.
+ */
+ skb_pull(skb,IPPP_MAX_HEADER);
+
+#ifdef CONFIG_IPPP_FILTER
+ /* check if we should pass this packet
+ * the filter instructions are constructed assuming
+ * a four-byte PPP header on each packet */
+ *skb_push(skb, 4) = 1; /* indicate outbound */
+
+ {
+ u_int16_t *p = (u_int16_t *) skb->data;
+
+ p++;
+ *p = htons(proto);
+ }
+
+ if (ipt->pass_filter
+ && sk_run_filter(skb, ipt->pass_filter, ipt->pass_len) == 0) {
+ if (ipt->debug & 0x4)
+ printk(KERN_DEBUG "IPPP: outbound frame filtered.\n");
+ kfree_skb(skb);
+ goto unlock;
+ }
+ if (!(ipt->active_filter
+ && sk_run_filter(skb, ipt->active_filter,
+ ipt->active_len) == 0)) {
+ if (ipt->debug & 0x4)
+ printk(KERN_DEBUG "IPPP: link-active filter: reseting huptimer.\n");
+ lp->huptimer = 0;
+ }
+ skb_pull(skb, 4);
+#else /* CONFIG_IPPP_FILTER */
+ lp->huptimer = 0;
+#endif /* CONFIG_IPPP_FILTER */
+
+ if (ipt->debug & 0x4)
+ printk(KERN_DEBUG "xmit skb, len %d\n", (int) skb->len);
+ if (ipts->debug & 0x40)
+ isdn_ppp_frame_log("xmit0", skb->data, skb->len, 32,ipts->unit,lp->ppp_slot);
+
+#ifdef CONFIG_ISDN_PPP_VJ
+ if (proto == PPP_IP && ipts->pppcfg & SC_COMP_TCP) { /* ipts here? probably yes, but check this again */
+ struct sk_buff *new_skb;
+ unsigned short hl;
+ /*
+ * we need to reserve enought space in front of
+ * sk_buff. old call to dev_alloc_skb only reserved
+ * 16 bytes, now we are looking what the driver want.
+ */
+ hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen + IPPP_MAX_HEADER;
+ /*
+ * Note: hl might still be insufficient because the method
+ * above does not account for a possibible MPPP slave channel
+ * which had larger HL header space requirements than the
+ * master.
+ */
+ new_skb = alloc_skb(hl+skb->len, GFP_ATOMIC);
+ if (new_skb) {
+ u_char *buf;
+ int pktlen;
+
+ skb_reserve(new_skb, hl);
+ new_skb->dev = skb->dev;
+ skb_put(new_skb, skb->len);
+ buf = skb->data;
+
+ pktlen = slhc_compress(ipts->slcomp, skb->data, skb->len, new_skb->data,
+ &buf, !(ipts->pppcfg & SC_NO_TCP_CCID));
+
+ if (buf != skb->data) {
+ if (new_skb->data != buf)
+ printk(KERN_ERR "isdn_ppp: FATAL error after slhc_compress!!\n");
+ dev_kfree_skb(skb);
+ skb = new_skb;
+ } else {
+ dev_kfree_skb(new_skb);
+ }
+
+ skb_trim(skb, pktlen);
+ if (skb->data[0] & SL_TYPE_COMPRESSED_TCP) { /* cslip? style -> PPP */
+ proto = PPP_VJC_COMP;
+ skb->data[0] ^= SL_TYPE_COMPRESSED_TCP;
+ } else {
+ if (skb->data[0] >= SL_TYPE_UNCOMPRESSED_TCP)
+ proto = PPP_VJC_UNCOMP;
+ skb->data[0] = (skb->data[0] & 0x0f) | 0x40;
+ }
+ }
+ }
+#endif
+
+ /*
+ * normal (single link) or bundle compression
+ */
+ if(ipts->compflags & SC_COMP_ON) {
+ /* We send compressed only if both down- und upstream
+ compression is negotiated, that means, CCP is up */
+ if(ipts->compflags & SC_DECOMP_ON) {
+ skb = isdn_ppp_compress(skb,&proto,ipt,ipts,0);
+ } else {
+ printk(KERN_DEBUG "isdn_ppp: CCP not yet up - sending as-is\n");
+ }
+ }
+
+ if (ipt->debug & 0x24)
+ printk(KERN_DEBUG "xmit2 skb, len %d, proto %04x\n", (int) skb->len, proto);
+
+#ifdef CONFIG_ISDN_MPP
+ if (ipt->mpppcfg & SC_MP_PROT) {
+ /* we get mp_seqno from static isdn_net_local */
+ long mp_seqno = ipts->mp_seqno;
+ ipts->mp_seqno++;
+ if (ipt->mpppcfg & SC_OUT_SHORT_SEQ) {
+ unsigned char *data = isdn_ppp_skb_push(&skb, 3);
+ if(!data)
+ goto unlock;
+ mp_seqno &= 0xfff;
+ data[0] = MP_BEGIN_FRAG | MP_END_FRAG | ((mp_seqno >> 8) & 0xf); /* (B)egin & (E)ndbit .. */
+ data[1] = mp_seqno & 0xff;
+ data[2] = proto; /* PID compression */
+ } else {
+ unsigned char *data = isdn_ppp_skb_push(&skb, 5);
+ if(!data)
+ goto unlock;
+ data[0] = MP_BEGIN_FRAG | MP_END_FRAG; /* (B)egin & (E)ndbit .. */
+ data[1] = (mp_seqno >> 16) & 0xff; /* sequence number: 24bit */
+ data[2] = (mp_seqno >> 8) & 0xff;
+ data[3] = (mp_seqno >> 0) & 0xff;
+ data[4] = proto; /* PID compression */
+ }
+ proto = PPP_MP; /* MP Protocol, 0x003d */
+ }
+#endif
+
+ /*
+ * 'link in bundle' compression ...
+ */
+ if(ipt->compflags & SC_LINK_COMP_ON)
+ skb = isdn_ppp_compress(skb,&proto,ipt,ipts,1);
+
+ if( (ipt->pppcfg & SC_COMP_PROT) && (proto <= 0xff) ) {
+ unsigned char *data = isdn_ppp_skb_push(&skb,1);
+ if(!data)
+ goto unlock;
+ data[0] = proto & 0xff;
+ }
+ else {
+ unsigned char *data = isdn_ppp_skb_push(&skb,2);
+ if(!data)
+ goto unlock;
+ data[0] = (proto >> 8) & 0xff;
+ data[1] = proto & 0xff;
+ }
+ if(!(ipt->pppcfg & SC_COMP_AC)) {
+ unsigned char *data = isdn_ppp_skb_push(&skb,2);
+ if(!data)
+ goto unlock;
+ data[0] = 0xff; /* All Stations */
+ data[1] = 0x03; /* Unnumbered information */
+ }
+
+ /* tx-stats are now updated via BSENT-callback */
+
+ if (ipts->debug & 0x40) {
+ printk(KERN_DEBUG "skb xmit: len: %d\n", (int) skb->len);
+ isdn_ppp_frame_log("xmit", skb->data, skb->len, 32,ipt->unit,lp->ppp_slot);
+ }
+
+ isdn_net_writebuf_skb(lp, skb);
+
+ unlock:
+ spin_unlock_bh(&lp->xmit_lock);
+ out:
+ return retval;
+}
+
+#ifdef CONFIG_IPPP_FILTER
+/*
+ * check if this packet may trigger auto-dial.
+ */
+
+int isdn_ppp_autodial_filter(struct sk_buff *skb, isdn_net_local *lp)
+{
+ struct ippp_struct *is = ippp_table[lp->ppp_slot];
+ u_int16_t proto;
+ int drop = 0;
+
+ switch (ntohs(skb->protocol)) {
+ case ETH_P_IP:
+ proto = PPP_IP;
+ break;
+ case ETH_P_IPX:
+ proto = PPP_IPX;
+ break;
+ default:
+ printk(KERN_ERR "isdn_ppp_autodial_filter: unsupported protocol 0x%x.\n",
+ skb->protocol);
+ return 1;
+ }
+
+ /* the filter instructions are constructed assuming
+ * a four-byte PPP header on each packet. we have to
+ * temporarily remove part of the fake header stuck on
+ * earlier.
+ */
+ *skb_pull(skb, IPPP_MAX_HEADER - 4) = 1; /* indicate outbound */
+
+ {
+ u_int16_t *p = (u_int16_t *) skb->data;
+
+ p++;
+ *p = htons(proto);
+ }
+
+ drop |= is->pass_filter
+ && sk_run_filter(skb, is->pass_filter, is->pass_len) == 0;
+ drop |= is->active_filter
+ && sk_run_filter(skb, is->active_filter, is->active_len) == 0;
+
+ skb_push(skb, IPPP_MAX_HEADER - 4);
+ return drop;
+}
+#endif
+#ifdef CONFIG_ISDN_MPP
+
+/* this is _not_ rfc1990 header, but something we convert both short and long
+ * headers to for convinience's sake:
+ * byte 0 is flags as in rfc1990
+ * bytes 1...4 is 24-bit seqence number converted to host byte order
+ */
+#define MP_HEADER_LEN 5
+
+#define MP_LONGSEQ_MASK 0x00ffffff
+#define MP_SHORTSEQ_MASK 0x00000fff
+#define MP_LONGSEQ_MAX MP_LONGSEQ_MASK
+#define MP_SHORTSEQ_MAX MP_SHORTSEQ_MASK
+#define MP_LONGSEQ_MAXBIT ((MP_LONGSEQ_MASK+1)>>1)
+#define MP_SHORTSEQ_MAXBIT ((MP_SHORTSEQ_MASK+1)>>1)
+
+/* sequence-wrap safe comparisions (for long sequence)*/
+#define MP_LT(a,b) ((a-b)&MP_LONGSEQ_MAXBIT)
+#define MP_LE(a,b) !((b-a)&MP_LONGSEQ_MAXBIT)
+#define MP_GT(a,b) ((b-a)&MP_LONGSEQ_MAXBIT)
+#define MP_GE(a,b) !((a-b)&MP_LONGSEQ_MAXBIT)
+
+#define MP_SEQ(f) ((*(u32*)(f->data+1)))
+#define MP_FLAGS(f) (f->data[0])
+
+static int isdn_ppp_mp_bundle_array_init(void)
+{
+ int i;
+ int sz = ISDN_MAX_CHANNELS*sizeof(ippp_bundle);
+ if( (isdn_ppp_bundle_arr = kzalloc(sz, GFP_KERNEL)) == NULL )
+ return -ENOMEM;
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ spin_lock_init(&isdn_ppp_bundle_arr[i].lock);
+ skb_queue_head_init(&isdn_ppp_bundle_arr[i].frags);
+ }
+ return 0;
+}
+
+static ippp_bundle * isdn_ppp_mp_bundle_alloc(void)
+{
+ int i;
+ for( i = 0; i < ISDN_MAX_CHANNELS; i++ )
+ if (isdn_ppp_bundle_arr[i].ref_ct <= 0)
+ return (isdn_ppp_bundle_arr + i);
+ return NULL;
+}
+
+static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to )
+{
+ struct ippp_struct * is;
+
+ if (lp->ppp_slot < 0) {
+ printk(KERN_ERR "%s: lp->ppp_slot(%d) out of range\n",
+ __func__, lp->ppp_slot);
+ return(-EINVAL);
+ }
+
+ is = ippp_table[lp->ppp_slot];
+ if (add_to) {
+ if( lp->netdev->pb )
+ lp->netdev->pb->ref_ct--;
+ lp->netdev->pb = add_to;
+ } else { /* first link in a bundle */
+ is->mp_seqno = 0;
+ if ((lp->netdev->pb = isdn_ppp_mp_bundle_alloc()) == NULL)
+ return -ENOMEM;
+ lp->next = lp->last = lp; /* nobody else in a queue */
+ skb_queue_head_init(&lp->netdev->pb->frags);
+ lp->netdev->pb->frames = 0;
+ lp->netdev->pb->seq = UINT_MAX;
+ }
+ lp->netdev->pb->ref_ct++;
+
+ is->last_link_seqno = 0;
+ return 0;
+}
+
+static u32 isdn_ppp_mp_get_seq( int short_seq,
+ struct sk_buff * skb, u32 last_seq );
+static void isdn_ppp_mp_discard(ippp_bundle *mp, struct sk_buff *from,
+ struct sk_buff *to);
+static void isdn_ppp_mp_reassembly(isdn_net_dev *net_dev, isdn_net_local *lp,
+ struct sk_buff *from, struct sk_buff *to,
+ u32 lastseq);
+static void isdn_ppp_mp_free_skb(ippp_bundle *mp, struct sk_buff *skb);
+static void isdn_ppp_mp_print_recv_pkt( int slot, struct sk_buff * skb );
+
+static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp,
+ struct sk_buff *skb)
+{
+ struct sk_buff *newfrag, *frag, *start, *nextf;
+ u32 newseq, minseq, thisseq;
+ isdn_mppp_stats *stats;
+ struct ippp_struct *is;
+ unsigned long flags;
+ isdn_net_local *lpq;
+ ippp_bundle *mp;
+ int slot;
+
+ spin_lock_irqsave(&net_dev->pb->lock, flags);
+ mp = net_dev->pb;
+ stats = &mp->stats;
+ slot = lp->ppp_slot;
+ if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
+ printk(KERN_ERR "%s: lp->ppp_slot(%d)\n",
+ __func__, lp->ppp_slot);
+ stats->frame_drops++;
+ dev_kfree_skb(skb);
+ spin_unlock_irqrestore(&mp->lock, flags);
+ return;
+ }
+ is = ippp_table[slot];
+ if (++mp->frames > stats->max_queue_len)
+ stats->max_queue_len = mp->frames;
+
+ if (is->debug & 0x8)
+ isdn_ppp_mp_print_recv_pkt(lp->ppp_slot, skb);
+
+ newseq = isdn_ppp_mp_get_seq(is->mpppcfg & SC_IN_SHORT_SEQ,
+ skb, is->last_link_seqno);
+
+ /* if this packet seq # is less than last already processed one,
+ * toss it right away, but check for sequence start case first
+ */
+ if (mp->seq > MP_LONGSEQ_MAX && (newseq & MP_LONGSEQ_MAXBIT)) {
+ mp->seq = newseq; /* the first packet: required for
+ * rfc1990 non-compliant clients --
+ * prevents constant packet toss */
+ } else if( MP_LT(newseq, mp->seq) ) {
+ stats->frame_drops++;
+ isdn_ppp_mp_free_skb(mp, skb);
+ spin_unlock_irqrestore(&mp->lock, flags);
+ return;
+ }
+
+ /* find the minimum received sequence number over all links */
+ is->last_link_seqno = minseq = newseq;
+ for (lpq = net_dev->queue;;) {
+ slot = lpq->ppp_slot;
+ if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
+ printk(KERN_ERR "%s: lpq->ppp_slot(%d)\n",
+ __func__, lpq->ppp_slot);
+ } else {
+ u32 lls = ippp_table[slot]->last_link_seqno;
+ if (MP_LT(lls, minseq))
+ minseq = lls;
+ }
+ if ((lpq = lpq->next) == net_dev->queue)
+ break;
+ }
+ if (MP_LT(minseq, mp->seq))
+ minseq = mp->seq; /* can't go beyond already processed
+ * packets */
+ newfrag = skb;
+
+ /* Insert new fragment into the proper sequence slot. */
+ skb_queue_walk(&mp->frags, frag) {
+ if (MP_SEQ(frag) == newseq) {
+ isdn_ppp_mp_free_skb(mp, newfrag);
+ newfrag = NULL;
+ break;
+ }
+ if (MP_LT(newseq, MP_SEQ(frag))) {
+ __skb_queue_before(&mp->frags, frag, newfrag);
+ newfrag = NULL;
+ break;
+ }
+ }
+ if (newfrag)
+ __skb_queue_tail(&mp->frags, newfrag);
+
+ frag = skb_peek(&mp->frags);
+ start = ((MP_FLAGS(frag) & MP_BEGIN_FRAG) &&
+ (MP_SEQ(frag) == mp->seq)) ? frag : NULL;
+ if (!start)
+ goto check_overflow;
+
+ /* main fragment traversing loop
+ *
+ * try to accomplish several tasks:
+ * - reassemble any complete fragment sequence (non-null 'start'
+ * indicates there is a continguous sequence present)
+ * - discard any incomplete sequences that are below minseq -- due
+ * to the fact that sender always increment sequence number, if there
+ * is an incomplete sequence below minseq, no new fragments would
+ * come to complete such sequence and it should be discarded
+ *
+ * loop completes when we accomplished the following tasks:
+ * - we hit a gap in the sequence, so no reassembly/processing is
+ * possible ('start' would be set to NULL)
+ *
+ * algorithm for this code is derived from code in the book
+ * 'PPP Design And Debugging' by James Carlson (Addison-Wesley)
+ */
+ skb_queue_walk_safe(&mp->frags, frag, nextf) {
+ thisseq = MP_SEQ(frag);
+
+ /* check for misplaced start */
+ if (start != frag && (MP_FLAGS(frag) & MP_BEGIN_FRAG)) {
+ printk(KERN_WARNING"isdn_mppp(seq %d): new "
+ "BEGIN flag with no prior END", thisseq);
+ stats->seqerrs++;
+ stats->frame_drops++;
+ isdn_ppp_mp_discard(mp, start, frag);
+ start = frag;
+ } else if (MP_LE(thisseq, minseq)) {
+ if (MP_FLAGS(frag) & MP_BEGIN_FRAG)
+ start = frag;
+ else {
+ if (MP_FLAGS(frag) & MP_END_FRAG)
+ stats->frame_drops++;
+ __skb_unlink(skb, &mp->frags);
+ isdn_ppp_mp_free_skb(mp, frag);
+ continue;
+ }
+ }
+
+ /* if we have end fragment, then we have full reassembly
+ * sequence -- reassemble and process packet now
+ */
+ if (MP_FLAGS(frag) & MP_END_FRAG) {
+ minseq = mp->seq = (thisseq+1) & MP_LONGSEQ_MASK;
+ /* Reassemble the packet then dispatch it */
+ isdn_ppp_mp_reassembly(net_dev, lp, start, frag, thisseq);
+
+ start = NULL;
+ frag = NULL;
+ }
+
+ /* check if need to update start pointer: if we just
+ * reassembled the packet and sequence is contiguous
+ * then next fragment should be the start of new reassembly
+ * if sequence is contiguous, but we haven't reassembled yet,
+ * keep going.
+ * if sequence is not contiguous, either clear everyting
+ * below low watermark and set start to the next frag or
+ * clear start ptr.
+ */
+ if (nextf != (struct sk_buff *)&mp->frags &&
+ ((thisseq+1) & MP_LONGSEQ_MASK) == MP_SEQ(nextf)) {
+ /* if we just reassembled and the next one is here,
+ * then start another reassembly.
+ */
+ if (frag == NULL) {
+ if (MP_FLAGS(nextf) & MP_BEGIN_FRAG)
+ start = nextf;
+ else {
+ printk(KERN_WARNING"isdn_mppp(seq %d):"
+ " END flag with no following "
+ "BEGIN", thisseq);
+ stats->seqerrs++;
+ }
+ }
+ } else {
+ if (nextf != (struct sk_buff *)&mp->frags &&
+ frag != NULL &&
+ MP_LT(thisseq, minseq)) {
+ /* we've got a break in the sequence
+ * and we not at the end yet
+ * and we did not just reassembled
+ *(if we did, there wouldn't be anything before)
+ * and we below the low watermark
+ * discard all the frames below low watermark
+ * and start over */
+ stats->frame_drops++;
+ isdn_ppp_mp_discard(mp, start, nextf);
+ }
+ /* break in the sequence, no reassembly */
+ start = NULL;
+ }
+ if (!start)
+ break;
+ }
+
+check_overflow:
+ /* rather straighforward way to deal with (not very) possible
+ * queue overflow
+ */
+ if (mp->frames > MP_MAX_QUEUE_LEN) {
+ stats->overflows++;
+ skb_queue_walk_safe(&mp->frags, frag, nextf) {
+ if (mp->frames <= MP_MAX_QUEUE_LEN)
+ break;
+ __skb_unlink(frag, &mp->frags);
+ isdn_ppp_mp_free_skb(mp, frag);
+ }
+ }
+ spin_unlock_irqrestore(&mp->lock, flags);
+}
+
+static void isdn_ppp_mp_cleanup(isdn_net_local *lp)
+{
+ struct sk_buff *skb, *tmp;
+
+ skb_queue_walk_safe(&lp->netdev->pb->frags, skb, tmp) {
+ __skb_unlink(skb, &lp->netdev->pb->frags);
+ isdn_ppp_mp_free_skb(lp->netdev->pb, skb);
+ }
+}
+
+static u32 isdn_ppp_mp_get_seq( int short_seq,
+ struct sk_buff * skb, u32 last_seq )
+{
+ u32 seq;
+ int flags = skb->data[0] & (MP_BEGIN_FRAG | MP_END_FRAG);
+
+ if( !short_seq )
+ {
+ seq = ntohl(*(u32*)skb->data) & MP_LONGSEQ_MASK;
+ skb_push(skb,1);
+ }
+ else
+ {
+ /* convert 12-bit short seq number to 24-bit long one
+ */
+ seq = ntohs(*(u16*)skb->data) & MP_SHORTSEQ_MASK;
+
+ /* check for seqence wrap */
+ if( !(seq & MP_SHORTSEQ_MAXBIT) &&
+ (last_seq & MP_SHORTSEQ_MAXBIT) &&
+ (unsigned long)last_seq <= MP_LONGSEQ_MAX )
+ seq |= (last_seq + MP_SHORTSEQ_MAX+1) &
+ (~MP_SHORTSEQ_MASK & MP_LONGSEQ_MASK);
+ else
+ seq |= last_seq & (~MP_SHORTSEQ_MASK & MP_LONGSEQ_MASK);
+
+ skb_push(skb, 3); /* put converted seqence back in skb */
+ }
+ *(u32*)(skb->data+1) = seq; /* put seqence back in _host_ byte
+ * order */
+ skb->data[0] = flags; /* restore flags */
+ return seq;
+}
+
+static void isdn_ppp_mp_discard(ippp_bundle *mp, struct sk_buff *from,
+ struct sk_buff *to)
+{
+ if (from) {
+ struct sk_buff *skb, *tmp;
+ int freeing = 0;
+
+ skb_queue_walk_safe(&mp->frags, skb, tmp) {
+ if (skb == to)
+ break;
+ if (skb == from)
+ freeing = 1;
+ if (!freeing)
+ continue;
+ __skb_unlink(skb, &mp->frags);
+ isdn_ppp_mp_free_skb(mp, skb);
+ }
+ }
+}
+
+static unsigned int calc_tot_len(struct sk_buff_head *queue,
+ struct sk_buff *from, struct sk_buff *to)
+{
+ unsigned int tot_len = 0;
+ struct sk_buff *skb;
+ int found_start = 0;
+
+ skb_queue_walk(queue, skb) {
+ if (skb == from)
+ found_start = 1;
+ if (!found_start)
+ continue;
+ tot_len += skb->len - MP_HEADER_LEN;
+ if (skb == to)
+ break;
+ }
+ return tot_len;
+}
+
+/* Reassemble packet using fragments in the reassembly queue from
+ * 'from' until 'to', inclusive.
+ */
+static void isdn_ppp_mp_reassembly(isdn_net_dev *net_dev, isdn_net_local *lp,
+ struct sk_buff *from, struct sk_buff *to,
+ u32 lastseq)
+{
+ ippp_bundle *mp = net_dev->pb;
+ unsigned int tot_len;
+ struct sk_buff *skb;
+ int proto;
+
+ if (lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS) {
+ printk(KERN_ERR "%s: lp->ppp_slot(%d) out of range\n",
+ __func__, lp->ppp_slot);
+ return;
+ }
+
+ tot_len = calc_tot_len(&mp->frags, from, to);
+
+ if (MP_FLAGS(from) == (MP_BEGIN_FRAG | MP_END_FRAG)) {
+ if (ippp_table[lp->ppp_slot]->debug & 0x40)
+ printk(KERN_DEBUG "isdn_mppp: reassembly: frame %d, "
+ "len %d\n", MP_SEQ(from), from->len);
+ skb = from;
+ skb_pull(skb, MP_HEADER_LEN);
+ __skb_unlink(skb, &mp->frags);
+ mp->frames--;
+ } else {
+ struct sk_buff *walk, *tmp;
+ int found_start = 0;
+
+ if (ippp_table[lp->ppp_slot]->debug & 0x40)
+ printk(KERN_DEBUG"isdn_mppp: reassembling frames %d "
+ "to %d, len %d\n", MP_SEQ(from), lastseq,
+ tot_len);
+
+ skb = dev_alloc_skb(tot_len);
+ if (!skb)
+ printk(KERN_ERR "isdn_mppp: cannot allocate sk buff "
+ "of size %d\n", tot_len);
+
+ found_start = 0;
+ skb_queue_walk_safe(&mp->frags, walk, tmp) {
+ if (walk == from)
+ found_start = 1;
+ if (!found_start)
+ continue;
+
+ if (skb) {
+ unsigned int len = walk->len - MP_HEADER_LEN;
+ skb_copy_from_linear_data_offset(walk, MP_HEADER_LEN,
+ skb_put(skb, len),
+ len);
+ }
+ __skb_unlink(walk, &mp->frags);
+ isdn_ppp_mp_free_skb(mp, walk);
+
+ if (walk == to)
+ break;
+ }
+ }
+ if (!skb)
+ return;
+
+ proto = isdn_ppp_strip_proto(skb);
+ isdn_ppp_push_higher(net_dev, lp, skb, proto);
+}
+
+static void isdn_ppp_mp_free_skb(ippp_bundle *mp, struct sk_buff *skb)
+{
+ dev_kfree_skb(skb);
+ mp->frames--;
+}
+
+static void isdn_ppp_mp_print_recv_pkt( int slot, struct sk_buff * skb )
+{
+ printk(KERN_DEBUG "mp_recv: %d/%d -> %02x %02x %02x %02x %02x %02x\n",
+ slot, (int) skb->len,
+ (int) skb->data[0], (int) skb->data[1], (int) skb->data[2],
+ (int) skb->data[3], (int) skb->data[4], (int) skb->data[5]);
+}
+
+static int
+isdn_ppp_bundle(struct ippp_struct *is, int unit)
+{
+ char ifn[IFNAMSIZ + 1];
+ isdn_net_dev *p;
+ isdn_net_local *lp, *nlp;
+ int rc;
+ unsigned long flags;
+
+ sprintf(ifn, "ippp%d", unit);
+ p = isdn_net_findif(ifn);
+ if (!p) {
+ printk(KERN_ERR "ippp_bundle: cannot find %s\n", ifn);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&p->pb->lock, flags);
+
+ nlp = is->lp;
+ lp = p->queue;
+ if( nlp->ppp_slot < 0 || nlp->ppp_slot >= ISDN_MAX_CHANNELS ||
+ lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS ) {
+ printk(KERN_ERR "ippp_bundle: binding to invalid slot %d\n",
+ nlp->ppp_slot < 0 || nlp->ppp_slot >= ISDN_MAX_CHANNELS ?
+ nlp->ppp_slot : lp->ppp_slot );
+ rc = -EINVAL;
+ goto out;
+ }
+
+ isdn_net_add_to_bundle(p, nlp);
+
+ ippp_table[nlp->ppp_slot]->unit = ippp_table[lp->ppp_slot]->unit;
+
+ /* maybe also SC_CCP stuff */
+ ippp_table[nlp->ppp_slot]->pppcfg |= ippp_table[lp->ppp_slot]->pppcfg &
+ (SC_ENABLE_IP | SC_NO_TCP_CCID | SC_REJ_COMP_TCP);
+ ippp_table[nlp->ppp_slot]->mpppcfg |= ippp_table[lp->ppp_slot]->mpppcfg &
+ (SC_MP_PROT | SC_REJ_MP_PROT | SC_OUT_SHORT_SEQ | SC_IN_SHORT_SEQ);
+ rc = isdn_ppp_mp_init(nlp, p->pb);
+out:
+ spin_unlock_irqrestore(&p->pb->lock, flags);
+ return rc;
+}
+
+#endif /* CONFIG_ISDN_MPP */
+
+/*
+ * network device ioctl handlers
+ */
+
+static int
+isdn_ppp_dev_ioctl_stats(int slot, struct ifreq *ifr, struct net_device *dev)
+{
+ struct ppp_stats __user *res = ifr->ifr_data;
+ struct ppp_stats t;
+ isdn_net_local *lp = (isdn_net_local *) dev->priv;
+
+ if (!access_ok(VERIFY_WRITE, res, sizeof(struct ppp_stats)))
+ return -EFAULT;
+
+ /* build a temporary stat struct and copy it to user space */
+
+ memset(&t, 0, sizeof(struct ppp_stats));
+ if (dev->flags & IFF_UP) {
+ t.p.ppp_ipackets = lp->stats.rx_packets;
+ t.p.ppp_ibytes = lp->stats.rx_bytes;
+ t.p.ppp_ierrors = lp->stats.rx_errors;
+ t.p.ppp_opackets = lp->stats.tx_packets;
+ t.p.ppp_obytes = lp->stats.tx_bytes;
+ t.p.ppp_oerrors = lp->stats.tx_errors;
+#ifdef CONFIG_ISDN_PPP_VJ
+ if (slot >= 0 && ippp_table[slot]->slcomp) {
+ struct slcompress *slcomp = ippp_table[slot]->slcomp;
+ t.vj.vjs_packets = slcomp->sls_o_compressed + slcomp->sls_o_uncompressed;
+ t.vj.vjs_compressed = slcomp->sls_o_compressed;
+ t.vj.vjs_searches = slcomp->sls_o_searches;
+ t.vj.vjs_misses = slcomp->sls_o_misses;
+ t.vj.vjs_errorin = slcomp->sls_i_error;
+ t.vj.vjs_tossed = slcomp->sls_i_tossed;
+ t.vj.vjs_uncompressedin = slcomp->sls_i_uncompressed;
+ t.vj.vjs_compressedin = slcomp->sls_i_compressed;
+ }
+#endif
+ }
+ if (copy_to_user(res, &t, sizeof(struct ppp_stats)))
+ return -EFAULT;
+ return 0;
+}
+
+int
+isdn_ppp_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ int error=0;
+ int len;
+ isdn_net_local *lp = (isdn_net_local *) dev->priv;
+
+
+ if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP)
+ return -EINVAL;
+
+ switch (cmd) {
+#define PPP_VERSION "2.3.7"
+ case SIOCGPPPVER:
+ len = strlen(PPP_VERSION) + 1;
+ if (copy_to_user(ifr->ifr_data, PPP_VERSION, len))
+ error = -EFAULT;
+ break;
+
+ case SIOCGPPPSTATS:
+ error = isdn_ppp_dev_ioctl_stats(lp->ppp_slot, ifr, dev);
+ break;
+ default:
+ error = -EINVAL;
+ break;
+ }
+ return error;
+}
+
+static int
+isdn_ppp_if_get_unit(char *name)
+{
+ int len,
+ i,
+ unit = 0,
+ deci;
+
+ len = strlen(name);
+
+ if (strncmp("ippp", name, 4) || len > 8)
+ return -1;
+
+ for (i = 0, deci = 1; i < len; i++, deci *= 10) {
+ char a = name[len - i - 1];
+ if (a >= '0' && a <= '9')
+ unit += (a - '0') * deci;
+ else
+ break;
+ }
+ if (!i || len - i != 4)
+ unit = -1;
+
+ return unit;
+}
+
+
+int
+isdn_ppp_dial_slave(char *name)
+{
+#ifdef CONFIG_ISDN_MPP
+ isdn_net_dev *ndev;
+ isdn_net_local *lp;
+ struct net_device *sdev;
+
+ if (!(ndev = isdn_net_findif(name)))
+ return 1;
+ lp = ndev->local;
+ if (!(lp->flags & ISDN_NET_CONNECTED))
+ return 5;
+
+ sdev = lp->slave;
+ while (sdev) {
+ isdn_net_local *mlp = (isdn_net_local *) sdev->priv;
+ if (!(mlp->flags & ISDN_NET_CONNECTED))
+ break;
+ sdev = mlp->slave;
+ }
+ if (!sdev)
+ return 2;
+
+ isdn_net_dial_req((isdn_net_local *) sdev->priv);
+ return 0;
+#else
+ return -1;
+#endif
+}
+
+int
+isdn_ppp_hangup_slave(char *name)
+{
+#ifdef CONFIG_ISDN_MPP
+ isdn_net_dev *ndev;
+ isdn_net_local *lp;
+ struct net_device *sdev;
+
+ if (!(ndev = isdn_net_findif(name)))
+ return 1;
+ lp = ndev->local;
+ if (!(lp->flags & ISDN_NET_CONNECTED))
+ return 5;
+
+ sdev = lp->slave;
+ while (sdev) {
+ isdn_net_local *mlp = (isdn_net_local *) sdev->priv;
+
+ if (mlp->slave) { /* find last connected link in chain */
+ isdn_net_local *nlp = (isdn_net_local *) mlp->slave->priv;
+
+ if (!(nlp->flags & ISDN_NET_CONNECTED))
+ break;
+ } else if (mlp->flags & ISDN_NET_CONNECTED)
+ break;
+
+ sdev = mlp->slave;
+ }
+ if (!sdev)
+ return 2;
+
+ isdn_net_hangup(sdev);
+ return 0;
+#else
+ return -1;
+#endif
+}
+
+/*
+ * PPP compression stuff
+ */
+
+
+/* Push an empty CCP Data Frame up to the daemon to wake it up and let it
+ generate a CCP Reset-Request or tear down CCP altogether */
+
+static void isdn_ppp_ccp_kickup(struct ippp_struct *is)
+{
+ isdn_ppp_fill_rq(NULL, 0, PPP_COMP, is->lp->ppp_slot);
+}
+
+/* In-kernel handling of CCP Reset-Request and Reset-Ack is necessary,
+ but absolutely nontrivial. The most abstruse problem we are facing is
+ that the generation, reception and all the handling of timeouts and
+ resends including proper request id management should be entirely left
+ to the (de)compressor, but indeed is not covered by the current API to
+ the (de)compressor. The API is a prototype version from PPP where only
+ some (de)compressors have yet been implemented and all of them are
+ rather simple in their reset handling. Especially, their is only one
+ outstanding ResetAck at a time with all of them and ResetReq/-Acks do
+ not have parameters. For this very special case it was sufficient to
+ just return an error code from the decompressor and have a single
+ reset() entry to communicate all the necessary information between
+ the framework and the (de)compressor. Bad enough, LZS is different
+ (and any other compressor may be different, too). It has multiple
+ histories (eventually) and needs to Reset each of them independently
+ and thus uses multiple outstanding Acks and history numbers as an
+ additional parameter to Reqs/Acks.
+ All that makes it harder to port the reset state engine into the
+ kernel because it is not just the same simple one as in (i)pppd but
+ it must be able to pass additional parameters and have multiple out-
+ standing Acks. We are trying to achieve the impossible by handling
+ reset transactions independent by their id. The id MUST change when
+ the data portion changes, thus any (de)compressor who uses more than
+ one resettable state must provide and recognize individual ids for
+ each individual reset transaction. The framework itself does _only_
+ differentiate them by id, because it has no other semantics like the
+ (de)compressor might.
+ This looks like a major redesign of the interface would be nice,
+ but I don't have an idea how to do it better. */
+
+/* Send a CCP Reset-Request or Reset-Ack directly from the kernel. This is
+ getting that lengthy because there is no simple "send-this-frame-out"
+ function above but every wrapper does a bit different. Hope I guess
+ correct in this hack... */
+
+static void isdn_ppp_ccp_xmit_reset(struct ippp_struct *is, int proto,
+ unsigned char code, unsigned char id,
+ unsigned char *data, int len)
+{
+ struct sk_buff *skb;
+ unsigned char *p;
+ int hl;
+ int cnt = 0;
+ isdn_net_local *lp = is->lp;
+
+ /* Alloc large enough skb */
+ hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen;
+ skb = alloc_skb(len + hl + 16,GFP_ATOMIC);
+ if(!skb) {
+ printk(KERN_WARNING
+ "ippp: CCP cannot send reset - out of memory\n");
+ return;
+ }
+ skb_reserve(skb, hl);
+
+ /* We may need to stuff an address and control field first */
+ if(!(is->pppcfg & SC_COMP_AC)) {
+ p = skb_put(skb, 2);
+ *p++ = 0xff;
+ *p++ = 0x03;
+ }
+
+ /* Stuff proto, code, id and length */
+ p = skb_put(skb, 6);
+ *p++ = (proto >> 8);
+ *p++ = (proto & 0xff);
+ *p++ = code;
+ *p++ = id;
+ cnt = 4 + len;
+ *p++ = (cnt >> 8);
+ *p++ = (cnt & 0xff);
+
+ /* Now stuff remaining bytes */
+ if(len) {
+ p = skb_put(skb, len);
+ memcpy(p, data, len);
+ }
+
+ /* skb is now ready for xmit */
+ printk(KERN_DEBUG "Sending CCP Frame:\n");
+ isdn_ppp_frame_log("ccp-xmit", skb->data, skb->len, 32, is->unit,lp->ppp_slot);
+
+ isdn_net_write_super(lp, skb);
+}
+
+/* Allocate the reset state vector */
+static struct ippp_ccp_reset *isdn_ppp_ccp_reset_alloc(struct ippp_struct *is)
+{
+ struct ippp_ccp_reset *r;
+ r = kzalloc(sizeof(struct ippp_ccp_reset), GFP_KERNEL);
+ if(!r) {
+ printk(KERN_ERR "ippp_ccp: failed to allocate reset data"
+ " structure - no mem\n");
+ return NULL;
+ }
+ printk(KERN_DEBUG "ippp_ccp: allocated reset data structure %p\n", r);
+ is->reset = r;
+ return r;
+}
+
+/* Destroy the reset state vector. Kill all pending timers first. */
+static void isdn_ppp_ccp_reset_free(struct ippp_struct *is)
+{
+ unsigned int id;
+
+ printk(KERN_DEBUG "ippp_ccp: freeing reset data structure %p\n",
+ is->reset);
+ for(id = 0; id < 256; id++) {
+ if(is->reset->rs[id]) {
+ isdn_ppp_ccp_reset_free_state(is, (unsigned char)id);
+ }
+ }
+ kfree(is->reset);
+ is->reset = NULL;
+}
+
+/* Free a given state and clear everything up for later reallocation */
+static void isdn_ppp_ccp_reset_free_state(struct ippp_struct *is,
+ unsigned char id)
+{
+ struct ippp_ccp_reset_state *rs;
+
+ if(is->reset->rs[id]) {
+ printk(KERN_DEBUG "ippp_ccp: freeing state for id %d\n", id);
+ rs = is->reset->rs[id];
+ /* Make sure the kernel will not call back later */
+ if(rs->ta)
+ del_timer(&rs->timer);
+ is->reset->rs[id] = NULL;
+ kfree(rs);
+ } else {
+ printk(KERN_WARNING "ippp_ccp: id %d is not allocated\n", id);
+ }
+}
+
+/* The timer callback function which is called when a ResetReq has timed out,
+ aka has never been answered by a ResetAck */
+static void isdn_ppp_ccp_timer_callback(unsigned long closure)
+{
+ struct ippp_ccp_reset_state *rs =
+ (struct ippp_ccp_reset_state *)closure;
+
+ if(!rs) {
+ printk(KERN_ERR "ippp_ccp: timer cb with zero closure.\n");
+ return;
+ }
+ if(rs->ta && rs->state == CCPResetSentReq) {
+ /* We are correct here */
+ if(!rs->expra) {
+ /* Hmm, there is no Ack really expected. We can clean
+ up the state now, it will be reallocated if the
+ decompressor insists on another reset */
+ rs->ta = 0;
+ isdn_ppp_ccp_reset_free_state(rs->is, rs->id);
+ return;
+ }
+ printk(KERN_DEBUG "ippp_ccp: CCP Reset timed out for id %d\n",
+ rs->id);
+ /* Push it again */
+ isdn_ppp_ccp_xmit_reset(rs->is, PPP_CCP, CCP_RESETREQ, rs->id,
+ rs->data, rs->dlen);
+ /* Restart timer */
+ rs->timer.expires = jiffies + HZ*5;
+ add_timer(&rs->timer);
+ } else {
+ printk(KERN_WARNING "ippp_ccp: timer cb in wrong state %d\n",
+ rs->state);
+ }
+}
+
+/* Allocate a new reset transaction state */
+static struct ippp_ccp_reset_state *isdn_ppp_ccp_reset_alloc_state(struct ippp_struct *is,
+ unsigned char id)
+{
+ struct ippp_ccp_reset_state *rs;
+ if(is->reset->rs[id]) {
+ printk(KERN_WARNING "ippp_ccp: old state exists for id %d\n",
+ id);
+ return NULL;
+ } else {
+ rs = kzalloc(sizeof(struct ippp_ccp_reset_state), GFP_KERNEL);
+ if(!rs)
+ return NULL;
+ rs->state = CCPResetIdle;
+ rs->is = is;
+ rs->id = id;
+ init_timer(&rs->timer);
+ rs->timer.data = (unsigned long)rs;
+ rs->timer.function = isdn_ppp_ccp_timer_callback;
+ is->reset->rs[id] = rs;
+ }
+ return rs;
+}
+
+
+/* A decompressor wants a reset with a set of parameters - do what is
+ necessary to fulfill it */
+static void isdn_ppp_ccp_reset_trans(struct ippp_struct *is,
+ struct isdn_ppp_resetparams *rp)
+{
+ struct ippp_ccp_reset_state *rs;
+
+ if(rp->valid) {
+ /* The decompressor defines parameters by itself */
+ if(rp->rsend) {
+ /* And he wants us to send a request */
+ if(!(rp->idval)) {
+ printk(KERN_ERR "ippp_ccp: decompressor must"
+ " specify reset id\n");
+ return;
+ }
+ if(is->reset->rs[rp->id]) {
+ /* There is already a transaction in existence
+ for this id. May be still waiting for a
+ Ack or may be wrong. */
+ rs = is->reset->rs[rp->id];
+ if(rs->state == CCPResetSentReq && rs->ta) {
+ printk(KERN_DEBUG "ippp_ccp: reset"
+ " trans still in progress"
+ " for id %d\n", rp->id);
+ } else {
+ printk(KERN_WARNING "ippp_ccp: reset"
+ " trans in wrong state %d for"
+ " id %d\n", rs->state, rp->id);
+ }
+ } else {
+ /* Ok, this is a new transaction */
+ printk(KERN_DEBUG "ippp_ccp: new trans for id"
+ " %d to be started\n", rp->id);
+ rs = isdn_ppp_ccp_reset_alloc_state(is, rp->id);
+ if(!rs) {
+ printk(KERN_ERR "ippp_ccp: out of mem"
+ " allocing ccp trans\n");
+ return;
+ }
+ rs->state = CCPResetSentReq;
+ rs->expra = rp->expra;
+ if(rp->dtval) {
+ rs->dlen = rp->dlen;
+ memcpy(rs->data, rp->data, rp->dlen);
+ }
+ /* HACK TODO - add link comp here */
+ isdn_ppp_ccp_xmit_reset(is, PPP_CCP,
+ CCP_RESETREQ, rs->id,
+ rs->data, rs->dlen);
+ /* Start the timer */
+ rs->timer.expires = jiffies + 5*HZ;
+ add_timer(&rs->timer);
+ rs->ta = 1;
+ }
+ } else {
+ printk(KERN_DEBUG "ippp_ccp: no reset sent\n");
+ }
+ } else {
+ /* The reset params are invalid. The decompressor does not
+ care about them, so we just send the minimal requests
+ and increase ids only when an Ack is received for a
+ given id */
+ if(is->reset->rs[is->reset->lastid]) {
+ /* There is already a transaction in existence
+ for this id. May be still waiting for a
+ Ack or may be wrong. */
+ rs = is->reset->rs[is->reset->lastid];
+ if(rs->state == CCPResetSentReq && rs->ta) {
+ printk(KERN_DEBUG "ippp_ccp: reset"
+ " trans still in progress"
+ " for id %d\n", rp->id);
+ } else {
+ printk(KERN_WARNING "ippp_ccp: reset"
+ " trans in wrong state %d for"
+ " id %d\n", rs->state, rp->id);
+ }
+ } else {
+ printk(KERN_DEBUG "ippp_ccp: new trans for id"
+ " %d to be started\n", is->reset->lastid);
+ rs = isdn_ppp_ccp_reset_alloc_state(is,
+ is->reset->lastid);
+ if(!rs) {
+ printk(KERN_ERR "ippp_ccp: out of mem"
+ " allocing ccp trans\n");
+ return;
+ }
+ rs->state = CCPResetSentReq;
+ /* We always expect an Ack if the decompressor doesn't
+ know better */
+ rs->expra = 1;
+ rs->dlen = 0;
+ /* HACK TODO - add link comp here */
+ isdn_ppp_ccp_xmit_reset(is, PPP_CCP, CCP_RESETREQ,
+ rs->id, NULL, 0);
+ /* Start the timer */
+ rs->timer.expires = jiffies + 5*HZ;
+ add_timer(&rs->timer);
+ rs->ta = 1;
+ }
+ }
+}
+
+/* An Ack was received for this id. This means we stop the timer and clean
+ up the state prior to calling the decompressors reset routine. */
+static void isdn_ppp_ccp_reset_ack_rcvd(struct ippp_struct *is,
+ unsigned char id)
+{
+ struct ippp_ccp_reset_state *rs = is->reset->rs[id];
+
+ if(rs) {
+ if(rs->ta && rs->state == CCPResetSentReq) {
+ /* Great, we are correct */
+ if(!rs->expra)
+ printk(KERN_DEBUG "ippp_ccp: ResetAck received"
+ " for id %d but not expected\n", id);
+ } else {
+ printk(KERN_INFO "ippp_ccp: ResetAck received out of"
+ "sync for id %d\n", id);
+ }
+ if(rs->ta) {
+ rs->ta = 0;
+ del_timer(&rs->timer);
+ }
+ isdn_ppp_ccp_reset_free_state(is, id);
+ } else {
+ printk(KERN_INFO "ippp_ccp: ResetAck received for unknown id"
+ " %d\n", id);
+ }
+ /* Make sure the simple reset stuff uses a new id next time */
+ is->reset->lastid++;
+}
+
+/*
+ * decompress packet
+ *
+ * if master = 0, we're trying to uncompress an per-link compressed packet,
+ * as opposed to an compressed reconstructed-from-MPPP packet.
+ * proto is updated to protocol field of uncompressed packet.
+ *
+ * retval: decompressed packet,
+ * same packet if uncompressed,
+ * NULL if decompression error
+ */
+
+static struct sk_buff *isdn_ppp_decompress(struct sk_buff *skb,struct ippp_struct *is,struct ippp_struct *master,
+ int *proto)
+{
+ void *stat = NULL;
+ struct isdn_ppp_compressor *ipc = NULL;
+ struct sk_buff *skb_out;
+ int len;
+ struct ippp_struct *ri;
+ struct isdn_ppp_resetparams rsparm;
+ unsigned char rsdata[IPPP_RESET_MAXDATABYTES];
+
+ if(!master) {
+ // per-link decompression
+ stat = is->link_decomp_stat;
+ ipc = is->link_decompressor;
+ ri = is;
+ } else {
+ stat = master->decomp_stat;
+ ipc = master->decompressor;
+ ri = master;
+ }
+
+ if (!ipc) {
+ // no decompressor -> we can't decompress.
+ printk(KERN_DEBUG "ippp: no decompressor defined!\n");
+ return skb;
+ }
+ BUG_ON(!stat); // if we have a compressor, stat has been set as well
+
+ if((master && *proto == PPP_COMP) || (!master && *proto == PPP_COMPFRAG) ) {
+ // compressed packets are compressed by their protocol type
+
+ // Set up reset params for the decompressor
+ memset(&rsparm, 0, sizeof(rsparm));
+ rsparm.data = rsdata;
+ rsparm.maxdlen = IPPP_RESET_MAXDATABYTES;
+
+ skb_out = dev_alloc_skb(is->mru + PPP_HDRLEN);
+ if (!skb_out) {
+ kfree_skb(skb);
+ printk(KERN_ERR "ippp: decomp memory allocation failure\n");
+ return NULL;
+ }
+ len = ipc->decompress(stat, skb, skb_out, &rsparm);
+ kfree_skb(skb);
+ if (len <= 0) {
+ switch(len) {
+ case DECOMP_ERROR:
+ printk(KERN_INFO "ippp: decomp wants reset %s params\n",
+ rsparm.valid ? "with" : "without");
+
+ isdn_ppp_ccp_reset_trans(ri, &rsparm);
+ break;
+ case DECOMP_FATALERROR:
+ ri->pppcfg |= SC_DC_FERROR;
+ /* Kick ipppd to recognize the error */
+ isdn_ppp_ccp_kickup(ri);
+ break;
+ }
+ kfree_skb(skb_out);
+ return NULL;
+ }
+ *proto = isdn_ppp_strip_proto(skb_out);
+ if (*proto < 0) {
+ kfree_skb(skb_out);
+ return NULL;
+ }
+ return skb_out;
+ } else {
+ // uncompressed packets are fed through the decompressor to
+ // update the decompressor state
+ ipc->incomp(stat, skb, *proto);
+ return skb;
+ }
+}
+
+/*
+ * compress a frame
+ * type=0: normal/bundle compression
+ * =1: link compression
+ * returns original skb if we haven't compressed the frame
+ * and a new skb pointer if we've done it
+ */
+static struct sk_buff *isdn_ppp_compress(struct sk_buff *skb_in,int *proto,
+ struct ippp_struct *is,struct ippp_struct *master,int type)
+{
+ int ret;
+ int new_proto;
+ struct isdn_ppp_compressor *compressor;
+ void *stat;
+ struct sk_buff *skb_out;
+
+ /* we do not compress control protocols */
+ if(*proto < 0 || *proto > 0x3fff) {
+ return skb_in;
+ }
+
+ if(type) { /* type=1 => Link compression */
+ return skb_in;
+ }
+ else {
+ if(!master) {
+ compressor = is->compressor;
+ stat = is->comp_stat;
+ }
+ else {
+ compressor = master->compressor;
+ stat = master->comp_stat;
+ }
+ new_proto = PPP_COMP;
+ }
+
+ if(!compressor) {
+ printk(KERN_ERR "isdn_ppp: No compressor set!\n");
+ return skb_in;
+ }
+ if(!stat) {
+ printk(KERN_ERR "isdn_ppp: Compressor not initialized?\n");
+ return skb_in;
+ }
+
+ /* Allow for at least 150 % expansion (for now) */
+ skb_out = alloc_skb(skb_in->len + skb_in->len/2 + 32 +
+ skb_headroom(skb_in), GFP_ATOMIC);
+ if(!skb_out)
+ return skb_in;
+ skb_reserve(skb_out, skb_headroom(skb_in));
+
+ ret = (compressor->compress)(stat,skb_in,skb_out,*proto);
+ if(!ret) {
+ dev_kfree_skb(skb_out);
+ return skb_in;
+ }
+
+ dev_kfree_skb(skb_in);
+ *proto = new_proto;
+ return skb_out;
+}
+
+/*
+ * we received a CCP frame ..
+ * not a clean solution, but we MUST handle a few cases in the kernel
+ */
+static void isdn_ppp_receive_ccp(isdn_net_dev *net_dev, isdn_net_local *lp,
+ struct sk_buff *skb,int proto)
+{
+ struct ippp_struct *is;
+ struct ippp_struct *mis;
+ int len;
+ struct isdn_ppp_resetparams rsparm;
+ unsigned char rsdata[IPPP_RESET_MAXDATABYTES];
+
+ printk(KERN_DEBUG "Received CCP frame from peer slot(%d)\n",
+ lp->ppp_slot);
+ if (lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS) {
+ printk(KERN_ERR "%s: lp->ppp_slot(%d) out of range\n",
+ __func__, lp->ppp_slot);
+ return;
+ }
+ is = ippp_table[lp->ppp_slot];
+ isdn_ppp_frame_log("ccp-rcv", skb->data, skb->len, 32, is->unit,lp->ppp_slot);
+
+ if(lp->master) {
+ int slot = ((isdn_net_local *) (lp->master->priv))->ppp_slot;
+ if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
+ printk(KERN_ERR "%s: slot(%d) out of range\n",
+ __func__, slot);
+ return;
+ }
+ mis = ippp_table[slot];
+ } else
+ mis = is;
+
+ switch(skb->data[0]) {
+ case CCP_CONFREQ:
+ if(is->debug & 0x10)
+ printk(KERN_DEBUG "Disable compression here!\n");
+ if(proto == PPP_CCP)
+ mis->compflags &= ~SC_COMP_ON;
+ else
+ is->compflags &= ~SC_LINK_COMP_ON;
+ break;
+ case CCP_TERMREQ:
+ case CCP_TERMACK:
+ if(is->debug & 0x10)
+ printk(KERN_DEBUG "Disable (de)compression here!\n");
+ if(proto == PPP_CCP)
+ mis->compflags &= ~(SC_DECOMP_ON|SC_COMP_ON);
+ else
+ is->compflags &= ~(SC_LINK_DECOMP_ON|SC_LINK_COMP_ON);
+ break;
+ case CCP_CONFACK:
+ /* if we RECEIVE an ackowledge we enable the decompressor */
+ if(is->debug & 0x10)
+ printk(KERN_DEBUG "Enable decompression here!\n");
+ if(proto == PPP_CCP) {
+ if (!mis->decompressor)
+ break;
+ mis->compflags |= SC_DECOMP_ON;
+ } else {
+ if (!is->decompressor)
+ break;
+ is->compflags |= SC_LINK_DECOMP_ON;
+ }
+ break;
+
+ case CCP_RESETACK:
+ printk(KERN_DEBUG "Received ResetAck from peer\n");
+ len = (skb->data[2] << 8) | skb->data[3];
+ len -= 4;
+
+ if(proto == PPP_CCP) {
+ /* If a reset Ack was outstanding for this id, then
+ clean up the state engine */
+ isdn_ppp_ccp_reset_ack_rcvd(mis, skb->data[1]);
+ if(mis->decompressor && mis->decomp_stat)
+ mis->decompressor->
+ reset(mis->decomp_stat,
+ skb->data[0],
+ skb->data[1],
+ len ? &skb->data[4] : NULL,
+ len, NULL);
+ /* TODO: This is not easy to decide here */
+ mis->compflags &= ~SC_DECOMP_DISCARD;
+ }
+ else {
+ isdn_ppp_ccp_reset_ack_rcvd(is, skb->data[1]);
+ if(is->link_decompressor && is->link_decomp_stat)
+ is->link_decompressor->
+ reset(is->link_decomp_stat,
+ skb->data[0],
+ skb->data[1],
+ len ? &skb->data[4] : NULL,
+ len, NULL);
+ /* TODO: neither here */
+ is->compflags &= ~SC_LINK_DECOMP_DISCARD;
+ }
+ break;
+
+ case CCP_RESETREQ:
+ printk(KERN_DEBUG "Received ResetReq from peer\n");
+ /* Receiving a ResetReq means we must reset our compressor */
+ /* Set up reset params for the reset entry */
+ memset(&rsparm, 0, sizeof(rsparm));
+ rsparm.data = rsdata;
+ rsparm.maxdlen = IPPP_RESET_MAXDATABYTES;
+ /* Isolate data length */
+ len = (skb->data[2] << 8) | skb->data[3];
+ len -= 4;
+ if(proto == PPP_CCP) {
+ if(mis->compressor && mis->comp_stat)
+ mis->compressor->
+ reset(mis->comp_stat,
+ skb->data[0],
+ skb->data[1],
+ len ? &skb->data[4] : NULL,
+ len, &rsparm);
+ }
+ else {
+ if(is->link_compressor && is->link_comp_stat)
+ is->link_compressor->
+ reset(is->link_comp_stat,
+ skb->data[0],
+ skb->data[1],
+ len ? &skb->data[4] : NULL,
+ len, &rsparm);
+ }
+ /* Ack the Req as specified by rsparm */
+ if(rsparm.valid) {
+ /* Compressor reset handler decided how to answer */
+ if(rsparm.rsend) {
+ /* We should send a Frame */
+ isdn_ppp_ccp_xmit_reset(is, proto, CCP_RESETACK,
+ rsparm.idval ? rsparm.id
+ : skb->data[1],
+ rsparm.dtval ?
+ rsparm.data : NULL,
+ rsparm.dtval ?
+ rsparm.dlen : 0);
+ } else {
+ printk(KERN_DEBUG "ResetAck suppressed\n");
+ }
+ } else {
+ /* We answer with a straight reflected Ack */
+ isdn_ppp_ccp_xmit_reset(is, proto, CCP_RESETACK,
+ skb->data[1],
+ len ? &skb->data[4] : NULL,
+ len);
+ }
+ break;
+ }
+}
+
+
+/*
+ * Daemon sends a CCP frame ...
+ */
+
+/* TODO: Clean this up with new Reset semantics */
+
+/* I believe the CCP handling as-is is done wrong. Compressed frames
+ * should only be sent/received after CCP reaches UP state, which means
+ * both sides have sent CONF_ACK. Currently, we handle both directions
+ * independently, which means we may accept compressed frames too early
+ * (supposedly not a problem), but may also mean we send compressed frames
+ * too early, which may turn out to be a problem.
+ * This part of state machine should actually be handled by (i)pppd, but
+ * that's too big of a change now. --kai
+ */
+
+/* Actually, we might turn this into an advantage: deal with the RFC in
+ * the old tradition of beeing generous on what we accept, but beeing
+ * strict on what we send. Thus we should just
+ * - accept compressed frames as soon as decompression is negotiated
+ * - send compressed frames only when decomp *and* comp are negotiated
+ * - drop rx compressed frames if we cannot decomp (instead of pushing them
+ * up to ipppd)
+ * and I tried to modify this file according to that. --abp
+ */
+
+static void isdn_ppp_send_ccp(isdn_net_dev *net_dev, isdn_net_local *lp, struct sk_buff *skb)
+{
+ struct ippp_struct *mis,*is;
+ int proto, slot = lp->ppp_slot;
+ unsigned char *data;
+
+ if(!skb || skb->len < 3)
+ return;
+ if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
+ printk(KERN_ERR "%s: lp->ppp_slot(%d) out of range\n",
+ __func__, slot);
+ return;
+ }
+ is = ippp_table[slot];
+ /* Daemon may send with or without address and control field comp */
+ data = skb->data;
+ if(!(is->pppcfg & SC_COMP_AC) && data[0] == 0xff && data[1] == 0x03) {
+ data += 2;
+ if(skb->len < 5)
+ return;
+ }
+
+ proto = ((int)data[0]<<8)+data[1];
+ if(proto != PPP_CCP && proto != PPP_CCPFRAG)
+ return;
+
+ printk(KERN_DEBUG "Received CCP frame from daemon:\n");
+ isdn_ppp_frame_log("ccp-xmit", skb->data, skb->len, 32, is->unit,lp->ppp_slot);
+
+ if (lp->master) {
+ slot = ((isdn_net_local *) (lp->master->priv))->ppp_slot;
+ if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
+ printk(KERN_ERR "%s: slot(%d) out of range\n",
+ __func__, slot);
+ return;
+ }
+ mis = ippp_table[slot];
+ } else
+ mis = is;
+ if (mis != is)
+ printk(KERN_DEBUG "isdn_ppp: Ouch! Master CCP sends on slave slot!\n");
+
+ switch(data[2]) {
+ case CCP_CONFREQ:
+ if(is->debug & 0x10)
+ printk(KERN_DEBUG "Disable decompression here!\n");
+ if(proto == PPP_CCP)
+ is->compflags &= ~SC_DECOMP_ON;
+ else
+ is->compflags &= ~SC_LINK_DECOMP_ON;
+ break;
+ case CCP_TERMREQ:
+ case CCP_TERMACK:
+ if(is->debug & 0x10)
+ printk(KERN_DEBUG "Disable (de)compression here!\n");
+ if(proto == PPP_CCP)
+ is->compflags &= ~(SC_DECOMP_ON|SC_COMP_ON);
+ else
+ is->compflags &= ~(SC_LINK_DECOMP_ON|SC_LINK_COMP_ON);
+ break;
+ case CCP_CONFACK:
+ /* if we SEND an ackowledge we can/must enable the compressor */
+ if(is->debug & 0x10)
+ printk(KERN_DEBUG "Enable compression here!\n");
+ if(proto == PPP_CCP) {
+ if (!is->compressor)
+ break;
+ is->compflags |= SC_COMP_ON;
+ } else {
+ if (!is->compressor)
+ break;
+ is->compflags |= SC_LINK_COMP_ON;
+ }
+ break;
+ case CCP_RESETACK:
+ /* If we send a ACK we should reset our compressor */
+ if(is->debug & 0x10)
+ printk(KERN_DEBUG "Reset decompression state here!\n");
+ printk(KERN_DEBUG "ResetAck from daemon passed by\n");
+ if(proto == PPP_CCP) {
+ /* link to master? */
+ if(is->compressor && is->comp_stat)
+ is->compressor->reset(is->comp_stat, 0, 0,
+ NULL, 0, NULL);
+ is->compflags &= ~SC_COMP_DISCARD;
+ }
+ else {
+ if(is->link_compressor && is->link_comp_stat)
+ is->link_compressor->reset(is->link_comp_stat,
+ 0, 0, NULL, 0, NULL);
+ is->compflags &= ~SC_LINK_COMP_DISCARD;
+ }
+ break;
+ case CCP_RESETREQ:
+ /* Just let it pass by */
+ printk(KERN_DEBUG "ResetReq from daemon passed by\n");
+ break;
+ }
+}
+
+int isdn_ppp_register_compressor(struct isdn_ppp_compressor *ipc)
+{
+ ipc->next = ipc_head;
+ ipc->prev = NULL;
+ if(ipc_head) {
+ ipc_head->prev = ipc;
+ }
+ ipc_head = ipc;
+ return 0;
+}
+
+int isdn_ppp_unregister_compressor(struct isdn_ppp_compressor *ipc)
+{
+ if(ipc->prev)
+ ipc->prev->next = ipc->next;
+ else
+ ipc_head = ipc->next;
+ if(ipc->next)
+ ipc->next->prev = ipc->prev;
+ ipc->prev = ipc->next = NULL;
+ return 0;
+}
+
+static int isdn_ppp_set_compressor(struct ippp_struct *is, struct isdn_ppp_comp_data *data)
+{
+ struct isdn_ppp_compressor *ipc = ipc_head;
+ int ret;
+ void *stat;
+ int num = data->num;
+
+ if(is->debug & 0x10)
+ printk(KERN_DEBUG "[%d] Set %s type %d\n",is->unit,
+ (data->flags&IPPP_COMP_FLAG_XMIT)?"compressor":"decompressor",num);
+
+ /* If is has no valid reset state vector, we cannot allocate a
+ decompressor. The decompressor would cause reset transactions
+ sooner or later, and they need that vector. */
+
+ if(!(data->flags & IPPP_COMP_FLAG_XMIT) && !is->reset) {
+ printk(KERN_ERR "ippp_ccp: no reset data structure - can't"
+ " allow decompression.\n");
+ return -ENOMEM;
+ }
+
+ while(ipc) {
+ if(ipc->num == num) {
+ stat = ipc->alloc(data);
+ if(stat) {
+ ret = ipc->init(stat,data,is->unit,0);
+ if(!ret) {
+ printk(KERN_ERR "Can't init (de)compression!\n");
+ ipc->free(stat);
+ stat = NULL;
+ break;
+ }
+ }
+ else {
+ printk(KERN_ERR "Can't alloc (de)compression!\n");
+ break;
+ }
+
+ if(data->flags & IPPP_COMP_FLAG_XMIT) {
+ if(data->flags & IPPP_COMP_FLAG_LINK) {
+ if(is->link_comp_stat)
+ is->link_compressor->free(is->link_comp_stat);
+ is->link_comp_stat = stat;
+ is->link_compressor = ipc;
+ }
+ else {
+ if(is->comp_stat)
+ is->compressor->free(is->comp_stat);
+ is->comp_stat = stat;
+ is->compressor = ipc;
+ }
+ }
+ else {
+ if(data->flags & IPPP_COMP_FLAG_LINK) {
+ if(is->link_decomp_stat)
+ is->link_decompressor->free(is->link_decomp_stat);
+ is->link_decomp_stat = stat;
+ is->link_decompressor = ipc;
+ }
+ else {
+ if(is->decomp_stat)
+ is->decompressor->free(is->decomp_stat);
+ is->decomp_stat = stat;
+ is->decompressor = ipc;
+ }
+ }
+ return 0;
+ }
+ ipc = ipc->next;
+ }
+ return -EINVAL;
+}
diff --git a/drivers/isdn/i4l/isdn_ppp.h b/drivers/isdn/i4l/isdn_ppp.h
new file mode 100644
index 0000000..8cc05c7
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_ppp.h
@@ -0,0 +1,43 @@
+/* $Id: isdn_ppp.h,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $
+ *
+ * header for Linux ISDN subsystem, functions for synchronous PPP (linklevel).
+ *
+ * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/ppp_defs.h> /* for PPP_PROTOCOL */
+#include <linux/isdn_ppp.h> /* for isdn_ppp info */
+
+extern int isdn_ppp_read(int, struct file *, char __user *, int);
+extern int isdn_ppp_write(int, struct file *, const char __user *, int);
+extern int isdn_ppp_open(int, struct file *);
+extern int isdn_ppp_init(void);
+extern void isdn_ppp_cleanup(void);
+extern int isdn_ppp_free(isdn_net_local *);
+extern int isdn_ppp_bind(isdn_net_local *);
+extern int isdn_ppp_autodial_filter(struct sk_buff *, isdn_net_local *);
+extern int isdn_ppp_xmit(struct sk_buff *, struct net_device *);
+extern void isdn_ppp_receive(isdn_net_dev *, isdn_net_local *, struct sk_buff *);
+extern int isdn_ppp_dev_ioctl(struct net_device *, struct ifreq *, int);
+extern unsigned int isdn_ppp_poll(struct file *, struct poll_table_struct *);
+extern int isdn_ppp_ioctl(int, struct file *, unsigned int, unsigned long);
+extern void isdn_ppp_release(int, struct file *);
+extern int isdn_ppp_dial_slave(char *);
+extern void isdn_ppp_wakeup_daemon(isdn_net_local *);
+
+extern int isdn_ppp_register_compressor(struct isdn_ppp_compressor *ipc);
+extern int isdn_ppp_unregister_compressor(struct isdn_ppp_compressor *ipc);
+
+#define IPPP_OPEN 0x01
+#define IPPP_CONNECT 0x02
+#define IPPP_CLOSEWAIT 0x04
+#define IPPP_NOBLOCK 0x08
+#define IPPP_ASSIGNED 0x10
+
+#define IPPP_MAX_HEADER 10
+
+
diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c
new file mode 100644
index 0000000..1a2222c
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_tty.c
@@ -0,0 +1,3895 @@
+/* $Id: isdn_tty.c,v 1.1.2.3 2004/02/10 01:07:13 keil Exp $
+ *
+ * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel).
+ *
+ * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de)
+ * Copyright 1995,96 by Thinking Objects Software GmbH Wuerzburg
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+#undef ISDN_TTY_STAT_DEBUG
+
+#include <linux/isdn.h>
+#include <linux/delay.h>
+#include "isdn_common.h"
+#include "isdn_tty.h"
+#ifdef CONFIG_ISDN_AUDIO
+#include "isdn_audio.h"
+#define VBUF 0x3e0
+#define VBUFX (VBUF/16)
+#endif
+
+#define FIX_FILE_TRANSFER
+#define DUMMY_HAYES_AT
+
+/* Prototypes */
+
+static int isdn_tty_edit_at(const char *, int, modem_info *);
+static void isdn_tty_check_esc(const u_char *, u_char, int, int *, u_long *);
+static void isdn_tty_modem_reset_regs(modem_info *, int);
+static void isdn_tty_cmd_ATA(modem_info *);
+static void isdn_tty_flush_buffer(struct tty_struct *);
+static void isdn_tty_modem_result(int, modem_info *);
+#ifdef CONFIG_ISDN_AUDIO
+static int isdn_tty_countDLE(unsigned char *, int);
+#endif
+
+/* Leave this unchanged unless you know what you do! */
+#define MODEM_PARANOIA_CHECK
+#define MODEM_DO_RESTART
+
+static int bit2si[8] =
+{1, 5, 7, 7, 7, 7, 7, 7};
+static int si2bit[8] =
+{4, 1, 4, 4, 4, 4, 4, 4};
+
+char *isdn_tty_revision = "$Revision: 1.1.2.3 $";
+
+
+/* isdn_tty_try_read() is called from within isdn_tty_rcv_skb()
+ * to stuff incoming data directly into a tty's flip-buffer. This
+ * is done to speed up tty-receiving if the receive-queue is empty.
+ * This routine MUST be called with interrupts off.
+ * Return:
+ * 1 = Success
+ * 0 = Failure, data has to be buffered and later processed by
+ * isdn_tty_readmodem().
+ */
+static int
+isdn_tty_try_read(modem_info * info, struct sk_buff *skb)
+{
+ int c;
+ int len;
+ struct tty_struct *tty;
+ char last;
+
+ if (info->online) {
+ if ((tty = info->tty)) {
+ if (info->mcr & UART_MCR_RTS) {
+ len = skb->len
+#ifdef CONFIG_ISDN_AUDIO
+ + ISDN_AUDIO_SKB_DLECOUNT(skb)
+#endif
+ ;
+
+ c = tty_buffer_request_room(tty, len);
+ if (c >= len) {
+#ifdef CONFIG_ISDN_AUDIO
+ if (ISDN_AUDIO_SKB_DLECOUNT(skb)) {
+ int l = skb->len;
+ unsigned char *dp = skb->data;
+ while (--l) {
+ if (*dp == DLE)
+ tty_insert_flip_char(tty, DLE, 0);
+ tty_insert_flip_char(tty, *dp++, 0);
+ }
+ if (*dp == DLE)
+ tty_insert_flip_char(tty, DLE, 0);
+ last = *dp;
+ } else {
+#endif
+ if(len > 1)
+ tty_insert_flip_string(tty, skb->data, len - 1);
+ last = skb->data[len - 1];
+#ifdef CONFIG_ISDN_AUDIO
+ }
+#endif
+ if (info->emu.mdmreg[REG_CPPP] & BIT_CPPP)
+ tty_insert_flip_char(tty, last, 0xFF);
+ else
+ tty_insert_flip_char(tty, last, TTY_NORMAL);
+ tty_flip_buffer_push(tty);
+ kfree_skb(skb);
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/* isdn_tty_readmodem() is called periodically from within timer-interrupt.
+ * It tries getting received data from the receive queue an stuff it into
+ * the tty's flip-buffer.
+ */
+void
+isdn_tty_readmodem(void)
+{
+ int resched = 0;
+ int midx;
+ int i;
+ int r;
+ struct tty_struct *tty;
+ modem_info *info;
+
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ if ((midx = dev->m_idx[i]) >= 0) {
+ info = &dev->mdm.info[midx];
+ if (info->online) {
+ r = 0;
+#ifdef CONFIG_ISDN_AUDIO
+ isdn_audio_eval_dtmf(info);
+ if ((info->vonline & 1) && (info->emu.vpar[1]))
+ isdn_audio_eval_silence(info);
+#endif
+ if ((tty = info->tty)) {
+ if (info->mcr & UART_MCR_RTS) {
+ /* CISCO AsyncPPP Hack */
+ if (!(info->emu.mdmreg[REG_CPPP] & BIT_CPPP))
+ r = isdn_readbchan_tty(info->isdn_driver, info->isdn_channel, tty, 0);
+ else
+ r = isdn_readbchan_tty(info->isdn_driver, info->isdn_channel, tty, 1);
+ if (r)
+ tty_flip_buffer_push(tty);
+ } else
+ r = 1;
+ } else
+ r = 1;
+ if (r) {
+ info->rcvsched = 0;
+ resched = 1;
+ } else
+ info->rcvsched = 1;
+ }
+ }
+ }
+ if (!resched)
+ isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 0);
+}
+
+int
+isdn_tty_rcv_skb(int i, int di, int channel, struct sk_buff *skb)
+{
+ ulong flags;
+ int midx;
+#ifdef CONFIG_ISDN_AUDIO
+ int ifmt;
+#endif
+ modem_info *info;
+
+ if ((midx = dev->m_idx[i]) < 0) {
+ /* if midx is invalid, packet is not for tty */
+ return 0;
+ }
+ info = &dev->mdm.info[midx];
+#ifdef CONFIG_ISDN_AUDIO
+ ifmt = 1;
+
+ if ((info->vonline) && (!info->emu.vpar[4]))
+ isdn_audio_calc_dtmf(info, skb->data, skb->len, ifmt);
+ if ((info->vonline & 1) && (info->emu.vpar[1]))
+ isdn_audio_calc_silence(info, skb->data, skb->len, ifmt);
+#endif
+ if ((info->online < 2)
+#ifdef CONFIG_ISDN_AUDIO
+ && (!(info->vonline & 1))
+#endif
+ ) {
+ /* If Modem not listening, drop data */
+ kfree_skb(skb);
+ return 1;
+ }
+ if (info->emu.mdmreg[REG_T70] & BIT_T70) {
+ if (info->emu.mdmreg[REG_T70] & BIT_T70_EXT) {
+ /* T.70 decoding: throw away the T.70 header (2 or 4 bytes) */
+ if (skb->data[0] == 3) /* pure data packet -> 4 byte headers */
+ skb_pull(skb, 4);
+ else
+ if (skb->data[0] == 1) /* keepalive packet -> 2 byte hdr */
+ skb_pull(skb, 2);
+ } else
+ /* T.70 decoding: Simply throw away the T.70 header (4 bytes) */
+ if ((skb->data[0] == 1) && ((skb->data[1] == 0) || (skb->data[1] == 1)))
+ skb_pull(skb, 4);
+ }
+#ifdef CONFIG_ISDN_AUDIO
+ ISDN_AUDIO_SKB_DLECOUNT(skb) = 0;
+ ISDN_AUDIO_SKB_LOCK(skb) = 0;
+ if (info->vonline & 1) {
+ /* voice conversion/compression */
+ switch (info->emu.vpar[3]) {
+ case 2:
+ case 3:
+ case 4:
+ /* adpcm
+ * Since compressed data takes less
+ * space, we can overwrite the buffer.
+ */
+ skb_trim(skb, isdn_audio_xlaw2adpcm(info->adpcmr,
+ ifmt,
+ skb->data,
+ skb->data,
+ skb->len));
+ break;
+ case 5:
+ /* a-law */
+ if (!ifmt)
+ isdn_audio_ulaw2alaw(skb->data, skb->len);
+ break;
+ case 6:
+ /* u-law */
+ if (ifmt)
+ isdn_audio_alaw2ulaw(skb->data, skb->len);
+ break;
+ }
+ ISDN_AUDIO_SKB_DLECOUNT(skb) =
+ isdn_tty_countDLE(skb->data, skb->len);
+ }
+#ifdef CONFIG_ISDN_TTY_FAX
+ else {
+ if (info->faxonline & 2) {
+ isdn_tty_fax_bitorder(info, skb);
+ ISDN_AUDIO_SKB_DLECOUNT(skb) =
+ isdn_tty_countDLE(skb->data, skb->len);
+ }
+ }
+#endif
+#endif
+ /* Try to deliver directly via tty-buf if queue is empty */
+ spin_lock_irqsave(&info->readlock, flags);
+ if (skb_queue_empty(&dev->drv[di]->rpqueue[channel]))
+ if (isdn_tty_try_read(info, skb)) {
+ spin_unlock_irqrestore(&info->readlock, flags);
+ return 1;
+ }
+ /* Direct deliver failed or queue wasn't empty.
+ * Queue up for later dequeueing via timer-irq.
+ */
+ __skb_queue_tail(&dev->drv[di]->rpqueue[channel], skb);
+ dev->drv[di]->rcvcount[channel] +=
+ (skb->len
+#ifdef CONFIG_ISDN_AUDIO
+ + ISDN_AUDIO_SKB_DLECOUNT(skb)
+#endif
+ );
+ spin_unlock_irqrestore(&info->readlock, flags);
+ /* Schedule dequeuing */
+ if ((dev->modempoll) && (info->rcvsched))
+ isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
+ return 1;
+}
+
+static void
+isdn_tty_cleanup_xmit(modem_info * info)
+{
+ skb_queue_purge(&info->xmit_queue);
+#ifdef CONFIG_ISDN_AUDIO
+ skb_queue_purge(&info->dtmf_queue);
+#endif
+}
+
+static void
+isdn_tty_tint(modem_info * info)
+{
+ struct sk_buff *skb = skb_dequeue(&info->xmit_queue);
+ int len, slen;
+
+ if (!skb)
+ return;
+ len = skb->len;
+ if ((slen = isdn_writebuf_skb_stub(info->isdn_driver,
+ info->isdn_channel, 1, skb)) == len) {
+ struct tty_struct *tty = info->tty;
+ info->send_outstanding++;
+ info->msr &= ~UART_MSR_CTS;
+ info->lsr &= ~UART_LSR_TEMT;
+ tty_wakeup(tty);
+ return;
+ }
+ if (slen < 0) {
+ /* Error: no channel, already shutdown, or wrong parameter */
+ dev_kfree_skb(skb);
+ return;
+ }
+ skb_queue_head(&info->xmit_queue, skb);
+}
+
+#ifdef CONFIG_ISDN_AUDIO
+static int
+isdn_tty_countDLE(unsigned char *buf, int len)
+{
+ int count = 0;
+
+ while (len--)
+ if (*buf++ == DLE)
+ count++;
+ return count;
+}
+
+/* This routine is called from within isdn_tty_write() to perform
+ * DLE-decoding when sending audio-data.
+ */
+static int
+isdn_tty_handleDLEdown(modem_info * info, atemu * m, int len)
+{
+ unsigned char *p = &info->xmit_buf[info->xmit_count];
+ int count = 0;
+
+ while (len > 0) {
+ if (m->lastDLE) {
+ m->lastDLE = 0;
+ switch (*p) {
+ case DLE:
+ /* Escape code */
+ if (len > 1)
+ memmove(p, p + 1, len - 1);
+ p--;
+ count++;
+ break;
+ case ETX:
+ /* End of data */
+ info->vonline |= 4;
+ return count;
+ case DC4:
+ /* Abort RX */
+ info->vonline &= ~1;
+#ifdef ISDN_DEBUG_MODEM_VOICE
+ printk(KERN_DEBUG
+ "DLEdown: got DLE-DC4, send DLE-ETX on ttyI%d\n",
+ info->line);
+#endif
+ isdn_tty_at_cout("\020\003", info);
+ if (!info->vonline) {
+#ifdef ISDN_DEBUG_MODEM_VOICE
+ printk(KERN_DEBUG
+ "DLEdown: send VCON on ttyI%d\n",
+ info->line);
+#endif
+ isdn_tty_at_cout("\r\nVCON\r\n", info);
+ }
+ /* Fall through */
+ case 'q':
+ case 's':
+ /* Silence */
+ if (len > 1)
+ memmove(p, p + 1, len - 1);
+ p--;
+ break;
+ }
+ } else {
+ if (*p == DLE)
+ m->lastDLE = 1;
+ else
+ count++;
+ }
+ p++;
+ len--;
+ }
+ if (len < 0) {
+ printk(KERN_WARNING "isdn_tty: len<0 in DLEdown\n");
+ return 0;
+ }
+ return count;
+}
+
+/* This routine is called from within isdn_tty_write() when receiving
+ * audio-data. It interrupts receiving, if an character other than
+ * ^S or ^Q is sent.
+ */
+static int
+isdn_tty_end_vrx(const char *buf, int c)
+{
+ char ch;
+
+ while (c--) {
+ ch = *buf;
+ if ((ch != 0x11) && (ch != 0x13))
+ return 1;
+ buf++;
+ }
+ return 0;
+}
+
+static int voice_cf[7] =
+{0, 0, 4, 3, 2, 0, 0};
+
+#endif /* CONFIG_ISDN_AUDIO */
+
+/* isdn_tty_senddown() is called either directly from within isdn_tty_write()
+ * or via timer-interrupt from within isdn_tty_modem_xmit(). It pulls
+ * outgoing data from the tty's xmit-buffer, handles voice-decompression or
+ * T.70 if necessary, and finally queues it up for sending via isdn_tty_tint.
+ */
+static void
+isdn_tty_senddown(modem_info * info)
+{
+ int buflen;
+ int skb_res;
+#ifdef CONFIG_ISDN_AUDIO
+ int audio_len;
+#endif
+ struct sk_buff *skb;
+
+#ifdef CONFIG_ISDN_AUDIO
+ if (info->vonline & 4) {
+ info->vonline &= ~6;
+ if (!info->vonline) {
+#ifdef ISDN_DEBUG_MODEM_VOICE
+ printk(KERN_DEBUG
+ "senddown: send VCON on ttyI%d\n",
+ info->line);
+#endif
+ isdn_tty_at_cout("\r\nVCON\r\n", info);
+ }
+ }
+#endif
+ if (!(buflen = info->xmit_count))
+ return;
+ if ((info->emu.mdmreg[REG_CTS] & BIT_CTS) != 0)
+ info->msr &= ~UART_MSR_CTS;
+ info->lsr &= ~UART_LSR_TEMT;
+ /* info->xmit_count is modified here and in isdn_tty_write().
+ * So we return here if isdn_tty_write() is in the
+ * critical section.
+ */
+ atomic_inc(&info->xmit_lock);
+ if (!(atomic_dec_and_test(&info->xmit_lock)))
+ return;
+ if (info->isdn_driver < 0) {
+ info->xmit_count = 0;
+ return;
+ }
+ skb_res = dev->drv[info->isdn_driver]->interface->hl_hdrlen + 4;
+#ifdef CONFIG_ISDN_AUDIO
+ if (info->vonline & 2)
+ audio_len = buflen * voice_cf[info->emu.vpar[3]];
+ else
+ audio_len = 0;
+ skb = dev_alloc_skb(skb_res + buflen + audio_len);
+#else
+ skb = dev_alloc_skb(skb_res + buflen);
+#endif
+ if (!skb) {
+ printk(KERN_WARNING
+ "isdn_tty: Out of memory in ttyI%d senddown\n",
+ info->line);
+ return;
+ }
+ skb_reserve(skb, skb_res);
+ memcpy(skb_put(skb, buflen), info->xmit_buf, buflen);
+ info->xmit_count = 0;
+#ifdef CONFIG_ISDN_AUDIO
+ if (info->vonline & 2) {
+ /* For now, ifmt is fixed to 1 (alaw), since this
+ * is used with ISDN everywhere in the world, except
+ * US, Canada and Japan.
+ * Later, when US-ISDN protocols are implemented,
+ * this setting will depend on the D-channel protocol.
+ */
+ int ifmt = 1;
+
+ /* voice conversion/decompression */
+ switch (info->emu.vpar[3]) {
+ case 2:
+ case 3:
+ case 4:
+ /* adpcm, compatible to ZyXel 1496 modem
+ * with ROM revision 6.01
+ */
+ audio_len = isdn_audio_adpcm2xlaw(info->adpcms,
+ ifmt,
+ skb->data,
+ skb_put(skb, audio_len),
+ buflen);
+ skb_pull(skb, buflen);
+ skb_trim(skb, audio_len);
+ break;
+ case 5:
+ /* a-law */
+ if (!ifmt)
+ isdn_audio_alaw2ulaw(skb->data,
+ buflen);
+ break;
+ case 6:
+ /* u-law */
+ if (ifmt)
+ isdn_audio_ulaw2alaw(skb->data,
+ buflen);
+ break;
+ }
+ }
+#endif /* CONFIG_ISDN_AUDIO */
+ if (info->emu.mdmreg[REG_T70] & BIT_T70) {
+ /* Add T.70 simplified header */
+ if (info->emu.mdmreg[REG_T70] & BIT_T70_EXT)
+ memcpy(skb_push(skb, 2), "\1\0", 2);
+ else
+ memcpy(skb_push(skb, 4), "\1\0\1\0", 4);
+ }
+ skb_queue_tail(&info->xmit_queue, skb);
+}
+
+/************************************************************
+ *
+ * Modem-functions
+ *
+ * mostly "stolen" from original Linux-serial.c and friends.
+ *
+ ************************************************************/
+
+/* The next routine is called once from within timer-interrupt
+ * triggered within isdn_tty_modem_ncarrier(). It calls
+ * isdn_tty_modem_result() to stuff a "NO CARRIER" Message
+ * into the tty's buffer.
+ */
+static void
+isdn_tty_modem_do_ncarrier(unsigned long data)
+{
+ modem_info *info = (modem_info *) data;
+ isdn_tty_modem_result(RESULT_NO_CARRIER, info);
+}
+
+/* Next routine is called, whenever the DTR-signal is raised.
+ * It checks the ncarrier-flag, and triggers the above routine
+ * when necessary. The ncarrier-flag is set, whenever DTR goes
+ * low.
+ */
+static void
+isdn_tty_modem_ncarrier(modem_info * info)
+{
+ if (info->ncarrier) {
+ info->nc_timer.expires = jiffies + HZ;
+ add_timer(&info->nc_timer);
+ }
+}
+
+/*
+ * return the usage calculated by si and layer 2 protocol
+ */
+static int
+isdn_calc_usage(int si, int l2)
+{
+ int usg = ISDN_USAGE_MODEM;
+
+#ifdef CONFIG_ISDN_AUDIO
+ if (si == 1) {
+ switch(l2) {
+ case ISDN_PROTO_L2_MODEM:
+ usg = ISDN_USAGE_MODEM;
+ break;
+#ifdef CONFIG_ISDN_TTY_FAX
+ case ISDN_PROTO_L2_FAX:
+ usg = ISDN_USAGE_FAX;
+ break;
+#endif
+ case ISDN_PROTO_L2_TRANS:
+ default:
+ usg = ISDN_USAGE_VOICE;
+ break;
+ }
+ }
+#endif
+ return(usg);
+}
+
+/* isdn_tty_dial() performs dialing of a tty an the necessary
+ * setup of the lower levels before that.
+ */
+static void
+isdn_tty_dial(char *n, modem_info * info, atemu * m)
+{
+ int usg = ISDN_USAGE_MODEM;
+ int si = 7;
+ int l2 = m->mdmreg[REG_L2PROT];
+ u_long flags;
+ isdn_ctrl cmd;
+ int i;
+ int j;
+
+ for (j = 7; j >= 0; j--)
+ if (m->mdmreg[REG_SI1] & (1 << j)) {
+ si = bit2si[j];
+ break;
+ }
+ usg = isdn_calc_usage(si, l2);
+#ifdef CONFIG_ISDN_AUDIO
+ if ((si == 1) &&
+ (l2 != ISDN_PROTO_L2_MODEM)
+#ifdef CONFIG_ISDN_TTY_FAX
+ && (l2 != ISDN_PROTO_L2_FAX)
+#endif
+ ) {
+ l2 = ISDN_PROTO_L2_TRANS;
+ usg = ISDN_USAGE_VOICE;
+ }
+#endif
+ m->mdmreg[REG_SI1I] = si2bit[si];
+ spin_lock_irqsave(&dev->lock, flags);
+ i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1, m->msn);
+ if (i < 0) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ isdn_tty_modem_result(RESULT_NO_DIALTONE, info);
+ } else {
+ info->isdn_driver = dev->drvmap[i];
+ info->isdn_channel = dev->chanmap[i];
+ info->drv_index = i;
+ dev->m_idx[i] = info->line;
+ dev->usage[i] |= ISDN_USAGE_OUTGOING;
+ info->last_dir = 1;
+ strcpy(info->last_num, n);
+ isdn_info_update();
+ spin_unlock_irqrestore(&dev->lock, flags);
+ cmd.driver = info->isdn_driver;
+ cmd.arg = info->isdn_channel;
+ cmd.command = ISDN_CMD_CLREAZ;
+ isdn_command(&cmd);
+ strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver));
+ cmd.driver = info->isdn_driver;
+ cmd.command = ISDN_CMD_SETEAZ;
+ isdn_command(&cmd);
+ cmd.driver = info->isdn_driver;
+ cmd.command = ISDN_CMD_SETL2;
+ info->last_l2 = l2;
+ cmd.arg = info->isdn_channel + (l2 << 8);
+ isdn_command(&cmd);
+ cmd.driver = info->isdn_driver;
+ cmd.command = ISDN_CMD_SETL3;
+ cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8);
+#ifdef CONFIG_ISDN_TTY_FAX
+ if (l2 == ISDN_PROTO_L2_FAX) {
+ cmd.parm.fax = info->fax;
+ info->fax->direction = ISDN_TTY_FAX_CONN_OUT;
+ }
+#endif
+ isdn_command(&cmd);
+ cmd.driver = info->isdn_driver;
+ cmd.arg = info->isdn_channel;
+ sprintf(cmd.parm.setup.phone, "%s", n);
+ sprintf(cmd.parm.setup.eazmsn, "%s",
+ isdn_map_eaz2msn(m->msn, info->isdn_driver));
+ cmd.parm.setup.si1 = si;
+ cmd.parm.setup.si2 = m->mdmreg[REG_SI2];
+ cmd.command = ISDN_CMD_DIAL;
+ info->dialing = 1;
+ info->emu.carrierwait = 0;
+ strcpy(dev->num[i], n);
+ isdn_info_update();
+ isdn_command(&cmd);
+ isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1);
+ }
+}
+
+/* isdn_tty_hangup() disassociates a tty from the real
+ * ISDN-line (hangup). The usage-status is cleared
+ * and some cleanup is done also.
+ */
+void
+isdn_tty_modem_hup(modem_info * info, int local)
+{
+ isdn_ctrl cmd;
+ int di, ch;
+
+ if (!info)
+ return;
+
+ di = info->isdn_driver;
+ ch = info->isdn_channel;
+ if (di < 0 || ch < 0)
+ return;
+
+ info->isdn_driver = -1;
+ info->isdn_channel = -1;
+
+#ifdef ISDN_DEBUG_MODEM_HUP
+ printk(KERN_DEBUG "Mhup ttyI%d\n", info->line);
+#endif
+ info->rcvsched = 0;
+ isdn_tty_flush_buffer(info->tty);
+ if (info->online) {
+ info->last_lhup = local;
+ info->online = 0;
+ isdn_tty_modem_result(RESULT_NO_CARRIER, info);
+ }
+#ifdef CONFIG_ISDN_AUDIO
+ info->vonline = 0;
+#ifdef CONFIG_ISDN_TTY_FAX
+ info->faxonline = 0;
+ info->fax->phase = ISDN_FAX_PHASE_IDLE;
+#endif
+ info->emu.vpar[4] = 0;
+ info->emu.vpar[5] = 8;
+ kfree(info->dtmf_state);
+ info->dtmf_state = NULL;
+ kfree(info->silence_state);
+ info->silence_state = NULL;
+ kfree(info->adpcms);
+ info->adpcms = NULL;
+ kfree(info->adpcmr);
+ info->adpcmr = NULL;
+#endif
+ if ((info->msr & UART_MSR_RI) &&
+ (info->emu.mdmreg[REG_RUNG] & BIT_RUNG))
+ isdn_tty_modem_result(RESULT_RUNG, info);
+ info->msr &= ~(UART_MSR_DCD | UART_MSR_RI);
+ info->lsr |= UART_LSR_TEMT;
+
+ if (local) {
+ cmd.driver = di;
+ cmd.command = ISDN_CMD_HANGUP;
+ cmd.arg = ch;
+ isdn_command(&cmd);
+ }
+
+ isdn_all_eaz(di, ch);
+ info->emu.mdmreg[REG_RINGCNT] = 0;
+ isdn_free_channel(di, ch, 0);
+
+ if (info->drv_index >= 0) {
+ dev->m_idx[info->drv_index] = -1;
+ info->drv_index = -1;
+ }
+}
+
+/*
+ * Begin of a CAPI like interface, currently used only for
+ * supplementary service (CAPI 2.0 part III)
+ */
+#include <linux/isdn/capicmd.h>
+
+int
+isdn_tty_capi_facility(capi_msg *cm) {
+ return(-1); /* dummy */
+}
+
+/* isdn_tty_suspend() tries to suspend the current tty connection
+ */
+static void
+isdn_tty_suspend(char *id, modem_info * info, atemu * m)
+{
+ isdn_ctrl cmd;
+
+ int l;
+
+ if (!info)
+ return;
+
+#ifdef ISDN_DEBUG_MODEM_SERVICES
+ printk(KERN_DEBUG "Msusp ttyI%d\n", info->line);
+#endif
+ l = strlen(id);
+ if ((info->isdn_driver >= 0)) {
+ cmd.parm.cmsg.Length = l+18;
+ cmd.parm.cmsg.Command = CAPI_FACILITY;
+ cmd.parm.cmsg.Subcommand = CAPI_REQ;
+ cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1;
+ cmd.parm.cmsg.para[0] = 3; /* 16 bit 0x0003 suplementary service */
+ cmd.parm.cmsg.para[1] = 0;
+ cmd.parm.cmsg.para[2] = l + 3;
+ cmd.parm.cmsg.para[3] = 4; /* 16 bit 0x0004 Suspend */
+ cmd.parm.cmsg.para[4] = 0;
+ cmd.parm.cmsg.para[5] = l;
+ strncpy(&cmd.parm.cmsg.para[6], id, l);
+ cmd.command = CAPI_PUT_MESSAGE;
+ cmd.driver = info->isdn_driver;
+ cmd.arg = info->isdn_channel;
+ isdn_command(&cmd);
+ }
+}
+
+/* isdn_tty_resume() tries to resume a suspended call
+ * setup of the lower levels before that. unfortunatly here is no
+ * checking for compatibility of used protocols implemented by Q931
+ * It does the same things like isdn_tty_dial, the last command
+ * is different, may be we can merge it.
+ */
+
+static void
+isdn_tty_resume(char *id, modem_info * info, atemu * m)
+{
+ int usg = ISDN_USAGE_MODEM;
+ int si = 7;
+ int l2 = m->mdmreg[REG_L2PROT];
+ isdn_ctrl cmd;
+ ulong flags;
+ int i;
+ int j;
+ int l;
+
+ l = strlen(id);
+ for (j = 7; j >= 0; j--)
+ if (m->mdmreg[REG_SI1] & (1 << j)) {
+ si = bit2si[j];
+ break;
+ }
+ usg = isdn_calc_usage(si, l2);
+#ifdef CONFIG_ISDN_AUDIO
+ if ((si == 1) &&
+ (l2 != ISDN_PROTO_L2_MODEM)
+#ifdef CONFIG_ISDN_TTY_FAX
+ && (l2 != ISDN_PROTO_L2_FAX)
+#endif
+ ) {
+ l2 = ISDN_PROTO_L2_TRANS;
+ usg = ISDN_USAGE_VOICE;
+ }
+#endif
+ m->mdmreg[REG_SI1I] = si2bit[si];
+ spin_lock_irqsave(&dev->lock, flags);
+ i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1, m->msn);
+ if (i < 0) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ isdn_tty_modem_result(RESULT_NO_DIALTONE, info);
+ } else {
+ info->isdn_driver = dev->drvmap[i];
+ info->isdn_channel = dev->chanmap[i];
+ info->drv_index = i;
+ dev->m_idx[i] = info->line;
+ dev->usage[i] |= ISDN_USAGE_OUTGOING;
+ info->last_dir = 1;
+// strcpy(info->last_num, n);
+ isdn_info_update();
+ spin_unlock_irqrestore(&dev->lock, flags);
+ cmd.driver = info->isdn_driver;
+ cmd.arg = info->isdn_channel;
+ cmd.command = ISDN_CMD_CLREAZ;
+ isdn_command(&cmd);
+ strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver));
+ cmd.driver = info->isdn_driver;
+ cmd.command = ISDN_CMD_SETEAZ;
+ isdn_command(&cmd);
+ cmd.driver = info->isdn_driver;
+ cmd.command = ISDN_CMD_SETL2;
+ info->last_l2 = l2;
+ cmd.arg = info->isdn_channel + (l2 << 8);
+ isdn_command(&cmd);
+ cmd.driver = info->isdn_driver;
+ cmd.command = ISDN_CMD_SETL3;
+ cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8);
+ isdn_command(&cmd);
+ cmd.driver = info->isdn_driver;
+ cmd.arg = info->isdn_channel;
+ cmd.parm.cmsg.Length = l+18;
+ cmd.parm.cmsg.Command = CAPI_FACILITY;
+ cmd.parm.cmsg.Subcommand = CAPI_REQ;
+ cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1;
+ cmd.parm.cmsg.para[0] = 3; /* 16 bit 0x0003 suplementary service */
+ cmd.parm.cmsg.para[1] = 0;
+ cmd.parm.cmsg.para[2] = l+3;
+ cmd.parm.cmsg.para[3] = 5; /* 16 bit 0x0005 Resume */
+ cmd.parm.cmsg.para[4] = 0;
+ cmd.parm.cmsg.para[5] = l;
+ strncpy(&cmd.parm.cmsg.para[6], id, l);
+ cmd.command =CAPI_PUT_MESSAGE;
+ info->dialing = 1;
+// strcpy(dev->num[i], n);
+ isdn_info_update();
+ isdn_command(&cmd);
+ isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1);
+ }
+}
+
+/* isdn_tty_send_msg() sends a message to a HL driver
+ * This is used for hybrid modem cards to send AT commands to it
+ */
+
+static void
+isdn_tty_send_msg(modem_info * info, atemu * m, char *msg)
+{
+ int usg = ISDN_USAGE_MODEM;
+ int si = 7;
+ int l2 = m->mdmreg[REG_L2PROT];
+ isdn_ctrl cmd;
+ ulong flags;
+ int i;
+ int j;
+ int l;
+
+ l = strlen(msg);
+ if (!l) {
+ isdn_tty_modem_result(RESULT_ERROR, info);
+ return;
+ }
+ for (j = 7; j >= 0; j--)
+ if (m->mdmreg[REG_SI1] & (1 << j)) {
+ si = bit2si[j];
+ break;
+ }
+ usg = isdn_calc_usage(si, l2);
+#ifdef CONFIG_ISDN_AUDIO
+ if ((si == 1) &&
+ (l2 != ISDN_PROTO_L2_MODEM)
+#ifdef CONFIG_ISDN_TTY_FAX
+ && (l2 != ISDN_PROTO_L2_FAX)
+#endif
+ ) {
+ l2 = ISDN_PROTO_L2_TRANS;
+ usg = ISDN_USAGE_VOICE;
+ }
+#endif
+ m->mdmreg[REG_SI1I] = si2bit[si];
+ spin_lock_irqsave(&dev->lock, flags);
+ i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1, m->msn);
+ if (i < 0) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ isdn_tty_modem_result(RESULT_NO_DIALTONE, info);
+ } else {
+ info->isdn_driver = dev->drvmap[i];
+ info->isdn_channel = dev->chanmap[i];
+ info->drv_index = i;
+ dev->m_idx[i] = info->line;
+ dev->usage[i] |= ISDN_USAGE_OUTGOING;
+ info->last_dir = 1;
+ isdn_info_update();
+ spin_unlock_irqrestore(&dev->lock, flags);
+ cmd.driver = info->isdn_driver;
+ cmd.arg = info->isdn_channel;
+ cmd.command = ISDN_CMD_CLREAZ;
+ isdn_command(&cmd);
+ strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver));
+ cmd.driver = info->isdn_driver;
+ cmd.command = ISDN_CMD_SETEAZ;
+ isdn_command(&cmd);
+ cmd.driver = info->isdn_driver;
+ cmd.command = ISDN_CMD_SETL2;
+ info->last_l2 = l2;
+ cmd.arg = info->isdn_channel + (l2 << 8);
+ isdn_command(&cmd);
+ cmd.driver = info->isdn_driver;
+ cmd.command = ISDN_CMD_SETL3;
+ cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8);
+ isdn_command(&cmd);
+ cmd.driver = info->isdn_driver;
+ cmd.arg = info->isdn_channel;
+ cmd.parm.cmsg.Length = l+14;
+ cmd.parm.cmsg.Command = CAPI_MANUFACTURER;
+ cmd.parm.cmsg.Subcommand = CAPI_REQ;
+ cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1;
+ cmd.parm.cmsg.para[0] = l+1;
+ strncpy(&cmd.parm.cmsg.para[1], msg, l);
+ cmd.parm.cmsg.para[l+1] = 0xd;
+ cmd.command =CAPI_PUT_MESSAGE;
+/* info->dialing = 1;
+ strcpy(dev->num[i], n);
+ isdn_info_update();
+*/
+ isdn_command(&cmd);
+ }
+}
+
+static inline int
+isdn_tty_paranoia_check(modem_info *info, char *name, const char *routine)
+{
+#ifdef MODEM_PARANOIA_CHECK
+ if (!info) {
+ printk(KERN_WARNING "isdn_tty: null info_struct for %s in %s\n",
+ name, routine);
+ return 1;
+ }
+ if (info->magic != ISDN_ASYNC_MAGIC) {
+ printk(KERN_WARNING "isdn_tty: bad magic for modem struct %s in %s\n",
+ name, routine);
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+/*
+ * This routine is called to set the UART divisor registers to match
+ * the specified baud rate for a serial port.
+ */
+static void
+isdn_tty_change_speed(modem_info * info)
+{
+ uint cflag,
+ cval,
+ fcr,
+ quot;
+ int i;
+
+ if (!info->tty || !info->tty->termios)
+ return;
+ cflag = info->tty->termios->c_cflag;
+
+ quot = i = cflag & CBAUD;
+ if (i & CBAUDEX) {
+ i &= ~CBAUDEX;
+ if (i < 1 || i > 2)
+ info->tty->termios->c_cflag &= ~CBAUDEX;
+ else
+ i += 15;
+ }
+ if (quot) {
+ info->mcr |= UART_MCR_DTR;
+ isdn_tty_modem_ncarrier(info);
+ } else {
+ info->mcr &= ~UART_MCR_DTR;
+ if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) {
+#ifdef ISDN_DEBUG_MODEM_HUP
+ printk(KERN_DEBUG "Mhup in changespeed\n");
+#endif
+ if (info->online)
+ info->ncarrier = 1;
+ isdn_tty_modem_reset_regs(info, 0);
+ isdn_tty_modem_hup(info, 1);
+ }
+ return;
+ }
+ /* byte size and parity */
+ cval = cflag & (CSIZE | CSTOPB);
+ cval >>= 4;
+ if (cflag & PARENB)
+ cval |= UART_LCR_PARITY;
+ if (!(cflag & PARODD))
+ cval |= UART_LCR_EPAR;
+ fcr = 0;
+
+ /* CTS flow control flag and modem status interrupts */
+ if (cflag & CRTSCTS) {
+ info->flags |= ISDN_ASYNC_CTS_FLOW;
+ } else
+ info->flags &= ~ISDN_ASYNC_CTS_FLOW;
+ if (cflag & CLOCAL)
+ info->flags &= ~ISDN_ASYNC_CHECK_CD;
+ else {
+ info->flags |= ISDN_ASYNC_CHECK_CD;
+ }
+}
+
+static int
+isdn_tty_startup(modem_info * info)
+{
+ if (info->flags & ISDN_ASYNC_INITIALIZED)
+ return 0;
+ isdn_lock_drivers();
+#ifdef ISDN_DEBUG_MODEM_OPEN
+ printk(KERN_DEBUG "starting up ttyi%d ...\n", info->line);
+#endif
+ /*
+ * Now, initialize the UART
+ */
+ info->mcr = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
+ if (info->tty)
+ clear_bit(TTY_IO_ERROR, &info->tty->flags);
+ /*
+ * and set the speed of the serial port
+ */
+ isdn_tty_change_speed(info);
+
+ info->flags |= ISDN_ASYNC_INITIALIZED;
+ info->msr |= (UART_MSR_DSR | UART_MSR_CTS);
+ info->send_outstanding = 0;
+ return 0;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void
+isdn_tty_shutdown(modem_info * info)
+{
+ if (!(info->flags & ISDN_ASYNC_INITIALIZED))
+ return;
+#ifdef ISDN_DEBUG_MODEM_OPEN
+ printk(KERN_DEBUG "Shutting down isdnmodem port %d ....\n", info->line);
+#endif
+ isdn_unlock_drivers();
+ info->msr &= ~UART_MSR_RI;
+ if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
+ info->mcr &= ~(UART_MCR_DTR | UART_MCR_RTS);
+ if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) {
+ isdn_tty_modem_reset_regs(info, 0);
+#ifdef ISDN_DEBUG_MODEM_HUP
+ printk(KERN_DEBUG "Mhup in isdn_tty_shutdown\n");
+#endif
+ isdn_tty_modem_hup(info, 1);
+ }
+ }
+ if (info->tty)
+ set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+ info->flags &= ~ISDN_ASYNC_INITIALIZED;
+}
+
+/* isdn_tty_write() is the main send-routine. It is called from the upper
+ * levels within the kernel to perform sending data. Depending on the
+ * online-flag it either directs output to the at-command-interpreter or
+ * to the lower level. Additional tasks done here:
+ * - If online, check for escape-sequence (+++)
+ * - If sending audio-data, call isdn_tty_DLEdown() to parse DLE-codes.
+ * - If receiving audio-data, call isdn_tty_end_vrx() to abort if needed.
+ * - If dialing, abort dial.
+ */
+static int
+isdn_tty_write(struct tty_struct *tty, const u_char * buf, int count)
+{
+ int c;
+ int total = 0;
+ modem_info *info = (modem_info *) tty->driver_data;
+ atemu *m = &info->emu;
+
+ if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_write"))
+ return 0;
+ /* See isdn_tty_senddown() */
+ atomic_inc(&info->xmit_lock);
+ while (1) {
+ c = count;
+ if (c > info->xmit_size - info->xmit_count)
+ c = info->xmit_size - info->xmit_count;
+ if (info->isdn_driver >= 0 && c > dev->drv[info->isdn_driver]->maxbufsize)
+ c = dev->drv[info->isdn_driver]->maxbufsize;
+ if (c <= 0)
+ break;
+ if ((info->online > 1)
+#ifdef CONFIG_ISDN_AUDIO
+ || (info->vonline & 3)
+#endif
+ ) {
+#ifdef CONFIG_ISDN_AUDIO
+ if (!info->vonline)
+#endif
+ isdn_tty_check_esc(buf, m->mdmreg[REG_ESC], c,
+ &(m->pluscount),
+ &(m->lastplus));
+ memcpy(&(info->xmit_buf[info->xmit_count]), buf, c);
+#ifdef CONFIG_ISDN_AUDIO
+ if (info->vonline) {
+ int cc = isdn_tty_handleDLEdown(info, m, c);
+ if (info->vonline & 2) {
+ if (!cc) {
+ /* If DLE decoding results in zero-transmit, but
+ * c originally was non-zero, do a wakeup.
+ */
+ tty_wakeup(tty);
+ info->msr |= UART_MSR_CTS;
+ info->lsr |= UART_LSR_TEMT;
+ }
+ info->xmit_count += cc;
+ }
+ if ((info->vonline & 3) == 1) {
+ /* Do NOT handle Ctrl-Q or Ctrl-S
+ * when in full-duplex audio mode.
+ */
+ if (isdn_tty_end_vrx(buf, c)) {
+ info->vonline &= ~1;
+#ifdef ISDN_DEBUG_MODEM_VOICE
+ printk(KERN_DEBUG
+ "got !^Q/^S, send DLE-ETX,VCON on ttyI%d\n",
+ info->line);
+#endif
+ isdn_tty_at_cout("\020\003\r\nVCON\r\n", info);
+ }
+ }
+ } else
+ if (TTY_IS_FCLASS1(info)) {
+ int cc = isdn_tty_handleDLEdown(info, m, c);
+
+ if (info->vonline & 4) { /* ETX seen */
+ isdn_ctrl c;
+
+ c.command = ISDN_CMD_FAXCMD;
+ c.driver = info->isdn_driver;
+ c.arg = info->isdn_channel;
+ c.parm.aux.cmd = ISDN_FAX_CLASS1_CTRL;
+ c.parm.aux.subcmd = ETX;
+ isdn_command(&c);
+ }
+ info->vonline = 0;
+#ifdef ISDN_DEBUG_MODEM_VOICE
+ printk(KERN_DEBUG "fax dle cc/c %d/%d\n", cc, c);
+#endif
+ info->xmit_count += cc;
+ } else
+#endif
+ info->xmit_count += c;
+ } else {
+ info->msr |= UART_MSR_CTS;
+ info->lsr |= UART_LSR_TEMT;
+ if (info->dialing) {
+ info->dialing = 0;
+#ifdef ISDN_DEBUG_MODEM_HUP
+ printk(KERN_DEBUG "Mhup in isdn_tty_write\n");
+#endif
+ isdn_tty_modem_result(RESULT_NO_CARRIER, info);
+ isdn_tty_modem_hup(info, 1);
+ } else
+ c = isdn_tty_edit_at(buf, c, info);
+ }
+ buf += c;
+ count -= c;
+ total += c;
+ }
+ atomic_dec(&info->xmit_lock);
+ if ((info->xmit_count) || !skb_queue_empty(&info->xmit_queue)) {
+ if (m->mdmreg[REG_DXMT] & BIT_DXMT) {
+ isdn_tty_senddown(info);
+ isdn_tty_tint(info);
+ }
+ isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1);
+ }
+ return total;
+}
+
+static int
+isdn_tty_write_room(struct tty_struct *tty)
+{
+ modem_info *info = (modem_info *) tty->driver_data;
+ int ret;
+
+ if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_write_room"))
+ return 0;
+ if (!info->online)
+ return info->xmit_size;
+ ret = info->xmit_size - info->xmit_count;
+ return (ret < 0) ? 0 : ret;
+}
+
+static int
+isdn_tty_chars_in_buffer(struct tty_struct *tty)
+{
+ modem_info *info = (modem_info *) tty->driver_data;
+
+ if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_chars_in_buffer"))
+ return 0;
+ if (!info->online)
+ return 0;
+ return (info->xmit_count);
+}
+
+static void
+isdn_tty_flush_buffer(struct tty_struct *tty)
+{
+ modem_info *info;
+
+ if (!tty) {
+ return;
+ }
+ info = (modem_info *) tty->driver_data;
+ if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_flush_buffer")) {
+ return;
+ }
+ isdn_tty_cleanup_xmit(info);
+ info->xmit_count = 0;
+ tty_wakeup(tty);
+}
+
+static void
+isdn_tty_flush_chars(struct tty_struct *tty)
+{
+ modem_info *info = (modem_info *) tty->driver_data;
+
+ if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_flush_chars"))
+ return;
+ if ((info->xmit_count) || !skb_queue_empty(&info->xmit_queue))
+ isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1);
+}
+
+/*
+ * ------------------------------------------------------------
+ * isdn_tty_throttle()
+ *
+ * This routine is called by the upper-layer tty layer to signal that
+ * incoming characters should be throttled.
+ * ------------------------------------------------------------
+ */
+static void
+isdn_tty_throttle(struct tty_struct *tty)
+{
+ modem_info *info = (modem_info *) tty->driver_data;
+
+ if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_throttle"))
+ return;
+ if (I_IXOFF(tty))
+ info->x_char = STOP_CHAR(tty);
+ info->mcr &= ~UART_MCR_RTS;
+}
+
+static void
+isdn_tty_unthrottle(struct tty_struct *tty)
+{
+ modem_info *info = (modem_info *) tty->driver_data;
+
+ if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_unthrottle"))
+ return;
+ if (I_IXOFF(tty)) {
+ if (info->x_char)
+ info->x_char = 0;
+ else
+ info->x_char = START_CHAR(tty);
+ }
+ info->mcr |= UART_MCR_RTS;
+}
+
+/*
+ * ------------------------------------------------------------
+ * isdn_tty_ioctl() and friends
+ * ------------------------------------------------------------
+ */
+
+/*
+ * isdn_tty_get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ * is emptied. On bus types like RS485, the transmitter must
+ * release the bus after transmitting. This must be done when
+ * the transmit shift register is empty, not be done when the
+ * transmit holding register is empty. This functionality
+ * allows RS485 driver to be written in user space.
+ */
+static int
+isdn_tty_get_lsr_info(modem_info * info, uint __user * value)
+{
+ u_char status;
+ uint result;
+
+ status = info->lsr;
+ result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
+ return put_user(result, value);
+}
+
+
+static int
+isdn_tty_tiocmget(struct tty_struct *tty, struct file *file)
+{
+ modem_info *info = (modem_info *) tty->driver_data;
+ u_char control, status;
+
+ if (isdn_tty_paranoia_check(info, tty->name, __func__))
+ return -ENODEV;
+ if (tty->flags & (1 << TTY_IO_ERROR))
+ return -EIO;
+
+ lock_kernel();
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+ printk(KERN_DEBUG "ttyI%d ioctl TIOCMGET\n", info->line);
+#endif
+
+ control = info->mcr;
+ status = info->msr;
+ unlock_kernel();
+ return ((control & UART_MCR_RTS) ? TIOCM_RTS : 0)
+ | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0)
+ | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0)
+ | ((status & UART_MSR_RI) ? TIOCM_RNG : 0)
+ | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0)
+ | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0);
+}
+
+static int
+isdn_tty_tiocmset(struct tty_struct *tty, struct file *file,
+ unsigned int set, unsigned int clear)
+{
+ modem_info *info = (modem_info *) tty->driver_data;
+
+ if (isdn_tty_paranoia_check(info, tty->name, __func__))
+ return -ENODEV;
+ if (tty->flags & (1 << TTY_IO_ERROR))
+ return -EIO;
+
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+ printk(KERN_DEBUG "ttyI%d ioctl TIOCMxxx: %x %x\n", info->line, set, clear);
+#endif
+
+ lock_kernel();
+ if (set & TIOCM_RTS)
+ info->mcr |= UART_MCR_RTS;
+ if (set & TIOCM_DTR) {
+ info->mcr |= UART_MCR_DTR;
+ isdn_tty_modem_ncarrier(info);
+ }
+
+ if (clear & TIOCM_RTS)
+ info->mcr &= ~UART_MCR_RTS;
+ if (clear & TIOCM_DTR) {
+ info->mcr &= ~UART_MCR_DTR;
+ if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) {
+ isdn_tty_modem_reset_regs(info, 0);
+#ifdef ISDN_DEBUG_MODEM_HUP
+ printk(KERN_DEBUG "Mhup in TIOCMSET\n");
+#endif
+ if (info->online)
+ info->ncarrier = 1;
+ isdn_tty_modem_hup(info, 1);
+ }
+ }
+ unlock_kernel();
+ return 0;
+}
+
+static int
+isdn_tty_ioctl(struct tty_struct *tty, struct file *file,
+ uint cmd, ulong arg)
+{
+ modem_info *info = (modem_info *) tty->driver_data;
+ int retval;
+
+ if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_ioctl"))
+ return -ENODEV;
+ if (tty->flags & (1 << TTY_IO_ERROR))
+ return -EIO;
+ switch (cmd) {
+ case TCSBRK: /* SVID version: non-zero arg --> no break */
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+ printk(KERN_DEBUG "ttyI%d ioctl TCSBRK\n", info->line);
+#endif
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ tty_wait_until_sent(tty, 0);
+ return 0;
+ case TCSBRKP: /* support for POSIX tcsendbreak() */
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+ printk(KERN_DEBUG "ttyI%d ioctl TCSBRKP\n", info->line);
+#endif
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+ tty_wait_until_sent(tty, 0);
+ return 0;
+ case TIOCSERGETLSR: /* Get line status register */
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+ printk(KERN_DEBUG "ttyI%d ioctl TIOCSERGETLSR\n", info->line);
+#endif
+ return isdn_tty_get_lsr_info(info, (uint __user *) arg);
+ default:
+#ifdef ISDN_DEBUG_MODEM_IOCTL
+ printk(KERN_DEBUG "UNKNOWN ioctl 0x%08x on ttyi%d\n", cmd, info->line);
+#endif
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static void
+isdn_tty_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
+{
+ modem_info *info = (modem_info *) tty->driver_data;
+
+ if (!old_termios)
+ isdn_tty_change_speed(info);
+ else {
+ if (tty->termios->c_cflag == old_termios->c_cflag &&
+ tty->termios->c_ispeed == old_termios->c_ispeed &&
+ tty->termios->c_ospeed == old_termios->c_ospeed)
+ return;
+ isdn_tty_change_speed(info);
+ if ((old_termios->c_cflag & CRTSCTS) &&
+ !(tty->termios->c_cflag & CRTSCTS))
+ tty->hw_stopped = 0;
+ }
+}
+
+/*
+ * ------------------------------------------------------------
+ * isdn_tty_open() and friends
+ * ------------------------------------------------------------
+ */
+static int
+isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * info)
+{
+ DECLARE_WAITQUEUE(wait, NULL);
+ int do_clocal = 0;
+ int retval;
+
+ /*
+ * If the device is in the middle of being closed, then block
+ * until it's done, and then try again.
+ */
+ if (tty_hung_up_p(filp) ||
+ (info->flags & ISDN_ASYNC_CLOSING)) {
+ if (info->flags & ISDN_ASYNC_CLOSING)
+ interruptible_sleep_on(&info->close_wait);
+#ifdef MODEM_DO_RESTART
+ if (info->flags & ISDN_ASYNC_HUP_NOTIFY)
+ return -EAGAIN;
+ else
+ return -ERESTARTSYS;
+#else
+ return -EAGAIN;
+#endif
+ }
+ /*
+ * If non-blocking mode is set, then make the check up front
+ * and then exit.
+ */
+ if ((filp->f_flags & O_NONBLOCK) ||
+ (tty->flags & (1 << TTY_IO_ERROR))) {
+ if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE)
+ return -EBUSY;
+ info->flags |= ISDN_ASYNC_NORMAL_ACTIVE;
+ return 0;
+ }
+ if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) {
+ if (info->normal_termios.c_cflag & CLOCAL)
+ do_clocal = 1;
+ } else {
+ if (tty->termios->c_cflag & CLOCAL)
+ do_clocal = 1;
+ }
+ /*
+ * Block waiting for the carrier detect and the line to become
+ * free (i.e., not in use by the callout). While we are in
+ * this loop, info->count is dropped by one, so that
+ * isdn_tty_close() knows when to free things. We restore it upon
+ * exit, either normal or abnormal.
+ */
+ retval = 0;
+ add_wait_queue(&info->open_wait, &wait);
+#ifdef ISDN_DEBUG_MODEM_OPEN
+ printk(KERN_DEBUG "isdn_tty_block_til_ready before block: ttyi%d, count = %d\n",
+ info->line, info->count);
+#endif
+ if (!(tty_hung_up_p(filp)))
+ info->count--;
+ info->blocked_open++;
+ while (1) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (tty_hung_up_p(filp) ||
+ !(info->flags & ISDN_ASYNC_INITIALIZED)) {
+#ifdef MODEM_DO_RESTART
+ if (info->flags & ISDN_ASYNC_HUP_NOTIFY)
+ retval = -EAGAIN;
+ else
+ retval = -ERESTARTSYS;
+#else
+ retval = -EAGAIN;
+#endif
+ break;
+ }
+ if (!(info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
+ !(info->flags & ISDN_ASYNC_CLOSING) &&
+ (do_clocal || (info->msr & UART_MSR_DCD))) {
+ break;
+ }
+ if (signal_pending(current)) {
+ retval = -ERESTARTSYS;
+ break;
+ }
+#ifdef ISDN_DEBUG_MODEM_OPEN
+ printk(KERN_DEBUG "isdn_tty_block_til_ready blocking: ttyi%d, count = %d\n",
+ info->line, info->count);
+#endif
+ schedule();
+ }
+ current->state = TASK_RUNNING;
+ remove_wait_queue(&info->open_wait, &wait);
+ if (!tty_hung_up_p(filp))
+ info->count++;
+ info->blocked_open--;
+#ifdef ISDN_DEBUG_MODEM_OPEN
+ printk(KERN_DEBUG "isdn_tty_block_til_ready after blocking: ttyi%d, count = %d\n",
+ info->line, info->count);
+#endif
+ if (retval)
+ return retval;
+ info->flags |= ISDN_ASYNC_NORMAL_ACTIVE;
+ return 0;
+}
+
+/*
+ * This routine is called whenever a serial port is opened. It
+ * enables interrupts for a serial port, linking in its async structure into
+ * the IRQ chain. It also performs the serial-specific
+ * initialization for the tty structure.
+ */
+static int
+isdn_tty_open(struct tty_struct *tty, struct file *filp)
+{
+ modem_info *info;
+ int retval, line;
+
+ line = tty->index;
+ if (line < 0 || line > ISDN_MAX_CHANNELS)
+ return -ENODEV;
+ info = &dev->mdm.info[line];
+ if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_open"))
+ return -ENODEV;
+ if (!try_module_get(info->owner)) {
+ printk(KERN_WARNING "%s: cannot reserve module\n", __func__);
+ return -ENODEV;
+ }
+#ifdef ISDN_DEBUG_MODEM_OPEN
+ printk(KERN_DEBUG "isdn_tty_open %s, count = %d\n", tty->name,
+ info->count);
+#endif
+ info->count++;
+ tty->driver_data = info;
+ info->tty = tty;
+ /*
+ * Start up serial port
+ */
+ retval = isdn_tty_startup(info);
+ if (retval) {
+#ifdef ISDN_DEBUG_MODEM_OPEN
+ printk(KERN_DEBUG "isdn_tty_open return after startup\n");
+#endif
+ module_put(info->owner);
+ return retval;
+ }
+ retval = isdn_tty_block_til_ready(tty, filp, info);
+ if (retval) {
+#ifdef ISDN_DEBUG_MODEM_OPEN
+ printk(KERN_DEBUG "isdn_tty_open return after isdn_tty_block_til_ready \n");
+#endif
+ module_put(info->owner);
+ return retval;
+ }
+#ifdef ISDN_DEBUG_MODEM_OPEN
+ printk(KERN_DEBUG "isdn_tty_open ttyi%d successful...\n", info->line);
+#endif
+ dev->modempoll++;
+#ifdef ISDN_DEBUG_MODEM_OPEN
+ printk(KERN_DEBUG "isdn_tty_open normal exit\n");
+#endif
+ return 0;
+}
+
+static void
+isdn_tty_close(struct tty_struct *tty, struct file *filp)
+{
+ modem_info *info = (modem_info *) tty->driver_data;
+ ulong timeout;
+
+ if (!info || isdn_tty_paranoia_check(info, tty->name, "isdn_tty_close"))
+ return;
+ if (tty_hung_up_p(filp)) {
+#ifdef ISDN_DEBUG_MODEM_OPEN
+ printk(KERN_DEBUG "isdn_tty_close return after tty_hung_up_p\n");
+#endif
+ return;
+ }
+ if ((tty->count == 1) && (info->count != 1)) {
+ /*
+ * Uh, oh. tty->count is 1, which means that the tty
+ * structure will be freed. Info->count should always
+ * be one in these conditions. If it's greater than
+ * one, we've got real problems, since it means the
+ * serial port won't be shutdown.
+ */
+ printk(KERN_ERR "isdn_tty_close: bad port count; tty->count is 1, "
+ "info->count is %d\n", info->count);
+ info->count = 1;
+ }
+ if (--info->count < 0) {
+ printk(KERN_ERR "isdn_tty_close: bad port count for ttyi%d: %d\n",
+ info->line, info->count);
+ info->count = 0;
+ }
+ if (info->count) {
+#ifdef ISDN_DEBUG_MODEM_OPEN
+ printk(KERN_DEBUG "isdn_tty_close after info->count != 0\n");
+#endif
+ module_put(info->owner);
+ return;
+ }
+ info->flags |= ISDN_ASYNC_CLOSING;
+ /*
+ * Save the termios structure, since this port may have
+ * separate termios for callout and dialin.
+ */
+ if (info->flags & ISDN_ASYNC_NORMAL_ACTIVE)
+ info->normal_termios = *tty->termios;
+ if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE)
+ info->callout_termios = *tty->termios;
+
+ tty->closing = 1;
+ /*
+ * At this point we stop accepting input. To do this, we
+ * disable the receive line status interrupts, and tell the
+ * interrupt driver to stop checking the data ready bit in the
+ * line status register.
+ */
+ if (info->flags & ISDN_ASYNC_INITIALIZED) {
+ tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */
+ /*
+ * Before we drop DTR, make sure the UART transmitter
+ * has completely drained; this is especially
+ * important if there is a transmit FIFO!
+ */
+ timeout = jiffies + HZ;
+ while (!(info->lsr & UART_LSR_TEMT)) {
+ schedule_timeout_interruptible(20);
+ if (time_after(jiffies,timeout))
+ break;
+ }
+ }
+ dev->modempoll--;
+ isdn_tty_shutdown(info);
+ isdn_tty_flush_buffer(tty);
+ tty_ldisc_flush(tty);
+ info->tty = NULL;
+ info->ncarrier = 0;
+ tty->closing = 0;
+ module_put(info->owner);
+ if (info->blocked_open) {
+ msleep_interruptible(500);
+ wake_up_interruptible(&info->open_wait);
+ }
+ info->flags &= ~(ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CLOSING);
+ wake_up_interruptible(&info->close_wait);
+#ifdef ISDN_DEBUG_MODEM_OPEN
+ printk(KERN_DEBUG "isdn_tty_close normal exit\n");
+#endif
+}
+
+/*
+ * isdn_tty_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+static void
+isdn_tty_hangup(struct tty_struct *tty)
+{
+ modem_info *info = (modem_info *) tty->driver_data;
+
+ if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_hangup"))
+ return;
+ isdn_tty_shutdown(info);
+ info->count = 0;
+ info->flags &= ~(ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE);
+ info->tty = NULL;
+ wake_up_interruptible(&info->open_wait);
+}
+
+/* This routine initializes all emulator-data.
+ */
+static void
+isdn_tty_reset_profile(atemu * m)
+{
+ m->profile[0] = 0;
+ m->profile[1] = 0;
+ m->profile[2] = 43;
+ m->profile[3] = 13;
+ m->profile[4] = 10;
+ m->profile[5] = 8;
+ m->profile[6] = 3;
+ m->profile[7] = 60;
+ m->profile[8] = 2;
+ m->profile[9] = 6;
+ m->profile[10] = 7;
+ m->profile[11] = 70;
+ m->profile[12] = 0x45;
+ m->profile[13] = 4;
+ m->profile[14] = ISDN_PROTO_L2_X75I;
+ m->profile[15] = ISDN_PROTO_L3_TRANS;
+ m->profile[16] = ISDN_SERIAL_XMIT_SIZE / 16;
+ m->profile[17] = ISDN_MODEM_WINSIZE;
+ m->profile[18] = 4;
+ m->profile[19] = 0;
+ m->profile[20] = 0;
+ m->profile[23] = 0;
+ m->pmsn[0] = '\0';
+ m->plmsn[0] = '\0';
+}
+
+#ifdef CONFIG_ISDN_AUDIO
+static void
+isdn_tty_modem_reset_vpar(atemu * m)
+{
+ m->vpar[0] = 2; /* Voice-device (2 = phone line) */
+ m->vpar[1] = 0; /* Silence detection level (0 = none ) */
+ m->vpar[2] = 70; /* Silence interval (7 sec. ) */
+ m->vpar[3] = 2; /* Compression type (1 = ADPCM-2 ) */
+ m->vpar[4] = 0; /* DTMF detection level (0 = softcode ) */
+ m->vpar[5] = 8; /* DTMF interval (8 * 5 ms. ) */
+}
+#endif
+
+#ifdef CONFIG_ISDN_TTY_FAX
+static void
+isdn_tty_modem_reset_faxpar(modem_info * info)
+{
+ T30_s *f = info->fax;
+
+ f->code = 0;
+ f->phase = ISDN_FAX_PHASE_IDLE;
+ f->direction = 0;
+ f->resolution = 1; /* fine */
+ f->rate = 5; /* 14400 bit/s */
+ f->width = 0;
+ f->length = 0;
+ f->compression = 0;
+ f->ecm = 0;
+ f->binary = 0;
+ f->scantime = 0;
+ memset(&f->id[0], 32, FAXIDLEN - 1);
+ f->id[FAXIDLEN - 1] = 0;
+ f->badlin = 0;
+ f->badmul = 0;
+ f->bor = 0;
+ f->nbc = 0;
+ f->cq = 0;
+ f->cr = 0;
+ f->ctcrty = 0;
+ f->minsp = 0;
+ f->phcto = 30;
+ f->rel = 0;
+ memset(&f->pollid[0], 32, FAXIDLEN - 1);
+ f->pollid[FAXIDLEN - 1] = 0;
+}
+#endif
+
+static void
+isdn_tty_modem_reset_regs(modem_info * info, int force)
+{
+ atemu *m = &info->emu;
+ if ((m->mdmreg[REG_DTRR] & BIT_DTRR) || force) {
+ memcpy(m->mdmreg, m->profile, ISDN_MODEM_NUMREG);
+ memcpy(m->msn, m->pmsn, ISDN_MSNLEN);
+ memcpy(m->lmsn, m->plmsn, ISDN_LMSNLEN);
+ info->xmit_size = m->mdmreg[REG_PSIZE] * 16;
+ }
+#ifdef CONFIG_ISDN_AUDIO
+ isdn_tty_modem_reset_vpar(m);
+#endif
+#ifdef CONFIG_ISDN_TTY_FAX
+ isdn_tty_modem_reset_faxpar(info);
+#endif
+ m->mdmcmdl = 0;
+}
+
+static void
+modem_write_profile(atemu * m)
+{
+ memcpy(m->profile, m->mdmreg, ISDN_MODEM_NUMREG);
+ memcpy(m->pmsn, m->msn, ISDN_MSNLEN);
+ memcpy(m->plmsn, m->lmsn, ISDN_LMSNLEN);
+ if (dev->profd)
+ send_sig(SIGIO, dev->profd, 1);
+}
+
+static const struct tty_operations modem_ops = {
+ .open = isdn_tty_open,
+ .close = isdn_tty_close,
+ .write = isdn_tty_write,
+ .flush_chars = isdn_tty_flush_chars,
+ .write_room = isdn_tty_write_room,
+ .chars_in_buffer = isdn_tty_chars_in_buffer,
+ .flush_buffer = isdn_tty_flush_buffer,
+ .ioctl = isdn_tty_ioctl,
+ .throttle = isdn_tty_throttle,
+ .unthrottle = isdn_tty_unthrottle,
+ .set_termios = isdn_tty_set_termios,
+ .hangup = isdn_tty_hangup,
+ .tiocmget = isdn_tty_tiocmget,
+ .tiocmset = isdn_tty_tiocmset,
+};
+
+int
+isdn_tty_modem_init(void)
+{
+ isdn_modem_t *m;
+ int i, retval;
+ modem_info *info;
+
+ m = &dev->mdm;
+ m->tty_modem = alloc_tty_driver(ISDN_MAX_CHANNELS);
+ if (!m->tty_modem)
+ return -ENOMEM;
+ m->tty_modem->name = "ttyI";
+ m->tty_modem->major = ISDN_TTY_MAJOR;
+ m->tty_modem->minor_start = 0;
+ m->tty_modem->type = TTY_DRIVER_TYPE_SERIAL;
+ m->tty_modem->subtype = SERIAL_TYPE_NORMAL;
+ m->tty_modem->init_termios = tty_std_termios;
+ m->tty_modem->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ m->tty_modem->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+ m->tty_modem->driver_name = "isdn_tty";
+ tty_set_operations(m->tty_modem, &modem_ops);
+ retval = tty_register_driver(m->tty_modem);
+ if (retval) {
+ printk(KERN_WARNING "isdn_tty: Couldn't register modem-device\n");
+ goto err;
+ }
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ info = &m->info[i];
+#ifdef CONFIG_ISDN_TTY_FAX
+ if (!(info->fax = kmalloc(sizeof(T30_s), GFP_KERNEL))) {
+ printk(KERN_ERR "Could not allocate fax t30-buffer\n");
+ retval = -ENOMEM;
+ goto err_unregister;
+ }
+#endif
+#ifdef MODULE
+ info->owner = THIS_MODULE;
+#endif
+ spin_lock_init(&info->readlock);
+ sprintf(info->last_cause, "0000");
+ sprintf(info->last_num, "none");
+ info->last_dir = 0;
+ info->last_lhup = 1;
+ info->last_l2 = -1;
+ info->last_si = 0;
+ isdn_tty_reset_profile(&info->emu);
+ isdn_tty_modem_reset_regs(info, 1);
+ info->magic = ISDN_ASYNC_MAGIC;
+ info->line = i;
+ info->tty = NULL;
+ info->x_char = 0;
+ info->count = 0;
+ info->blocked_open = 0;
+ init_waitqueue_head(&info->open_wait);
+ init_waitqueue_head(&info->close_wait);
+ info->isdn_driver = -1;
+ info->isdn_channel = -1;
+ info->drv_index = -1;
+ info->xmit_size = ISDN_SERIAL_XMIT_SIZE;
+ init_timer(&info->nc_timer);
+ info->nc_timer.function = isdn_tty_modem_do_ncarrier;
+ info->nc_timer.data = (unsigned long) info;
+ skb_queue_head_init(&info->xmit_queue);
+#ifdef CONFIG_ISDN_AUDIO
+ skb_queue_head_init(&info->dtmf_queue);
+#endif
+ if (!(info->xmit_buf = kmalloc(ISDN_SERIAL_XMIT_MAX + 5, GFP_KERNEL))) {
+ printk(KERN_ERR "Could not allocate modem xmit-buffer\n");
+ retval = -ENOMEM;
+ goto err_unregister;
+ }
+ /* Make room for T.70 header */
+ info->xmit_buf += 4;
+ }
+ return 0;
+err_unregister:
+ for (i--; i >= 0; i--) {
+ info = &m->info[i];
+#ifdef CONFIG_ISDN_TTY_FAX
+ kfree(info->fax);
+#endif
+ kfree(info->xmit_buf - 4);
+ }
+ tty_unregister_driver(m->tty_modem);
+ err:
+ put_tty_driver(m->tty_modem);
+ m->tty_modem = NULL;
+ return retval;
+}
+
+void
+isdn_tty_exit(void)
+{
+ modem_info *info;
+ int i;
+
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ info = &dev->mdm.info[i];
+ isdn_tty_cleanup_xmit(info);
+#ifdef CONFIG_ISDN_TTY_FAX
+ kfree(info->fax);
+#endif
+ kfree(info->xmit_buf - 4);
+ }
+ tty_unregister_driver(dev->mdm.tty_modem);
+ put_tty_driver(dev->mdm.tty_modem);
+ dev->mdm.tty_modem = NULL;
+}
+
+
+/*
+ * isdn_tty_match_icall(char *MSN, atemu *tty_emulator, int dev_idx)
+ * match the MSN against the MSNs (glob patterns) defined for tty_emulator,
+ * and return 0 for match, 1 for no match, 2 if MSN could match if longer.
+ */
+
+static int
+isdn_tty_match_icall(char *cid, atemu *emu, int di)
+{
+#ifdef ISDN_DEBUG_MODEM_ICALL
+ printk(KERN_DEBUG "m_fi: msn=%s lmsn=%s mmsn=%s mreg[SI1]=%d mreg[SI2]=%d\n",
+ emu->msn, emu->lmsn, isdn_map_eaz2msn(emu->msn, di),
+ emu->mdmreg[REG_SI1], emu->mdmreg[REG_SI2]);
+#endif
+ if (strlen(emu->lmsn)) {
+ char *p = emu->lmsn;
+ char *q;
+ int tmp;
+ int ret = 0;
+
+ while (1) {
+ if ((q = strchr(p, ';')))
+ *q = '\0';
+ if ((tmp = isdn_msncmp(cid, isdn_map_eaz2msn(p, di))) > ret)
+ ret = tmp;
+#ifdef ISDN_DEBUG_MODEM_ICALL
+ printk(KERN_DEBUG "m_fi: lmsnX=%s mmsn=%s -> tmp=%d\n",
+ p, isdn_map_eaz2msn(emu->msn, di), tmp);
+#endif
+ if (q) {
+ *q = ';';
+ p = q;
+ p++;
+ }
+ if (!tmp)
+ return 0;
+ if (!q)
+ break;
+ }
+ return ret;
+ } else {
+ int tmp;
+ tmp = isdn_msncmp(cid, isdn_map_eaz2msn(emu->msn, di));
+#ifdef ISDN_DEBUG_MODEM_ICALL
+ printk(KERN_DEBUG "m_fi: mmsn=%s -> tmp=%d\n",
+ isdn_map_eaz2msn(emu->msn, di), tmp);
+#endif
+ return tmp;
+ }
+}
+
+/*
+ * An incoming call-request has arrived.
+ * Search the tty-devices for an appropriate device and bind
+ * it to the ISDN-Channel.
+ * Return:
+ *
+ * 0 = No matching device found.
+ * 1 = A matching device found.
+ * 3 = No match found, but eventually would match, if
+ * CID is longer.
+ */
+int
+isdn_tty_find_icall(int di, int ch, setup_parm *setup)
+{
+ char *eaz;
+ int i;
+ int wret;
+ int idx;
+ int si1;
+ int si2;
+ char *nr;
+ ulong flags;
+
+ if (!setup->phone[0]) {
+ nr = "0";
+ printk(KERN_INFO "isdn_tty: Incoming call without OAD, assuming '0'\n");
+ } else
+ nr = setup->phone;
+ si1 = (int) setup->si1;
+ si2 = (int) setup->si2;
+ if (!setup->eazmsn[0]) {
+ printk(KERN_WARNING "isdn_tty: Incoming call without CPN, assuming '0'\n");
+ eaz = "0";
+ } else
+ eaz = setup->eazmsn;
+#ifdef ISDN_DEBUG_MODEM_ICALL
+ printk(KERN_DEBUG "m_fi: eaz=%s si1=%d si2=%d\n", eaz, si1, si2);
+#endif
+ wret = 0;
+ spin_lock_irqsave(&dev->lock, flags);
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ modem_info *info = &dev->mdm.info[i];
+
+ if (info->count == 0)
+ continue;
+ if ((info->emu.mdmreg[REG_SI1] & si2bit[si1]) && /* SI1 is matching */
+ (info->emu.mdmreg[REG_SI2] == si2)) { /* SI2 is matching */
+ idx = isdn_dc2minor(di, ch);
+#ifdef ISDN_DEBUG_MODEM_ICALL
+ printk(KERN_DEBUG "m_fi: match1 wret=%d\n", wret);
+ printk(KERN_DEBUG "m_fi: idx=%d flags=%08lx drv=%d ch=%d usg=%d\n", idx,
+ info->flags, info->isdn_driver, info->isdn_channel,
+ dev->usage[idx]);
+#endif
+ if (
+#ifndef FIX_FILE_TRANSFER
+ (info->flags & ISDN_ASYNC_NORMAL_ACTIVE) &&
+#endif
+ (info->isdn_driver == -1) &&
+ (info->isdn_channel == -1) &&
+ (USG_NONE(dev->usage[idx]))) {
+ int matchret;
+
+ if ((matchret = isdn_tty_match_icall(eaz, &info->emu, di)) > wret)
+ wret = matchret;
+ if (!matchret) { /* EAZ is matching */
+ info->isdn_driver = di;
+ info->isdn_channel = ch;
+ info->drv_index = idx;
+ dev->m_idx[idx] = info->line;
+ dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE;
+ dev->usage[idx] |= isdn_calc_usage(si1, info->emu.mdmreg[REG_L2PROT]);
+ strcpy(dev->num[idx], nr);
+ strcpy(info->emu.cpn, eaz);
+ info->emu.mdmreg[REG_SI1I] = si2bit[si1];
+ info->emu.mdmreg[REG_PLAN] = setup->plan;
+ info->emu.mdmreg[REG_SCREEN] = setup->screen;
+ isdn_info_update();
+ spin_unlock_irqrestore(&dev->lock, flags);
+ printk(KERN_INFO "isdn_tty: call from %s, -> RING on ttyI%d\n", nr,
+ info->line);
+ info->msr |= UART_MSR_RI;
+ isdn_tty_modem_result(RESULT_RING, info);
+ isdn_timer_ctrl(ISDN_TIMER_MODEMRING, 1);
+ return 1;
+ }
+ }
+ }
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+ printk(KERN_INFO "isdn_tty: call from %s -> %s %s\n", nr, eaz,
+ ((dev->drv[di]->flags & DRV_FLAG_REJBUS) && (wret != 2))? "rejected" : "ignored");
+ return (wret == 2)?3:0;
+}
+
+#define TTY_IS_ACTIVE(info) \
+ (info->flags & (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE))
+
+int
+isdn_tty_stat_callback(int i, isdn_ctrl *c)
+{
+ int mi;
+ modem_info *info;
+ char *e;
+
+ if (i < 0)
+ return 0;
+ if ((mi = dev->m_idx[i]) >= 0) {
+ info = &dev->mdm.info[mi];
+ switch (c->command) {
+ case ISDN_STAT_CINF:
+ printk(KERN_DEBUG "CHARGEINFO on ttyI%d: %ld %s\n", info->line, c->arg, c->parm.num);
+ info->emu.charge = (unsigned) simple_strtoul(c->parm.num, &e, 10);
+ if (e == (char *)c->parm.num)
+ info->emu.charge = 0;
+
+ break;
+ case ISDN_STAT_BSENT:
+#ifdef ISDN_TTY_STAT_DEBUG
+ printk(KERN_DEBUG "tty_STAT_BSENT ttyI%d\n", info->line);
+#endif
+ if ((info->isdn_driver == c->driver) &&
+ (info->isdn_channel == c->arg)) {
+ info->msr |= UART_MSR_CTS;
+ if (info->send_outstanding)
+ if (!(--info->send_outstanding))
+ info->lsr |= UART_LSR_TEMT;
+ isdn_tty_tint(info);
+ return 1;
+ }
+ break;
+ case ISDN_STAT_CAUSE:
+#ifdef ISDN_TTY_STAT_DEBUG
+ printk(KERN_DEBUG "tty_STAT_CAUSE ttyI%d\n", info->line);
+#endif
+ /* Signal cause to tty-device */
+ strncpy(info->last_cause, c->parm.num, 5);
+ return 1;
+ case ISDN_STAT_DISPLAY:
+#ifdef ISDN_TTY_STAT_DEBUG
+ printk(KERN_DEBUG "tty_STAT_DISPLAY ttyI%d\n", info->line);
+#endif
+ /* Signal display to tty-device */
+ if ((info->emu.mdmreg[REG_DISPLAY] & BIT_DISPLAY) &&
+ !(info->emu.mdmreg[REG_RESPNUM] & BIT_RESPNUM)) {
+ isdn_tty_at_cout("\r\n", info);
+ isdn_tty_at_cout("DISPLAY: ", info);
+ isdn_tty_at_cout(c->parm.display, info);
+ isdn_tty_at_cout("\r\n", info);
+ }
+ return 1;
+ case ISDN_STAT_DCONN:
+#ifdef ISDN_TTY_STAT_DEBUG
+ printk(KERN_DEBUG "tty_STAT_DCONN ttyI%d\n", info->line);
+#endif
+ if (TTY_IS_ACTIVE(info)) {
+ if (info->dialing == 1) {
+ info->dialing = 2;
+ return 1;
+ }
+ }
+ break;
+ case ISDN_STAT_DHUP:
+#ifdef ISDN_TTY_STAT_DEBUG
+ printk(KERN_DEBUG "tty_STAT_DHUP ttyI%d\n", info->line);
+#endif
+ if (TTY_IS_ACTIVE(info)) {
+ if (info->dialing == 1)
+ isdn_tty_modem_result(RESULT_BUSY, info);
+ if (info->dialing > 1)
+ isdn_tty_modem_result(RESULT_NO_CARRIER, info);
+ info->dialing = 0;
+#ifdef ISDN_DEBUG_MODEM_HUP
+ printk(KERN_DEBUG "Mhup in ISDN_STAT_DHUP\n");
+#endif
+ isdn_tty_modem_hup(info, 0);
+ return 1;
+ }
+ break;
+ case ISDN_STAT_BCONN:
+#ifdef ISDN_TTY_STAT_DEBUG
+ printk(KERN_DEBUG "tty_STAT_BCONN ttyI%d\n", info->line);
+#endif
+ /* Wake up any processes waiting
+ * for incoming call of this device when
+ * DCD follow the state of incoming carrier
+ */
+ if (info->blocked_open &&
+ (info->emu.mdmreg[REG_DCD] & BIT_DCD)) {
+ wake_up_interruptible(&info->open_wait);
+ }
+
+ /* Schedule CONNECT-Message to any tty
+ * waiting for it and
+ * set DCD-bit of its modem-status.
+ */
+ if (TTY_IS_ACTIVE(info) ||
+ (info->blocked_open && (info->emu.mdmreg[REG_DCD] & BIT_DCD))) {
+ info->msr |= UART_MSR_DCD;
+ info->emu.charge = 0;
+ if (info->dialing & 0xf)
+ info->last_dir = 1;
+ else
+ info->last_dir = 0;
+ info->dialing = 0;
+ info->rcvsched = 1;
+ if (USG_MODEM(dev->usage[i])) {
+ if (info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM) {
+ strcpy(info->emu.connmsg, c->parm.num);
+ isdn_tty_modem_result(RESULT_CONNECT, info);
+ } else
+ isdn_tty_modem_result(RESULT_CONNECT64000, info);
+ }
+ if (USG_VOICE(dev->usage[i]))
+ isdn_tty_modem_result(RESULT_VCON, info);
+ return 1;
+ }
+ break;
+ case ISDN_STAT_BHUP:
+#ifdef ISDN_TTY_STAT_DEBUG
+ printk(KERN_DEBUG "tty_STAT_BHUP ttyI%d\n", info->line);
+#endif
+ if (TTY_IS_ACTIVE(info)) {
+#ifdef ISDN_DEBUG_MODEM_HUP
+ printk(KERN_DEBUG "Mhup in ISDN_STAT_BHUP\n");
+#endif
+ isdn_tty_modem_hup(info, 0);
+ return 1;
+ }
+ break;
+ case ISDN_STAT_NODCH:
+#ifdef ISDN_TTY_STAT_DEBUG
+ printk(KERN_DEBUG "tty_STAT_NODCH ttyI%d\n", info->line);
+#endif
+ if (TTY_IS_ACTIVE(info)) {
+ if (info->dialing) {
+ info->dialing = 0;
+ info->last_l2 = -1;
+ info->last_si = 0;
+ sprintf(info->last_cause, "0000");
+ isdn_tty_modem_result(RESULT_NO_DIALTONE, info);
+ }
+ isdn_tty_modem_hup(info, 0);
+ return 1;
+ }
+ break;
+ case ISDN_STAT_UNLOAD:
+#ifdef ISDN_TTY_STAT_DEBUG
+ printk(KERN_DEBUG "tty_STAT_UNLOAD ttyI%d\n", info->line);
+#endif
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ info = &dev->mdm.info[i];
+ if (info->isdn_driver == c->driver) {
+ if (info->online)
+ isdn_tty_modem_hup(info, 1);
+ }
+ }
+ return 1;
+#ifdef CONFIG_ISDN_TTY_FAX
+ case ISDN_STAT_FAXIND:
+ if (TTY_IS_ACTIVE(info)) {
+ isdn_tty_fax_command(info, c);
+ }
+ break;
+#endif
+#ifdef CONFIG_ISDN_AUDIO
+ case ISDN_STAT_AUDIO:
+ if (TTY_IS_ACTIVE(info)) {
+ switch(c->parm.num[0]) {
+ case ISDN_AUDIO_DTMF:
+ if (info->vonline) {
+ isdn_audio_put_dle_code(info,
+ c->parm.num[1]);
+ }
+ break;
+ }
+ }
+ break;
+#endif
+ }
+ }
+ return 0;
+}
+
+/*********************************************************************
+ Modem-Emulator-Routines
+ *********************************************************************/
+
+#define cmdchar(c) ((c>=' ')&&(c<=0x7f))
+
+/*
+ * Put a message from the AT-emulator into receive-buffer of tty,
+ * convert CR, LF, and BS to values in modem-registers 3, 4 and 5.
+ */
+void
+isdn_tty_at_cout(char *msg, modem_info * info)
+{
+ struct tty_struct *tty;
+ atemu *m = &info->emu;
+ char *p;
+ char c;
+ u_long flags;
+ struct sk_buff *skb = NULL;
+ char *sp = NULL;
+ int l;
+
+ if (!msg) {
+ printk(KERN_WARNING "isdn_tty: Null-Message in isdn_tty_at_cout\n");
+ return;
+ }
+
+ l = strlen(msg);
+
+ spin_lock_irqsave(&info->readlock, flags);
+ tty = info->tty;
+ if ((info->flags & ISDN_ASYNC_CLOSING) || (!tty)) {
+ spin_unlock_irqrestore(&info->readlock, flags);
+ return;
+ }
+
+ /* use queue instead of direct, if online and */
+ /* data is in queue or buffer is full */
+ if (info->online && ((tty_buffer_request_room(tty, l) < l) ||
+ !skb_queue_empty(&dev->drv[info->isdn_driver]->rpqueue[info->isdn_channel]))) {
+ skb = alloc_skb(l, GFP_ATOMIC);
+ if (!skb) {
+ spin_unlock_irqrestore(&info->readlock, flags);
+ return;
+ }
+ sp = skb_put(skb, l);
+#ifdef CONFIG_ISDN_AUDIO
+ ISDN_AUDIO_SKB_DLECOUNT(skb) = 0;
+ ISDN_AUDIO_SKB_LOCK(skb) = 0;
+#endif
+ }
+
+ for (p = msg; *p; p++) {
+ switch (*p) {
+ case '\r':
+ c = m->mdmreg[REG_CR];
+ break;
+ case '\n':
+ c = m->mdmreg[REG_LF];
+ break;
+ case '\b':
+ c = m->mdmreg[REG_BS];
+ break;
+ default:
+ c = *p;
+ }
+ if (skb) {
+ *sp++ = c;
+ } else {
+ if(tty_insert_flip_char(tty, c, TTY_NORMAL) == 0)
+ break;
+ }
+ }
+ if (skb) {
+ __skb_queue_tail(&dev->drv[info->isdn_driver]->rpqueue[info->isdn_channel], skb);
+ dev->drv[info->isdn_driver]->rcvcount[info->isdn_channel] += skb->len;
+ spin_unlock_irqrestore(&info->readlock, flags);
+ /* Schedule dequeuing */
+ if (dev->modempoll && info->rcvsched)
+ isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1);
+
+ } else {
+ spin_unlock_irqrestore(&info->readlock, flags);
+ tty_flip_buffer_push(tty);
+ }
+}
+
+/*
+ * Perform ATH Hangup
+ */
+static void
+isdn_tty_on_hook(modem_info * info)
+{
+ if (info->isdn_channel >= 0) {
+#ifdef ISDN_DEBUG_MODEM_HUP
+ printk(KERN_DEBUG "Mhup in isdn_tty_on_hook\n");
+#endif
+ isdn_tty_modem_hup(info, 1);
+ }
+}
+
+static void
+isdn_tty_off_hook(void)
+{
+ printk(KERN_DEBUG "isdn_tty_off_hook\n");
+}
+
+#define PLUSWAIT1 (HZ/2) /* 0.5 sec. */
+#define PLUSWAIT2 (HZ*3/2) /* 1.5 sec */
+
+/*
+ * Check Buffer for Modem-escape-sequence, activate timer-callback to
+ * isdn_tty_modem_escape() if sequence found.
+ *
+ * Parameters:
+ * p pointer to databuffer
+ * plus escape-character
+ * count length of buffer
+ * pluscount count of valid escape-characters so far
+ * lastplus timestamp of last character
+ */
+static void
+isdn_tty_check_esc(const u_char * p, u_char plus, int count, int *pluscount,
+ u_long *lastplus)
+{
+ if (plus > 127)
+ return;
+ if (count > 3) {
+ p += count - 3;
+ count = 3;
+ *pluscount = 0;
+ }
+ while (count > 0) {
+ if (*(p++) == plus) {
+ if ((*pluscount)++) {
+ /* Time since last '+' > 0.5 sec. ? */
+ if (time_after(jiffies, *lastplus + PLUSWAIT1))
+ *pluscount = 1;
+ } else {
+ /* Time since last non-'+' < 1.5 sec. ? */
+ if (time_before(jiffies, *lastplus + PLUSWAIT2))
+ *pluscount = 0;
+ }
+ if ((*pluscount == 3) && (count == 1))
+ isdn_timer_ctrl(ISDN_TIMER_MODEMPLUS, 1);
+ if (*pluscount > 3)
+ *pluscount = 1;
+ } else
+ *pluscount = 0;
+ *lastplus = jiffies;
+ count--;
+ }
+}
+
+/*
+ * Return result of AT-emulator to tty-receive-buffer, depending on
+ * modem-register 12, bit 0 and 1.
+ * For CONNECT-messages also switch to online-mode.
+ * For RING-message handle auto-ATA if register 0 != 0
+ */
+
+static void
+isdn_tty_modem_result(int code, modem_info * info)
+{
+ atemu *m = &info->emu;
+ static char *msg[] =
+ {"OK", "CONNECT", "RING", "NO CARRIER", "ERROR",
+ "CONNECT 64000", "NO DIALTONE", "BUSY", "NO ANSWER",
+ "RINGING", "NO MSN/EAZ", "VCON", "RUNG"};
+ char s[ISDN_MSNLEN+10];
+
+ switch (code) {
+ case RESULT_RING:
+ m->mdmreg[REG_RINGCNT]++;
+ if (m->mdmreg[REG_RINGCNT] == m->mdmreg[REG_RINGATA])
+ /* Automatically accept incoming call */
+ isdn_tty_cmd_ATA(info);
+ break;
+ case RESULT_NO_CARRIER:
+#ifdef ISDN_DEBUG_MODEM_HUP
+ printk(KERN_DEBUG "modem_result: NO CARRIER %d %d\n",
+ (info->flags & ISDN_ASYNC_CLOSING),
+ (!info->tty));
+#endif
+ m->mdmreg[REG_RINGCNT] = 0;
+ del_timer(&info->nc_timer);
+ info->ncarrier = 0;
+ if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) {
+ return;
+ }
+#ifdef CONFIG_ISDN_AUDIO
+ if (info->vonline & 1) {
+#ifdef ISDN_DEBUG_MODEM_VOICE
+ printk(KERN_DEBUG "res3: send DLE-ETX on ttyI%d\n",
+ info->line);
+#endif
+ /* voice-recording, add DLE-ETX */
+ isdn_tty_at_cout("\020\003", info);
+ }
+ if (info->vonline & 2) {
+#ifdef ISDN_DEBUG_MODEM_VOICE
+ printk(KERN_DEBUG "res3: send DLE-DC4 on ttyI%d\n",
+ info->line);
+#endif
+ /* voice-playing, add DLE-DC4 */
+ isdn_tty_at_cout("\020\024", info);
+ }
+#endif
+ break;
+ case RESULT_CONNECT:
+ case RESULT_CONNECT64000:
+ sprintf(info->last_cause, "0000");
+ if (!info->online)
+ info->online = 2;
+ break;
+ case RESULT_VCON:
+#ifdef ISDN_DEBUG_MODEM_VOICE
+ printk(KERN_DEBUG "res3: send VCON on ttyI%d\n",
+ info->line);
+#endif
+ sprintf(info->last_cause, "0000");
+ if (!info->online)
+ info->online = 1;
+ break;
+ } /* switch(code) */
+
+ if (m->mdmreg[REG_RESP] & BIT_RESP) {
+ /* Show results */
+ if (m->mdmreg[REG_RESPNUM] & BIT_RESPNUM) {
+ /* Show numeric results only */
+ sprintf(s, "\r\n%d\r\n", code);
+ isdn_tty_at_cout(s, info);
+ } else {
+ if (code == RESULT_RING) {
+ /* return if "show RUNG" and ringcounter>1 */
+ if ((m->mdmreg[REG_RUNG] & BIT_RUNG) &&
+ (m->mdmreg[REG_RINGCNT] > 1))
+ return;
+ /* print CID, _before_ _every_ ring */
+ if (!(m->mdmreg[REG_CIDONCE] & BIT_CIDONCE)) {
+ isdn_tty_at_cout("\r\nCALLER NUMBER: ", info);
+ isdn_tty_at_cout(dev->num[info->drv_index], info);
+ if (m->mdmreg[REG_CDN] & BIT_CDN) {
+ isdn_tty_at_cout("\r\nCALLED NUMBER: ", info);
+ isdn_tty_at_cout(info->emu.cpn, info);
+ }
+ }
+ }
+ isdn_tty_at_cout("\r\n", info);
+ isdn_tty_at_cout(msg[code], info);
+ switch (code) {
+ case RESULT_CONNECT:
+ switch (m->mdmreg[REG_L2PROT]) {
+ case ISDN_PROTO_L2_MODEM:
+ isdn_tty_at_cout(" ", info);
+ isdn_tty_at_cout(m->connmsg, info);
+ break;
+ }
+ break;
+ case RESULT_RING:
+ /* Append CPN, if enabled */
+ if ((m->mdmreg[REG_CPN] & BIT_CPN)) {
+ sprintf(s, "/%s", m->cpn);
+ isdn_tty_at_cout(s, info);
+ }
+ /* Print CID only once, _after_ 1st RING */
+ if ((m->mdmreg[REG_CIDONCE] & BIT_CIDONCE) &&
+ (m->mdmreg[REG_RINGCNT] == 1)) {
+ isdn_tty_at_cout("\r\n", info);
+ isdn_tty_at_cout("CALLER NUMBER: ", info);
+ isdn_tty_at_cout(dev->num[info->drv_index], info);
+ if (m->mdmreg[REG_CDN] & BIT_CDN) {
+ isdn_tty_at_cout("\r\nCALLED NUMBER: ", info);
+ isdn_tty_at_cout(info->emu.cpn, info);
+ }
+ }
+ break;
+ case RESULT_NO_CARRIER:
+ case RESULT_NO_DIALTONE:
+ case RESULT_BUSY:
+ case RESULT_NO_ANSWER:
+ m->mdmreg[REG_RINGCNT] = 0;
+ /* Append Cause-Message if enabled */
+ if (m->mdmreg[REG_RESPXT] & BIT_RESPXT) {
+ sprintf(s, "/%s", info->last_cause);
+ isdn_tty_at_cout(s, info);
+ }
+ break;
+ case RESULT_CONNECT64000:
+ /* Append Protocol to CONNECT message */
+ switch (m->mdmreg[REG_L2PROT]) {
+ case ISDN_PROTO_L2_X75I:
+ case ISDN_PROTO_L2_X75UI:
+ case ISDN_PROTO_L2_X75BUI:
+ isdn_tty_at_cout("/X.75", info);
+ break;
+ case ISDN_PROTO_L2_HDLC:
+ isdn_tty_at_cout("/HDLC", info);
+ break;
+ case ISDN_PROTO_L2_V11096:
+ isdn_tty_at_cout("/V110/9600", info);
+ break;
+ case ISDN_PROTO_L2_V11019:
+ isdn_tty_at_cout("/V110/19200", info);
+ break;
+ case ISDN_PROTO_L2_V11038:
+ isdn_tty_at_cout("/V110/38400", info);
+ break;
+ }
+ if (m->mdmreg[REG_T70] & BIT_T70) {
+ isdn_tty_at_cout("/T.70", info);
+ if (m->mdmreg[REG_T70] & BIT_T70_EXT)
+ isdn_tty_at_cout("+", info);
+ }
+ break;
+ }
+ isdn_tty_at_cout("\r\n", info);
+ }
+ }
+ if (code == RESULT_NO_CARRIER) {
+ if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) {
+ return;
+ }
+#ifdef CONFIG_ISDN_AUDIO
+ if ( !info->vonline )
+ tty_ldisc_flush(info->tty);
+#else
+ tty_ldisc_flush(info->tty);
+#endif
+ if ((info->flags & ISDN_ASYNC_CHECK_CD) &&
+ (!((info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) &&
+ (info->flags & ISDN_ASYNC_CALLOUT_NOHUP)))) {
+ tty_hangup(info->tty);
+ }
+ }
+}
+
+
+/*
+ * Display a modem-register-value.
+ */
+static void
+isdn_tty_show_profile(int ridx, modem_info * info)
+{
+ char v[6];
+
+ sprintf(v, "\r\n%d", info->emu.mdmreg[ridx]);
+ isdn_tty_at_cout(v, info);
+}
+
+/*
+ * Get MSN-string from char-pointer, set pointer to end of number
+ */
+static void
+isdn_tty_get_msnstr(char *n, char **p)
+{
+ int limit = ISDN_MSNLEN - 1;
+
+ while (((*p[0] >= '0' && *p[0] <= '9') ||
+ /* Why a comma ??? */
+ (*p[0] == ',') || (*p[0] == ':')) &&
+ (limit--))
+ *n++ = *p[0]++;
+ *n = '\0';
+}
+
+/*
+ * Get phone-number from modem-commandbuffer
+ */
+static void
+isdn_tty_getdial(char *p, char *q,int cnt)
+{
+ int first = 1;
+ int limit = ISDN_MSNLEN - 1; /* MUST match the size of interface var to avoid
+ buffer overflow */
+
+ while (strchr(" 0123456789,#.*WPTSR-", *p) && *p && --cnt>0) {
+ if ((*p >= '0' && *p <= '9') || ((*p == 'S') && first) ||
+ ((*p == 'R') && first) ||
+ (*p == '*') || (*p == '#')) {
+ *q++ = *p;
+ limit--;
+ }
+ if(!limit)
+ break;
+ p++;
+ first = 0;
+ }
+ *q = 0;
+}
+
+#define PARSE_ERROR { isdn_tty_modem_result(RESULT_ERROR, info); return; }
+#define PARSE_ERROR1 { isdn_tty_modem_result(RESULT_ERROR, info); return 1; }
+
+static void
+isdn_tty_report(modem_info * info)
+{
+ atemu *m = &info->emu;
+ char s[80];
+
+ isdn_tty_at_cout("\r\nStatistics of last connection:\r\n\r\n", info);
+ sprintf(s, " Remote Number: %s\r\n", info->last_num);
+ isdn_tty_at_cout(s, info);
+ sprintf(s, " Direction: %s\r\n", info->last_dir ? "outgoing" : "incoming");
+ isdn_tty_at_cout(s, info);
+ isdn_tty_at_cout(" Layer-2 Protocol: ", info);
+ switch (info->last_l2) {
+ case ISDN_PROTO_L2_X75I:
+ isdn_tty_at_cout("X.75i", info);
+ break;
+ case ISDN_PROTO_L2_X75UI:
+ isdn_tty_at_cout("X.75ui", info);
+ break;
+ case ISDN_PROTO_L2_X75BUI:
+ isdn_tty_at_cout("X.75bui", info);
+ break;
+ case ISDN_PROTO_L2_HDLC:
+ isdn_tty_at_cout("HDLC", info);
+ break;
+ case ISDN_PROTO_L2_V11096:
+ isdn_tty_at_cout("V.110 9600 Baud", info);
+ break;
+ case ISDN_PROTO_L2_V11019:
+ isdn_tty_at_cout("V.110 19200 Baud", info);
+ break;
+ case ISDN_PROTO_L2_V11038:
+ isdn_tty_at_cout("V.110 38400 Baud", info);
+ break;
+ case ISDN_PROTO_L2_TRANS:
+ isdn_tty_at_cout("transparent", info);
+ break;
+ case ISDN_PROTO_L2_MODEM:
+ isdn_tty_at_cout("modem", info);
+ break;
+ case ISDN_PROTO_L2_FAX:
+ isdn_tty_at_cout("fax", info);
+ break;
+ default:
+ isdn_tty_at_cout("unknown", info);
+ break;
+ }
+ if (m->mdmreg[REG_T70] & BIT_T70) {
+ isdn_tty_at_cout("/T.70", info);
+ if (m->mdmreg[REG_T70] & BIT_T70_EXT)
+ isdn_tty_at_cout("+", info);
+ }
+ isdn_tty_at_cout("\r\n", info);
+ isdn_tty_at_cout(" Service: ", info);
+ switch (info->last_si) {
+ case 1:
+ isdn_tty_at_cout("audio\r\n", info);
+ break;
+ case 5:
+ isdn_tty_at_cout("btx\r\n", info);
+ break;
+ case 7:
+ isdn_tty_at_cout("data\r\n", info);
+ break;
+ default:
+ sprintf(s, "%d\r\n", info->last_si);
+ isdn_tty_at_cout(s, info);
+ break;
+ }
+ sprintf(s, " Hangup location: %s\r\n", info->last_lhup ? "local" : "remote");
+ isdn_tty_at_cout(s, info);
+ sprintf(s, " Last cause: %s\r\n", info->last_cause);
+ isdn_tty_at_cout(s, info);
+}
+
+/*
+ * Parse AT&.. commands.
+ */
+static int
+isdn_tty_cmd_ATand(char **p, modem_info * info)
+{
+ atemu *m = &info->emu;
+ int i;
+ char rb[100];
+
+#define MAXRB (sizeof(rb) - 1)
+
+ switch (*p[0]) {
+ case 'B':
+ /* &B - Set Buffersize */
+ p[0]++;
+ i = isdn_getnum(p);
+ if ((i < 0) || (i > ISDN_SERIAL_XMIT_MAX))
+ PARSE_ERROR1;
+#ifdef CONFIG_ISDN_AUDIO
+ if ((m->mdmreg[REG_SI1] & 1) && (i > VBUF))
+ PARSE_ERROR1;
+#endif
+ m->mdmreg[REG_PSIZE] = i / 16;
+ info->xmit_size = m->mdmreg[REG_PSIZE] * 16;
+ switch (m->mdmreg[REG_L2PROT]) {
+ case ISDN_PROTO_L2_V11096:
+ case ISDN_PROTO_L2_V11019:
+ case ISDN_PROTO_L2_V11038:
+ info->xmit_size /= 10;
+ }
+ break;
+ case 'C':
+ /* &C - DCD Status */
+ p[0]++;
+ switch (isdn_getnum(p)) {
+ case 0:
+ m->mdmreg[REG_DCD] &= ~BIT_DCD;
+ break;
+ case 1:
+ m->mdmreg[REG_DCD] |= BIT_DCD;
+ break;
+ default:
+ PARSE_ERROR1
+ }
+ break;
+ case 'D':
+ /* &D - Set DTR-Low-behavior */
+ p[0]++;
+ switch (isdn_getnum(p)) {
+ case 0:
+ m->mdmreg[REG_DTRHUP] &= ~BIT_DTRHUP;
+ m->mdmreg[REG_DTRR] &= ~BIT_DTRR;
+ break;
+ case 2:
+ m->mdmreg[REG_DTRHUP] |= BIT_DTRHUP;
+ m->mdmreg[REG_DTRR] &= ~BIT_DTRR;
+ break;
+ case 3:
+ m->mdmreg[REG_DTRHUP] |= BIT_DTRHUP;
+ m->mdmreg[REG_DTRR] |= BIT_DTRR;
+ break;
+ default:
+ PARSE_ERROR1
+ }
+ break;
+ case 'E':
+ /* &E -Set EAZ/MSN */
+ p[0]++;
+ isdn_tty_get_msnstr(m->msn, p);
+ break;
+ case 'F':
+ /* &F -Set Factory-Defaults */
+ p[0]++;
+ if (info->msr & UART_MSR_DCD)
+ PARSE_ERROR1;
+ isdn_tty_reset_profile(m);
+ isdn_tty_modem_reset_regs(info, 1);
+ break;
+#ifdef DUMMY_HAYES_AT
+ case 'K':
+ /* only for be compilant with common scripts */
+ /* &K Flowcontrol - no function */
+ p[0]++;
+ isdn_getnum(p);
+ break;
+#endif
+ case 'L':
+ /* &L -Set Numbers to listen on */
+ p[0]++;
+ i = 0;
+ while (*p[0] && (strchr("0123456789,-*[]?;", *p[0])) &&
+ (i < ISDN_LMSNLEN - 1))
+ m->lmsn[i++] = *p[0]++;
+ m->lmsn[i] = '\0';
+ break;
+ case 'R':
+ /* &R - Set V.110 bitrate adaption */
+ p[0]++;
+ i = isdn_getnum(p);
+ switch (i) {
+ case 0:
+ /* Switch off V.110, back to X.75 */
+ m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I;
+ m->mdmreg[REG_SI2] = 0;
+ info->xmit_size = m->mdmreg[REG_PSIZE] * 16;
+ break;
+ case 9600:
+ m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_V11096;
+ m->mdmreg[REG_SI2] = 197;
+ info->xmit_size = m->mdmreg[REG_PSIZE] * 16 / 10;
+ break;
+ case 19200:
+ m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_V11019;
+ m->mdmreg[REG_SI2] = 199;
+ info->xmit_size = m->mdmreg[REG_PSIZE] * 16 / 10;
+ break;
+ case 38400:
+ m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_V11038;
+ m->mdmreg[REG_SI2] = 198; /* no existing standard for this */
+ info->xmit_size = m->mdmreg[REG_PSIZE] * 16 / 10;
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ /* Switch off T.70 */
+ m->mdmreg[REG_T70] &= ~(BIT_T70 | BIT_T70_EXT);
+ /* Set Service 7 */
+ m->mdmreg[REG_SI1] |= 4;
+ break;
+ case 'S':
+ /* &S - Set Windowsize */
+ p[0]++;
+ i = isdn_getnum(p);
+ if ((i > 0) && (i < 9))
+ m->mdmreg[REG_WSIZE] = i;
+ else
+ PARSE_ERROR1;
+ break;
+ case 'V':
+ /* &V - Show registers */
+ p[0]++;
+ isdn_tty_at_cout("\r\n", info);
+ for (i = 0; i < ISDN_MODEM_NUMREG; i++) {
+ sprintf(rb, "S%02d=%03d%s", i,
+ m->mdmreg[i], ((i + 1) % 10) ? " " : "\r\n");
+ isdn_tty_at_cout(rb, info);
+ }
+ sprintf(rb, "\r\nEAZ/MSN: %.50s\r\n",
+ strlen(m->msn) ? m->msn : "None");
+ isdn_tty_at_cout(rb, info);
+ if (strlen(m->lmsn)) {
+ isdn_tty_at_cout("\r\nListen: ", info);
+ isdn_tty_at_cout(m->lmsn, info);
+ isdn_tty_at_cout("\r\n", info);
+ }
+ break;
+ case 'W':
+ /* &W - Write Profile */
+ p[0]++;
+ switch (*p[0]) {
+ case '0':
+ p[0]++;
+ modem_write_profile(m);
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ break;
+ case 'X':
+ /* &X - Switch to BTX-Mode and T.70 */
+ p[0]++;
+ switch (isdn_getnum(p)) {
+ case 0:
+ m->mdmreg[REG_T70] &= ~(BIT_T70 | BIT_T70_EXT);
+ info->xmit_size = m->mdmreg[REG_PSIZE] * 16;
+ break;
+ case 1:
+ m->mdmreg[REG_T70] |= BIT_T70;
+ m->mdmreg[REG_T70] &= ~BIT_T70_EXT;
+ m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I;
+ info->xmit_size = 112;
+ m->mdmreg[REG_SI1] = 4;
+ m->mdmreg[REG_SI2] = 0;
+ break;
+ case 2:
+ m->mdmreg[REG_T70] |= (BIT_T70 | BIT_T70_EXT);
+ m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I;
+ info->xmit_size = 112;
+ m->mdmreg[REG_SI1] = 4;
+ m->mdmreg[REG_SI2] = 0;
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ return 0;
+}
+
+static int
+isdn_tty_check_ats(int mreg, int mval, modem_info * info, atemu * m)
+{
+ /* Some plausibility checks */
+ switch (mreg) {
+ case REG_L2PROT:
+ if (mval > ISDN_PROTO_L2_MAX)
+ return 1;
+ break;
+ case REG_PSIZE:
+ if ((mval * 16) > ISDN_SERIAL_XMIT_MAX)
+ return 1;
+#ifdef CONFIG_ISDN_AUDIO
+ if ((m->mdmreg[REG_SI1] & 1) && (mval > VBUFX))
+ return 1;
+#endif
+ info->xmit_size = mval * 16;
+ switch (m->mdmreg[REG_L2PROT]) {
+ case ISDN_PROTO_L2_V11096:
+ case ISDN_PROTO_L2_V11019:
+ case ISDN_PROTO_L2_V11038:
+ info->xmit_size /= 10;
+ }
+ break;
+ case REG_SI1I:
+ case REG_PLAN:
+ case REG_SCREEN:
+ /* readonly registers */
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Perform ATS command
+ */
+static int
+isdn_tty_cmd_ATS(char **p, modem_info * info)
+{
+ atemu *m = &info->emu;
+ int bitpos;
+ int mreg;
+ int mval;
+ int bval;
+
+ mreg = isdn_getnum(p);
+ if (mreg < 0 || mreg >= ISDN_MODEM_NUMREG)
+ PARSE_ERROR1;
+ switch (*p[0]) {
+ case '=':
+ p[0]++;
+ mval = isdn_getnum(p);
+ if (mval < 0 || mval > 255)
+ PARSE_ERROR1;
+ if (isdn_tty_check_ats(mreg, mval, info, m))
+ PARSE_ERROR1;
+ m->mdmreg[mreg] = mval;
+ break;
+ case '.':
+ /* Set/Clear a single bit */
+ p[0]++;
+ bitpos = isdn_getnum(p);
+ if ((bitpos < 0) || (bitpos > 7))
+ PARSE_ERROR1;
+ switch (*p[0]) {
+ case '=':
+ p[0]++;
+ bval = isdn_getnum(p);
+ if (bval < 0 || bval > 1)
+ PARSE_ERROR1;
+ if (bval)
+ mval = m->mdmreg[mreg] | (1 << bitpos);
+ else
+ mval = m->mdmreg[mreg] & ~(1 << bitpos);
+ if (isdn_tty_check_ats(mreg, mval, info, m))
+ PARSE_ERROR1;
+ m->mdmreg[mreg] = mval;
+ break;
+ case '?':
+ p[0]++;
+ isdn_tty_at_cout("\r\n", info);
+ isdn_tty_at_cout((m->mdmreg[mreg] & (1 << bitpos)) ? "1" : "0",
+ info);
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ break;
+ case '?':
+ p[0]++;
+ isdn_tty_show_profile(mreg, info);
+ break;
+ default:
+ PARSE_ERROR1;
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Perform ATA command
+ */
+static void
+isdn_tty_cmd_ATA(modem_info * info)
+{
+ atemu *m = &info->emu;
+ isdn_ctrl cmd;
+ int l2;
+
+ if (info->msr & UART_MSR_RI) {
+ /* Accept incoming call */
+ info->last_dir = 0;
+ strcpy(info->last_num, dev->num[info->drv_index]);
+ m->mdmreg[REG_RINGCNT] = 0;
+ info->msr &= ~UART_MSR_RI;
+ l2 = m->mdmreg[REG_L2PROT];
+#ifdef CONFIG_ISDN_AUDIO
+ /* If more than one bit set in reg18, autoselect Layer2 */
+ if ((m->mdmreg[REG_SI1] & m->mdmreg[REG_SI1I]) != m->mdmreg[REG_SI1]) {
+ if (m->mdmreg[REG_SI1I] == 1) {
+ if ((l2 != ISDN_PROTO_L2_MODEM) && (l2 != ISDN_PROTO_L2_FAX))
+ l2 = ISDN_PROTO_L2_TRANS;
+ } else
+ l2 = ISDN_PROTO_L2_X75I;
+ }
+#endif
+ cmd.driver = info->isdn_driver;
+ cmd.command = ISDN_CMD_SETL2;
+ cmd.arg = info->isdn_channel + (l2 << 8);
+ info->last_l2 = l2;
+ isdn_command(&cmd);
+ cmd.driver = info->isdn_driver;
+ cmd.command = ISDN_CMD_SETL3;
+ cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8);
+#ifdef CONFIG_ISDN_TTY_FAX
+ if (l2 == ISDN_PROTO_L2_FAX) {
+ cmd.parm.fax = info->fax;
+ info->fax->direction = ISDN_TTY_FAX_CONN_IN;
+ }
+#endif
+ isdn_command(&cmd);
+ cmd.driver = info->isdn_driver;
+ cmd.arg = info->isdn_channel;
+ cmd.command = ISDN_CMD_ACCEPTD;
+ info->dialing = 16;
+ info->emu.carrierwait = 0;
+ isdn_command(&cmd);
+ isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1);
+ } else
+ isdn_tty_modem_result(RESULT_NO_ANSWER, info);
+}
+
+#ifdef CONFIG_ISDN_AUDIO
+/*
+ * Parse AT+F.. commands
+ */
+static int
+isdn_tty_cmd_PLUSF(char **p, modem_info * info)
+{
+ atemu *m = &info->emu;
+ char rs[20];
+
+ if (!strncmp(p[0], "CLASS", 5)) {
+ p[0] += 5;
+ switch (*p[0]) {
+ case '?':
+ p[0]++;
+ sprintf(rs, "\r\n%d",
+ (m->mdmreg[REG_SI1] & 1) ? 8 : 0);
+#ifdef CONFIG_ISDN_TTY_FAX
+ if (TTY_IS_FCLASS2(info))
+ sprintf(rs, "\r\n2");
+ else if (TTY_IS_FCLASS1(info))
+ sprintf(rs, "\r\n1");
+#endif
+ isdn_tty_at_cout(rs, info);
+ break;
+ case '=':
+ p[0]++;
+ switch (*p[0]) {
+ case '0':
+ p[0]++;
+ m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I;
+ m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_TRANS;
+ m->mdmreg[REG_SI1] = 4;
+ info->xmit_size =
+ m->mdmreg[REG_PSIZE] * 16;
+ break;
+#ifdef CONFIG_ISDN_TTY_FAX
+ case '1':
+ p[0]++;
+ if (!(dev->global_features &
+ ISDN_FEATURE_L3_FCLASS1))
+ PARSE_ERROR1;
+ m->mdmreg[REG_SI1] = 1;
+ m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_FAX;
+ m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_FCLASS1;
+ info->xmit_size =
+ m->mdmreg[REG_PSIZE] * 16;
+ break;
+ case '2':
+ p[0]++;
+ if (!(dev->global_features &
+ ISDN_FEATURE_L3_FCLASS2))
+ PARSE_ERROR1;
+ m->mdmreg[REG_SI1] = 1;
+ m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_FAX;
+ m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_FCLASS2;
+ info->xmit_size =
+ m->mdmreg[REG_PSIZE] * 16;
+ break;
+#endif
+ case '8':
+ p[0]++;
+ /* L2 will change on dialout with si=1 */
+ m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I;
+ m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_TRANS;
+ m->mdmreg[REG_SI1] = 5;
+ info->xmit_size = VBUF;
+ break;
+ case '?':
+ p[0]++;
+ strcpy(rs, "\r\n0,");
+#ifdef CONFIG_ISDN_TTY_FAX
+ if (dev->global_features &
+ ISDN_FEATURE_L3_FCLASS1)
+ strcat(rs, "1,");
+ if (dev->global_features &
+ ISDN_FEATURE_L3_FCLASS2)
+ strcat(rs, "2,");
+#endif
+ strcat(rs, "8");
+ isdn_tty_at_cout(rs, info);
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ return 0;
+ }
+#ifdef CONFIG_ISDN_TTY_FAX
+ return (isdn_tty_cmd_PLUSF_FAX(p, info));
+#else
+ PARSE_ERROR1;
+#endif
+}
+
+/*
+ * Parse AT+V.. commands
+ */
+static int
+isdn_tty_cmd_PLUSV(char **p, modem_info * info)
+{
+ atemu *m = &info->emu;
+ isdn_ctrl cmd;
+ static char *vcmd[] =
+ {"NH", "IP", "LS", "RX", "SD", "SM", "TX", "DD", NULL};
+ int i;
+ int par1;
+ int par2;
+ char rs[20];
+
+ i = 0;
+ while (vcmd[i]) {
+ if (!strncmp(vcmd[i], p[0], 2)) {
+ p[0] += 2;
+ break;
+ }
+ i++;
+ }
+ switch (i) {
+ case 0:
+ /* AT+VNH - Auto hangup feature */
+ switch (*p[0]) {
+ case '?':
+ p[0]++;
+ isdn_tty_at_cout("\r\n1", info);
+ break;
+ case '=':
+ p[0]++;
+ switch (*p[0]) {
+ case '1':
+ p[0]++;
+ break;
+ case '?':
+ p[0]++;
+ isdn_tty_at_cout("\r\n1", info);
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ break;
+ case 1:
+ /* AT+VIP - Reset all voice parameters */
+ isdn_tty_modem_reset_vpar(m);
+ break;
+ case 2:
+ /* AT+VLS - Select device, accept incoming call */
+ switch (*p[0]) {
+ case '?':
+ p[0]++;
+ sprintf(rs, "\r\n%d", m->vpar[0]);
+ isdn_tty_at_cout(rs, info);
+ break;
+ case '=':
+ p[0]++;
+ switch (*p[0]) {
+ case '0':
+ p[0]++;
+ m->vpar[0] = 0;
+ break;
+ case '2':
+ p[0]++;
+ m->vpar[0] = 2;
+ break;
+ case '?':
+ p[0]++;
+ isdn_tty_at_cout("\r\n0,2", info);
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ break;
+ case 3:
+ /* AT+VRX - Start recording */
+ if (!m->vpar[0])
+ PARSE_ERROR1;
+ if (info->online != 1) {
+ isdn_tty_modem_result(RESULT_NO_ANSWER, info);
+ return 1;
+ }
+ info->dtmf_state = isdn_audio_dtmf_init(info->dtmf_state);
+ if (!info->dtmf_state) {
+ printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n");
+ PARSE_ERROR1;
+ }
+ info->silence_state = isdn_audio_silence_init(info->silence_state);
+ if (!info->silence_state) {
+ printk(KERN_WARNING "isdn_tty: Couldn't malloc silence state\n");
+ PARSE_ERROR1;
+ }
+ if (m->vpar[3] < 5) {
+ info->adpcmr = isdn_audio_adpcm_init(info->adpcmr, m->vpar[3]);
+ if (!info->adpcmr) {
+ printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n");
+ PARSE_ERROR1;
+ }
+ }
+#ifdef ISDN_DEBUG_AT
+ printk(KERN_DEBUG "AT: +VRX\n");
+#endif
+ info->vonline |= 1;
+ isdn_tty_modem_result(RESULT_CONNECT, info);
+ return 0;
+ break;
+ case 4:
+ /* AT+VSD - Silence detection */
+ switch (*p[0]) {
+ case '?':
+ p[0]++;
+ sprintf(rs, "\r\n<%d>,<%d>",
+ m->vpar[1],
+ m->vpar[2]);
+ isdn_tty_at_cout(rs, info);
+ break;
+ case '=':
+ p[0]++;
+ if ((*p[0]>='0') && (*p[0]<='9')) {
+ par1 = isdn_getnum(p);
+ if ((par1 < 0) || (par1 > 31))
+ PARSE_ERROR1;
+ if (*p[0] != ',')
+ PARSE_ERROR1;
+ p[0]++;
+ par2 = isdn_getnum(p);
+ if ((par2 < 0) || (par2 > 255))
+ PARSE_ERROR1;
+ m->vpar[1] = par1;
+ m->vpar[2] = par2;
+ break;
+ } else
+ if (*p[0] == '?') {
+ p[0]++;
+ isdn_tty_at_cout("\r\n<0-31>,<0-255>",
+ info);
+ break;
+ } else
+ PARSE_ERROR1;
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ break;
+ case 5:
+ /* AT+VSM - Select compression */
+ switch (*p[0]) {
+ case '?':
+ p[0]++;
+ sprintf(rs, "\r\n<%d>,<%d><8000>",
+ m->vpar[3],
+ m->vpar[1]);
+ isdn_tty_at_cout(rs, info);
+ break;
+ case '=':
+ p[0]++;
+ switch (*p[0]) {
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ par1 = isdn_getnum(p);
+ if ((par1 < 2) || (par1 > 6))
+ PARSE_ERROR1;
+ m->vpar[3] = par1;
+ break;
+ case '?':
+ p[0]++;
+ isdn_tty_at_cout("\r\n2;ADPCM;2;0;(8000)\r\n",
+ info);
+ isdn_tty_at_cout("3;ADPCM;3;0;(8000)\r\n",
+ info);
+ isdn_tty_at_cout("4;ADPCM;4;0;(8000)\r\n",
+ info);
+ isdn_tty_at_cout("5;ALAW;8;0;(8000)\r\n",
+ info);
+ isdn_tty_at_cout("6;ULAW;8;0;(8000)\r\n",
+ info);
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ break;
+ case 6:
+ /* AT+VTX - Start sending */
+ if (!m->vpar[0])
+ PARSE_ERROR1;
+ if (info->online != 1) {
+ isdn_tty_modem_result(RESULT_NO_ANSWER, info);
+ return 1;
+ }
+ info->dtmf_state = isdn_audio_dtmf_init(info->dtmf_state);
+ if (!info->dtmf_state) {
+ printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n");
+ PARSE_ERROR1;
+ }
+ if (m->vpar[3] < 5) {
+ info->adpcms = isdn_audio_adpcm_init(info->adpcms, m->vpar[3]);
+ if (!info->adpcms) {
+ printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n");
+ PARSE_ERROR1;
+ }
+ }
+#ifdef ISDN_DEBUG_AT
+ printk(KERN_DEBUG "AT: +VTX\n");
+#endif
+ m->lastDLE = 0;
+ info->vonline |= 2;
+ isdn_tty_modem_result(RESULT_CONNECT, info);
+ return 0;
+ break;
+ case 7:
+ /* AT+VDD - DTMF detection */
+ switch (*p[0]) {
+ case '?':
+ p[0]++;
+ sprintf(rs, "\r\n<%d>,<%d>",
+ m->vpar[4],
+ m->vpar[5]);
+ isdn_tty_at_cout(rs, info);
+ break;
+ case '=':
+ p[0]++;
+ if ((*p[0]>='0') && (*p[0]<='9')) {
+ if (info->online != 1)
+ PARSE_ERROR1;
+ par1 = isdn_getnum(p);
+ if ((par1 < 0) || (par1 > 15))
+ PARSE_ERROR1;
+ if (*p[0] != ',')
+ PARSE_ERROR1;
+ p[0]++;
+ par2 = isdn_getnum(p);
+ if ((par2 < 0) || (par2 > 255))
+ PARSE_ERROR1;
+ m->vpar[4] = par1;
+ m->vpar[5] = par2;
+ cmd.driver = info->isdn_driver;
+ cmd.command = ISDN_CMD_AUDIO;
+ cmd.arg = info->isdn_channel + (ISDN_AUDIO_SETDD << 8);
+ cmd.parm.num[0] = par1;
+ cmd.parm.num[1] = par2;
+ isdn_command(&cmd);
+ break;
+ } else
+ if (*p[0] == '?') {
+ p[0]++;
+ isdn_tty_at_cout("\r\n<0-15>,<0-255>",
+ info);
+ break;
+ } else
+ PARSE_ERROR1;
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ return 0;
+}
+#endif /* CONFIG_ISDN_AUDIO */
+
+/*
+ * Parse and perform an AT-command-line.
+ */
+static void
+isdn_tty_parse_at(modem_info * info)
+{
+ atemu *m = &info->emu;
+ char *p;
+ char ds[40];
+
+#ifdef ISDN_DEBUG_AT
+ printk(KERN_DEBUG "AT: '%s'\n", m->mdmcmd);
+#endif
+ for (p = &m->mdmcmd[2]; *p;) {
+ switch (*p) {
+ case ' ':
+ p++;
+ break;
+ case 'A':
+ /* A - Accept incoming call */
+ p++;
+ isdn_tty_cmd_ATA(info);
+ return;
+ break;
+ case 'D':
+ /* D - Dial */
+ if (info->msr & UART_MSR_DCD)
+ PARSE_ERROR;
+ if (info->msr & UART_MSR_RI) {
+ isdn_tty_modem_result(RESULT_NO_CARRIER, info);
+ return;
+ }
+ isdn_tty_getdial(++p, ds, sizeof ds);
+ p += strlen(p);
+ if (!strlen(m->msn))
+ isdn_tty_modem_result(RESULT_NO_MSN_EAZ, info);
+ else if (strlen(ds))
+ isdn_tty_dial(ds, info, m);
+ else
+ PARSE_ERROR;
+ return;
+ case 'E':
+ /* E - Turn Echo on/off */
+ p++;
+ switch (isdn_getnum(&p)) {
+ case 0:
+ m->mdmreg[REG_ECHO] &= ~BIT_ECHO;
+ break;
+ case 1:
+ m->mdmreg[REG_ECHO] |= BIT_ECHO;
+ break;
+ default:
+ PARSE_ERROR;
+ }
+ break;
+ case 'H':
+ /* H - On/Off-hook */
+ p++;
+ switch (*p) {
+ case '0':
+ p++;
+ isdn_tty_on_hook(info);
+ break;
+ case '1':
+ p++;
+ isdn_tty_off_hook();
+ break;
+ default:
+ isdn_tty_on_hook(info);
+ break;
+ }
+ break;
+ case 'I':
+ /* I - Information */
+ p++;
+ isdn_tty_at_cout("\r\nLinux ISDN", info);
+ switch (*p) {
+ case '0':
+ case '1':
+ p++;
+ break;
+ case '2':
+ p++;
+ isdn_tty_report(info);
+ break;
+ case '3':
+ p++;
+ sprintf(ds, "\r\n%d", info->emu.charge);
+ isdn_tty_at_cout(ds, info);
+ break;
+ default:;
+ }
+ break;
+#ifdef DUMMY_HAYES_AT
+ case 'L':
+ case 'M':
+ /* only for be compilant with common scripts */
+ /* no function */
+ p++;
+ isdn_getnum(&p);
+ break;
+#endif
+ case 'O':
+ /* O - Go online */
+ p++;
+ if (info->msr & UART_MSR_DCD)
+ /* if B-Channel is up */
+ isdn_tty_modem_result((m->mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM) ? RESULT_CONNECT:RESULT_CONNECT64000, info);
+ else
+ isdn_tty_modem_result(RESULT_NO_CARRIER, info);
+ return;
+ case 'Q':
+ /* Q - Turn Emulator messages on/off */
+ p++;
+ switch (isdn_getnum(&p)) {
+ case 0:
+ m->mdmreg[REG_RESP] |= BIT_RESP;
+ break;
+ case 1:
+ m->mdmreg[REG_RESP] &= ~BIT_RESP;
+ break;
+ default:
+ PARSE_ERROR;
+ }
+ break;
+ case 'S':
+ /* S - Set/Get Register */
+ p++;
+ if (isdn_tty_cmd_ATS(&p, info))
+ return;
+ break;
+ case 'V':
+ /* V - Numeric or ASCII Emulator-messages */
+ p++;
+ switch (isdn_getnum(&p)) {
+ case 0:
+ m->mdmreg[REG_RESP] |= BIT_RESPNUM;
+ break;
+ case 1:
+ m->mdmreg[REG_RESP] &= ~BIT_RESPNUM;
+ break;
+ default:
+ PARSE_ERROR;
+ }
+ break;
+ case 'Z':
+ /* Z - Load Registers from Profile */
+ p++;
+ if (info->msr & UART_MSR_DCD) {
+ info->online = 0;
+ isdn_tty_on_hook(info);
+ }
+ isdn_tty_modem_reset_regs(info, 1);
+ break;
+ case '+':
+ p++;
+ switch (*p) {
+#ifdef CONFIG_ISDN_AUDIO
+ case 'F':
+ p++;
+ if (isdn_tty_cmd_PLUSF(&p, info))
+ return;
+ break;
+ case 'V':
+ if ((!(m->mdmreg[REG_SI1] & 1)) ||
+ (m->mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM))
+ PARSE_ERROR;
+ p++;
+ if (isdn_tty_cmd_PLUSV(&p, info))
+ return;
+ break;
+#endif /* CONFIG_ISDN_AUDIO */
+ case 'S': /* SUSPEND */
+ p++;
+ isdn_tty_get_msnstr(ds, &p);
+ isdn_tty_suspend(ds, info, m);
+ break;
+ case 'R': /* RESUME */
+ p++;
+ isdn_tty_get_msnstr(ds, &p);
+ isdn_tty_resume(ds, info, m);
+ break;
+ case 'M': /* MESSAGE */
+ p++;
+ isdn_tty_send_msg(info, m, p);
+ break;
+ default:
+ PARSE_ERROR;
+ }
+ break;
+ case '&':
+ p++;
+ if (isdn_tty_cmd_ATand(&p, info))
+ return;
+ break;
+ default:
+ PARSE_ERROR;
+ }
+ }
+#ifdef CONFIG_ISDN_AUDIO
+ if (!info->vonline)
+#endif
+ isdn_tty_modem_result(RESULT_OK, info);
+}
+
+/* Need own toupper() because standard-toupper is not available
+ * within modules.
+ */
+#define my_toupper(c) (((c>='a')&&(c<='z'))?(c&0xdf):c)
+
+/*
+ * Perform line-editing of AT-commands
+ *
+ * Parameters:
+ * p inputbuffer
+ * count length of buffer
+ * channel index to line (minor-device)
+ */
+static int
+isdn_tty_edit_at(const char *p, int count, modem_info * info)
+{
+ atemu *m = &info->emu;
+ int total = 0;
+ u_char c;
+ char eb[2];
+ int cnt;
+
+ for (cnt = count; cnt > 0; p++, cnt--) {
+ c = *p;
+ total++;
+ if (c == m->mdmreg[REG_CR] || c == m->mdmreg[REG_LF]) {
+ /* Separator (CR or LF) */
+ m->mdmcmd[m->mdmcmdl] = 0;
+ if (m->mdmreg[REG_ECHO] & BIT_ECHO) {
+ eb[0] = c;
+ eb[1] = 0;
+ isdn_tty_at_cout(eb, info);
+ }
+ if ((m->mdmcmdl >= 2) && (!(strncmp(m->mdmcmd, "AT", 2))))
+ isdn_tty_parse_at(info);
+ m->mdmcmdl = 0;
+ continue;
+ }
+ if (c == m->mdmreg[REG_BS] && m->mdmreg[REG_BS] < 128) {
+ /* Backspace-Function */
+ if ((m->mdmcmdl > 2) || (!m->mdmcmdl)) {
+ if (m->mdmcmdl)
+ m->mdmcmdl--;
+ if (m->mdmreg[REG_ECHO] & BIT_ECHO)
+ isdn_tty_at_cout("\b", info);
+ }
+ continue;
+ }
+ if (cmdchar(c)) {
+ if (m->mdmreg[REG_ECHO] & BIT_ECHO) {
+ eb[0] = c;
+ eb[1] = 0;
+ isdn_tty_at_cout(eb, info);
+ }
+ if (m->mdmcmdl < 255) {
+ c = my_toupper(c);
+ switch (m->mdmcmdl) {
+ case 1:
+ if (c == 'T') {
+ m->mdmcmd[m->mdmcmdl] = c;
+ m->mdmcmd[++m->mdmcmdl] = 0;
+ break;
+ } else
+ m->mdmcmdl = 0;
+ /* Fall through, check for 'A' */
+ case 0:
+ if (c == 'A') {
+ m->mdmcmd[m->mdmcmdl] = c;
+ m->mdmcmd[++m->mdmcmdl] = 0;
+ }
+ break;
+ default:
+ m->mdmcmd[m->mdmcmdl] = c;
+ m->mdmcmd[++m->mdmcmdl] = 0;
+ }
+ }
+ }
+ }
+ return total;
+}
+
+/*
+ * Switch all modem-channels who are online and got a valid
+ * escape-sequence 1.5 seconds ago, to command-mode.
+ * This function is called every second via timer-interrupt from within
+ * timer-dispatcher isdn_timer_function()
+ */
+void
+isdn_tty_modem_escape(void)
+{
+ int ton = 0;
+ int i;
+ int midx;
+
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++)
+ if (USG_MODEM(dev->usage[i]))
+ if ((midx = dev->m_idx[i]) >= 0) {
+ modem_info *info = &dev->mdm.info[midx];
+ if (info->online) {
+ ton = 1;
+ if ((info->emu.pluscount == 3) &&
+ time_after(jiffies , info->emu.lastplus + PLUSWAIT2)) {
+ info->emu.pluscount = 0;
+ info->online = 0;
+ isdn_tty_modem_result(RESULT_OK, info);
+ }
+ }
+ }
+ isdn_timer_ctrl(ISDN_TIMER_MODEMPLUS, ton);
+}
+
+/*
+ * Put a RING-message to all modem-channels who have the RI-bit set.
+ * This function is called every second via timer-interrupt from within
+ * timer-dispatcher isdn_timer_function()
+ */
+void
+isdn_tty_modem_ring(void)
+{
+ int ton = 0;
+ int i;
+
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ modem_info *info = &dev->mdm.info[i];
+ if (info->msr & UART_MSR_RI) {
+ ton = 1;
+ isdn_tty_modem_result(RESULT_RING, info);
+ }
+ }
+ isdn_timer_ctrl(ISDN_TIMER_MODEMRING, ton);
+}
+
+/*
+ * For all online tty's, try sending data to
+ * the lower levels.
+ */
+void
+isdn_tty_modem_xmit(void)
+{
+ int ton = 1;
+ int i;
+
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ modem_info *info = &dev->mdm.info[i];
+ if (info->online) {
+ ton = 1;
+ isdn_tty_senddown(info);
+ isdn_tty_tint(info);
+ }
+ }
+ isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, ton);
+}
+
+/*
+ * Check all channels if we have a 'no carrier' timeout.
+ * Timeout value is set by Register S7.
+ */
+void
+isdn_tty_carrier_timeout(void)
+{
+ int ton = 0;
+ int i;
+
+ for (i = 0; i < ISDN_MAX_CHANNELS; i++) {
+ modem_info *info = &dev->mdm.info[i];
+ if (info->dialing) {
+ if (info->emu.carrierwait++ > info->emu.mdmreg[REG_WAITC]) {
+ info->dialing = 0;
+ isdn_tty_modem_result(RESULT_NO_CARRIER, info);
+ isdn_tty_modem_hup(info, 1);
+ }
+ else
+ ton = 1;
+ }
+ }
+ isdn_timer_ctrl(ISDN_TIMER_CARRIER, ton);
+}
diff --git a/drivers/isdn/i4l/isdn_tty.h b/drivers/isdn/i4l/isdn_tty.h
new file mode 100644
index 0000000..692c74d
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_tty.h
@@ -0,0 +1,120 @@
+/* $Id: isdn_tty.h,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $
+ *
+ * header for Linux ISDN subsystem, tty related functions (linklevel).
+ *
+ * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de)
+ * Copyright 1995,96 by Thinking Objects Software GmbH Wuerzburg
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+
+#define DLE 0x10
+#define ETX 0x03
+#define DC4 0x14
+
+
+/*
+ * Definition of some special Registers of AT-Emulator
+ */
+#define REG_RINGATA 0
+#define REG_RINGCNT 1 /* ring counter register */
+#define REG_ESC 2
+#define REG_CR 3
+#define REG_LF 4
+#define REG_BS 5
+
+#define REG_WAITC 7
+
+#define REG_RESP 12 /* show response messages register */
+#define BIT_RESP 1 /* show response messages bit */
+#define REG_RESPNUM 12 /* show numeric responses register */
+#define BIT_RESPNUM 2 /* show numeric responses bit */
+#define REG_ECHO 12
+#define BIT_ECHO 4
+#define REG_DCD 12
+#define BIT_DCD 8
+#define REG_CTS 12
+#define BIT_CTS 16
+#define REG_DTRR 12
+#define BIT_DTRR 32
+#define REG_DSR 12
+#define BIT_DSR 64
+#define REG_CPPP 12
+#define BIT_CPPP 128
+
+#define REG_DXMT 13
+#define BIT_DXMT 1
+#define REG_T70 13
+#define BIT_T70 2
+#define BIT_T70_EXT 32
+#define REG_DTRHUP 13
+#define BIT_DTRHUP 4
+#define REG_RESPXT 13
+#define BIT_RESPXT 8
+#define REG_CIDONCE 13
+#define BIT_CIDONCE 16
+#define REG_RUNG 13 /* show RUNG message register */
+#define BIT_RUNG 64 /* show RUNG message bit */
+#define REG_DISPLAY 13
+#define BIT_DISPLAY 128
+
+#define REG_L2PROT 14
+#define REG_L3PROT 15
+#define REG_PSIZE 16
+#define REG_WSIZE 17
+#define REG_SI1 18
+#define REG_SI2 19
+#define REG_SI1I 20
+#define REG_PLAN 21
+#define REG_SCREEN 22
+
+#define REG_CPN 23
+#define BIT_CPN 1
+#define REG_CPNFCON 23
+#define BIT_CPNFCON 2
+#define REG_CDN 23
+#define BIT_CDN 4
+
+/* defines for result codes */
+#define RESULT_OK 0
+#define RESULT_CONNECT 1
+#define RESULT_RING 2
+#define RESULT_NO_CARRIER 3
+#define RESULT_ERROR 4
+#define RESULT_CONNECT64000 5
+#define RESULT_NO_DIALTONE 6
+#define RESULT_BUSY 7
+#define RESULT_NO_ANSWER 8
+#define RESULT_RINGING 9
+#define RESULT_NO_MSN_EAZ 10
+#define RESULT_VCON 11
+#define RESULT_RUNG 12
+
+#define TTY_IS_FCLASS1(info) \
+ ((info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_FAX) && \
+ (info->emu.mdmreg[REG_L3PROT] == ISDN_PROTO_L3_FCLASS1))
+#define TTY_IS_FCLASS2(info) \
+ ((info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_FAX) && \
+ (info->emu.mdmreg[REG_L3PROT] == ISDN_PROTO_L3_FCLASS2))
+
+extern void isdn_tty_modem_escape(void);
+extern void isdn_tty_modem_ring(void);
+extern void isdn_tty_carrier_timeout(void);
+extern void isdn_tty_modem_xmit(void);
+extern int isdn_tty_modem_init(void);
+extern void isdn_tty_exit(void);
+extern void isdn_tty_readmodem(void);
+extern int isdn_tty_find_icall(int, int, setup_parm *);
+extern int isdn_tty_stat_callback(int, isdn_ctrl *);
+extern int isdn_tty_rcv_skb(int, int, int, struct sk_buff *);
+extern int isdn_tty_capi_facility(capi_msg *cm);
+extern void isdn_tty_at_cout(char *, modem_info *);
+extern void isdn_tty_modem_hup(modem_info *, int);
+#ifdef CONFIG_ISDN_TTY_FAX
+extern int isdn_tty_cmd_PLUSF_FAX(char **, modem_info *);
+extern int isdn_tty_fax_command(modem_info *, isdn_ctrl *);
+extern void isdn_tty_fax_bitorder(modem_info *, struct sk_buff *);
+#endif
diff --git a/drivers/isdn/i4l/isdn_ttyfax.c b/drivers/isdn/i4l/isdn_ttyfax.c
new file mode 100644
index 0000000..78f7660
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_ttyfax.c
@@ -0,0 +1,1123 @@
+/* $Id: isdn_ttyfax.c,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $
+ *
+ * Linux ISDN subsystem, tty_fax AT-command emulator (linklevel).
+ *
+ * Copyright 1999 by Armin Schindler (mac@melware.de)
+ * Copyright 1999 by Ralf Spachmann (mel@melware.de)
+ * Copyright 1999 by Cytronics & Melware
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#undef ISDN_TTY_FAX_STAT_DEBUG
+#undef ISDN_TTY_FAX_CMD_DEBUG
+
+#include <linux/isdn.h>
+#include "isdn_common.h"
+#include "isdn_tty.h"
+#include "isdn_ttyfax.h"
+
+
+static char *isdn_tty_fax_revision = "$Revision: 1.1.2.2 $";
+
+#define PARSE_ERROR1 { isdn_tty_fax_modem_result(1, info); return 1; }
+
+static char *
+isdn_getrev(const char *revision)
+{
+ char *rev;
+ char *p;
+
+ if ((p = strchr(revision, ':'))) {
+ rev = p + 2;
+ p = strchr(rev, '$');
+ *--p = 0;
+ } else
+ rev = "???";
+ return rev;
+}
+
+/*
+ * Fax Class 2 Modem results
+ *
+ */
+
+static void
+isdn_tty_fax_modem_result(int code, modem_info * info)
+{
+ atemu *m = &info->emu;
+ T30_s *f = info->fax;
+ char rs[50];
+ char rss[50];
+ char *rp;
+ int i;
+ static char *msg[] =
+ {"OK", "ERROR", "+FCON", "+FCSI:", "+FDIS:",
+ "+FHNG:", "+FDCS:", "CONNECT", "+FTSI:",
+ "+FCFR", "+FPTS:", "+FET:"};
+
+
+ isdn_tty_at_cout("\r\n", info);
+ isdn_tty_at_cout(msg[code], info);
+
+#ifdef ISDN_TTY_FAX_CMD_DEBUG
+ printk(KERN_DEBUG "isdn_tty: Fax send %s on ttyI%d\n",
+ msg[code], info->line);
+#endif
+ switch (code) {
+ case 0: /* OK */
+ break;
+ case 1: /* ERROR */
+ break;
+ case 2: /* +FCON */
+ /* Append CPN, if enabled */
+ if ((m->mdmreg[REG_CPNFCON] & BIT_CPNFCON) &&
+ (!(dev->usage[info->isdn_channel] & ISDN_USAGE_OUTGOING))) {
+ sprintf(rs, "/%s", m->cpn);
+ isdn_tty_at_cout(rs, info);
+ }
+ info->online = 1;
+ f->fet = 0;
+ if (f->phase == ISDN_FAX_PHASE_A)
+ f->phase = ISDN_FAX_PHASE_B;
+ break;
+ case 3: /* +FCSI */
+ case 8: /* +FTSI */
+ sprintf(rs, "\"%s\"", f->r_id);
+ isdn_tty_at_cout(rs, info);
+ break;
+ case 4: /* +FDIS */
+ rs[0] = 0;
+ rp = &f->r_resolution;
+ for (i = 0; i < 8; i++) {
+ sprintf(rss, "%c%s", rp[i] + 48,
+ (i < 7) ? "," : "");
+ strcat(rs, rss);
+ }
+ isdn_tty_at_cout(rs, info);
+#ifdef ISDN_TTY_FAX_CMD_DEBUG
+ printk(KERN_DEBUG "isdn_tty: Fax DIS=%s on ttyI%d\n",
+ rs, info->line);
+#endif
+ break;
+ case 5: /* +FHNG */
+ sprintf(rs, "%d", f->code);
+ isdn_tty_at_cout(rs, info);
+ info->faxonline = 0;
+ break;
+ case 6: /* +FDCS */
+ rs[0] = 0;
+ rp = &f->r_resolution;
+ for (i = 0; i < 8; i++) {
+ sprintf(rss, "%c%s", rp[i] + 48,
+ (i < 7) ? "," : "");
+ strcat(rs, rss);
+ }
+ isdn_tty_at_cout(rs, info);
+#ifdef ISDN_TTY_FAX_CMD_DEBUG
+ printk(KERN_DEBUG "isdn_tty: Fax DCS=%s on ttyI%d\n",
+ rs, info->line);
+#endif
+ break;
+ case 7: /* CONNECT */
+ info->faxonline |= 2;
+ break;
+ case 9: /* FCFR */
+ break;
+ case 10: /* FPTS */
+ isdn_tty_at_cout("1", info);
+ break;
+ case 11: /* FET */
+ sprintf(rs, "%d", f->fet);
+ isdn_tty_at_cout(rs, info);
+ break;
+ }
+
+ isdn_tty_at_cout("\r\n", info);
+
+ switch (code) {
+ case 7: /* CONNECT */
+ info->online = 2;
+ if (info->faxonline & 1) {
+ sprintf(rs, "%c", XON);
+ isdn_tty_at_cout(rs, info);
+ }
+ break;
+ }
+}
+
+static int
+isdn_tty_fax_command1(modem_info * info, isdn_ctrl * c)
+{
+ static char *msg[] =
+ {"OK", "CONNECT", "NO CARRIER", "ERROR", "FCERROR"};
+
+#ifdef ISDN_TTY_FAX_CMD_DEBUG
+ printk(KERN_DEBUG "isdn_tty: FCLASS1 cmd(%d)\n", c->parm.aux.cmd);
+#endif
+ if (c->parm.aux.cmd < ISDN_FAX_CLASS1_QUERY) {
+ if (info->online)
+ info->online = 1;
+ isdn_tty_at_cout("\r\n", info);
+ isdn_tty_at_cout(msg[c->parm.aux.cmd], info);
+ isdn_tty_at_cout("\r\n", info);
+ }
+ switch (c->parm.aux.cmd) {
+ case ISDN_FAX_CLASS1_CONNECT:
+ info->online = 2;
+ break;
+ case ISDN_FAX_CLASS1_OK:
+ case ISDN_FAX_CLASS1_FCERROR:
+ case ISDN_FAX_CLASS1_ERROR:
+ case ISDN_FAX_CLASS1_NOCARR:
+ break;
+ case ISDN_FAX_CLASS1_QUERY:
+ isdn_tty_at_cout("\r\n", info);
+ if (!c->parm.aux.para[0]) {
+ isdn_tty_at_cout(msg[ISDN_FAX_CLASS1_ERROR], info);
+ isdn_tty_at_cout("\r\n", info);
+ } else {
+ isdn_tty_at_cout(c->parm.aux.para, info);
+ isdn_tty_at_cout("\r\nOK\r\n", info);
+ }
+ break;
+ }
+ return (0);
+}
+
+int
+isdn_tty_fax_command(modem_info * info, isdn_ctrl * c)
+{
+ T30_s *f = info->fax;
+ char rs[10];
+
+ if (TTY_IS_FCLASS1(info))
+ return (isdn_tty_fax_command1(info, c));
+
+#ifdef ISDN_TTY_FAX_CMD_DEBUG
+ printk(KERN_DEBUG "isdn_tty: Fax cmd %d on ttyI%d\n",
+ f->r_code, info->line);
+#endif
+ switch (f->r_code) {
+ case ISDN_TTY_FAX_FCON:
+ info->faxonline = 1;
+ isdn_tty_fax_modem_result(2, info); /* +FCON */
+ return (0);
+ case ISDN_TTY_FAX_FCON_I:
+ info->faxonline = 16;
+ isdn_tty_fax_modem_result(2, info); /* +FCON */
+ return (0);
+ case ISDN_TTY_FAX_RID:
+ if (info->faxonline & 1)
+ isdn_tty_fax_modem_result(3, info); /* +FCSI */
+ if (info->faxonline & 16)
+ isdn_tty_fax_modem_result(8, info); /* +FTSI */
+ return (0);
+ case ISDN_TTY_FAX_DIS:
+ isdn_tty_fax_modem_result(4, info); /* +FDIS */
+ return (0);
+ case ISDN_TTY_FAX_HNG:
+ if (f->phase == ISDN_FAX_PHASE_C) {
+ if (f->direction == ISDN_TTY_FAX_CONN_IN) {
+ sprintf(rs, "%c%c", DLE, ETX);
+ isdn_tty_at_cout(rs, info);
+ } else {
+ sprintf(rs, "%c", 0x18);
+ isdn_tty_at_cout(rs, info);
+ }
+ info->faxonline &= ~2; /* leave data mode */
+ info->online = 1;
+ }
+ f->phase = ISDN_FAX_PHASE_E;
+ isdn_tty_fax_modem_result(5, info); /* +FHNG */
+ isdn_tty_fax_modem_result(0, info); /* OK */
+ return (0);
+ case ISDN_TTY_FAX_DCS:
+ isdn_tty_fax_modem_result(6, info); /* +FDCS */
+ isdn_tty_fax_modem_result(7, info); /* CONNECT */
+ f->phase = ISDN_FAX_PHASE_C;
+ return (0);
+ case ISDN_TTY_FAX_TRAIN_OK:
+ isdn_tty_fax_modem_result(6, info); /* +FDCS */
+ isdn_tty_fax_modem_result(0, info); /* OK */
+ return (0);
+ case ISDN_TTY_FAX_SENT:
+ isdn_tty_fax_modem_result(0, info); /* OK */
+ return (0);
+ case ISDN_TTY_FAX_CFR:
+ isdn_tty_fax_modem_result(9, info); /* +FCFR */
+ return (0);
+ case ISDN_TTY_FAX_ET:
+ sprintf(rs, "%c%c", DLE, ETX);
+ isdn_tty_at_cout(rs, info);
+ isdn_tty_fax_modem_result(10, info); /* +FPTS */
+ isdn_tty_fax_modem_result(11, info); /* +FET */
+ isdn_tty_fax_modem_result(0, info); /* OK */
+ info->faxonline &= ~2; /* leave data mode */
+ info->online = 1;
+ f->phase = ISDN_FAX_PHASE_D;
+ return (0);
+ case ISDN_TTY_FAX_PTS:
+ isdn_tty_fax_modem_result(10, info); /* +FPTS */
+ if (f->direction == ISDN_TTY_FAX_CONN_OUT) {
+ if (f->fet == 1)
+ f->phase = ISDN_FAX_PHASE_B;
+ if (f->fet == 0)
+ isdn_tty_fax_modem_result(0, info); /* OK */
+ }
+ return (0);
+ case ISDN_TTY_FAX_EOP:
+ info->faxonline &= ~2; /* leave data mode */
+ info->online = 1;
+ f->phase = ISDN_FAX_PHASE_D;
+ return (0);
+
+ }
+ return (-1);
+}
+
+
+void
+isdn_tty_fax_bitorder(modem_info * info, struct sk_buff *skb)
+{
+ __u8 LeftMask;
+ __u8 RightMask;
+ __u8 fBit;
+ __u8 Data;
+ int i;
+
+ if (!info->fax->bor) {
+ for (i = 0; i < skb->len; i++) {
+ Data = skb->data[i];
+ for (
+ LeftMask = 0x80, RightMask = 0x01;
+ LeftMask > RightMask;
+ LeftMask >>= 1, RightMask <<= 1
+ ) {
+ fBit = (Data & LeftMask);
+ if (Data & RightMask)
+ Data |= LeftMask;
+ else
+ Data &= ~LeftMask;
+ if (fBit)
+ Data |= RightMask;
+ else
+ Data &= ~RightMask;
+
+ }
+ skb->data[i] = Data;
+ }
+ }
+}
+
+/*
+ * Parse AT+F.. FAX class 1 commands
+ */
+
+static int
+isdn_tty_cmd_FCLASS1(char **p, modem_info * info)
+{
+ static char *cmd[] =
+ {"AE", "TS", "RS", "TM", "RM", "TH", "RH"};
+ isdn_ctrl c;
+ int par, i;
+ u_long flags;
+
+ for (c.parm.aux.cmd = 0; c.parm.aux.cmd < 7; c.parm.aux.cmd++)
+ if (!strncmp(p[0], cmd[c.parm.aux.cmd], 2))
+ break;
+
+#ifdef ISDN_TTY_FAX_CMD_DEBUG
+ printk(KERN_DEBUG "isdn_tty_cmd_FCLASS1 (%s,%d)\n", p[0], c.parm.aux.cmd);
+#endif
+ if (c.parm.aux.cmd == 7)
+ PARSE_ERROR1;
+
+ p[0] += 2;
+ switch (*p[0]) {
+ case '?':
+ p[0]++;
+ c.parm.aux.subcmd = AT_QUERY;
+ break;
+ case '=':
+ p[0]++;
+ if (*p[0] == '?') {
+ p[0]++;
+ c.parm.aux.subcmd = AT_EQ_QUERY;
+ } else {
+ par = isdn_getnum(p);
+ if ((par < 0) || (par > 255))
+ PARSE_ERROR1;
+ c.parm.aux.subcmd = AT_EQ_VALUE;
+ c.parm.aux.para[0] = par;
+ }
+ break;
+ case 0:
+ c.parm.aux.subcmd = AT_COMMAND;
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ c.command = ISDN_CMD_FAXCMD;
+#ifdef ISDN_TTY_FAX_CMD_DEBUG
+ printk(KERN_DEBUG "isdn_tty_cmd_FCLASS1 %d/%d/%d)\n",
+ c.parm.aux.cmd, c.parm.aux.subcmd, c.parm.aux.para[0]);
+#endif
+ if (info->isdn_driver < 0) {
+ if ((c.parm.aux.subcmd == AT_EQ_VALUE) ||
+ (c.parm.aux.subcmd == AT_COMMAND)) {
+ PARSE_ERROR1;
+ }
+ spin_lock_irqsave(&dev->lock, flags);
+ /* get a temporary connection to the first free fax driver */
+ i = isdn_get_free_channel(ISDN_USAGE_FAX, ISDN_PROTO_L2_FAX,
+ ISDN_PROTO_L3_FCLASS1, -1, -1, "00");
+ if (i < 0) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ PARSE_ERROR1;
+ }
+ info->isdn_driver = dev->drvmap[i];
+ info->isdn_channel = dev->chanmap[i];
+ info->drv_index = i;
+ dev->m_idx[i] = info->line;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ c.driver = info->isdn_driver;
+ c.arg = info->isdn_channel;
+ isdn_command(&c);
+ spin_lock_irqsave(&dev->lock, flags);
+ isdn_free_channel(info->isdn_driver, info->isdn_channel,
+ ISDN_USAGE_FAX);
+ info->isdn_driver = -1;
+ info->isdn_channel = -1;
+ if (info->drv_index >= 0) {
+ dev->m_idx[info->drv_index] = -1;
+ info->drv_index = -1;
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+ } else {
+ c.driver = info->isdn_driver;
+ c.arg = info->isdn_channel;
+ isdn_command(&c);
+ }
+ return 1;
+}
+
+/*
+ * Parse AT+F.. FAX class 2 commands
+ */
+
+static int
+isdn_tty_cmd_FCLASS2(char **p, modem_info * info)
+{
+ atemu *m = &info->emu;
+ T30_s *f = info->fax;
+ isdn_ctrl cmd;
+ int par;
+ char rs[50];
+ char rss[50];
+ int maxdccval[] =
+ {1, 5, 2, 2, 3, 2, 0, 7};
+
+ /* FAA still unchanged */
+ if (!strncmp(p[0], "AA", 2)) { /* TODO */
+ p[0] += 2;
+ switch (*p[0]) {
+ case '?':
+ p[0]++;
+ sprintf(rs, "\r\n%d", 0);
+ isdn_tty_at_cout(rs, info);
+ break;
+ case '=':
+ p[0]++;
+ par = isdn_getnum(p);
+ if ((par < 0) || (par > 255))
+ PARSE_ERROR1;
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ return 0;
+ }
+ /* BADLIN=value - dummy 0=disable errorchk disabled, 1-255 nr. of lines for making page bad */
+ if (!strncmp(p[0], "BADLIN", 6)) {
+ p[0] += 6;
+ switch (*p[0]) {
+ case '?':
+ p[0]++;
+ sprintf(rs, "\r\n%d", f->badlin);
+ isdn_tty_at_cout(rs, info);
+ break;
+ case '=':
+ p[0]++;
+ if (*p[0] == '?') {
+ p[0]++;
+ sprintf(rs, "\r\n0-255");
+ isdn_tty_at_cout(rs, info);
+ } else {
+ par = isdn_getnum(p);
+ if ((par < 0) || (par > 255))
+ PARSE_ERROR1;
+ f->badlin = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+ printk(KERN_DEBUG "isdn_tty: Fax FBADLIN=%d\n", par);
+#endif
+ }
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ return 0;
+ }
+ /* BADMUL=value - dummy 0=disable errorchk disabled (treshold multiplier) */
+ if (!strncmp(p[0], "BADMUL", 6)) {
+ p[0] += 6;
+ switch (*p[0]) {
+ case '?':
+ p[0]++;
+ sprintf(rs, "\r\n%d", f->badmul);
+ isdn_tty_at_cout(rs, info);
+ break;
+ case '=':
+ p[0]++;
+ if (*p[0] == '?') {
+ p[0]++;
+ sprintf(rs, "\r\n0-255");
+ isdn_tty_at_cout(rs, info);
+ } else {
+ par = isdn_getnum(p);
+ if ((par < 0) || (par > 255))
+ PARSE_ERROR1;
+ f->badmul = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+ printk(KERN_DEBUG "isdn_tty: Fax FBADMUL=%d\n", par);
+#endif
+ }
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ return 0;
+ }
+ /* BOR=n - Phase C bit order, 0=direct, 1=reverse */
+ if (!strncmp(p[0], "BOR", 3)) {
+ p[0] += 3;
+ switch (*p[0]) {
+ case '?':
+ p[0]++;
+ sprintf(rs, "\r\n%d", f->bor);
+ isdn_tty_at_cout(rs, info);
+ break;
+ case '=':
+ p[0]++;
+ if (*p[0] == '?') {
+ p[0]++;
+ sprintf(rs, "\r\n0,1");
+ isdn_tty_at_cout(rs, info);
+ } else {
+ par = isdn_getnum(p);
+ if ((par < 0) || (par > 1))
+ PARSE_ERROR1;
+ f->bor = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+ printk(KERN_DEBUG "isdn_tty: Fax FBOR=%d\n", par);
+#endif
+ }
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ return 0;
+ }
+ /* NBC=n - No Best Capabilities */
+ if (!strncmp(p[0], "NBC", 3)) {
+ p[0] += 3;
+ switch (*p[0]) {
+ case '?':
+ p[0]++;
+ sprintf(rs, "\r\n%d", f->nbc);
+ isdn_tty_at_cout(rs, info);
+ break;
+ case '=':
+ p[0]++;
+ if (*p[0] == '?') {
+ p[0]++;
+ sprintf(rs, "\r\n0,1");
+ isdn_tty_at_cout(rs, info);
+ } else {
+ par = isdn_getnum(p);
+ if ((par < 0) || (par > 1))
+ PARSE_ERROR1;
+ f->nbc = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+ printk(KERN_DEBUG "isdn_tty: Fax FNBC=%d\n", par);
+#endif
+ }
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ return 0;
+ }
+ /* BUF? - Readonly buffersize readout */
+ if (!strncmp(p[0], "BUF?", 4)) {
+ p[0] += 4;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+ printk(KERN_DEBUG "isdn_tty: Fax FBUF? (%d) \n", (16 * m->mdmreg[REG_PSIZE]));
+#endif
+ p[0]++;
+ sprintf(rs, "\r\n %d ", (16 * m->mdmreg[REG_PSIZE]));
+ isdn_tty_at_cout(rs, info);
+ return 0;
+ }
+ /* CIG=string - local fax station id string for polling rx */
+ if (!strncmp(p[0], "CIG", 3)) {
+ int i, r;
+ p[0] += 3;
+ switch (*p[0]) {
+ case '?':
+ p[0]++;
+ sprintf(rs, "\r\n\"%s\"", f->pollid);
+ isdn_tty_at_cout(rs, info);
+ break;
+ case '=':
+ p[0]++;
+ if (*p[0] == '?') {
+ p[0]++;
+ sprintf(rs, "\r\n\"STRING\"");
+ isdn_tty_at_cout(rs, info);
+ } else {
+ if (*p[0] == '"')
+ p[0]++;
+ for (i = 0; (*p[0]) && i < (FAXIDLEN - 1) && (*p[0] != '"'); i++) {
+ f->pollid[i] = *p[0]++;
+ }
+ if (*p[0] == '"')
+ p[0]++;
+ for (r = i; r < FAXIDLEN; r++) {
+ f->pollid[r] = 32;
+ }
+ f->pollid[FAXIDLEN - 1] = 0;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+ printk(KERN_DEBUG "isdn_tty: Fax local poll ID rx \"%s\"\n", f->pollid);
+#endif
+ }
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ return 0;
+ }
+ /* CQ=n - copy qlty chk, 0= no chk, 1=only 1D chk, 2=1D+2D chk */
+ if (!strncmp(p[0], "CQ", 2)) {
+ p[0] += 2;
+ switch (*p[0]) {
+ case '?':
+ p[0]++;
+ sprintf(rs, "\r\n%d", f->cq);
+ isdn_tty_at_cout(rs, info);
+ break;
+ case '=':
+ p[0]++;
+ if (*p[0] == '?') {
+ p[0]++;
+ sprintf(rs, "\r\n0,1,2");
+ isdn_tty_at_cout(rs, info);
+ } else {
+ par = isdn_getnum(p);
+ if ((par < 0) || (par > 2))
+ PARSE_ERROR1;
+ f->cq = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+ printk(KERN_DEBUG "isdn_tty: Fax FCQ=%d\n", par);
+#endif
+ }
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ return 0;
+ }
+ /* CR=n - can receive? 0= no data rx or poll remote dev, 1=do receive data or poll remote dev */
+ if (!strncmp(p[0], "CR", 2)) {
+ p[0] += 2;
+ switch (*p[0]) {
+ case '?':
+ p[0]++;
+ sprintf(rs, "\r\n%d", f->cr); /* read actual value from struct and print */
+ isdn_tty_at_cout(rs, info);
+ break;
+ case '=':
+ p[0]++;
+ if (*p[0] == '?') {
+ p[0]++;
+ sprintf(rs, "\r\n0,1"); /* display online help */
+ isdn_tty_at_cout(rs, info);
+ } else {
+ par = isdn_getnum(p);
+ if ((par < 0) || (par > 1))
+ PARSE_ERROR1;
+ f->cr = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+ printk(KERN_DEBUG "isdn_tty: Fax FCR=%d\n", par);
+#endif
+ }
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ return 0;
+ }
+ /* CTCRTY=value - ECM retry count */
+ if (!strncmp(p[0], "CTCRTY", 6)) {
+ p[0] += 6;
+ switch (*p[0]) {
+ case '?':
+ p[0]++;
+ sprintf(rs, "\r\n%d", f->ctcrty);
+ isdn_tty_at_cout(rs, info);
+ break;
+ case '=':
+ p[0]++;
+ if (*p[0] == '?') {
+ p[0]++;
+ sprintf(rs, "\r\n0-255");
+ isdn_tty_at_cout(rs, info);
+ } else {
+ par = isdn_getnum(p);
+ if ((par < 0) || (par > 255))
+ PARSE_ERROR1;
+ f->ctcrty = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+ printk(KERN_DEBUG "isdn_tty: Fax FCTCRTY=%d\n", par);
+#endif
+ }
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ return 0;
+ }
+ /* DCC=vr,br,wd,ln,df,ec,bf,st - DCE capabilities parms */
+ if (!strncmp(p[0], "DCC", 3)) {
+ char *rp = &f->resolution;
+ int i;
+
+ p[0] += 3;
+ switch (*p[0]) {
+ case '?':
+ p[0]++;
+ strcpy(rs, "\r\n");
+ for (i = 0; i < 8; i++) {
+ sprintf(rss, "%c%s", rp[i] + 48,
+ (i < 7) ? "," : "");
+ strcat(rs, rss);
+ }
+ isdn_tty_at_cout(rs, info);
+ break;
+ case '=':
+ p[0]++;
+ if (*p[0] == '?') {
+ isdn_tty_at_cout("\r\n(0,1),(0-5),(0-2),(0-2),(0-3),(0-2),(0),(0-7)", info);
+ p[0]++;
+ } else {
+ for (i = 0; (((*p[0] >= '0') && (*p[0] <= '9')) || (*p[0] == ',')) && (i < 8); i++) {
+ if (*p[0] != ',') {
+ if ((*p[0] - 48) > maxdccval[i]) {
+ PARSE_ERROR1;
+ }
+ rp[i] = *p[0] - 48;
+ p[0]++;
+ if (*p[0] == ',')
+ p[0]++;
+ } else
+ p[0]++;
+ }
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+ printk(KERN_DEBUG "isdn_tty: Fax FDCC capabilities DCE=%d,%d,%d,%d,%d,%d,%d,%d\n",
+ rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], rp[6], rp[7]);
+#endif
+ }
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ return 0;
+ }
+ /* DIS=vr,br,wd,ln,df,ec,bf,st - current session parms */
+ if (!strncmp(p[0], "DIS", 3)) {
+ char *rp = &f->resolution;
+ int i;
+
+ p[0] += 3;
+ switch (*p[0]) {
+ case '?':
+ p[0]++;
+ strcpy(rs, "\r\n");
+ for (i = 0; i < 8; i++) {
+ sprintf(rss, "%c%s", rp[i] + 48,
+ (i < 7) ? "," : "");
+ strcat(rs, rss);
+ }
+ isdn_tty_at_cout(rs, info);
+ break;
+ case '=':
+ p[0]++;
+ if (*p[0] == '?') {
+ isdn_tty_at_cout("\r\n(0,1),(0-5),(0-2),(0-2),(0-3),(0-2),(0),(0-7)", info);
+ p[0]++;
+ } else {
+ for (i = 0; (((*p[0] >= '0') && (*p[0] <= '9')) || (*p[0] == ',')) && (i < 8); i++) {
+ if (*p[0] != ',') {
+ if ((*p[0] - 48) > maxdccval[i]) {
+ PARSE_ERROR1;
+ }
+ rp[i] = *p[0] - 48;
+ p[0]++;
+ if (*p[0] == ',')
+ p[0]++;
+ } else
+ p[0]++;
+ }
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+ printk(KERN_DEBUG "isdn_tty: Fax FDIS session parms=%d,%d,%d,%d,%d,%d,%d,%d\n",
+ rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], rp[6], rp[7]);
+#endif
+ }
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ return 0;
+ }
+ /* DR - Receive Phase C data command, initiates document reception */
+ if (!strncmp(p[0], "DR", 2)) {
+ p[0] += 2;
+ if ((info->faxonline & 16) && /* incoming connection */
+ ((f->phase == ISDN_FAX_PHASE_B) || (f->phase == ISDN_FAX_PHASE_D))) {
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+ printk(KERN_DEBUG "isdn_tty: Fax FDR\n");
+#endif
+ f->code = ISDN_TTY_FAX_DR;
+ cmd.driver = info->isdn_driver;
+ cmd.arg = info->isdn_channel;
+ cmd.command = ISDN_CMD_FAXCMD;
+ isdn_command(&cmd);
+ if (f->phase == ISDN_FAX_PHASE_B) {
+ f->phase = ISDN_FAX_PHASE_C;
+ } else if (f->phase == ISDN_FAX_PHASE_D) {
+ switch (f->fet) {
+ case 0: /* next page will be received */
+ f->phase = ISDN_FAX_PHASE_C;
+ isdn_tty_fax_modem_result(7, info); /* CONNECT */
+ break;
+ case 1: /* next doc will be received */
+ f->phase = ISDN_FAX_PHASE_B;
+ break;
+ case 2: /* fax session is terminating */
+ f->phase = ISDN_FAX_PHASE_E;
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ }
+ } else {
+ PARSE_ERROR1;
+ }
+ return 1;
+ }
+ /* DT=df,vr,wd,ln - TX phase C data command (release DCE to proceed with negotiation) */
+ if (!strncmp(p[0], "DT", 2)) {
+ int i, val[] =
+ {4, 0, 2, 3};
+ char *rp = &f->resolution;
+
+ p[0] += 2;
+ if (!(info->faxonline & 1)) /* not outgoing connection */
+ PARSE_ERROR1;
+
+ for (i = 0; (((*p[0] >= '0') && (*p[0] <= '9')) || (*p[0] == ',')) && (i < 4); i++) {
+ if (*p[0] != ',') {
+ if ((*p[0] - 48) > maxdccval[val[i]]) {
+ PARSE_ERROR1;
+ }
+ rp[val[i]] = *p[0] - 48;
+ p[0]++;
+ if (*p[0] == ',')
+ p[0]++;
+ } else
+ p[0]++;
+ }
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+ printk(KERN_DEBUG "isdn_tty: Fax FDT tx data command parms=%d,%d,%d,%d\n",
+ rp[4], rp[0], rp[2], rp[3]);
+#endif
+ if ((f->phase == ISDN_FAX_PHASE_B) || (f->phase == ISDN_FAX_PHASE_D)) {
+ f->code = ISDN_TTY_FAX_DT;
+ cmd.driver = info->isdn_driver;
+ cmd.arg = info->isdn_channel;
+ cmd.command = ISDN_CMD_FAXCMD;
+ isdn_command(&cmd);
+ if (f->phase == ISDN_FAX_PHASE_D) {
+ f->phase = ISDN_FAX_PHASE_C;
+ isdn_tty_fax_modem_result(7, info); /* CONNECT */
+ }
+ } else {
+ PARSE_ERROR1;
+ }
+ return 1;
+ }
+ /* ECM=n - Error mode control 0=disabled, 2=enabled, handled by DCE alone incl. buff of partial pages */
+ if (!strncmp(p[0], "ECM", 3)) {
+ p[0] += 3;
+ switch (*p[0]) {
+ case '?':
+ p[0]++;
+ sprintf(rs, "\r\n%d", f->ecm);
+ isdn_tty_at_cout(rs, info);
+ break;
+ case '=':
+ p[0]++;
+ if (*p[0] == '?') {
+ p[0]++;
+ sprintf(rs, "\r\n0,2");
+ isdn_tty_at_cout(rs, info);
+ } else {
+ par = isdn_getnum(p);
+ if ((par != 0) && (par != 2))
+ PARSE_ERROR1;
+ f->ecm = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+ printk(KERN_DEBUG "isdn_tty: Fax FECM=%d\n", par);
+#endif
+ }
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ return 0;
+ }
+ /* ET=n - End of page or document */
+ if (!strncmp(p[0], "ET=", 3)) {
+ p[0] += 3;
+ if (*p[0] == '?') {
+ p[0]++;
+ sprintf(rs, "\r\n0-2");
+ isdn_tty_at_cout(rs, info);
+ } else {
+ if ((f->phase != ISDN_FAX_PHASE_D) ||
+ (!(info->faxonline & 1)))
+ PARSE_ERROR1;
+ par = isdn_getnum(p);
+ if ((par < 0) || (par > 2))
+ PARSE_ERROR1;
+ f->fet = par;
+ f->code = ISDN_TTY_FAX_ET;
+ cmd.driver = info->isdn_driver;
+ cmd.arg = info->isdn_channel;
+ cmd.command = ISDN_CMD_FAXCMD;
+ isdn_command(&cmd);
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+ printk(KERN_DEBUG "isdn_tty: Fax FET=%d\n", par);
+#endif
+ return 1;
+ }
+ return 0;
+ }
+ /* K - terminate */
+ if (!strncmp(p[0], "K", 1)) {
+ p[0] += 1;
+ if ((f->phase == ISDN_FAX_PHASE_IDLE) || (f->phase == ISDN_FAX_PHASE_E))
+ PARSE_ERROR1;
+ isdn_tty_modem_hup(info, 1);
+ return 1;
+ }
+ /* LID=string - local fax ID */
+ if (!strncmp(p[0], "LID", 3)) {
+ int i, r;
+ p[0] += 3;
+ switch (*p[0]) {
+ case '?':
+ p[0]++;
+ sprintf(rs, "\r\n\"%s\"", f->id);
+ isdn_tty_at_cout(rs, info);
+ break;
+ case '=':
+ p[0]++;
+ if (*p[0] == '?') {
+ p[0]++;
+ sprintf(rs, "\r\n\"STRING\"");
+ isdn_tty_at_cout(rs, info);
+ } else {
+ if (*p[0] == '"')
+ p[0]++;
+ for (i = 0; (*p[0]) && i < (FAXIDLEN - 1) && (*p[0] != '"'); i++) {
+ f->id[i] = *p[0]++;
+ }
+ if (*p[0] == '"')
+ p[0]++;
+ for (r = i; r < FAXIDLEN; r++) {
+ f->id[r] = 32;
+ }
+ f->id[FAXIDLEN - 1] = 0;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+ printk(KERN_DEBUG "isdn_tty: Fax local ID \"%s\"\n", f->id);
+#endif
+ }
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ return 0;
+ }
+
+ /* MDL? - DCE Model */
+ if (!strncmp(p[0], "MDL?", 4)) {
+ p[0] += 4;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+ printk(KERN_DEBUG "isdn_tty: FMDL?\n");
+#endif
+ isdn_tty_at_cout("\r\nisdn4linux", info);
+ return 0;
+ }
+ /* MFR? - DCE Manufacturer */
+ if (!strncmp(p[0], "MFR?", 4)) {
+ p[0] += 4;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+ printk(KERN_DEBUG "isdn_tty: FMFR?\n");
+#endif
+ isdn_tty_at_cout("\r\nisdn4linux", info);
+ return 0;
+ }
+ /* MINSP=n - Minimum Speed for Phase C */
+ if (!strncmp(p[0], "MINSP", 5)) {
+ p[0] += 5;
+ switch (*p[0]) {
+ case '?':
+ p[0]++;
+ sprintf(rs, "\r\n%d", f->minsp);
+ isdn_tty_at_cout(rs, info);
+ break;
+ case '=':
+ p[0]++;
+ if (*p[0] == '?') {
+ p[0]++;
+ sprintf(rs, "\r\n0-5");
+ isdn_tty_at_cout(rs, info);
+ } else {
+ par = isdn_getnum(p);
+ if ((par < 0) || (par > 5))
+ PARSE_ERROR1;
+ f->minsp = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+ printk(KERN_DEBUG "isdn_tty: Fax FMINSP=%d\n", par);
+#endif
+ }
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ return 0;
+ }
+ /* PHCTO=value - DTE phase C timeout */
+ if (!strncmp(p[0], "PHCTO", 5)) {
+ p[0] += 5;
+ switch (*p[0]) {
+ case '?':
+ p[0]++;
+ sprintf(rs, "\r\n%d", f->phcto);
+ isdn_tty_at_cout(rs, info);
+ break;
+ case '=':
+ p[0]++;
+ if (*p[0] == '?') {
+ p[0]++;
+ sprintf(rs, "\r\n0-255");
+ isdn_tty_at_cout(rs, info);
+ } else {
+ par = isdn_getnum(p);
+ if ((par < 0) || (par > 255))
+ PARSE_ERROR1;
+ f->phcto = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+ printk(KERN_DEBUG "isdn_tty: Fax FPHCTO=%d\n", par);
+#endif
+ }
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ return 0;
+ }
+
+ /* REL=n - Phase C received EOL alignment */
+ if (!strncmp(p[0], "REL", 3)) {
+ p[0] += 3;
+ switch (*p[0]) {
+ case '?':
+ p[0]++;
+ sprintf(rs, "\r\n%d", f->rel);
+ isdn_tty_at_cout(rs, info);
+ break;
+ case '=':
+ p[0]++;
+ if (*p[0] == '?') {
+ p[0]++;
+ sprintf(rs, "\r\n0,1");
+ isdn_tty_at_cout(rs, info);
+ } else {
+ par = isdn_getnum(p);
+ if ((par < 0) || (par > 1))
+ PARSE_ERROR1;
+ f->rel = par;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+ printk(KERN_DEBUG "isdn_tty: Fax FREL=%d\n", par);
+#endif
+ }
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ return 0;
+ }
+ /* REV? - DCE Revision */
+ if (!strncmp(p[0], "REV?", 4)) {
+ p[0] += 4;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+ printk(KERN_DEBUG "isdn_tty: FREV?\n");
+#endif
+ strcpy(rss, isdn_tty_fax_revision);
+ sprintf(rs, "\r\nRev: %s", isdn_getrev(rss));
+ isdn_tty_at_cout(rs, info);
+ return 0;
+ }
+
+ /* Phase C Transmit Data Block Size */
+ if (!strncmp(p[0], "TBC=", 4)) { /* dummy, not used */
+ p[0] += 4;
+#ifdef ISDN_TTY_FAX_STAT_DEBUG
+ printk(KERN_DEBUG "isdn_tty: Fax FTBC=%c\n", *p[0]);
+#endif
+ switch (*p[0]) {
+ case '0':
+ p[0]++;
+ break;
+ default:
+ PARSE_ERROR1;
+ }
+ return 0;
+ }
+ printk(KERN_DEBUG "isdn_tty: unknown token=>AT+F%s<\n", p[0]);
+ PARSE_ERROR1;
+}
+
+int
+isdn_tty_cmd_PLUSF_FAX(char **p, modem_info * info)
+{
+ if (TTY_IS_FCLASS2(info))
+ return (isdn_tty_cmd_FCLASS2(p, info));
+ else if (TTY_IS_FCLASS1(info))
+ return (isdn_tty_cmd_FCLASS1(p, info));
+ PARSE_ERROR1;
+}
diff --git a/drivers/isdn/i4l/isdn_ttyfax.h b/drivers/isdn/i4l/isdn_ttyfax.h
new file mode 100644
index 0000000..757a890
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_ttyfax.h
@@ -0,0 +1,18 @@
+/* $Id: isdn_ttyfax.h,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $
+ *
+ * header for Linux ISDN subsystem, tty_fax related functions (linklevel).
+ *
+ * Copyright 1999 by Armin Schindler (mac@melware.de)
+ * Copyright 1999 by Ralf Spachmann (mel@melware.de)
+ * Copyright 1999 by Cytronics & Melware
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+
+#define XON 0x11
+#define XOFF 0x13
+#define DC2 0x12
+
diff --git a/drivers/isdn/i4l/isdn_v110.c b/drivers/isdn/i4l/isdn_v110.c
new file mode 100644
index 0000000..c5d02b6
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_v110.c
@@ -0,0 +1,616 @@
+/* $Id: isdn_v110.c,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $
+ *
+ * Linux ISDN subsystem, V.110 related functions (linklevel).
+ *
+ * Copyright by Thomas Pfeiffer (pfeiffer@pds.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+
+#include <linux/isdn.h>
+#include "isdn_v110.h"
+
+#undef ISDN_V110_DEBUG
+
+char *isdn_v110_revision = "$Revision: 1.1.2.2 $";
+
+#define V110_38400 255
+#define V110_19200 15
+#define V110_9600 3
+
+/*
+ * The following data are precoded matrices, online and offline matrix
+ * for 9600, 19200 und 38400, respectively
+ */
+static unsigned char V110_OnMatrix_9600[] =
+{0xfc, 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff,
+ 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd,
+ 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff,
+ 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd};
+
+static unsigned char V110_OffMatrix_9600[] =
+{0xfc, 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+static unsigned char V110_OnMatrix_19200[] =
+{0xf0, 0xf0, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7,
+ 0xfd, 0xff, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7};
+
+static unsigned char V110_OffMatrix_19200[] =
+{0xf0, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+static unsigned char V110_OnMatrix_38400[] =
+{0x00, 0x7f, 0x7f, 0x7f, 0x7f, 0xfd, 0x7f, 0x7f, 0x7f, 0x7f};
+
+static unsigned char V110_OffMatrix_38400[] =
+{0x00, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff};
+
+/*
+ * FlipBits reorders sequences of keylen bits in one byte.
+ * E.g. source order 7654321 will be converted to 45670123 when keylen = 4,
+ * and to 67452301 when keylen = 2. This is necessary because ordering on
+ * the isdn line is the other way.
+ */
+static inline unsigned char
+FlipBits(unsigned char c, int keylen)
+{
+ unsigned char b = c;
+ unsigned char bit = 128;
+ int i;
+ int j;
+ int hunks = (8 / keylen);
+
+ c = 0;
+ for (i = 0; i < hunks; i++) {
+ for (j = 0; j < keylen; j++) {
+ if (b & (bit >> j))
+ c |= bit >> (keylen - j - 1);
+ }
+ bit >>= keylen;
+ }
+ return c;
+}
+
+
+/* isdn_v110_open allocates and initializes private V.110 data
+ * structures and returns a pointer to these.
+ */
+static isdn_v110_stream *
+isdn_v110_open(unsigned char key, int hdrlen, int maxsize)
+{
+ int i;
+ isdn_v110_stream *v;
+
+ if ((v = kzalloc(sizeof(isdn_v110_stream), GFP_ATOMIC)) == NULL)
+ return NULL;
+ v->key = key;
+ v->nbits = 0;
+ for (i = 0; key & (1 << i); i++)
+ v->nbits++;
+
+ v->nbytes = 8 / v->nbits;
+ v->decodelen = 0;
+
+ switch (key) {
+ case V110_38400:
+ v->OnlineFrame = V110_OnMatrix_38400;
+ v->OfflineFrame = V110_OffMatrix_38400;
+ break;
+ case V110_19200:
+ v->OnlineFrame = V110_OnMatrix_19200;
+ v->OfflineFrame = V110_OffMatrix_19200;
+ break;
+ default:
+ v->OnlineFrame = V110_OnMatrix_9600;
+ v->OfflineFrame = V110_OffMatrix_9600;
+ break;
+ }
+ v->framelen = v->nbytes * 10;
+ v->SyncInit = 5;
+ v->introducer = 0;
+ v->dbit = 1;
+ v->b = 0;
+ v->skbres = hdrlen;
+ v->maxsize = maxsize - hdrlen;
+ if ((v->encodebuf = kmalloc(maxsize, GFP_ATOMIC)) == NULL) {
+ kfree(v);
+ return NULL;
+ }
+ return v;
+}
+
+/* isdn_v110_close frees private V.110 data structures */
+void
+isdn_v110_close(isdn_v110_stream * v)
+{
+ if (v == NULL)
+ return;
+#ifdef ISDN_V110_DEBUG
+ printk(KERN_DEBUG "v110 close\n");
+#endif
+ kfree(v->encodebuf);
+ kfree(v);
+}
+
+
+/*
+ * ValidHeaderBytes return the number of valid bytes in v->decodebuf
+ */
+static int
+ValidHeaderBytes(isdn_v110_stream * v)
+{
+ int i;
+ for (i = 0; (i < v->decodelen) && (i < v->nbytes); i++)
+ if ((v->decodebuf[i] & v->key) != 0)
+ break;
+ return i;
+}
+
+/*
+ * SyncHeader moves the decodebuf ptr to the next valid header
+ */
+static void
+SyncHeader(isdn_v110_stream * v)
+{
+ unsigned char *rbuf = v->decodebuf;
+ int len = v->decodelen;
+
+ if (len == 0)
+ return;
+ for (rbuf++, len--; len > 0; len--, rbuf++) /* such den SyncHeader in buf ! */
+ if ((*rbuf & v->key) == 0) /* erstes byte gefunden ? */
+ break; /* jupp! */
+ if (len)
+ memcpy(v->decodebuf, rbuf, len);
+
+ v->decodelen = len;
+#ifdef ISDN_V110_DEBUG
+ printk(KERN_DEBUG "isdn_v110: Header resync\n");
+#endif
+}
+
+/* DecodeMatrix takes n (n>=1) matrices (v110 frames, 10 bytes) where
+ len is the number of matrix-lines. len must be a multiple of 10, i.e.
+ only complete matices must be given.
+ From these, netto data is extracted and returned in buf. The return-value
+ is the bytecount of the decoded data.
+ */
+static int
+DecodeMatrix(isdn_v110_stream * v, unsigned char *m, int len, unsigned char *buf)
+{
+ int line = 0;
+ int buflen = 0;
+ int mbit = 64;
+ int introducer = v->introducer;
+ int dbit = v->dbit;
+ unsigned char b = v->b;
+
+ while (line < len) { /* Are we done with all lines of the matrix? */
+ if ((line % 10) == 0) { /* the 0. line of the matrix is always 0 ! */
+ if (m[line] != 0x00) { /* not 0 ? -> error! */
+#ifdef ISDN_V110_DEBUG
+ printk(KERN_DEBUG "isdn_v110: DecodeMatrix, V110 Bad Header\n");
+ /* returning now is not the right thing, though :-( */
+#endif
+ }
+ line++; /* next line of matrix */
+ continue;
+ } else if ((line % 10) == 5) { /* in line 5 there's only e-bits ! */
+ if ((m[line] & 0x70) != 0x30) { /* 011 has to be at the beginning! */
+#ifdef ISDN_V110_DEBUG
+ printk(KERN_DEBUG "isdn_v110: DecodeMatrix, V110 Bad 5th line\n");
+ /* returning now is not the right thing, though :-( */
+#endif
+ }
+ line++; /* next line */
+ continue;
+ } else if (!introducer) { /* every byte starts with 10 (stopbit, startbit) */
+ introducer = (m[line] & mbit) ? 0 : 1; /* current bit of the matrix */
+ next_byte:
+ if (mbit > 2) { /* was it the last bit in this line ? */
+ mbit >>= 1; /* no -> take next */
+ continue;
+ } /* otherwise start with leftmost bit in the next line */
+ mbit = 64;
+ line++;
+ continue;
+ } else { /* otherwise we need to set a data bit */
+ if (m[line] & mbit) /* was that bit set in the matrix ? */
+ b |= dbit; /* yes -> set it in the data byte */
+ else
+ b &= dbit - 1; /* no -> clear it in the data byte */
+ if (dbit < 128) /* is that data byte done ? */
+ dbit <<= 1; /* no, got the next bit */
+ else { /* data byte is done */
+ buf[buflen++] = b; /* copy byte into the output buffer */
+ introducer = b = 0; /* init of the intro sequence and of the data byte */
+ dbit = 1; /* next we look for the 0th bit */
+ }
+ goto next_byte; /* look for next bit in the matrix */
+ }
+ }
+ v->introducer = introducer;
+ v->dbit = dbit;
+ v->b = b;
+ return buflen; /* return number of bytes in the output buffer */
+}
+
+/*
+ * DecodeStream receives V.110 coded data from the input stream. It recovers the
+ * original frames.
+ * The input stream doesn't need to be framed
+ */
+struct sk_buff *
+isdn_v110_decode(isdn_v110_stream * v, struct sk_buff *skb)
+{
+ int i;
+ int j;
+ int len;
+ unsigned char *v110_buf;
+ unsigned char *rbuf;
+
+ if (!skb) {
+ printk(KERN_WARNING "isdn_v110_decode called with NULL skb!\n");
+ return NULL;
+ }
+ rbuf = skb->data;
+ len = skb->len;
+ if (v == NULL) {
+ /* invalid handle, no chance to proceed */
+ printk(KERN_WARNING "isdn_v110_decode called with NULL stream!\n");
+ dev_kfree_skb(skb);
+ return NULL;
+ }
+ if (v->decodelen == 0) /* cache empty? */
+ for (; len > 0; len--, rbuf++) /* scan for SyncHeader in buf */
+ if ((*rbuf & v->key) == 0)
+ break; /* found first byte */
+ if (len == 0) {
+ dev_kfree_skb(skb);
+ return NULL;
+ }
+ /* copy new data to decode-buffer */
+ memcpy(&(v->decodebuf[v->decodelen]), rbuf, len);
+ v->decodelen += len;
+ ReSync:
+ if (v->decodelen < v->nbytes) { /* got a new header ? */
+ dev_kfree_skb(skb);
+ return NULL; /* no, try later */
+ }
+ if (ValidHeaderBytes(v) != v->nbytes) { /* is that a valid header? */
+ SyncHeader(v); /* no -> look for header */
+ goto ReSync;
+ }
+ len = (v->decodelen - (v->decodelen % (10 * v->nbytes))) / v->nbytes;
+ if ((v110_buf = kmalloc(len, GFP_ATOMIC)) == NULL) {
+ printk(KERN_WARNING "isdn_v110_decode: Couldn't allocate v110_buf\n");
+ dev_kfree_skb(skb);
+ return NULL;
+ }
+ for (i = 0; i < len; i++) {
+ v110_buf[i] = 0;
+ for (j = 0; j < v->nbytes; j++)
+ v110_buf[i] |= (v->decodebuf[(i * v->nbytes) + j] & v->key) << (8 - ((j + 1) * v->nbits));
+ v110_buf[i] = FlipBits(v110_buf[i], v->nbits);
+ }
+ v->decodelen = (v->decodelen % (10 * v->nbytes));
+ memcpy(v->decodebuf, &(v->decodebuf[len * v->nbytes]), v->decodelen);
+
+ skb_trim(skb, DecodeMatrix(v, v110_buf, len, skb->data));
+ kfree(v110_buf);
+ if (skb->len)
+ return skb;
+ else {
+ kfree_skb(skb);
+ return NULL;
+ }
+}
+
+/* EncodeMatrix takes input data in buf, len is the bytecount.
+ Data is encoded into v110 frames in m. Return value is the number of
+ matrix-lines generated.
+ */
+static int
+EncodeMatrix(unsigned char *buf, int len, unsigned char *m, int mlen)
+{
+ int line = 0;
+ int i = 0;
+ int mbit = 128;
+ int dbit = 1;
+ int introducer = 3;
+ int ibit[] = {0, 1, 1};
+
+ while ((i < len) && (line < mlen)) { /* while we still have input data */
+ switch (line % 10) { /* in which line of the matrix are we? */
+ case 0:
+ m[line++] = 0x00; /* line 0 is always 0 */
+ mbit = 128; /* go on with the 7th bit */
+ break;
+ case 5:
+ m[line++] = 0xbf; /* line 5 is always 10111111 */
+ mbit = 128; /* go on with the 7th bit */
+ break;
+ }
+ if (line >= mlen) {
+ printk(KERN_WARNING "isdn_v110 (EncodeMatrix): buffer full!\n");
+ return line;
+ }
+ next_bit:
+ switch (mbit) { /* leftmost or rightmost bit ? */
+ case 1:
+ line++; /* rightmost -> go to next line */
+ if (line >= mlen) {
+ printk(KERN_WARNING "isdn_v110 (EncodeMatrix): buffer full!\n");
+ return line;
+ }
+ case 128:
+ m[line] = 128; /* leftmost -> set byte to 1000000 */
+ mbit = 64; /* current bit in the matrix line */
+ continue;
+ }
+ if (introducer) { /* set 110 sequence ? */
+ introducer--; /* set on digit less */
+ m[line] |= ibit[introducer] ? mbit : 0; /* set corresponding bit */
+ mbit >>= 1; /* bit of matrix line >> 1 */
+ goto next_bit; /* and go on there */
+ } /* else push data bits into the matrix! */
+ m[line] |= (buf[i] & dbit) ? mbit : 0; /* set data bit in matrix */
+ if (dbit == 128) { /* was it the last one? */
+ dbit = 1; /* then go on with first bit of */
+ i++; /* next byte in input buffer */
+ if (i < len) /* input buffer done ? */
+ introducer = 3; /* no, write introducer 110 */
+ else { /* input buffer done ! */
+ m[line] |= (mbit - 1) & 0xfe; /* set remaining bits in line to 1 */
+ break;
+ }
+ } else /* not the last data bit */
+ dbit <<= 1; /* then go to next data bit */
+ mbit >>= 1; /* go to next bit of matrix */
+ goto next_bit;
+
+ }
+ /* if necessary, generate remaining lines of the matrix... */
+ if ((line) && ((line + 10) < mlen))
+ switch (++line % 10) {
+ case 1:
+ m[line++] = 0xfe;
+ case 2:
+ m[line++] = 0xfe;
+ case 3:
+ m[line++] = 0xfe;
+ case 4:
+ m[line++] = 0xfe;
+ case 5:
+ m[line++] = 0xbf;
+ case 6:
+ m[line++] = 0xfe;
+ case 7:
+ m[line++] = 0xfe;
+ case 8:
+ m[line++] = 0xfe;
+ case 9:
+ m[line++] = 0xfe;
+ }
+ return line; /* that's how many lines we have */
+}
+
+/*
+ * Build a sync frame.
+ */
+static struct sk_buff *
+isdn_v110_sync(isdn_v110_stream *v)
+{
+ struct sk_buff *skb;
+
+ if (v == NULL) {
+ /* invalid handle, no chance to proceed */
+ printk(KERN_WARNING "isdn_v110_sync called with NULL stream!\n");
+ return NULL;
+ }
+ if ((skb = dev_alloc_skb(v->framelen + v->skbres))) {
+ skb_reserve(skb, v->skbres);
+ memcpy(skb_put(skb, v->framelen), v->OfflineFrame, v->framelen);
+ }
+ return skb;
+}
+
+/*
+ * Build an idle frame.
+ */
+static struct sk_buff *
+isdn_v110_idle(isdn_v110_stream *v)
+{
+ struct sk_buff *skb;
+
+ if (v == NULL) {
+ /* invalid handle, no chance to proceed */
+ printk(KERN_WARNING "isdn_v110_sync called with NULL stream!\n");
+ return NULL;
+ }
+ if ((skb = dev_alloc_skb(v->framelen + v->skbres))) {
+ skb_reserve(skb, v->skbres);
+ memcpy(skb_put(skb, v->framelen), v->OnlineFrame, v->framelen);
+ }
+ return skb;
+}
+
+struct sk_buff *
+isdn_v110_encode(isdn_v110_stream * v, struct sk_buff *skb)
+{
+ int i;
+ int j;
+ int rlen;
+ int mlen;
+ int olen;
+ int size;
+ int sval1;
+ int sval2;
+ int nframes;
+ unsigned char *v110buf;
+ unsigned char *rbuf;
+ struct sk_buff *nskb;
+
+ if (v == NULL) {
+ /* invalid handle, no chance to proceed */
+ printk(KERN_WARNING "isdn_v110_encode called with NULL stream!\n");
+ return NULL;
+ }
+ if (!skb) {
+ /* invalid skb, no chance to proceed */
+ printk(KERN_WARNING "isdn_v110_encode called with NULL skb!\n");
+ return NULL;
+ }
+ rlen = skb->len;
+ nframes = (rlen + 3) / 4;
+ v110buf = v->encodebuf;
+ if ((nframes * 40) > v->maxsize) {
+ size = v->maxsize;
+ rlen = v->maxsize / 40;
+ } else
+ size = nframes * 40;
+ if (!(nskb = dev_alloc_skb(size + v->skbres + sizeof(int)))) {
+ printk(KERN_WARNING "isdn_v110_encode: Couldn't alloc skb\n");
+ return NULL;
+ }
+ skb_reserve(nskb, v->skbres + sizeof(int));
+ if (skb->len == 0) {
+ memcpy(skb_put(nskb, v->framelen), v->OnlineFrame, v->framelen);
+ *((int *)skb_push(nskb, sizeof(int))) = 0;
+ return nskb;
+ }
+ mlen = EncodeMatrix(skb->data, rlen, v110buf, size);
+ /* now distribute 2 or 4 bits each to the output stream! */
+ rbuf = skb_put(nskb, size);
+ olen = 0;
+ sval1 = 8 - v->nbits;
+ sval2 = v->key << sval1;
+ for (i = 0; i < mlen; i++) {
+ v110buf[i] = FlipBits(v110buf[i], v->nbits);
+ for (j = 0; j < v->nbytes; j++) {
+ if (size--)
+ *rbuf++ = ~v->key | (((v110buf[i] << (j * v->nbits)) & sval2) >> sval1);
+ else {
+ printk(KERN_WARNING "isdn_v110_encode: buffers full!\n");
+ goto buffer_full;
+ }
+ olen++;
+ }
+ }
+buffer_full:
+ skb_trim(nskb, olen);
+ *((int *)skb_push(nskb, sizeof(int))) = rlen;
+ return nskb;
+}
+
+int
+isdn_v110_stat_callback(int idx, isdn_ctrl *c)
+{
+ isdn_v110_stream *v = NULL;
+ int i;
+ int ret = 0;
+
+ if (idx < 0)
+ return 0;
+ switch (c->command) {
+ case ISDN_STAT_BSENT:
+ /* Keep the send-queue of the driver filled
+ * with frames:
+ * If number of outstanding frames < 3,
+ * send down an Idle-Frame (or an Sync-Frame, if
+ * v->SyncInit != 0).
+ */
+ if (!(v = dev->v110[idx]))
+ return 0;
+ atomic_inc(&dev->v110use[idx]);
+ for (i=0; i * v->framelen < c->parm.length; i++) {
+ if (v->skbidle > 0) {
+ v->skbidle--;
+ ret = 1;
+ } else {
+ if (v->skbuser > 0)
+ v->skbuser--;
+ ret = 0;
+ }
+ }
+ for (i = v->skbuser + v->skbidle; i < 2; i++) {
+ struct sk_buff *skb;
+ if (v->SyncInit > 0)
+ skb = isdn_v110_sync(v);
+ else
+ skb = isdn_v110_idle(v);
+ if (skb) {
+ if (dev->drv[c->driver]->interface->writebuf_skb(c->driver, c->arg, 1, skb) <= 0) {
+ dev_kfree_skb(skb);
+ break;
+ } else {
+ if (v->SyncInit)
+ v->SyncInit--;
+ v->skbidle++;
+ }
+ } else
+ break;
+ }
+ atomic_dec(&dev->v110use[idx]);
+ return ret;
+ case ISDN_STAT_DHUP:
+ case ISDN_STAT_BHUP:
+ while (1) {
+ atomic_inc(&dev->v110use[idx]);
+ if (atomic_dec_and_test(&dev->v110use[idx])) {
+ isdn_v110_close(dev->v110[idx]);
+ dev->v110[idx] = NULL;
+ break;
+ }
+ mdelay(1);
+ }
+ break;
+ case ISDN_STAT_BCONN:
+ if (dev->v110emu[idx] && (dev->v110[idx] == NULL)) {
+ int hdrlen = dev->drv[c->driver]->interface->hl_hdrlen;
+ int maxsize = dev->drv[c->driver]->interface->maxbufsize;
+ atomic_inc(&dev->v110use[idx]);
+ switch (dev->v110emu[idx]) {
+ case ISDN_PROTO_L2_V11096:
+ dev->v110[idx] = isdn_v110_open(V110_9600, hdrlen, maxsize);
+ break;
+ case ISDN_PROTO_L2_V11019:
+ dev->v110[idx] = isdn_v110_open(V110_19200, hdrlen, maxsize);
+ break;
+ case ISDN_PROTO_L2_V11038:
+ dev->v110[idx] = isdn_v110_open(V110_38400, hdrlen, maxsize);
+ break;
+ default:;
+ }
+ if ((v = dev->v110[idx])) {
+ while (v->SyncInit) {
+ struct sk_buff *skb = isdn_v110_sync(v);
+ if (dev->drv[c->driver]->interface->writebuf_skb(c->driver, c->arg, 1, skb) <= 0) {
+ dev_kfree_skb(skb);
+ /* Unable to send, try later */
+ break;
+ }
+ v->SyncInit--;
+ v->skbidle++;
+ }
+ } else
+ printk(KERN_WARNING "isdn_v110: Couldn't open stream for chan %d\n", idx);
+ atomic_dec(&dev->v110use[idx]);
+ }
+ break;
+ default:
+ return 0;
+ }
+ return 0;
+}
diff --git a/drivers/isdn/i4l/isdn_v110.h b/drivers/isdn/i4l/isdn_v110.h
new file mode 100644
index 0000000..08f274b
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_v110.h
@@ -0,0 +1,29 @@
+/* $Id: isdn_v110.h,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $
+ *
+ * Linux ISDN subsystem, V.110 related functions (linklevel).
+ *
+ * Copyright by Thomas Pfeiffer (pfeiffer@pds.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef _isdn_v110_h_
+#define _isdn_v110_h_
+
+/*
+ * isdn_v110_encode will take raw data and encode it using V.110
+ */
+extern struct sk_buff *isdn_v110_encode(isdn_v110_stream *, struct sk_buff *);
+
+/*
+ * isdn_v110_decode receives V.110 coded data from the stream and rebuilds
+ * frames from them. The source stream doesn't need to be framed.
+ */
+extern struct sk_buff *isdn_v110_decode(isdn_v110_stream *, struct sk_buff *);
+
+extern int isdn_v110_stat_callback(int, isdn_ctrl *);
+extern void isdn_v110_close(isdn_v110_stream * v);
+
+#endif
diff --git a/drivers/isdn/i4l/isdn_x25iface.c b/drivers/isdn/i4l/isdn_x25iface.c
new file mode 100644
index 0000000..8b3efc2
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_x25iface.c
@@ -0,0 +1,330 @@
+/* $Id: isdn_x25iface.c,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $
+ *
+ * Linux ISDN subsystem, X.25 related functions
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * stuff needed to support the Linux X.25 PLP code on top of devices that
+ * can provide a lab_b service using the concap_proto mechanism.
+ * This module supports a network interface which provides lapb_sematics
+ * -- as defined in Documentation/networking/x25-iface.txt -- to
+ * the upper layer and assumes that the lower layer provides a reliable
+ * data link service by means of the concap_device_ops callbacks.
+ *
+ * Only protocol specific stuff goes here. Device specific stuff
+ * goes to another -- device related -- concap_proto support source file.
+ *
+ */
+
+/* #include <linux/isdn.h> */
+#include <linux/netdevice.h>
+#include <linux/concap.h>
+#include <linux/wanrouter.h>
+#include <net/x25device.h>
+#include "isdn_x25iface.h"
+
+/* for debugging messages not to cause an oops when device pointer is NULL*/
+#define MY_DEVNAME(dev) ( (dev) ? (dev)->name : "DEVICE UNSPECIFIED" )
+
+
+typedef struct isdn_x25iface_proto_data {
+ int magic;
+ enum wan_states state;
+ /* Private stuff, not to be accessed via proto_data. We provide the
+ other storage for the concap_proto instance here as well,
+ enabling us to allocate both with just one kmalloc(): */
+ struct concap_proto priv;
+} ix25_pdata_t;
+
+
+
+/* is now in header file (extern): struct concap_proto * isdn_x25iface_proto_new(void); */
+static void isdn_x25iface_proto_del( struct concap_proto * );
+static int isdn_x25iface_proto_close( struct concap_proto * );
+static int isdn_x25iface_proto_restart( struct concap_proto *,
+ struct net_device *,
+ struct concap_device_ops *);
+static int isdn_x25iface_xmit( struct concap_proto *, struct sk_buff * );
+static int isdn_x25iface_receive( struct concap_proto *, struct sk_buff * );
+static int isdn_x25iface_connect_ind( struct concap_proto * );
+static int isdn_x25iface_disconn_ind( struct concap_proto * );
+
+
+static struct concap_proto_ops ix25_pops = {
+ &isdn_x25iface_proto_new,
+ &isdn_x25iface_proto_del,
+ &isdn_x25iface_proto_restart,
+ &isdn_x25iface_proto_close,
+ &isdn_x25iface_xmit,
+ &isdn_x25iface_receive,
+ &isdn_x25iface_connect_ind,
+ &isdn_x25iface_disconn_ind
+};
+
+/* error message helper function */
+static void illegal_state_warn( unsigned state, unsigned char firstbyte)
+{
+ printk( KERN_WARNING "isdn_x25iface: firstbyte %x illegal in"
+ "current state %d\n",firstbyte, state );
+}
+
+/* check protocol data field for consistency */
+static int pdata_is_bad( ix25_pdata_t * pda ){
+
+ if( pda && pda -> magic == ISDN_X25IFACE_MAGIC ) return 0;
+ printk( KERN_WARNING
+ "isdn_x25iface_xxx: illegal pointer to proto data\n" );
+ return 1;
+}
+
+/* create a new x25 interface protocol instance
+ */
+struct concap_proto * isdn_x25iface_proto_new(void)
+{
+ ix25_pdata_t * tmp = kmalloc(sizeof(ix25_pdata_t),GFP_KERNEL);
+ IX25DEBUG("isdn_x25iface_proto_new\n");
+ if( tmp ){
+ tmp -> magic = ISDN_X25IFACE_MAGIC;
+ tmp -> state = WAN_UNCONFIGURED;
+ /* private data space used to hold the concap_proto data.
+ Only to be accessed via the returned pointer */
+ spin_lock_init(&tmp->priv.lock);
+ tmp -> priv.dops = NULL;
+ tmp -> priv.net_dev = NULL;
+ tmp -> priv.pops = &ix25_pops;
+ tmp -> priv.flags = 0;
+ tmp -> priv.proto_data = tmp;
+ return( &(tmp -> priv) );
+ }
+ return NULL;
+};
+
+/* close the x25iface encapsulation protocol
+ */
+static int isdn_x25iface_proto_close(struct concap_proto *cprot){
+
+ ix25_pdata_t *tmp;
+ int ret = 0;
+ ulong flags;
+
+ if( ! cprot ){
+ printk( KERN_ERR "isdn_x25iface_proto_close: "
+ "invalid concap_proto pointer\n" );
+ return -1;
+ }
+ IX25DEBUG( "isdn_x25iface_proto_close %s \n", MY_DEVNAME(cprot -> net_dev) );
+ spin_lock_irqsave(&cprot->lock, flags);
+ cprot -> dops = NULL;
+ cprot -> net_dev = NULL;
+ tmp = cprot -> proto_data;
+ if( pdata_is_bad( tmp ) ){
+ ret = -1;
+ } else {
+ tmp -> state = WAN_UNCONFIGURED;
+ }
+ spin_unlock_irqrestore(&cprot->lock, flags);
+ return ret;
+}
+
+/* Delete the x25iface encapsulation protocol instance
+ */
+static void isdn_x25iface_proto_del(struct concap_proto *cprot){
+
+ ix25_pdata_t * tmp;
+
+ IX25DEBUG( "isdn_x25iface_proto_del \n" );
+ if( ! cprot ){
+ printk( KERN_ERR "isdn_x25iface_proto_del: "
+ "concap_proto pointer is NULL\n" );
+ return;
+ }
+ tmp = cprot -> proto_data;
+ if( tmp == NULL ){
+ printk( KERN_ERR "isdn_x25iface_proto_del: inconsistent "
+ "proto_data pointer (maybe already deleted?)\n");
+ return;
+ }
+ /* close if the protocol is still open */
+ if( cprot -> dops ) isdn_x25iface_proto_close(cprot);
+ /* freeing the storage should be sufficient now. But some additional
+ settings might help to catch wild pointer bugs */
+ tmp -> magic = 0;
+ cprot -> proto_data = NULL;
+
+ kfree( tmp );
+ return;
+}
+
+/* (re-)initialize the data structures for x25iface encapsulation
+ */
+static int isdn_x25iface_proto_restart(struct concap_proto *cprot,
+ struct net_device *ndev,
+ struct concap_device_ops *dops)
+{
+ ix25_pdata_t * pda = cprot -> proto_data ;
+ ulong flags;
+
+ IX25DEBUG( "isdn_x25iface_proto_restart %s \n", MY_DEVNAME(ndev) );
+
+ if ( pdata_is_bad( pda ) ) return -1;
+
+ if( !( dops && dops -> data_req && dops -> connect_req
+ && dops -> disconn_req ) ){
+ printk( KERN_WARNING "isdn_x25iface_restart: required dops"
+ " missing\n" );
+ isdn_x25iface_proto_close(cprot);
+ return -1;
+ }
+ spin_lock_irqsave(&cprot->lock, flags);
+ cprot -> net_dev = ndev;
+ cprot -> pops = &ix25_pops;
+ cprot -> dops = dops;
+ pda -> state = WAN_DISCONNECTED;
+ spin_unlock_irqrestore(&cprot->lock, flags);
+ return 0;
+}
+
+/* deliver a dl_data frame received from i4l HL driver to the network layer
+ */
+static int isdn_x25iface_receive(struct concap_proto *cprot, struct sk_buff *skb)
+{
+ IX25DEBUG( "isdn_x25iface_receive %s \n", MY_DEVNAME(cprot->net_dev) );
+ if ( ( (ix25_pdata_t*) (cprot->proto_data) )
+ -> state == WAN_CONNECTED ){
+ if( skb_push(skb, 1)){
+ skb -> data[0]=0x00;
+ skb->protocol = x25_type_trans(skb, cprot->net_dev);
+ netif_rx(skb);
+ return 0;
+ }
+ }
+ printk(KERN_WARNING "isdn_x25iface_receive %s: not connected, skb dropped\n", MY_DEVNAME(cprot->net_dev) );
+ dev_kfree_skb(skb);
+ return -1;
+}
+
+/* a connection set up is indicated by lower layer
+ */
+static int isdn_x25iface_connect_ind(struct concap_proto *cprot)
+{
+ struct sk_buff * skb;
+ enum wan_states *state_p
+ = &( ( (ix25_pdata_t*) (cprot->proto_data) ) -> state);
+ IX25DEBUG( "isdn_x25iface_connect_ind %s \n"
+ , MY_DEVNAME(cprot->net_dev) );
+ if( *state_p == WAN_UNCONFIGURED ){
+ printk(KERN_WARNING
+ "isdn_x25iface_connect_ind while unconfigured %s\n"
+ , MY_DEVNAME(cprot->net_dev) );
+ return -1;
+ }
+ *state_p = WAN_CONNECTED;
+
+ skb = dev_alloc_skb(1);
+ if( skb ){
+ *( skb_put(skb, 1) ) = 0x01;
+ skb->protocol = x25_type_trans(skb, cprot->net_dev);
+ netif_rx(skb);
+ return 0;
+ } else {
+ printk(KERN_WARNING "isdn_x25iface_connect_ind: "
+ " out of memory -- disconnecting\n");
+ cprot -> dops -> disconn_req(cprot);
+ return -1;
+ }
+}
+
+/* a disconnect is indicated by lower layer
+ */
+static int isdn_x25iface_disconn_ind(struct concap_proto *cprot)
+{
+ struct sk_buff *skb;
+ enum wan_states *state_p
+ = &( ( (ix25_pdata_t*) (cprot->proto_data) ) -> state);
+ IX25DEBUG( "isdn_x25iface_disconn_ind %s \n", MY_DEVNAME(cprot -> net_dev) );
+ if( *state_p == WAN_UNCONFIGURED ){
+ printk(KERN_WARNING
+ "isdn_x25iface_disconn_ind while unconfigured\n");
+ return -1;
+ }
+ if(! cprot -> net_dev) return -1;
+ *state_p = WAN_DISCONNECTED;
+ skb = dev_alloc_skb(1);
+ if( skb ){
+ *( skb_put(skb, 1) ) = 0x02;
+ skb->protocol = x25_type_trans(skb, cprot->net_dev);
+ netif_rx(skb);
+ return 0;
+ } else {
+ printk(KERN_WARNING "isdn_x25iface_disconn_ind:"
+ " out of memory\n");
+ return -1;
+ }
+}
+
+/* process a frame handed over to us from linux network layer. First byte
+ semantics as defined in Documentation/networking/x25-iface.txt
+ */
+static int isdn_x25iface_xmit(struct concap_proto *cprot, struct sk_buff *skb)
+{
+ unsigned char firstbyte = skb->data[0];
+ enum wan_states *state = &((ix25_pdata_t*)cprot->proto_data)->state;
+ int ret = 0;
+ IX25DEBUG( "isdn_x25iface_xmit: %s first=%x state=%d \n", MY_DEVNAME(cprot -> net_dev), firstbyte, *state );
+ switch ( firstbyte ){
+ case 0x00: /* dl_data request */
+ if( *state == WAN_CONNECTED ){
+ skb_pull(skb, 1);
+ cprot -> net_dev -> trans_start = jiffies;
+ ret = ( cprot -> dops -> data_req(cprot, skb) );
+ /* prepare for future retransmissions */
+ if( ret ) skb_push(skb,1);
+ return ret;
+ }
+ illegal_state_warn( *state, firstbyte );
+ break;
+ case 0x01: /* dl_connect request */
+ if( *state == WAN_DISCONNECTED ){
+ *state = WAN_CONNECTING;
+ ret = cprot -> dops -> connect_req(cprot);
+ if(ret){
+ /* reset state and notify upper layer about
+ * immidiatly failed attempts */
+ isdn_x25iface_disconn_ind(cprot);
+ }
+ } else {
+ illegal_state_warn( *state, firstbyte );
+ }
+ break;
+ case 0x02: /* dl_disconnect request */
+ switch ( *state ){
+ case WAN_DISCONNECTED:
+ /* Should not happen. However, give upper layer a
+ chance to recover from inconstistency but don't
+ trust the lower layer sending the disconn_confirm
+ when already disconnected */
+ printk(KERN_WARNING "isdn_x25iface_xmit: disconnect "
+ " requested while disconnected\n" );
+ isdn_x25iface_disconn_ind(cprot);
+ break; /* prevent infinite loops */
+ case WAN_CONNECTING:
+ case WAN_CONNECTED:
+ *state = WAN_DISCONNECTED;
+ cprot -> dops -> disconn_req(cprot);
+ break;
+ default:
+ illegal_state_warn( *state, firstbyte );
+ }
+ break;
+ case 0x03: /* changing lapb parameters requested */
+ printk(KERN_WARNING "isdn_x25iface_xmit: setting of lapb"
+ " options not yet supported\n");
+ break;
+ default:
+ printk(KERN_WARNING "isdn_x25iface_xmit: frame with illegal"
+ " first byte %x ignored:\n", firstbyte);
+ }
+ dev_kfree_skb(skb);
+ return 0;
+}
diff --git a/drivers/isdn/i4l/isdn_x25iface.h b/drivers/isdn/i4l/isdn_x25iface.h
new file mode 100644
index 0000000..41a3d49
--- /dev/null
+++ b/drivers/isdn/i4l/isdn_x25iface.h
@@ -0,0 +1,39 @@
+/* $Id: isdn_x25iface.h,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $
+ *
+ * header for Linux ISDN subsystem, x.25 related functions
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef _LINUX_ISDN_X25IFACE_H
+#define _LINUX_ISDN_X25IFACE_H
+
+#define ISDN_X25IFACE_MAGIC 0x1e75a2b9
+/* #define DEBUG_ISDN_X25 if you want isdn_x25 debugging messages */
+#ifdef DEBUG_ISDN_X25
+# define IX25DEBUG(fmt,args...) printk(KERN_DEBUG fmt , ## args)
+#else
+# define IX25DEBUG(fmt,args...)
+#endif
+
+#include <linux/skbuff.h>
+#include <linux/wanrouter.h>
+#include <linux/isdn.h>
+#include <linux/concap.h>
+
+extern struct concap_proto_ops * isdn_x25iface_concap_proto_ops_pt;
+extern struct concap_proto * isdn_x25iface_proto_new(void);
+
+
+
+#endif
+
+
+
+
+
+
+
+
diff --git a/drivers/isdn/icn/Kconfig b/drivers/isdn/icn/Kconfig
new file mode 100644
index 0000000..89d15ee
--- /dev/null
+++ b/drivers/isdn/icn/Kconfig
@@ -0,0 +1,16 @@
+#
+# Config.in for ICN ISDN driver
+#
+config ISDN_DRV_ICN
+ tristate "ICN 2B and 4B support"
+ depends on ISA
+ help
+ This enables support for two kinds of ISDN-cards made by a German
+ company called ICN. 2B is the standard version for a single ISDN
+ line with two B-channels, 4B supports two ISDN lines. For running
+ this card, additional firmware is necessary, which has to be
+ downloaded into the card using a utility which is distributed
+ separately. See <file:Documentation/isdn/README> and
+ <file:Documentation/isdn/README.icn> for more
+ information.
+
diff --git a/drivers/isdn/icn/Makefile b/drivers/isdn/icn/Makefile
new file mode 100644
index 0000000..d9b476f
--- /dev/null
+++ b/drivers/isdn/icn/Makefile
@@ -0,0 +1,5 @@
+# Makefile for the icn ISDN device driver
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_ISDN_DRV_ICN) += icn.o
diff --git a/drivers/isdn/icn/icn.c b/drivers/isdn/icn/icn.c
new file mode 100644
index 0000000..bf7997a
--- /dev/null
+++ b/drivers/isdn/icn/icn.c
@@ -0,0 +1,1692 @@
+/* $Id: icn.c,v 1.65.6.8 2001/09/23 22:24:55 kai Exp $
+ *
+ * ISDN low-level module for the ICN active ISDN-Card.
+ *
+ * Copyright 1994,95,96 by Fritz Elfert (fritz@isdn4linux.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include "icn.h"
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+
+static int portbase = ICN_BASEADDR;
+static unsigned long membase = ICN_MEMADDR;
+static char *icn_id = "\0";
+static char *icn_id2 = "\0";
+
+MODULE_DESCRIPTION("ISDN4Linux: Driver for ICN active ISDN card");
+MODULE_AUTHOR("Fritz Elfert");
+MODULE_LICENSE("GPL");
+module_param(portbase, int, 0);
+MODULE_PARM_DESC(portbase, "Port address of first card");
+module_param(membase, ulong, 0);
+MODULE_PARM_DESC(membase, "Shared memory address of all cards");
+module_param(icn_id, charp, 0);
+MODULE_PARM_DESC(icn_id, "ID-String of first card");
+module_param(icn_id2, charp, 0);
+MODULE_PARM_DESC(icn_id2, "ID-String of first card, second S0 (4B only)");
+
+/*
+ * Verbose bootcode- and protocol-downloading.
+ */
+#undef BOOT_DEBUG
+
+/*
+ * Verbose Shmem-Mapping.
+ */
+#undef MAP_DEBUG
+
+static char
+*revision = "$Revision: 1.65.6.8 $";
+
+static int icn_addcard(int, char *, char *);
+
+/*
+ * Free send-queue completely.
+ * Parameter:
+ * card = pointer to card struct
+ * channel = channel number
+ */
+static void
+icn_free_queue(icn_card * card, int channel)
+{
+ struct sk_buff_head *queue = &card->spqueue[channel];
+ struct sk_buff *skb;
+
+ skb_queue_purge(queue);
+ card->xlen[channel] = 0;
+ card->sndcount[channel] = 0;
+ if ((skb = card->xskb[channel])) {
+ card->xskb[channel] = NULL;
+ dev_kfree_skb(skb);
+ }
+}
+
+/* Put a value into a shift-register, highest bit first.
+ * Parameters:
+ * port = port for output (bit 0 is significant)
+ * val = value to be output
+ * firstbit = Bit-Number of highest bit
+ * bitcount = Number of bits to output
+ */
+static inline void
+icn_shiftout(unsigned short port,
+ unsigned long val,
+ int firstbit,
+ int bitcount)
+{
+
+ register u_char s;
+ register u_char c;
+
+ for (s = firstbit, c = bitcount; c > 0; s--, c--)
+ OUTB_P((u_char) ((val >> s) & 1) ? 0xff : 0, port);
+}
+
+/*
+ * disable a cards shared memory
+ */
+static inline void
+icn_disable_ram(icn_card * card)
+{
+ OUTB_P(0, ICN_MAPRAM);
+}
+
+/*
+ * enable a cards shared memory
+ */
+static inline void
+icn_enable_ram(icn_card * card)
+{
+ OUTB_P(0xff, ICN_MAPRAM);
+}
+
+/*
+ * Map a cards channel0 (Bank0/Bank8) or channel1 (Bank4/Bank12)
+ *
+ * must called with holding the devlock
+ */
+static inline void
+icn_map_channel(icn_card * card, int channel)
+{
+#ifdef MAP_DEBUG
+ printk(KERN_DEBUG "icn_map_channel %d %d\n", dev.channel, channel);
+#endif
+ if ((channel == dev.channel) && (card == dev.mcard))
+ return;
+ if (dev.mcard)
+ icn_disable_ram(dev.mcard);
+ icn_shiftout(ICN_BANK, chan2bank[channel], 3, 4); /* Select Bank */
+ icn_enable_ram(card);
+ dev.mcard = card;
+ dev.channel = channel;
+#ifdef MAP_DEBUG
+ printk(KERN_DEBUG "icn_map_channel done\n");
+#endif
+}
+
+/*
+ * Lock a cards channel.
+ * Return 0 if requested card/channel is unmapped (failure).
+ * Return 1 on success.
+ *
+ * must called with holding the devlock
+ */
+static inline int
+icn_lock_channel(icn_card * card, int channel)
+{
+ register int retval;
+
+#ifdef MAP_DEBUG
+ printk(KERN_DEBUG "icn_lock_channel %d\n", channel);
+#endif
+ if ((dev.channel == channel) && (card == dev.mcard)) {
+ dev.chanlock++;
+ retval = 1;
+#ifdef MAP_DEBUG
+ printk(KERN_DEBUG "icn_lock_channel %d OK\n", channel);
+#endif
+ } else {
+ retval = 0;
+#ifdef MAP_DEBUG
+ printk(KERN_DEBUG "icn_lock_channel %d FAILED, dc=%d\n", channel, dev.channel);
+#endif
+ }
+ return retval;
+}
+
+/*
+ * Release current card/channel lock
+ *
+ * must called with holding the devlock
+ */
+static inline void
+__icn_release_channel(void)
+{
+#ifdef MAP_DEBUG
+ printk(KERN_DEBUG "icn_release_channel l=%d\n", dev.chanlock);
+#endif
+ if (dev.chanlock > 0)
+ dev.chanlock--;
+}
+
+/*
+ * Release current card/channel lock
+ */
+static inline void
+icn_release_channel(void)
+{
+ ulong flags;
+
+ spin_lock_irqsave(&dev.devlock, flags);
+ __icn_release_channel();
+ spin_unlock_irqrestore(&dev.devlock, flags);
+}
+
+/*
+ * Try to map and lock a cards channel.
+ * Return 1 on success, 0 on failure.
+ */
+static inline int
+icn_trymaplock_channel(icn_card * card, int channel)
+{
+ ulong flags;
+
+#ifdef MAP_DEBUG
+ printk(KERN_DEBUG "trymaplock c=%d dc=%d l=%d\n", channel, dev.channel,
+ dev.chanlock);
+#endif
+ spin_lock_irqsave(&dev.devlock, flags);
+ if ((!dev.chanlock) ||
+ ((dev.channel == channel) && (dev.mcard == card))) {
+ dev.chanlock++;
+ icn_map_channel(card, channel);
+ spin_unlock_irqrestore(&dev.devlock, flags);
+#ifdef MAP_DEBUG
+ printk(KERN_DEBUG "trymaplock %d OK\n", channel);
+#endif
+ return 1;
+ }
+ spin_unlock_irqrestore(&dev.devlock, flags);
+#ifdef MAP_DEBUG
+ printk(KERN_DEBUG "trymaplock %d FAILED\n", channel);
+#endif
+ return 0;
+}
+
+/*
+ * Release current card/channel lock,
+ * then map same or other channel without locking.
+ */
+static inline void
+icn_maprelease_channel(icn_card * card, int channel)
+{
+ ulong flags;
+
+#ifdef MAP_DEBUG
+ printk(KERN_DEBUG "map_release c=%d l=%d\n", channel, dev.chanlock);
+#endif
+ spin_lock_irqsave(&dev.devlock, flags);
+ if (dev.chanlock > 0)
+ dev.chanlock--;
+ if (!dev.chanlock)
+ icn_map_channel(card, channel);
+ spin_unlock_irqrestore(&dev.devlock, flags);
+}
+
+/* Get Data from the B-Channel, assemble fragmented packets and put them
+ * into receive-queue. Wake up any B-Channel-reading processes.
+ * This routine is called via timer-callback from icn_pollbchan().
+ */
+
+static void
+icn_pollbchan_receive(int channel, icn_card * card)
+{
+ int mch = channel + ((card->secondhalf) ? 2 : 0);
+ int eflag;
+ int cnt;
+ struct sk_buff *skb;
+
+ if (icn_trymaplock_channel(card, mch)) {
+ while (rbavl) {
+ cnt = readb(&rbuf_l);
+ if ((card->rcvidx[channel] + cnt) > 4000) {
+ printk(KERN_WARNING
+ "icn: (%s) bogus packet on ch%d, dropping.\n",
+ CID,
+ channel + 1);
+ card->rcvidx[channel] = 0;
+ eflag = 0;
+ } else {
+ memcpy_fromio(&card->rcvbuf[channel][card->rcvidx[channel]],
+ &rbuf_d, cnt);
+ card->rcvidx[channel] += cnt;
+ eflag = readb(&rbuf_f);
+ }
+ rbnext;
+ icn_maprelease_channel(card, mch & 2);
+ if (!eflag) {
+ if ((cnt = card->rcvidx[channel])) {
+ if (!(skb = dev_alloc_skb(cnt))) {
+ printk(KERN_WARNING "icn: receive out of memory\n");
+ break;
+ }
+ memcpy(skb_put(skb, cnt), card->rcvbuf[channel], cnt);
+ card->rcvidx[channel] = 0;
+ card->interface.rcvcallb_skb(card->myid, channel, skb);
+ }
+ }
+ if (!icn_trymaplock_channel(card, mch))
+ break;
+ }
+ icn_maprelease_channel(card, mch & 2);
+ }
+}
+
+/* Send data-packet to B-Channel, split it up into fragments of
+ * ICN_FRAGSIZE length. If last fragment is sent out, signal
+ * success to upper layers via statcallb with ISDN_STAT_BSENT argument.
+ * This routine is called via timer-callback from icn_pollbchan() or
+ * directly from icn_sendbuf().
+ */
+
+static void
+icn_pollbchan_send(int channel, icn_card * card)
+{
+ int mch = channel + ((card->secondhalf) ? 2 : 0);
+ int cnt;
+ unsigned long flags;
+ struct sk_buff *skb;
+ isdn_ctrl cmd;
+
+ if (!(card->sndcount[channel] || card->xskb[channel] ||
+ !skb_queue_empty(&card->spqueue[channel])))
+ return;
+ if (icn_trymaplock_channel(card, mch)) {
+ while (sbfree &&
+ (card->sndcount[channel] ||
+ !skb_queue_empty(&card->spqueue[channel]) ||
+ card->xskb[channel])) {
+ spin_lock_irqsave(&card->lock, flags);
+ if (card->xmit_lock[channel]) {
+ spin_unlock_irqrestore(&card->lock, flags);
+ break;
+ }
+ card->xmit_lock[channel]++;
+ spin_unlock_irqrestore(&card->lock, flags);
+ skb = card->xskb[channel];
+ if (!skb) {
+ skb = skb_dequeue(&card->spqueue[channel]);
+ if (skb) {
+ /* Pop ACK-flag off skb.
+ * Store length to xlen.
+ */
+ if (*(skb_pull(skb,1)))
+ card->xlen[channel] = skb->len;
+ else
+ card->xlen[channel] = 0;
+ }
+ }
+ if (!skb)
+ break;
+ if (skb->len > ICN_FRAGSIZE) {
+ writeb(0xff, &sbuf_f);
+ cnt = ICN_FRAGSIZE;
+ } else {
+ writeb(0x0, &sbuf_f);
+ cnt = skb->len;
+ }
+ writeb(cnt, &sbuf_l);
+ memcpy_toio(&sbuf_d, skb->data, cnt);
+ skb_pull(skb, cnt);
+ sbnext; /* switch to next buffer */
+ icn_maprelease_channel(card, mch & 2);
+ spin_lock_irqsave(&card->lock, flags);
+ card->sndcount[channel] -= cnt;
+ if (!skb->len) {
+ if (card->xskb[channel])
+ card->xskb[channel] = NULL;
+ card->xmit_lock[channel] = 0;
+ spin_unlock_irqrestore(&card->lock, flags);
+ dev_kfree_skb(skb);
+ if (card->xlen[channel]) {
+ cmd.command = ISDN_STAT_BSENT;
+ cmd.driver = card->myid;
+ cmd.arg = channel;
+ cmd.parm.length = card->xlen[channel];
+ card->interface.statcallb(&cmd);
+ }
+ } else {
+ card->xskb[channel] = skb;
+ card->xmit_lock[channel] = 0;
+ spin_unlock_irqrestore(&card->lock, flags);
+ }
+ if (!icn_trymaplock_channel(card, mch))
+ break;
+ }
+ icn_maprelease_channel(card, mch & 2);
+ }
+}
+
+/* Send/Receive Data to/from the B-Channel.
+ * This routine is called via timer-callback.
+ * It schedules itself while any B-Channel is open.
+ */
+
+static void
+icn_pollbchan(unsigned long data)
+{
+ icn_card *card = (icn_card *) data;
+ unsigned long flags;
+
+ if (card->flags & ICN_FLAGS_B1ACTIVE) {
+ icn_pollbchan_receive(0, card);
+ icn_pollbchan_send(0, card);
+ }
+ if (card->flags & ICN_FLAGS_B2ACTIVE) {
+ icn_pollbchan_receive(1, card);
+ icn_pollbchan_send(1, card);
+ }
+ if (card->flags & (ICN_FLAGS_B1ACTIVE | ICN_FLAGS_B2ACTIVE)) {
+ /* schedule b-channel polling again */
+ spin_lock_irqsave(&card->lock, flags);
+ mod_timer(&card->rb_timer, jiffies+ICN_TIMER_BCREAD);
+ card->flags |= ICN_FLAGS_RBTIMER;
+ spin_unlock_irqrestore(&card->lock, flags);
+ } else
+ card->flags &= ~ICN_FLAGS_RBTIMER;
+}
+
+typedef struct icn_stat {
+ char *statstr;
+ int command;
+ int action;
+} icn_stat;
+/* *INDENT-OFF* */
+static icn_stat icn_stat_table[] =
+{
+ {"BCON_", ISDN_STAT_BCONN, 1}, /* B-Channel connected */
+ {"BDIS_", ISDN_STAT_BHUP, 2}, /* B-Channel disconnected */
+ /*
+ ** add d-channel connect and disconnect support to link-level
+ */
+ {"DCON_", ISDN_STAT_DCONN, 10}, /* D-Channel connected */
+ {"DDIS_", ISDN_STAT_DHUP, 11}, /* D-Channel disconnected */
+ {"DCAL_I", ISDN_STAT_ICALL, 3}, /* Incoming call dialup-line */
+ {"DSCA_I", ISDN_STAT_ICALL, 3}, /* Incoming call 1TR6-SPV */
+ {"FCALL", ISDN_STAT_ICALL, 4}, /* Leased line connection up */
+ {"CIF", ISDN_STAT_CINF, 5}, /* Charge-info, 1TR6-type */
+ {"AOC", ISDN_STAT_CINF, 6}, /* Charge-info, DSS1-type */
+ {"CAU", ISDN_STAT_CAUSE, 7}, /* Cause code */
+ {"TEI OK", ISDN_STAT_RUN, 0}, /* Card connected to wallplug */
+ {"E_L1: ACT FAIL", ISDN_STAT_BHUP, 8}, /* Layer-1 activation failed */
+ {"E_L2: DATA LIN", ISDN_STAT_BHUP, 8}, /* Layer-2 data link lost */
+ {"E_L1: ACTIVATION FAILED",
+ ISDN_STAT_BHUP, 8}, /* Layer-1 activation failed */
+ {NULL, 0, -1}
+};
+/* *INDENT-ON* */
+
+
+/*
+ * Check Statusqueue-Pointer from isdn-cards.
+ * If there are new status-replies from the interface, check
+ * them against B-Channel-connects/disconnects and set flags accordingly.
+ * Wake-Up any processes, who are reading the status-device.
+ * If there are B-Channels open, initiate a timer-callback to
+ * icn_pollbchan().
+ * This routine is called periodically via timer.
+ */
+
+static void
+icn_parse_status(u_char * status, int channel, icn_card * card)
+{
+ icn_stat *s = icn_stat_table;
+ int action = -1;
+ unsigned long flags;
+ isdn_ctrl cmd;
+
+ while (s->statstr) {
+ if (!strncmp(status, s->statstr, strlen(s->statstr))) {
+ cmd.command = s->command;
+ action = s->action;
+ break;
+ }
+ s++;
+ }
+ if (action == -1)
+ return;
+ cmd.driver = card->myid;
+ cmd.arg = channel;
+ switch (action) {
+ case 11:
+ spin_lock_irqsave(&card->lock, flags);
+ icn_free_queue(card,channel);
+ card->rcvidx[channel] = 0;
+
+ if (card->flags &
+ ((channel)?ICN_FLAGS_B2ACTIVE:ICN_FLAGS_B1ACTIVE)) {
+
+ isdn_ctrl ncmd;
+
+ card->flags &= ~((channel)?
+ ICN_FLAGS_B2ACTIVE:ICN_FLAGS_B1ACTIVE);
+
+ memset(&ncmd, 0, sizeof(ncmd));
+
+ ncmd.driver = card->myid;
+ ncmd.arg = channel;
+ ncmd.command = ISDN_STAT_BHUP;
+ spin_unlock_irqrestore(&card->lock, flags);
+ card->interface.statcallb(&cmd);
+ } else
+ spin_unlock_irqrestore(&card->lock, flags);
+ break;
+ case 1:
+ spin_lock_irqsave(&card->lock, flags);
+ icn_free_queue(card,channel);
+ card->flags |= (channel) ?
+ ICN_FLAGS_B2ACTIVE : ICN_FLAGS_B1ACTIVE;
+ spin_unlock_irqrestore(&card->lock, flags);
+ break;
+ case 2:
+ spin_lock_irqsave(&card->lock, flags);
+ card->flags &= ~((channel) ?
+ ICN_FLAGS_B2ACTIVE : ICN_FLAGS_B1ACTIVE);
+ icn_free_queue(card, channel);
+ card->rcvidx[channel] = 0;
+ spin_unlock_irqrestore(&card->lock, flags);
+ break;
+ case 3:
+ {
+ char *t = status + 6;
+ char *s = strchr(t, ',');
+
+ *s++ = '\0';
+ strlcpy(cmd.parm.setup.phone, t,
+ sizeof(cmd.parm.setup.phone));
+ s = strchr(t = s, ',');
+ *s++ = '\0';
+ if (!strlen(t))
+ cmd.parm.setup.si1 = 0;
+ else
+ cmd.parm.setup.si1 =
+ simple_strtoul(t, NULL, 10);
+ s = strchr(t = s, ',');
+ *s++ = '\0';
+ if (!strlen(t))
+ cmd.parm.setup.si2 = 0;
+ else
+ cmd.parm.setup.si2 =
+ simple_strtoul(t, NULL, 10);
+ strlcpy(cmd.parm.setup.eazmsn, s,
+ sizeof(cmd.parm.setup.eazmsn));
+ }
+ cmd.parm.setup.plan = 0;
+ cmd.parm.setup.screen = 0;
+ break;
+ case 4:
+ sprintf(cmd.parm.setup.phone, "LEASED%d", card->myid);
+ sprintf(cmd.parm.setup.eazmsn, "%d", channel + 1);
+ cmd.parm.setup.si1 = 7;
+ cmd.parm.setup.si2 = 0;
+ cmd.parm.setup.plan = 0;
+ cmd.parm.setup.screen = 0;
+ break;
+ case 5:
+ strlcpy(cmd.parm.num, status + 3, sizeof(cmd.parm.num));
+ break;
+ case 6:
+ snprintf(cmd.parm.num, sizeof(cmd.parm.num), "%d",
+ (int) simple_strtoul(status + 7, NULL, 16));
+ break;
+ case 7:
+ status += 3;
+ if (strlen(status) == 4)
+ snprintf(cmd.parm.num, sizeof(cmd.parm.num), "%s%c%c",
+ status + 2, *status, *(status + 1));
+ else
+ strlcpy(cmd.parm.num, status + 1, sizeof(cmd.parm.num));
+ break;
+ case 8:
+ spin_lock_irqsave(&card->lock, flags);
+ card->flags &= ~ICN_FLAGS_B1ACTIVE;
+ icn_free_queue(card, 0);
+ card->rcvidx[0] = 0;
+ spin_unlock_irqrestore(&card->lock, flags);
+ cmd.arg = 0;
+ cmd.driver = card->myid;
+ card->interface.statcallb(&cmd);
+ cmd.command = ISDN_STAT_DHUP;
+ cmd.arg = 0;
+ cmd.driver = card->myid;
+ card->interface.statcallb(&cmd);
+ cmd.command = ISDN_STAT_BHUP;
+ spin_lock_irqsave(&card->lock, flags);
+ card->flags &= ~ICN_FLAGS_B2ACTIVE;
+ icn_free_queue(card, 1);
+ card->rcvidx[1] = 0;
+ spin_unlock_irqrestore(&card->lock, flags);
+ cmd.arg = 1;
+ cmd.driver = card->myid;
+ card->interface.statcallb(&cmd);
+ cmd.command = ISDN_STAT_DHUP;
+ cmd.arg = 1;
+ cmd.driver = card->myid;
+ break;
+ }
+ card->interface.statcallb(&cmd);
+ return;
+}
+
+static void
+icn_putmsg(icn_card * card, unsigned char c)
+{
+ ulong flags;
+
+ spin_lock_irqsave(&card->lock, flags);
+ *card->msg_buf_write++ = (c == 0xff) ? '\n' : c;
+ if (card->msg_buf_write == card->msg_buf_read) {
+ if (++card->msg_buf_read > card->msg_buf_end)
+ card->msg_buf_read = card->msg_buf;
+ }
+ if (card->msg_buf_write > card->msg_buf_end)
+ card->msg_buf_write = card->msg_buf;
+ spin_unlock_irqrestore(&card->lock, flags);
+}
+
+static void
+icn_polldchan(unsigned long data)
+{
+ icn_card *card = (icn_card *) data;
+ int mch = card->secondhalf ? 2 : 0;
+ int avail = 0;
+ int left;
+ u_char c;
+ int ch;
+ unsigned long flags;
+ int i;
+ u_char *p;
+ isdn_ctrl cmd;
+
+ if (icn_trymaplock_channel(card, mch)) {
+ avail = msg_avail;
+ for (left = avail, i = readb(&msg_o); left > 0; i++, left--) {
+ c = readb(&dev.shmem->comm_buffers.iopc_buf[i & 0xff]);
+ icn_putmsg(card, c);
+ if (c == 0xff) {
+ card->imsg[card->iptr] = 0;
+ card->iptr = 0;
+ if (card->imsg[0] == '0' && card->imsg[1] >= '0' &&
+ card->imsg[1] <= '2' && card->imsg[2] == ';') {
+ ch = (card->imsg[1] - '0') - 1;
+ p = &card->imsg[3];
+ icn_parse_status(p, ch, card);
+ } else {
+ p = card->imsg;
+ if (!strncmp(p, "DRV1.", 5)) {
+ u_char vstr[10];
+ u_char *q = vstr;
+
+ printk(KERN_INFO "icn: (%s) %s\n", CID, p);
+ if (!strncmp(p + 7, "TC", 2)) {
+ card->ptype = ISDN_PTYPE_1TR6;
+ card->interface.features |= ISDN_FEATURE_P_1TR6;
+ printk(KERN_INFO
+ "icn: (%s) 1TR6-Protocol loaded and running\n", CID);
+ }
+ if (!strncmp(p + 7, "EC", 2)) {
+ card->ptype = ISDN_PTYPE_EURO;
+ card->interface.features |= ISDN_FEATURE_P_EURO;
+ printk(KERN_INFO
+ "icn: (%s) Euro-Protocol loaded and running\n", CID);
+ }
+ p = strstr(card->imsg, "BRV") + 3;
+ while (*p) {
+ if (*p >= '0' && *p <= '9')
+ *q++ = *p;
+ p++;
+ }
+ *q = '\0';
+ strcat(vstr, "000");
+ vstr[3] = '\0';
+ card->fw_rev = (int) simple_strtoul(vstr, NULL, 10);
+ continue;
+
+ }
+ }
+ } else {
+ card->imsg[card->iptr] = c;
+ if (card->iptr < 59)
+ card->iptr++;
+ }
+ }
+ writeb((readb(&msg_o) + avail) & 0xff, &msg_o);
+ icn_release_channel();
+ }
+ if (avail) {
+ cmd.command = ISDN_STAT_STAVAIL;
+ cmd.driver = card->myid;
+ cmd.arg = avail;
+ card->interface.statcallb(&cmd);
+ }
+ spin_lock_irqsave(&card->lock, flags);
+ if (card->flags & (ICN_FLAGS_B1ACTIVE | ICN_FLAGS_B2ACTIVE))
+ if (!(card->flags & ICN_FLAGS_RBTIMER)) {
+ /* schedule b-channel polling */
+ card->flags |= ICN_FLAGS_RBTIMER;
+ del_timer(&card->rb_timer);
+ card->rb_timer.function = icn_pollbchan;
+ card->rb_timer.data = (unsigned long) card;
+ card->rb_timer.expires = jiffies + ICN_TIMER_BCREAD;
+ add_timer(&card->rb_timer);
+ }
+ /* schedule again */
+ mod_timer(&card->st_timer, jiffies+ICN_TIMER_DCREAD);
+ spin_unlock_irqrestore(&card->lock, flags);
+}
+
+/* Append a packet to the transmit buffer-queue.
+ * Parameters:
+ * channel = Number of B-channel
+ * skb = pointer to sk_buff
+ * card = pointer to card-struct
+ * Return:
+ * Number of bytes transferred, -E??? on error
+ */
+
+static int
+icn_sendbuf(int channel, int ack, struct sk_buff *skb, icn_card * card)
+{
+ int len = skb->len;
+ unsigned long flags;
+ struct sk_buff *nskb;
+
+ if (len > 4000) {
+ printk(KERN_WARNING
+ "icn: Send packet too large\n");
+ return -EINVAL;
+ }
+ if (len) {
+ if (!(card->flags & (channel) ? ICN_FLAGS_B2ACTIVE : ICN_FLAGS_B1ACTIVE))
+ return 0;
+ if (card->sndcount[channel] > ICN_MAX_SQUEUE)
+ return 0;
+ #warning TODO test headroom or use skb->nb to flag ACK
+ nskb = skb_clone(skb, GFP_ATOMIC);
+ if (nskb) {
+ /* Push ACK flag as one
+ * byte in front of data.
+ */
+ *(skb_push(nskb, 1)) = ack?1:0;
+ skb_queue_tail(&card->spqueue[channel], nskb);
+ dev_kfree_skb(skb);
+ } else
+ len = 0;
+ spin_lock_irqsave(&card->lock, flags);
+ card->sndcount[channel] += len;
+ spin_unlock_irqrestore(&card->lock, flags);
+ }
+ return len;
+}
+
+/*
+ * Check card's status after starting the bootstrap loader.
+ * On entry, the card's shared memory has already to be mapped.
+ * Return:
+ * 0 on success (Boot loader ready)
+ * -EIO on failure (timeout)
+ */
+static int
+icn_check_loader(int cardnumber)
+{
+ int timer = 0;
+
+ while (1) {
+#ifdef BOOT_DEBUG
+ printk(KERN_DEBUG "Loader %d ?\n", cardnumber);
+#endif
+ if (readb(&dev.shmem->data_control.scns) ||
+ readb(&dev.shmem->data_control.scnr)) {
+ if (timer++ > 5) {
+ printk(KERN_WARNING
+ "icn: Boot-Loader %d timed out.\n",
+ cardnumber);
+ icn_release_channel();
+ return -EIO;
+ }
+#ifdef BOOT_DEBUG
+ printk(KERN_DEBUG "Loader %d TO?\n", cardnumber);
+#endif
+ msleep_interruptible(ICN_BOOT_TIMEOUT1);
+ } else {
+#ifdef BOOT_DEBUG
+ printk(KERN_DEBUG "Loader %d OK\n", cardnumber);
+#endif
+ icn_release_channel();
+ return 0;
+ }
+ }
+}
+
+/* Load the boot-code into the interface-card's memory and start it.
+ * Always called from user-process.
+ *
+ * Parameters:
+ * buffer = pointer to packet
+ * Return:
+ * 0 if successfully loaded
+ */
+
+#ifdef BOOT_DEBUG
+#define SLEEP(sec) { \
+int slsec = sec; \
+ printk(KERN_DEBUG "SLEEP(%d)\n",slsec); \
+ while (slsec) { \
+ msleep_interruptible(1000); \
+ slsec--; \
+ } \
+}
+#else
+#define SLEEP(sec)
+#endif
+
+static int
+icn_loadboot(u_char __user * buffer, icn_card * card)
+{
+ int ret;
+ u_char *codebuf;
+ unsigned long flags;
+
+#ifdef BOOT_DEBUG
+ printk(KERN_DEBUG "icn_loadboot called, buffaddr=%08lx\n", (ulong) buffer);
+#endif
+ if (!(codebuf = kmalloc(ICN_CODE_STAGE1, GFP_KERNEL))) {
+ printk(KERN_WARNING "icn: Could not allocate code buffer\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+ if (copy_from_user(codebuf, buffer, ICN_CODE_STAGE1)) {
+ ret = -EFAULT;
+ goto out_kfree;
+ }
+ if (!card->rvalid) {
+ if (!request_region(card->port, ICN_PORTLEN, card->regname)) {
+ printk(KERN_WARNING
+ "icn: (%s) ports 0x%03x-0x%03x in use.\n",
+ CID,
+ card->port,
+ card->port + ICN_PORTLEN);
+ ret = -EBUSY;
+ goto out_kfree;
+ }
+ card->rvalid = 1;
+ if (card->doubleS0)
+ card->other->rvalid = 1;
+ }
+ if (!dev.mvalid) {
+ if (!request_mem_region(dev.memaddr, 0x4000, "icn-isdn (all cards)")) {
+ printk(KERN_WARNING
+ "icn: memory at 0x%08lx in use.\n", dev.memaddr);
+ ret = -EBUSY;
+ goto out_kfree;
+ }
+ dev.shmem = ioremap(dev.memaddr, 0x4000);
+ dev.mvalid = 1;
+ }
+ OUTB_P(0, ICN_RUN); /* Reset Controller */
+ OUTB_P(0, ICN_MAPRAM); /* Disable RAM */
+ icn_shiftout(ICN_CFG, 0x0f, 3, 4); /* Windowsize= 16k */
+ icn_shiftout(ICN_CFG, dev.memaddr, 23, 10); /* Set RAM-Addr. */
+#ifdef BOOT_DEBUG
+ printk(KERN_DEBUG "shmem=%08lx\n", dev.memaddr);
+#endif
+ SLEEP(1);
+#ifdef BOOT_DEBUG
+ printk(KERN_DEBUG "Map Bank 0\n");
+#endif
+ spin_lock_irqsave(&dev.devlock, flags);
+ icn_map_channel(card, 0); /* Select Bank 0 */
+ icn_lock_channel(card, 0); /* Lock Bank 0 */
+ spin_unlock_irqrestore(&dev.devlock, flags);
+ SLEEP(1);
+ memcpy_toio(dev.shmem, codebuf, ICN_CODE_STAGE1); /* Copy code */
+#ifdef BOOT_DEBUG
+ printk(KERN_DEBUG "Bootloader transferred\n");
+#endif
+ if (card->doubleS0) {
+ SLEEP(1);
+#ifdef BOOT_DEBUG
+ printk(KERN_DEBUG "Map Bank 8\n");
+#endif
+ spin_lock_irqsave(&dev.devlock, flags);
+ __icn_release_channel();
+ icn_map_channel(card, 2); /* Select Bank 8 */
+ icn_lock_channel(card, 2); /* Lock Bank 8 */
+ spin_unlock_irqrestore(&dev.devlock, flags);
+ SLEEP(1);
+ memcpy_toio(dev.shmem, codebuf, ICN_CODE_STAGE1); /* Copy code */
+#ifdef BOOT_DEBUG
+ printk(KERN_DEBUG "Bootloader transferred\n");
+#endif
+ }
+ SLEEP(1);
+ OUTB_P(0xff, ICN_RUN); /* Start Boot-Code */
+ if ((ret = icn_check_loader(card->doubleS0 ? 2 : 1))) {
+ goto out_kfree;
+ }
+ if (!card->doubleS0) {
+ ret = 0;
+ goto out_kfree;
+ }
+ /* reached only, if we have a Double-S0-Card */
+#ifdef BOOT_DEBUG
+ printk(KERN_DEBUG "Map Bank 0\n");
+#endif
+ spin_lock_irqsave(&dev.devlock, flags);
+ icn_map_channel(card, 0); /* Select Bank 0 */
+ icn_lock_channel(card, 0); /* Lock Bank 0 */
+ spin_unlock_irqrestore(&dev.devlock, flags);
+ SLEEP(1);
+ ret = (icn_check_loader(1));
+
+ out_kfree:
+ kfree(codebuf);
+ out:
+ return ret;
+}
+
+static int
+icn_loadproto(u_char __user * buffer, icn_card * card)
+{
+ register u_char __user *p = buffer;
+ u_char codebuf[256];
+ uint left = ICN_CODE_STAGE2;
+ uint cnt;
+ int timer;
+ unsigned long flags;
+
+#ifdef BOOT_DEBUG
+ printk(KERN_DEBUG "icn_loadproto called\n");
+#endif
+ if (!access_ok(VERIFY_READ, buffer, ICN_CODE_STAGE2))
+ return -EFAULT;
+ timer = 0;
+ spin_lock_irqsave(&dev.devlock, flags);
+ if (card->secondhalf) {
+ icn_map_channel(card, 2);
+ icn_lock_channel(card, 2);
+ } else {
+ icn_map_channel(card, 0);
+ icn_lock_channel(card, 0);
+ }
+ spin_unlock_irqrestore(&dev.devlock, flags);
+ while (left) {
+ if (sbfree) { /* If there is a free buffer... */
+ cnt = left;
+ if (cnt > 256)
+ cnt = 256;
+ if (copy_from_user(codebuf, p, cnt)) {
+ icn_maprelease_channel(card, 0);
+ return -EFAULT;
+ }
+ memcpy_toio(&sbuf_l, codebuf, cnt); /* copy data */
+ sbnext; /* switch to next buffer */
+ p += cnt;
+ left -= cnt;
+ timer = 0;
+ } else {
+#ifdef BOOT_DEBUG
+ printk(KERN_DEBUG "boot 2 !sbfree\n");
+#endif
+ if (timer++ > 5) {
+ icn_maprelease_channel(card, 0);
+ return -EIO;
+ }
+ schedule_timeout_interruptible(10);
+ }
+ }
+ writeb(0x20, &sbuf_n);
+ timer = 0;
+ while (1) {
+ if (readb(&cmd_o) || readb(&cmd_i)) {
+#ifdef BOOT_DEBUG
+ printk(KERN_DEBUG "Proto?\n");
+#endif
+ if (timer++ > 5) {
+ printk(KERN_WARNING
+ "icn: (%s) Protocol timed out.\n",
+ CID);
+#ifdef BOOT_DEBUG
+ printk(KERN_DEBUG "Proto TO!\n");
+#endif
+ icn_maprelease_channel(card, 0);
+ return -EIO;
+ }
+#ifdef BOOT_DEBUG
+ printk(KERN_DEBUG "Proto TO?\n");
+#endif
+ msleep_interruptible(ICN_BOOT_TIMEOUT1);
+ } else {
+ if ((card->secondhalf) || (!card->doubleS0)) {
+#ifdef BOOT_DEBUG
+ printk(KERN_DEBUG "Proto loaded, install poll-timer %d\n",
+ card->secondhalf);
+#endif
+ spin_lock_irqsave(&card->lock, flags);
+ init_timer(&card->st_timer);
+ card->st_timer.expires = jiffies + ICN_TIMER_DCREAD;
+ card->st_timer.function = icn_polldchan;
+ card->st_timer.data = (unsigned long) card;
+ add_timer(&card->st_timer);
+ card->flags |= ICN_FLAGS_RUNNING;
+ if (card->doubleS0) {
+ init_timer(&card->other->st_timer);
+ card->other->st_timer.expires = jiffies + ICN_TIMER_DCREAD;
+ card->other->st_timer.function = icn_polldchan;
+ card->other->st_timer.data = (unsigned long) card->other;
+ add_timer(&card->other->st_timer);
+ card->other->flags |= ICN_FLAGS_RUNNING;
+ }
+ spin_unlock_irqrestore(&card->lock, flags);
+ }
+ icn_maprelease_channel(card, 0);
+ return 0;
+ }
+ }
+}
+
+/* Read the Status-replies from the Interface */
+static int
+icn_readstatus(u_char __user *buf, int len, icn_card * card)
+{
+ int count;
+ u_char __user *p;
+
+ for (p = buf, count = 0; count < len; p++, count++) {
+ if (card->msg_buf_read == card->msg_buf_write)
+ return count;
+ if (put_user(*card->msg_buf_read++, p))
+ return -EFAULT;
+ if (card->msg_buf_read > card->msg_buf_end)
+ card->msg_buf_read = card->msg_buf;
+ }
+ return count;
+}
+
+/* Put command-strings into the command-queue of the Interface */
+static int
+icn_writecmd(const u_char * buf, int len, int user, icn_card * card)
+{
+ int mch = card->secondhalf ? 2 : 0;
+ int pp;
+ int i;
+ int count;
+ int xcount;
+ int ocount;
+ int loop;
+ unsigned long flags;
+ int lastmap_channel;
+ struct icn_card *lastmap_card;
+ u_char *p;
+ isdn_ctrl cmd;
+ u_char msg[0x100];
+
+ ocount = 1;
+ xcount = loop = 0;
+ while (len) {
+ count = cmd_free;
+ if (count > len)
+ count = len;
+ if (user) {
+ if (copy_from_user(msg, buf, count))
+ return -EFAULT;
+ } else
+ memcpy(msg, buf, count);
+
+ spin_lock_irqsave(&dev.devlock, flags);
+ lastmap_card = dev.mcard;
+ lastmap_channel = dev.channel;
+ icn_map_channel(card, mch);
+
+ icn_putmsg(card, '>');
+ for (p = msg, pp = readb(&cmd_i), i = count; i > 0; i--, p++, pp
+ ++) {
+ writeb((*p == '\n') ? 0xff : *p,
+ &dev.shmem->comm_buffers.pcio_buf[pp & 0xff]);
+ len--;
+ xcount++;
+ icn_putmsg(card, *p);
+ if ((*p == '\n') && (i > 1)) {
+ icn_putmsg(card, '>');
+ ocount++;
+ }
+ ocount++;
+ }
+ writeb((readb(&cmd_i) + count) & 0xff, &cmd_i);
+ if (lastmap_card)
+ icn_map_channel(lastmap_card, lastmap_channel);
+ spin_unlock_irqrestore(&dev.devlock, flags);
+ if (len) {
+ mdelay(1);
+ if (loop++ > 20)
+ break;
+ } else
+ break;
+ }
+ if (len && (!user))
+ printk(KERN_WARNING "icn: writemsg incomplete!\n");
+ cmd.command = ISDN_STAT_STAVAIL;
+ cmd.driver = card->myid;
+ cmd.arg = ocount;
+ card->interface.statcallb(&cmd);
+ return xcount;
+}
+
+/*
+ * Delete card's pending timers, send STOP to linklevel
+ */
+static void
+icn_stopcard(icn_card * card)
+{
+ unsigned long flags;
+ isdn_ctrl cmd;
+
+ spin_lock_irqsave(&card->lock, flags);
+ if (card->flags & ICN_FLAGS_RUNNING) {
+ card->flags &= ~ICN_FLAGS_RUNNING;
+ del_timer(&card->st_timer);
+ del_timer(&card->rb_timer);
+ spin_unlock_irqrestore(&card->lock, flags);
+ cmd.command = ISDN_STAT_STOP;
+ cmd.driver = card->myid;
+ card->interface.statcallb(&cmd);
+ if (card->doubleS0)
+ icn_stopcard(card->other);
+ } else
+ spin_unlock_irqrestore(&card->lock, flags);
+}
+
+static void
+icn_stopallcards(void)
+{
+ icn_card *p = cards;
+
+ while (p) {
+ icn_stopcard(p);
+ p = p->next;
+ }
+}
+
+/*
+ * Unmap all cards, because some of them may be mapped accidetly during
+ * autoprobing of some network drivers (SMC-driver?)
+ */
+static void
+icn_disable_cards(void)
+{
+ icn_card *card = cards;
+
+ while (card) {
+ if (!request_region(card->port, ICN_PORTLEN, "icn-isdn")) {
+ printk(KERN_WARNING
+ "icn: (%s) ports 0x%03x-0x%03x in use.\n",
+ CID,
+ card->port,
+ card->port + ICN_PORTLEN);
+ } else {
+ OUTB_P(0, ICN_RUN); /* Reset Controller */
+ OUTB_P(0, ICN_MAPRAM); /* Disable RAM */
+ release_region(card->port, ICN_PORTLEN);
+ }
+ card = card->next;
+ }
+}
+
+static int
+icn_command(isdn_ctrl * c, icn_card * card)
+{
+ ulong a;
+ ulong flags;
+ int i;
+ char cbuf[60];
+ isdn_ctrl cmd;
+ icn_cdef cdef;
+ char __user *arg;
+
+ switch (c->command) {
+ case ISDN_CMD_IOCTL:
+ memcpy(&a, c->parm.num, sizeof(ulong));
+ arg = (char __user *)a;
+ switch (c->arg) {
+ case ICN_IOCTL_SETMMIO:
+ if (dev.memaddr != (a & 0x0ffc000)) {
+ if (!request_mem_region(a & 0x0ffc000, 0x4000, "icn-isdn (all cards)")) {
+ printk(KERN_WARNING
+ "icn: memory at 0x%08lx in use.\n",
+ a & 0x0ffc000);
+ return -EINVAL;
+ }
+ release_mem_region(a & 0x0ffc000, 0x4000);
+ icn_stopallcards();
+ spin_lock_irqsave(&card->lock, flags);
+ if (dev.mvalid) {
+ iounmap(dev.shmem);
+ release_mem_region(dev.memaddr, 0x4000);
+ }
+ dev.mvalid = 0;
+ dev.memaddr = a & 0x0ffc000;
+ spin_unlock_irqrestore(&card->lock, flags);
+ printk(KERN_INFO
+ "icn: (%s) mmio set to 0x%08lx\n",
+ CID,
+ dev.memaddr);
+ }
+ break;
+ case ICN_IOCTL_GETMMIO:
+ return (long) dev.memaddr;
+ case ICN_IOCTL_SETPORT:
+ if (a == 0x300 || a == 0x310 || a == 0x320 || a == 0x330
+ || a == 0x340 || a == 0x350 || a == 0x360 ||
+ a == 0x308 || a == 0x318 || a == 0x328 || a == 0x338
+ || a == 0x348 || a == 0x358 || a == 0x368) {
+ if (card->port != (unsigned short) a) {
+ if (!request_region((unsigned short) a, ICN_PORTLEN, "icn-isdn")) {
+ printk(KERN_WARNING
+ "icn: (%s) ports 0x%03x-0x%03x in use.\n",
+ CID, (int) a, (int) a + ICN_PORTLEN);
+ return -EINVAL;
+ }
+ release_region((unsigned short) a, ICN_PORTLEN);
+ icn_stopcard(card);
+ spin_lock_irqsave(&card->lock, flags);
+ if (card->rvalid)
+ release_region(card->port, ICN_PORTLEN);
+ card->port = (unsigned short) a;
+ card->rvalid = 0;
+ if (card->doubleS0) {
+ card->other->port = (unsigned short) a;
+ card->other->rvalid = 0;
+ }
+ spin_unlock_irqrestore(&card->lock, flags);
+ printk(KERN_INFO
+ "icn: (%s) port set to 0x%03x\n",
+ CID, card->port);
+ }
+ } else
+ return -EINVAL;
+ break;
+ case ICN_IOCTL_GETPORT:
+ return (int) card->port;
+ case ICN_IOCTL_GETDOUBLE:
+ return (int) card->doubleS0;
+ case ICN_IOCTL_DEBUGVAR:
+ if (copy_to_user(arg,
+ &card,
+ sizeof(ulong)))
+ return -EFAULT;
+ a += sizeof(ulong);
+ {
+ ulong l = (ulong) & dev;
+ if (copy_to_user(arg,
+ &l,
+ sizeof(ulong)))
+ return -EFAULT;
+ }
+ return 0;
+ case ICN_IOCTL_LOADBOOT:
+ if (dev.firstload) {
+ icn_disable_cards();
+ dev.firstload = 0;
+ }
+ icn_stopcard(card);
+ return (icn_loadboot(arg, card));
+ case ICN_IOCTL_LOADPROTO:
+ icn_stopcard(card);
+ if ((i = (icn_loadproto(arg, card))))
+ return i;
+ if (card->doubleS0)
+ i = icn_loadproto(arg + ICN_CODE_STAGE2, card->other);
+ return i;
+ break;
+ case ICN_IOCTL_ADDCARD:
+ if (!dev.firstload)
+ return -EBUSY;
+ if (copy_from_user(&cdef,
+ arg,
+ sizeof(cdef)))
+ return -EFAULT;
+ return (icn_addcard(cdef.port, cdef.id1, cdef.id2));
+ break;
+ case ICN_IOCTL_LEASEDCFG:
+ if (a) {
+ if (!card->leased) {
+ card->leased = 1;
+ while (card->ptype == ISDN_PTYPE_UNKNOWN) {
+ msleep_interruptible(ICN_BOOT_TIMEOUT1);
+ }
+ msleep_interruptible(ICN_BOOT_TIMEOUT1);
+ sprintf(cbuf, "00;FV2ON\n01;EAZ%c\n02;EAZ%c\n",
+ (a & 1)?'1':'C', (a & 2)?'2':'C');
+ i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+ printk(KERN_INFO
+ "icn: (%s) Leased-line mode enabled\n",
+ CID);
+ cmd.command = ISDN_STAT_RUN;
+ cmd.driver = card->myid;
+ cmd.arg = 0;
+ card->interface.statcallb(&cmd);
+ }
+ } else {
+ if (card->leased) {
+ card->leased = 0;
+ sprintf(cbuf, "00;FV2OFF\n");
+ i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+ printk(KERN_INFO
+ "icn: (%s) Leased-line mode disabled\n",
+ CID);
+ cmd.command = ISDN_STAT_RUN;
+ cmd.driver = card->myid;
+ cmd.arg = 0;
+ card->interface.statcallb(&cmd);
+ }
+ }
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case ISDN_CMD_DIAL:
+ if (!(card->flags & ICN_FLAGS_RUNNING))
+ return -ENODEV;
+ if (card->leased)
+ break;
+ if ((c->arg & 255) < ICN_BCH) {
+ char *p;
+ char dial[50];
+ char dcode[4];
+
+ a = c->arg;
+ p = c->parm.setup.phone;
+ if (*p == 's' || *p == 'S') {
+ /* Dial for SPV */
+ p++;
+ strcpy(dcode, "SCA");
+ } else
+ /* Normal Dial */
+ strcpy(dcode, "CAL");
+ strcpy(dial, p);
+ sprintf(cbuf, "%02d;D%s_R%s,%02d,%02d,%s\n", (int) (a + 1),
+ dcode, dial, c->parm.setup.si1,
+ c->parm.setup.si2, c->parm.setup.eazmsn);
+ i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+ }
+ break;
+ case ISDN_CMD_ACCEPTD:
+ if (!(card->flags & ICN_FLAGS_RUNNING))
+ return -ENODEV;
+ if (c->arg < ICN_BCH) {
+ a = c->arg + 1;
+ if (card->fw_rev >= 300) {
+ switch (card->l2_proto[a - 1]) {
+ case ISDN_PROTO_L2_X75I:
+ sprintf(cbuf, "%02d;BX75\n", (int) a);
+ break;
+ case ISDN_PROTO_L2_HDLC:
+ sprintf(cbuf, "%02d;BTRA\n", (int) a);
+ break;
+ }
+ i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+ }
+ sprintf(cbuf, "%02d;DCON_R\n", (int) a);
+ i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+ }
+ break;
+ case ISDN_CMD_ACCEPTB:
+ if (!(card->flags & ICN_FLAGS_RUNNING))
+ return -ENODEV;
+ if (c->arg < ICN_BCH) {
+ a = c->arg + 1;
+ if (card->fw_rev >= 300)
+ switch (card->l2_proto[a - 1]) {
+ case ISDN_PROTO_L2_X75I:
+ sprintf(cbuf, "%02d;BCON_R,BX75\n", (int) a);
+ break;
+ case ISDN_PROTO_L2_HDLC:
+ sprintf(cbuf, "%02d;BCON_R,BTRA\n", (int) a);
+ break;
+ } else
+ sprintf(cbuf, "%02d;BCON_R\n", (int) a);
+ i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+ }
+ break;
+ case ISDN_CMD_HANGUP:
+ if (!(card->flags & ICN_FLAGS_RUNNING))
+ return -ENODEV;
+ if (c->arg < ICN_BCH) {
+ a = c->arg + 1;
+ sprintf(cbuf, "%02d;BDIS_R\n%02d;DDIS_R\n", (int) a, (int) a);
+ i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+ }
+ break;
+ case ISDN_CMD_SETEAZ:
+ if (!(card->flags & ICN_FLAGS_RUNNING))
+ return -ENODEV;
+ if (card->leased)
+ break;
+ if (c->arg < ICN_BCH) {
+ a = c->arg + 1;
+ if (card->ptype == ISDN_PTYPE_EURO) {
+ sprintf(cbuf, "%02d;MS%s%s\n", (int) a,
+ c->parm.num[0] ? "N" : "ALL", c->parm.num);
+ } else
+ sprintf(cbuf, "%02d;EAZ%s\n", (int) a,
+ c->parm.num[0] ? (char *)(c->parm.num) : "0123456789");
+ i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+ }
+ break;
+ case ISDN_CMD_CLREAZ:
+ if (!(card->flags & ICN_FLAGS_RUNNING))
+ return -ENODEV;
+ if (card->leased)
+ break;
+ if (c->arg < ICN_BCH) {
+ a = c->arg + 1;
+ if (card->ptype == ISDN_PTYPE_EURO)
+ sprintf(cbuf, "%02d;MSNC\n", (int) a);
+ else
+ sprintf(cbuf, "%02d;EAZC\n", (int) a);
+ i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+ }
+ break;
+ case ISDN_CMD_SETL2:
+ if (!(card->flags & ICN_FLAGS_RUNNING))
+ return -ENODEV;
+ if ((c->arg & 255) < ICN_BCH) {
+ a = c->arg;
+ switch (a >> 8) {
+ case ISDN_PROTO_L2_X75I:
+ sprintf(cbuf, "%02d;BX75\n", (int) (a & 255) + 1);
+ break;
+ case ISDN_PROTO_L2_HDLC:
+ sprintf(cbuf, "%02d;BTRA\n", (int) (a & 255) + 1);
+ break;
+ default:
+ return -EINVAL;
+ }
+ i = icn_writecmd(cbuf, strlen(cbuf), 0, card);
+ card->l2_proto[a & 255] = (a >> 8);
+ }
+ break;
+ case ISDN_CMD_SETL3:
+ if (!(card->flags & ICN_FLAGS_RUNNING))
+ return -ENODEV;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * Find card with given driverId
+ */
+static inline icn_card *
+icn_findcard(int driverid)
+{
+ icn_card *p = cards;
+
+ while (p) {
+ if (p->myid == driverid)
+ return p;
+ p = p->next;
+ }
+ return (icn_card *) 0;
+}
+
+/*
+ * Wrapper functions for interface to linklevel
+ */
+static int
+if_command(isdn_ctrl * c)
+{
+ icn_card *card = icn_findcard(c->driver);
+
+ if (card)
+ return (icn_command(c, card));
+ printk(KERN_ERR
+ "icn: if_command %d called with invalid driverId %d!\n",
+ c->command, c->driver);
+ return -ENODEV;
+}
+
+static int
+if_writecmd(const u_char __user *buf, int len, int id, int channel)
+{
+ icn_card *card = icn_findcard(id);
+
+ if (card) {
+ if (!(card->flags & ICN_FLAGS_RUNNING))
+ return -ENODEV;
+ return (icn_writecmd(buf, len, 1, card));
+ }
+ printk(KERN_ERR
+ "icn: if_writecmd called with invalid driverId!\n");
+ return -ENODEV;
+}
+
+static int
+if_readstatus(u_char __user *buf, int len, int id, int channel)
+{
+ icn_card *card = icn_findcard(id);
+
+ if (card) {
+ if (!(card->flags & ICN_FLAGS_RUNNING))
+ return -ENODEV;
+ return (icn_readstatus(buf, len, card));
+ }
+ printk(KERN_ERR
+ "icn: if_readstatus called with invalid driverId!\n");
+ return -ENODEV;
+}
+
+static int
+if_sendbuf(int id, int channel, int ack, struct sk_buff *skb)
+{
+ icn_card *card = icn_findcard(id);
+
+ if (card) {
+ if (!(card->flags & ICN_FLAGS_RUNNING))
+ return -ENODEV;
+ return (icn_sendbuf(channel, ack, skb, card));
+ }
+ printk(KERN_ERR
+ "icn: if_sendbuf called with invalid driverId!\n");
+ return -ENODEV;
+}
+
+/*
+ * Allocate a new card-struct, initialize it
+ * link it into cards-list and register it at linklevel.
+ */
+static icn_card *
+icn_initcard(int port, char *id)
+{
+ icn_card *card;
+ int i;
+
+ if (!(card = kzalloc(sizeof(icn_card), GFP_KERNEL))) {
+ printk(KERN_WARNING
+ "icn: (%s) Could not allocate card-struct.\n", id);
+ return (icn_card *) 0;
+ }
+ spin_lock_init(&card->lock);
+ card->port = port;
+ card->interface.owner = THIS_MODULE;
+ card->interface.hl_hdrlen = 1;
+ card->interface.channels = ICN_BCH;
+ card->interface.maxbufsize = 4000;
+ card->interface.command = if_command;
+ card->interface.writebuf_skb = if_sendbuf;
+ card->interface.writecmd = if_writecmd;
+ card->interface.readstat = if_readstatus;
+ card->interface.features = ISDN_FEATURE_L2_X75I |
+ ISDN_FEATURE_L2_HDLC |
+ ISDN_FEATURE_L3_TRANS |
+ ISDN_FEATURE_P_UNKNOWN;
+ card->ptype = ISDN_PTYPE_UNKNOWN;
+ strlcpy(card->interface.id, id, sizeof(card->interface.id));
+ card->msg_buf_write = card->msg_buf;
+ card->msg_buf_read = card->msg_buf;
+ card->msg_buf_end = &card->msg_buf[sizeof(card->msg_buf) - 1];
+ for (i = 0; i < ICN_BCH; i++) {
+ card->l2_proto[i] = ISDN_PROTO_L2_X75I;
+ skb_queue_head_init(&card->spqueue[i]);
+ }
+ card->next = cards;
+ cards = card;
+ if (!register_isdn(&card->interface)) {
+ cards = cards->next;
+ printk(KERN_WARNING
+ "icn: Unable to register %s\n", id);
+ kfree(card);
+ return (icn_card *) 0;
+ }
+ card->myid = card->interface.channels;
+ sprintf(card->regname, "icn-isdn (%s)", card->interface.id);
+ return card;
+}
+
+static int
+icn_addcard(int port, char *id1, char *id2)
+{
+ icn_card *card;
+ icn_card *card2;
+
+ if (!(card = icn_initcard(port, id1))) {
+ return -EIO;
+ }
+ if (!strlen(id2)) {
+ printk(KERN_INFO
+ "icn: (%s) ICN-2B, port 0x%x added\n",
+ card->interface.id, port);
+ return 0;
+ }
+ if (!(card2 = icn_initcard(port, id2))) {
+ printk(KERN_INFO
+ "icn: (%s) half ICN-4B, port 0x%x added\n",
+ card2->interface.id, port);
+ return 0;
+ }
+ card->doubleS0 = 1;
+ card->secondhalf = 0;
+ card->other = card2;
+ card2->doubleS0 = 1;
+ card2->secondhalf = 1;
+ card2->other = card;
+ printk(KERN_INFO
+ "icn: (%s and %s) ICN-4B, port 0x%x added\n",
+ card->interface.id, card2->interface.id, port);
+ return 0;
+}
+
+#ifndef MODULE
+static int __init
+icn_setup(char *line)
+{
+ char *p, *str;
+ int ints[3];
+ static char sid[20];
+ static char sid2[20];
+
+ str = get_options(line, 2, ints);
+ if (ints[0])
+ portbase = ints[1];
+ if (ints[0] > 1)
+ membase = (unsigned long)ints[2];
+ if (str && *str) {
+ strcpy(sid, str);
+ icn_id = sid;
+ if ((p = strchr(sid, ','))) {
+ *p++ = 0;
+ strcpy(sid2, p);
+ icn_id2 = sid2;
+ }
+ }
+ return(1);
+}
+__setup("icn=", icn_setup);
+#endif /* MODULE */
+
+static int __init icn_init(void)
+{
+ char *p;
+ char rev[10];
+
+ memset(&dev, 0, sizeof(icn_dev));
+ dev.memaddr = (membase & 0x0ffc000);
+ dev.channel = -1;
+ dev.mcard = NULL;
+ dev.firstload = 1;
+ spin_lock_init(&dev.devlock);
+
+ if ((p = strchr(revision, ':'))) {
+ strcpy(rev, p + 1);
+ p = strchr(rev, '$');
+ *p = 0;
+ } else
+ strcpy(rev, " ??? ");
+ printk(KERN_NOTICE "ICN-ISDN-driver Rev%smem=0x%08lx\n", rev,
+ dev.memaddr);
+ return (icn_addcard(portbase, icn_id, icn_id2));
+}
+
+static void __exit icn_exit(void)
+{
+ isdn_ctrl cmd;
+ icn_card *card = cards;
+ icn_card *last, *tmpcard;
+ int i;
+ unsigned long flags;
+
+ icn_stopallcards();
+ while (card) {
+ cmd.command = ISDN_STAT_UNLOAD;
+ cmd.driver = card->myid;
+ card->interface.statcallb(&cmd);
+ spin_lock_irqsave(&card->lock, flags);
+ if (card->rvalid) {
+ OUTB_P(0, ICN_RUN); /* Reset Controller */
+ OUTB_P(0, ICN_MAPRAM); /* Disable RAM */
+ if (card->secondhalf || (!card->doubleS0)) {
+ release_region(card->port, ICN_PORTLEN);
+ card->rvalid = 0;
+ }
+ for (i = 0; i < ICN_BCH; i++)
+ icn_free_queue(card, i);
+ }
+ tmpcard = card->next;
+ spin_unlock_irqrestore(&card->lock, flags);
+ card = tmpcard;
+ }
+ card = cards;
+ cards = NULL;
+ while (card) {
+ last = card;
+ card = card->next;
+ kfree(last);
+ }
+ if (dev.mvalid) {
+ iounmap(dev.shmem);
+ release_mem_region(dev.memaddr, 0x4000);
+ }
+ printk(KERN_NOTICE "ICN-ISDN-driver unloaded\n");
+}
+
+module_init(icn_init);
+module_exit(icn_exit);
diff --git a/drivers/isdn/icn/icn.h b/drivers/isdn/icn/icn.h
new file mode 100644
index 0000000..7d7245f
--- /dev/null
+++ b/drivers/isdn/icn/icn.h
@@ -0,0 +1,253 @@
+/* $Id: icn.h,v 1.30.6.5 2001/09/23 22:24:55 kai Exp $
+ *
+ * ISDN lowlevel-module for the ICN active ISDN-Card.
+ *
+ * Copyright 1994 by Fritz Elfert (fritz@isdn4linux.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef icn_h
+#define icn_h
+
+#define ICN_IOCTL_SETMMIO 0
+#define ICN_IOCTL_GETMMIO 1
+#define ICN_IOCTL_SETPORT 2
+#define ICN_IOCTL_GETPORT 3
+#define ICN_IOCTL_LOADBOOT 4
+#define ICN_IOCTL_LOADPROTO 5
+#define ICN_IOCTL_LEASEDCFG 6
+#define ICN_IOCTL_GETDOUBLE 7
+#define ICN_IOCTL_DEBUGVAR 8
+#define ICN_IOCTL_ADDCARD 9
+
+/* Struct for adding new cards */
+typedef struct icn_cdef {
+ int port;
+ char id1[10];
+ char id2[10];
+} icn_cdef;
+
+#if defined(__KERNEL__) || defined(__DEBUGVAR__)
+
+#ifdef __KERNEL__
+/* Kernel includes */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <asm/io.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/delay.h>
+#include <linux/isdnif.h>
+
+#endif /* __KERNEL__ */
+
+/* some useful macros for debugging */
+#ifdef ICN_DEBUG_PORT
+#define OUTB_P(v,p) {printk(KERN_DEBUG "icn: outb_p(0x%02x,0x%03x)\n",v,p); outb_p(v,p);}
+#else
+#define OUTB_P outb
+#endif
+
+/* Defaults for Port-Address and shared-memory */
+#define ICN_BASEADDR 0x320
+#define ICN_PORTLEN (0x04)
+#define ICN_MEMADDR 0x0d0000
+
+#define ICN_FLAGS_B1ACTIVE 1 /* B-Channel-1 is open */
+#define ICN_FLAGS_B2ACTIVE 2 /* B-Channel-2 is open */
+#define ICN_FLAGS_RUNNING 4 /* Cards driver activated */
+#define ICN_FLAGS_RBTIMER 8 /* cyclic scheduling of B-Channel-poll */
+
+#define ICN_BOOT_TIMEOUT1 1000 /* Delay for Boot-download (msecs) */
+
+#define ICN_TIMER_BCREAD (HZ/100) /* B-Channel poll-cycle */
+#define ICN_TIMER_DCREAD (HZ/2) /* D-Channel poll-cycle */
+
+#define ICN_CODE_STAGE1 4096 /* Size of bootcode */
+#define ICN_CODE_STAGE2 65536 /* Size of protocol-code */
+
+#define ICN_MAX_SQUEUE 8000 /* Max. outstanding send-data (2* hw-buf.) */
+#define ICN_FRAGSIZE (250) /* Max. size of send-fragments */
+#define ICN_BCH 2 /* Number of supported channels per card */
+
+/* type-definitions for accessing the mmap-io-areas */
+
+#define SHM_DCTL_OFFSET (0) /* Offset to data-controlstructures in shm */
+#define SHM_CCTL_OFFSET (0x1d2) /* Offset to comm-controlstructures in shm */
+#define SHM_CBUF_OFFSET (0x200) /* Offset to comm-buffers in shm */
+#define SHM_DBUF_OFFSET (0x2000) /* Offset to data-buffers in shm */
+
+/*
+ * Layout of card's data buffers
+ */
+typedef struct {
+ unsigned char length; /* Bytecount of fragment (max 250) */
+ unsigned char endflag; /* 0=last frag., 0xff=frag. continued */
+ unsigned char data[ICN_FRAGSIZE]; /* The data */
+ /* Fill to 256 bytes */
+ char unused[0x100 - ICN_FRAGSIZE - 2];
+} frag_buf;
+
+/*
+ * Layout of card's shared memory
+ */
+typedef union {
+ struct {
+ unsigned char scns; /* Index to free SendFrag. */
+ unsigned char scnr; /* Index to active SendFrag READONLY */
+ unsigned char ecns; /* Index to free RcvFrag. READONLY */
+ unsigned char ecnr; /* Index to valid RcvFrag */
+ char unused[6];
+ unsigned short fuell1; /* Internal Buf Bytecount */
+ } data_control;
+ struct {
+ char unused[SHM_CCTL_OFFSET];
+ unsigned char iopc_i; /* Read-Ptr Status-Queue READONLY */
+ unsigned char iopc_o; /* Write-Ptr Status-Queue */
+ unsigned char pcio_i; /* Write-Ptr Command-Queue */
+ unsigned char pcio_o; /* Read-Ptr Command Queue READONLY */
+ } comm_control;
+ struct {
+ char unused[SHM_CBUF_OFFSET];
+ unsigned char pcio_buf[0x100]; /* Ring-Buffer Command-Queue */
+ unsigned char iopc_buf[0x100]; /* Ring-Buffer Status-Queue */
+ } comm_buffers;
+ struct {
+ char unused[SHM_DBUF_OFFSET];
+ frag_buf receive_buf[0x10];
+ frag_buf send_buf[0x10];
+ } data_buffers;
+} icn_shmem;
+
+/*
+ * Per card driver data
+ */
+typedef struct icn_card {
+ struct icn_card *next; /* Pointer to next device struct */
+ struct icn_card *other; /* Pointer to other card for ICN4B */
+ unsigned short port; /* Base-port-address */
+ int myid; /* Driver-Nr. assigned by linklevel */
+ int rvalid; /* IO-portregion has been requested */
+ int leased; /* Flag: This Adapter is connected */
+ /* to a leased line */
+ unsigned short flags; /* Statusflags */
+ int doubleS0; /* Flag: ICN4B */
+ int secondhalf; /* Flag: Second half of a doubleS0 */
+ int fw_rev; /* Firmware revision loaded */
+ int ptype; /* Protocol type (1TR6 or Euro) */
+ struct timer_list st_timer; /* Timer for Status-Polls */
+ struct timer_list rb_timer; /* Timer for B-Channel-Polls */
+ u_char rcvbuf[ICN_BCH][4096]; /* B-Channel-Receive-Buffers */
+ int rcvidx[ICN_BCH]; /* Index for above buffers */
+ int l2_proto[ICN_BCH]; /* Current layer-2-protocol */
+ isdn_if interface; /* Interface to upper layer */
+ int iptr; /* Index to imsg-buffer */
+ char imsg[60]; /* Internal buf for status-parsing */
+ char msg_buf[2048]; /* Buffer for status-messages */
+ char *msg_buf_write; /* Writepointer for statusbuffer */
+ char *msg_buf_read; /* Readpointer for statusbuffer */
+ char *msg_buf_end; /* Pointer to end of statusbuffer */
+ int sndcount[ICN_BCH]; /* Byte-counters for B-Ch.-send */
+ int xlen[ICN_BCH]; /* Byte-counters/Flags for sent-ACK */
+ struct sk_buff *xskb[ICN_BCH]; /* Current transmitted skb */
+ struct sk_buff_head spqueue[ICN_BCH]; /* Sendqueue */
+ char regname[35]; /* Name used for request_region */
+ u_char xmit_lock[ICN_BCH]; /* Semaphore for pollbchan_send()*/
+ spinlock_t lock; /* protect critical operations */
+} icn_card;
+
+/*
+ * Main driver data
+ */
+typedef struct icn_dev {
+ spinlock_t devlock; /* spinlock to protect this struct */
+ unsigned long memaddr; /* Address of memory mapped buffers */
+ icn_shmem __iomem *shmem; /* Pointer to memory-mapped-buffers */
+ int mvalid; /* IO-shmem has been requested */
+ int channel; /* Currently mapped channel */
+ struct icn_card *mcard; /* Currently mapped card */
+ int chanlock; /* Semaphore for channel-mapping */
+ int firstload; /* Flag: firmware never loaded */
+} icn_dev;
+
+typedef icn_dev *icn_devptr;
+
+#ifdef __KERNEL__
+
+static icn_card *cards = (icn_card *) 0;
+static u_char chan2bank[] =
+{0, 4, 8, 12}; /* for icn_map_channel() */
+
+static icn_dev dev;
+
+#endif /* __KERNEL__ */
+
+/* Utility-Macros */
+
+/* Macros for accessing ports */
+#define ICN_CFG (card->port)
+#define ICN_MAPRAM (card->port+1)
+#define ICN_RUN (card->port+2)
+#define ICN_BANK (card->port+3)
+
+/* Return true, if there is a free transmit-buffer */
+#define sbfree (((readb(&dev.shmem->data_control.scns)+1) & 0xf) != \
+ readb(&dev.shmem->data_control.scnr))
+
+/* Switch to next transmit-buffer */
+#define sbnext (writeb((readb(&dev.shmem->data_control.scns)+1) & 0xf, \
+ &dev.shmem->data_control.scns))
+
+/* Shortcuts for transmit-buffer-access */
+#define sbuf_n dev.shmem->data_control.scns
+#define sbuf_d dev.shmem->data_buffers.send_buf[readb(&sbuf_n)].data
+#define sbuf_l dev.shmem->data_buffers.send_buf[readb(&sbuf_n)].length
+#define sbuf_f dev.shmem->data_buffers.send_buf[readb(&sbuf_n)].endflag
+
+/* Return true, if there is receive-data is available */
+#define rbavl (readb(&dev.shmem->data_control.ecnr) != \
+ readb(&dev.shmem->data_control.ecns))
+
+/* Switch to next receive-buffer */
+#define rbnext (writeb((readb(&dev.shmem->data_control.ecnr)+1) & 0xf, \
+ &dev.shmem->data_control.ecnr))
+
+/* Shortcuts for receive-buffer-access */
+#define rbuf_n dev.shmem->data_control.ecnr
+#define rbuf_d dev.shmem->data_buffers.receive_buf[readb(&rbuf_n)].data
+#define rbuf_l dev.shmem->data_buffers.receive_buf[readb(&rbuf_n)].length
+#define rbuf_f dev.shmem->data_buffers.receive_buf[readb(&rbuf_n)].endflag
+
+/* Shortcuts for command-buffer-access */
+#define cmd_o (dev.shmem->comm_control.pcio_o)
+#define cmd_i (dev.shmem->comm_control.pcio_i)
+
+/* Return free space in command-buffer */
+#define cmd_free ((readb(&cmd_i)>=readb(&cmd_o))? \
+ 0x100-readb(&cmd_i)+readb(&cmd_o): \
+ readb(&cmd_o)-readb(&cmd_i))
+
+/* Shortcuts for message-buffer-access */
+#define msg_o (dev.shmem->comm_control.iopc_o)
+#define msg_i (dev.shmem->comm_control.iopc_i)
+
+/* Return length of Message, if avail. */
+#define msg_avail ((readb(&msg_o)>readb(&msg_i))? \
+ 0x100-readb(&msg_o)+readb(&msg_i): \
+ readb(&msg_i)-readb(&msg_o))
+
+#define CID (card->interface.id)
+
+#endif /* defined(__KERNEL__) || defined(__DEBUGVAR__) */
+#endif /* icn_h */
diff --git a/drivers/isdn/isdnloop/Makefile b/drivers/isdn/isdnloop/Makefile
new file mode 100644
index 0000000..317cd3c
--- /dev/null
+++ b/drivers/isdn/isdnloop/Makefile
@@ -0,0 +1,5 @@
+# Makefile for the isdnloop ISDN device driver
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_ISDN_DRV_LOOP) += isdnloop.o
diff --git a/drivers/isdn/isdnloop/isdnloop.c b/drivers/isdn/isdnloop/isdnloop.c
new file mode 100644
index 0000000..a335c85
--- /dev/null
+++ b/drivers/isdn/isdnloop/isdnloop.c
@@ -0,0 +1,1541 @@
+/* $Id: isdnloop.c,v 1.11.6.7 2001/11/11 19:54:31 kai Exp $
+ *
+ * ISDN low-level module implementing a dummy loop driver.
+ *
+ * Copyright 1997 by Fritz Elfert (fritz@isdn4linux.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include "isdnloop.h"
+
+static char *revision = "$Revision: 1.11.6.7 $";
+static char *isdnloop_id = "loop0";
+
+MODULE_DESCRIPTION("ISDN4Linux: Pseudo Driver that simulates an ISDN card");
+MODULE_AUTHOR("Fritz Elfert");
+MODULE_LICENSE("GPL");
+module_param(isdnloop_id, charp, 0);
+MODULE_PARM_DESC(isdnloop_id, "ID-String of first card");
+
+static int isdnloop_addcard(char *);
+
+/*
+ * Free queue completely.
+ *
+ * Parameter:
+ * card = pointer to card struct
+ * channel = channel number
+ */
+static void
+isdnloop_free_queue(isdnloop_card * card, int channel)
+{
+ struct sk_buff_head *queue = &card->bqueue[channel];
+
+ skb_queue_purge(queue);
+ card->sndcount[channel] = 0;
+}
+
+/*
+ * Send B-Channel data to another virtual card.
+ * This routine is called via timer-callback from isdnloop_pollbchan().
+ *
+ * Parameter:
+ * card = pointer to card struct.
+ * ch = channel number (0-based)
+ */
+static void
+isdnloop_bchan_send(isdnloop_card * card, int ch)
+{
+ isdnloop_card *rcard = card->rcard[ch];
+ int rch = card->rch[ch], len, ack;
+ struct sk_buff *skb;
+ isdn_ctrl cmd;
+
+ while (card->sndcount[ch]) {
+ if ((skb = skb_dequeue(&card->bqueue[ch]))) {
+ len = skb->len;
+ card->sndcount[ch] -= len;
+ ack = *(skb->head); /* used as scratch area */
+ cmd.driver = card->myid;
+ cmd.arg = ch;
+ if (rcard){
+ rcard->interface.rcvcallb_skb(rcard->myid, rch, skb);
+ } else {
+ printk(KERN_WARNING "isdnloop: no rcard, skb dropped\n");
+ dev_kfree_skb(skb);
+
+ };
+ cmd.command = ISDN_STAT_BSENT;
+ cmd.parm.length = len;
+ card->interface.statcallb(&cmd);
+ } else
+ card->sndcount[ch] = 0;
+ }
+}
+
+/*
+ * Send/Receive Data to/from the B-Channel.
+ * This routine is called via timer-callback.
+ * It schedules itself while any B-Channel is open.
+ *
+ * Parameter:
+ * data = pointer to card struct, set by kernel timer.data
+ */
+static void
+isdnloop_pollbchan(unsigned long data)
+{
+ isdnloop_card *card = (isdnloop_card *) data;
+ unsigned long flags;
+
+ if (card->flags & ISDNLOOP_FLAGS_B1ACTIVE)
+ isdnloop_bchan_send(card, 0);
+ if (card->flags & ISDNLOOP_FLAGS_B2ACTIVE)
+ isdnloop_bchan_send(card, 1);
+ if (card->flags & (ISDNLOOP_FLAGS_B1ACTIVE | ISDNLOOP_FLAGS_B2ACTIVE)) {
+ /* schedule b-channel polling again */
+ spin_lock_irqsave(&card->isdnloop_lock, flags);
+ card->rb_timer.expires = jiffies + ISDNLOOP_TIMER_BCREAD;
+ add_timer(&card->rb_timer);
+ card->flags |= ISDNLOOP_FLAGS_RBTIMER;
+ spin_unlock_irqrestore(&card->isdnloop_lock, flags);
+ } else
+ card->flags &= ~ISDNLOOP_FLAGS_RBTIMER;
+}
+
+/*
+ * Parse ICN-type setup string and fill fields of setup-struct
+ * with parsed data.
+ *
+ * Parameter:
+ * setup = setup string, format: [caller-id],si1,si2,[called-id]
+ * cmd = pointer to struct to be filled.
+ */
+static void
+isdnloop_parse_setup(char *setup, isdn_ctrl * cmd)
+{
+ char *t = setup;
+ char *s = strchr(t, ',');
+
+ *s++ = '\0';
+ strlcpy(cmd->parm.setup.phone, t, sizeof(cmd->parm.setup.phone));
+ s = strchr(t = s, ',');
+ *s++ = '\0';
+ if (!strlen(t))
+ cmd->parm.setup.si1 = 0;
+ else
+ cmd->parm.setup.si1 = simple_strtoul(t, NULL, 10);
+ s = strchr(t = s, ',');
+ *s++ = '\0';
+ if (!strlen(t))
+ cmd->parm.setup.si2 = 0;
+ else
+ cmd->parm.setup.si2 =
+ simple_strtoul(t, NULL, 10);
+ strlcpy(cmd->parm.setup.eazmsn, s, sizeof(cmd->parm.setup.eazmsn));
+ cmd->parm.setup.plan = 0;
+ cmd->parm.setup.screen = 0;
+}
+
+typedef struct isdnloop_stat {
+ char *statstr;
+ int command;
+ int action;
+} isdnloop_stat;
+/* *INDENT-OFF* */
+static isdnloop_stat isdnloop_stat_table[] =
+{
+ {"BCON_", ISDN_STAT_BCONN, 1}, /* B-Channel connected */
+ {"BDIS_", ISDN_STAT_BHUP, 2}, /* B-Channel disconnected */
+ {"DCON_", ISDN_STAT_DCONN, 0}, /* D-Channel connected */
+ {"DDIS_", ISDN_STAT_DHUP, 0}, /* D-Channel disconnected */
+ {"DCAL_I", ISDN_STAT_ICALL, 3}, /* Incoming call dialup-line */
+ {"DSCA_I", ISDN_STAT_ICALL, 3}, /* Incoming call 1TR6-SPV */
+ {"FCALL", ISDN_STAT_ICALL, 4}, /* Leased line connection up */
+ {"CIF", ISDN_STAT_CINF, 5}, /* Charge-info, 1TR6-type */
+ {"AOC", ISDN_STAT_CINF, 6}, /* Charge-info, DSS1-type */
+ {"CAU", ISDN_STAT_CAUSE, 7}, /* Cause code */
+ {"TEI OK", ISDN_STAT_RUN, 0}, /* Card connected to wallplug */
+ {"E_L1: ACT FAIL", ISDN_STAT_BHUP, 8}, /* Layer-1 activation failed */
+ {"E_L2: DATA LIN", ISDN_STAT_BHUP, 8}, /* Layer-2 data link lost */
+ {"E_L1: ACTIVATION FAILED",
+ ISDN_STAT_BHUP, 8}, /* Layer-1 activation failed */
+ {NULL, 0, -1}
+};
+/* *INDENT-ON* */
+
+
+/*
+ * Parse Status message-strings from virtual card.
+ * Depending on status, call statcallb for sending messages to upper
+ * levels. Also set/reset B-Channel active-flags.
+ *
+ * Parameter:
+ * status = status string to parse.
+ * channel = channel where message comes from.
+ * card = card where message comes from.
+ */
+static void
+isdnloop_parse_status(u_char * status, int channel, isdnloop_card * card)
+{
+ isdnloop_stat *s = isdnloop_stat_table;
+ int action = -1;
+ isdn_ctrl cmd;
+
+ while (s->statstr) {
+ if (!strncmp(status, s->statstr, strlen(s->statstr))) {
+ cmd.command = s->command;
+ action = s->action;
+ break;
+ }
+ s++;
+ }
+ if (action == -1)
+ return;
+ cmd.driver = card->myid;
+ cmd.arg = channel;
+ switch (action) {
+ case 1:
+ /* BCON_x */
+ card->flags |= (channel) ?
+ ISDNLOOP_FLAGS_B2ACTIVE : ISDNLOOP_FLAGS_B1ACTIVE;
+ break;
+ case 2:
+ /* BDIS_x */
+ card->flags &= ~((channel) ?
+ ISDNLOOP_FLAGS_B2ACTIVE : ISDNLOOP_FLAGS_B1ACTIVE);
+ isdnloop_free_queue(card, channel);
+ break;
+ case 3:
+ /* DCAL_I and DSCA_I */
+ isdnloop_parse_setup(status + 6, &cmd);
+ break;
+ case 4:
+ /* FCALL */
+ sprintf(cmd.parm.setup.phone, "LEASED%d", card->myid);
+ sprintf(cmd.parm.setup.eazmsn, "%d", channel + 1);
+ cmd.parm.setup.si1 = 7;
+ cmd.parm.setup.si2 = 0;
+ cmd.parm.setup.plan = 0;
+ cmd.parm.setup.screen = 0;
+ break;
+ case 5:
+ /* CIF */
+ strlcpy(cmd.parm.num, status + 3, sizeof(cmd.parm.num));
+ break;
+ case 6:
+ /* AOC */
+ snprintf(cmd.parm.num, sizeof(cmd.parm.num), "%d",
+ (int) simple_strtoul(status + 7, NULL, 16));
+ break;
+ case 7:
+ /* CAU */
+ status += 3;
+ if (strlen(status) == 4)
+ snprintf(cmd.parm.num, sizeof(cmd.parm.num), "%s%c%c",
+ status + 2, *status, *(status + 1));
+ else
+ strlcpy(cmd.parm.num, status + 1, sizeof(cmd.parm.num));
+ break;
+ case 8:
+ /* Misc Errors on L1 and L2 */
+ card->flags &= ~ISDNLOOP_FLAGS_B1ACTIVE;
+ isdnloop_free_queue(card, 0);
+ cmd.arg = 0;
+ cmd.driver = card->myid;
+ card->interface.statcallb(&cmd);
+ cmd.command = ISDN_STAT_DHUP;
+ cmd.arg = 0;
+ cmd.driver = card->myid;
+ card->interface.statcallb(&cmd);
+ cmd.command = ISDN_STAT_BHUP;
+ card->flags &= ~ISDNLOOP_FLAGS_B2ACTIVE;
+ isdnloop_free_queue(card, 1);
+ cmd.arg = 1;
+ cmd.driver = card->myid;
+ card->interface.statcallb(&cmd);
+ cmd.command = ISDN_STAT_DHUP;
+ cmd.arg = 1;
+ cmd.driver = card->myid;
+ break;
+ }
+ card->interface.statcallb(&cmd);
+}
+
+/*
+ * Store a cwcharacter into ringbuffer for reading from /dev/isdnctrl
+ *
+ * Parameter:
+ * card = pointer to card struct.
+ * c = char to store.
+ */
+static void
+isdnloop_putmsg(isdnloop_card * card, unsigned char c)
+{
+ ulong flags;
+
+ spin_lock_irqsave(&card->isdnloop_lock, flags);
+ *card->msg_buf_write++ = (c == 0xff) ? '\n' : c;
+ if (card->msg_buf_write == card->msg_buf_read) {
+ if (++card->msg_buf_read > card->msg_buf_end)
+ card->msg_buf_read = card->msg_buf;
+ }
+ if (card->msg_buf_write > card->msg_buf_end)
+ card->msg_buf_write = card->msg_buf;
+ spin_unlock_irqrestore(&card->isdnloop_lock, flags);
+}
+
+/*
+ * Poll a virtual cards message queue.
+ * If there are new status-replies from the card, copy them to
+ * ringbuffer for reading on /dev/isdnctrl and call
+ * isdnloop_parse_status() for processing them. Watch for special
+ * Firmware bootmessage and parse it, to get the D-Channel protocol.
+ * If there are B-Channels open, initiate a timer-callback to
+ * isdnloop_pollbchan().
+ * This routine is called periodically via timer interrupt.
+ *
+ * Parameter:
+ * data = pointer to card struct
+ */
+static void
+isdnloop_polldchan(unsigned long data)
+{
+ isdnloop_card *card = (isdnloop_card *) data;
+ struct sk_buff *skb;
+ int avail;
+ int left;
+ u_char c;
+ int ch;
+ unsigned long flags;
+ u_char *p;
+ isdn_ctrl cmd;
+
+ if ((skb = skb_dequeue(&card->dqueue)))
+ avail = skb->len;
+ else
+ avail = 0;
+ for (left = avail; left > 0; left--) {
+ c = *skb->data;
+ skb_pull(skb, 1);
+ isdnloop_putmsg(card, c);
+ card->imsg[card->iptr] = c;
+ if (card->iptr < 59)
+ card->iptr++;
+ if (!skb->len) {
+ avail++;
+ isdnloop_putmsg(card, '\n');
+ card->imsg[card->iptr] = 0;
+ card->iptr = 0;
+ if (card->imsg[0] == '0' && card->imsg[1] >= '0' &&
+ card->imsg[1] <= '2' && card->imsg[2] == ';') {
+ ch = (card->imsg[1] - '0') - 1;
+ p = &card->imsg[3];
+ isdnloop_parse_status(p, ch, card);
+ } else {
+ p = card->imsg;
+ if (!strncmp(p, "DRV1.", 5)) {
+ printk(KERN_INFO "isdnloop: (%s) %s\n", CID, p);
+ if (!strncmp(p + 7, "TC", 2)) {
+ card->ptype = ISDN_PTYPE_1TR6;
+ card->interface.features |= ISDN_FEATURE_P_1TR6;
+ printk(KERN_INFO
+ "isdnloop: (%s) 1TR6-Protocol loaded and running\n", CID);
+ }
+ if (!strncmp(p + 7, "EC", 2)) {
+ card->ptype = ISDN_PTYPE_EURO;
+ card->interface.features |= ISDN_FEATURE_P_EURO;
+ printk(KERN_INFO
+ "isdnloop: (%s) Euro-Protocol loaded and running\n", CID);
+ }
+ continue;
+
+ }
+ }
+ }
+ }
+ if (avail) {
+ cmd.command = ISDN_STAT_STAVAIL;
+ cmd.driver = card->myid;
+ cmd.arg = avail;
+ card->interface.statcallb(&cmd);
+ }
+ if (card->flags & (ISDNLOOP_FLAGS_B1ACTIVE | ISDNLOOP_FLAGS_B2ACTIVE))
+ if (!(card->flags & ISDNLOOP_FLAGS_RBTIMER)) {
+ /* schedule b-channel polling */
+ card->flags |= ISDNLOOP_FLAGS_RBTIMER;
+ spin_lock_irqsave(&card->isdnloop_lock, flags);
+ del_timer(&card->rb_timer);
+ card->rb_timer.function = isdnloop_pollbchan;
+ card->rb_timer.data = (unsigned long) card;
+ card->rb_timer.expires = jiffies + ISDNLOOP_TIMER_BCREAD;
+ add_timer(&card->rb_timer);
+ spin_unlock_irqrestore(&card->isdnloop_lock, flags);
+ }
+ /* schedule again */
+ spin_lock_irqsave(&card->isdnloop_lock, flags);
+ card->st_timer.expires = jiffies + ISDNLOOP_TIMER_DCREAD;
+ add_timer(&card->st_timer);
+ spin_unlock_irqrestore(&card->isdnloop_lock, flags);
+}
+
+/*
+ * Append a packet to the transmit buffer-queue.
+ *
+ * Parameter:
+ * channel = Number of B-channel
+ * skb = packet to send.
+ * card = pointer to card-struct
+ * Return:
+ * Number of bytes transferred, -E??? on error
+ */
+static int
+isdnloop_sendbuf(int channel, struct sk_buff *skb, isdnloop_card * card)
+{
+ int len = skb->len;
+ unsigned long flags;
+ struct sk_buff *nskb;
+
+ if (len > 4000) {
+ printk(KERN_WARNING
+ "isdnloop: Send packet too large\n");
+ return -EINVAL;
+ }
+ if (len) {
+ if (!(card->flags & (channel) ? ISDNLOOP_FLAGS_B2ACTIVE : ISDNLOOP_FLAGS_B1ACTIVE))
+ return 0;
+ if (card->sndcount[channel] > ISDNLOOP_MAX_SQUEUE)
+ return 0;
+ spin_lock_irqsave(&card->isdnloop_lock, flags);
+ nskb = dev_alloc_skb(skb->len);
+ if (nskb) {
+ skb_copy_from_linear_data(skb,
+ skb_put(nskb, len), len);
+ skb_queue_tail(&card->bqueue[channel], nskb);
+ dev_kfree_skb(skb);
+ } else
+ len = 0;
+ card->sndcount[channel] += len;
+ spin_unlock_irqrestore(&card->isdnloop_lock, flags);
+ }
+ return len;
+}
+
+/*
+ * Read the messages from the card's ringbuffer
+ *
+ * Parameter:
+ * buf = pointer to buffer.
+ * len = number of bytes to read.
+ * user = flag, 1: called from userlevel 0: called from kernel.
+ * card = pointer to card struct.
+ * Return:
+ * number of bytes actually transferred.
+ */
+static int
+isdnloop_readstatus(u_char __user *buf, int len, isdnloop_card * card)
+{
+ int count;
+ u_char __user *p;
+
+ for (p = buf, count = 0; count < len; p++, count++) {
+ if (card->msg_buf_read == card->msg_buf_write)
+ return count;
+ if (put_user(*card->msg_buf_read++, p))
+ return -EFAULT;
+ if (card->msg_buf_read > card->msg_buf_end)
+ card->msg_buf_read = card->msg_buf;
+ }
+ return count;
+}
+
+/*
+ * Simulate a card's response by appending it to the cards
+ * message queue.
+ *
+ * Parameter:
+ * card = pointer to card struct.
+ * s = pointer to message-string.
+ * ch = channel: 0 = generic messages, 1 and 2 = D-channel messages.
+ * Return:
+ * 0 on success, 1 on memory squeeze.
+ */
+static int
+isdnloop_fake(isdnloop_card * card, char *s, int ch)
+{
+ struct sk_buff *skb;
+ int len = strlen(s) + ((ch >= 0) ? 3 : 0);
+
+ if (!(skb = dev_alloc_skb(len))) {
+ printk(KERN_WARNING "isdnloop: Out of memory in isdnloop_fake\n");
+ return 1;
+ }
+ if (ch >= 0)
+ sprintf(skb_put(skb, 3), "%02d;", ch);
+ memcpy(skb_put(skb, strlen(s)), s, strlen(s));
+ skb_queue_tail(&card->dqueue, skb);
+ return 0;
+}
+/* *INDENT-OFF* */
+static isdnloop_stat isdnloop_cmd_table[] =
+{
+ {"BCON_R", 0, 1}, /* B-Channel connect */
+ {"BCON_I", 0, 17}, /* B-Channel connect ind */
+ {"BDIS_R", 0, 2}, /* B-Channel disconnect */
+ {"DDIS_R", 0, 3}, /* D-Channel disconnect */
+ {"DCON_R", 0, 16}, /* D-Channel connect */
+ {"DSCA_R", 0, 4}, /* Dial 1TR6-SPV */
+ {"DCAL_R", 0, 5}, /* Dial */
+ {"EAZC", 0, 6}, /* Clear EAZ listener */
+ {"EAZ", 0, 7}, /* Set EAZ listener */
+ {"SEEAZ", 0, 8}, /* Get EAZ listener */
+ {"MSN", 0, 9}, /* Set/Clear MSN listener */
+ {"MSALL", 0, 10}, /* Set multi MSN listeners */
+ {"SETSIL", 0, 11}, /* Set SI list */
+ {"SEESIL", 0, 12}, /* Get SI list */
+ {"SILC", 0, 13}, /* Clear SI list */
+ {"LOCK", 0, -1}, /* LOCK channel */
+ {"UNLOCK", 0, -1}, /* UNLOCK channel */
+ {"FV2ON", 1, 14}, /* Leased mode on */
+ {"FV2OFF", 1, 15}, /* Leased mode off */
+ {NULL, 0, -1}
+};
+/* *INDENT-ON* */
+
+
+/*
+ * Simulate an error-response from a card.
+ *
+ * Parameter:
+ * card = pointer to card struct.
+ */
+static void
+isdnloop_fake_err(isdnloop_card * card)
+{
+ char buf[60];
+
+ sprintf(buf, "E%s", card->omsg);
+ isdnloop_fake(card, buf, -1);
+ isdnloop_fake(card, "NAK", -1);
+}
+
+static u_char ctable_eu[] =
+{0x00, 0x11, 0x01, 0x12};
+static u_char ctable_1t[] =
+{0x00, 0x3b, 0x01, 0x3a};
+
+/*
+ * Assemble a simplified cause message depending on the
+ * D-channel protocol used.
+ *
+ * Parameter:
+ * card = pointer to card struct.
+ * loc = location: 0 = local, 1 = remote.
+ * cau = cause: 1 = busy, 2 = nonexistent callerid, 3 = no user responding.
+ * Return:
+ * Pointer to buffer containing the assembled message.
+ */
+static char *
+isdnloop_unicause(isdnloop_card * card, int loc, int cau)
+{
+ static char buf[6];
+
+ switch (card->ptype) {
+ case ISDN_PTYPE_EURO:
+ sprintf(buf, "E%02X%02X", (loc) ? 4 : 2, ctable_eu[cau]);
+ break;
+ case ISDN_PTYPE_1TR6:
+ sprintf(buf, "%02X44", ctable_1t[cau]);
+ break;
+ default:
+ return ("0000");
+ }
+ return (buf);
+}
+
+/*
+ * Release a virtual connection. Called from timer interrupt, when
+ * called party did not respond.
+ *
+ * Parameter:
+ * card = pointer to card struct.
+ * ch = channel (0-based)
+ */
+static void
+isdnloop_atimeout(isdnloop_card * card, int ch)
+{
+ unsigned long flags;
+ char buf[60];
+
+ spin_lock_irqsave(&card->isdnloop_lock, flags);
+ if (card->rcard) {
+ isdnloop_fake(card->rcard[ch], "DDIS_I", card->rch[ch] + 1);
+ card->rcard[ch]->rcard[card->rch[ch]] = NULL;
+ card->rcard[ch] = NULL;
+ }
+ isdnloop_fake(card, "DDIS_I", ch + 1);
+ /* No user responding */
+ sprintf(buf, "CAU%s", isdnloop_unicause(card, 1, 3));
+ isdnloop_fake(card, buf, ch + 1);
+ spin_unlock_irqrestore(&card->isdnloop_lock, flags);
+}
+
+/*
+ * Wrapper for isdnloop_atimeout().
+ */
+static void
+isdnloop_atimeout0(unsigned long data)
+{
+ isdnloop_card *card = (isdnloop_card *) data;
+ isdnloop_atimeout(card, 0);
+}
+
+/*
+ * Wrapper for isdnloop_atimeout().
+ */
+static void
+isdnloop_atimeout1(unsigned long data)
+{
+ isdnloop_card *card = (isdnloop_card *) data;
+ isdnloop_atimeout(card, 1);
+}
+
+/*
+ * Install a watchdog for a user, not responding.
+ *
+ * Parameter:
+ * card = pointer to card struct.
+ * ch = channel to watch for.
+ */
+static void
+isdnloop_start_ctimer(isdnloop_card * card, int ch)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&card->isdnloop_lock, flags);
+ init_timer(&card->c_timer[ch]);
+ card->c_timer[ch].expires = jiffies + ISDNLOOP_TIMER_ALERTWAIT;
+ if (ch)
+ card->c_timer[ch].function = isdnloop_atimeout1;
+ else
+ card->c_timer[ch].function = isdnloop_atimeout0;
+ card->c_timer[ch].data = (unsigned long) card;
+ add_timer(&card->c_timer[ch]);
+ spin_unlock_irqrestore(&card->isdnloop_lock, flags);
+}
+
+/*
+ * Kill a pending channel watchdog.
+ *
+ * Parameter:
+ * card = pointer to card struct.
+ * ch = channel (0-based).
+ */
+static void
+isdnloop_kill_ctimer(isdnloop_card * card, int ch)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&card->isdnloop_lock, flags);
+ del_timer(&card->c_timer[ch]);
+ spin_unlock_irqrestore(&card->isdnloop_lock, flags);
+}
+
+static u_char si2bit[] =
+{0, 1, 0, 0, 0, 2, 0, 4, 0, 0};
+static u_char bit2si[] =
+{1, 5, 7};
+
+/*
+ * Try finding a listener for an outgoing call.
+ *
+ * Parameter:
+ * card = pointer to calling card.
+ * p = pointer to ICN-type setup-string.
+ * lch = channel of calling card.
+ * cmd = pointer to struct to be filled when parsing setup.
+ * Return:
+ * 0 = found match, alerting should happen.
+ * 1 = found matching number but it is busy.
+ * 2 = no matching listener.
+ * 3 = found matching number but SI does not match.
+ */
+static int
+isdnloop_try_call(isdnloop_card * card, char *p, int lch, isdn_ctrl * cmd)
+{
+ isdnloop_card *cc = cards;
+ unsigned long flags;
+ int ch;
+ int num_match;
+ int i;
+ char *e;
+ char nbuf[32];
+
+ isdnloop_parse_setup(p, cmd);
+ while (cc) {
+ for (ch = 0; ch < 2; ch++) {
+ /* Exclude ourself */
+ if ((cc == card) && (ch == lch))
+ continue;
+ num_match = 0;
+ switch (cc->ptype) {
+ case ISDN_PTYPE_EURO:
+ for (i = 0; i < 3; i++)
+ if (!(strcmp(cc->s0num[i], cmd->parm.setup.phone)))
+ num_match = 1;
+ break;
+ case ISDN_PTYPE_1TR6:
+ e = cc->eazlist[ch];
+ while (*e) {
+ sprintf(nbuf, "%s%c", cc->s0num[0], *e);
+ if (!(strcmp(nbuf, cmd->parm.setup.phone)))
+ num_match = 1;
+ e++;
+ }
+ }
+ if (num_match) {
+ spin_lock_irqsave(&card->isdnloop_lock, flags);
+ /* channel idle? */
+ if (!(cc->rcard[ch])) {
+ /* Check SI */
+ if (!(si2bit[cmd->parm.setup.si1] & cc->sil[ch])) {
+ spin_unlock_irqrestore(&card->isdnloop_lock, flags);
+ return 3;
+ }
+ /* ch is idle, si and number matches */
+ cc->rcard[ch] = card;
+ cc->rch[ch] = lch;
+ card->rcard[lch] = cc;
+ card->rch[lch] = ch;
+ spin_unlock_irqrestore(&card->isdnloop_lock, flags);
+ return 0;
+ } else {
+ spin_unlock_irqrestore(&card->isdnloop_lock, flags);
+ /* num matches, but busy */
+ if (ch == 1)
+ return 1;
+ }
+ }
+ }
+ cc = cc->next;
+ }
+ return 2;
+}
+
+/*
+ * Depending on D-channel protocol and caller/called, modify
+ * phone number.
+ *
+ * Parameter:
+ * card = pointer to card struct.
+ * phone = pointer phone number.
+ * caller = flag: 1 = caller, 0 = called.
+ * Return:
+ * pointer to new phone number.
+ */
+static char *
+isdnloop_vstphone(isdnloop_card * card, char *phone, int caller)
+{
+ int i;
+ static char nphone[30];
+
+ if (!card) {
+ printk("BUG!!!\n");
+ return "";
+ }
+ switch (card->ptype) {
+ case ISDN_PTYPE_EURO:
+ if (caller) {
+ for (i = 0; i < 2; i++)
+ if (!(strcmp(card->s0num[i], phone)))
+ return (phone);
+ return (card->s0num[0]);
+ }
+ return (phone);
+ break;
+ case ISDN_PTYPE_1TR6:
+ if (caller) {
+ sprintf(nphone, "%s%c", card->s0num[0], phone[0]);
+ return (nphone);
+ } else
+ return (&phone[strlen(phone) - 1]);
+ break;
+ }
+ return "";
+}
+
+/*
+ * Parse an ICN-type command string sent to the 'card'.
+ * Perform misc. actions depending on the command.
+ *
+ * Parameter:
+ * card = pointer to card struct.
+ */
+static void
+isdnloop_parse_cmd(isdnloop_card * card)
+{
+ char *p = card->omsg;
+ isdn_ctrl cmd;
+ char buf[60];
+ isdnloop_stat *s = isdnloop_cmd_table;
+ int action = -1;
+ int i;
+ int ch;
+
+ if ((card->omsg[0] != '0') && (card->omsg[2] != ';')) {
+ isdnloop_fake_err(card);
+ return;
+ }
+ ch = card->omsg[1] - '0';
+ if ((ch < 0) || (ch > 2)) {
+ isdnloop_fake_err(card);
+ return;
+ }
+ p += 3;
+ while (s->statstr) {
+ if (!strncmp(p, s->statstr, strlen(s->statstr))) {
+ action = s->action;
+ if (s->command && (ch != 0)) {
+ isdnloop_fake_err(card);
+ return;
+ }
+ break;
+ }
+ s++;
+ }
+ if (action == -1)
+ return;
+ switch (action) {
+ case 1:
+ /* 0x;BCON_R */
+ if (card->rcard[ch - 1]) {
+ isdnloop_fake(card->rcard[ch - 1], "BCON_I",
+ card->rch[ch - 1] + 1);
+ isdnloop_fake(card, "BCON_C", ch);
+ }
+ break;
+ case 17:
+ /* 0x;BCON_I */
+ if (card->rcard[ch - 1]) {
+ isdnloop_fake(card->rcard[ch - 1], "BCON_C",
+ card->rch[ch - 1] + 1);
+ }
+ break;
+ case 2:
+ /* 0x;BDIS_R */
+ isdnloop_fake(card, "BDIS_C", ch);
+ if (card->rcard[ch - 1]) {
+ isdnloop_fake(card->rcard[ch - 1], "BDIS_I",
+ card->rch[ch - 1] + 1);
+ }
+ break;
+ case 16:
+ /* 0x;DCON_R */
+ isdnloop_kill_ctimer(card, ch - 1);
+ if (card->rcard[ch - 1]) {
+ isdnloop_kill_ctimer(card->rcard[ch - 1], card->rch[ch - 1]);
+ isdnloop_fake(card->rcard[ch - 1], "DCON_C",
+ card->rch[ch - 1] + 1);
+ isdnloop_fake(card, "DCON_C", ch);
+ }
+ break;
+ case 3:
+ /* 0x;DDIS_R */
+ isdnloop_kill_ctimer(card, ch - 1);
+ if (card->rcard[ch - 1]) {
+ isdnloop_kill_ctimer(card->rcard[ch - 1], card->rch[ch - 1]);
+ isdnloop_fake(card->rcard[ch - 1], "DDIS_I",
+ card->rch[ch - 1] + 1);
+ card->rcard[ch - 1] = NULL;
+ }
+ isdnloop_fake(card, "DDIS_C", ch);
+ break;
+ case 4:
+ /* 0x;DSCA_Rdd,yy,zz,oo */
+ if (card->ptype != ISDN_PTYPE_1TR6) {
+ isdnloop_fake_err(card);
+ return;
+ }
+ /* Fall through */
+ case 5:
+ /* 0x;DCAL_Rdd,yy,zz,oo */
+ p += 6;
+ switch (isdnloop_try_call(card, p, ch - 1, &cmd)) {
+ case 0:
+ /* Alerting */
+ sprintf(buf, "D%s_I%s,%02d,%02d,%s",
+ (action == 4) ? "SCA" : "CAL",
+ isdnloop_vstphone(card, cmd.parm.setup.eazmsn, 1),
+ cmd.parm.setup.si1,
+ cmd.parm.setup.si2,
+ isdnloop_vstphone(card->rcard[ch - 1],
+ cmd.parm.setup.phone, 0));
+ isdnloop_fake(card->rcard[ch - 1], buf, card->rch[ch - 1] + 1);
+ /* Fall through */
+ case 3:
+ /* si1 does not match, don't alert but start timer */
+ isdnloop_start_ctimer(card, ch - 1);
+ break;
+ case 1:
+ /* Remote busy */
+ isdnloop_fake(card, "DDIS_I", ch);
+ sprintf(buf, "CAU%s", isdnloop_unicause(card, 1, 1));
+ isdnloop_fake(card, buf, ch);
+ break;
+ case 2:
+ /* No such user */
+ isdnloop_fake(card, "DDIS_I", ch);
+ sprintf(buf, "CAU%s", isdnloop_unicause(card, 1, 2));
+ isdnloop_fake(card, buf, ch);
+ break;
+ }
+ break;
+ case 6:
+ /* 0x;EAZC */
+ card->eazlist[ch - 1][0] = '\0';
+ break;
+ case 7:
+ /* 0x;EAZ */
+ p += 3;
+ strcpy(card->eazlist[ch - 1], p);
+ break;
+ case 8:
+ /* 0x;SEEAZ */
+ sprintf(buf, "EAZ-LIST: %s", card->eazlist[ch - 1]);
+ isdnloop_fake(card, buf, ch + 1);
+ break;
+ case 9:
+ /* 0x;MSN */
+ break;
+ case 10:
+ /* 0x;MSNALL */
+ break;
+ case 11:
+ /* 0x;SETSIL */
+ p += 6;
+ i = 0;
+ while (strchr("0157", *p)) {
+ if (i)
+ card->sil[ch - 1] |= si2bit[*p - '0'];
+ i = (*p++ == '0');
+ }
+ if (*p)
+ isdnloop_fake_err(card);
+ break;
+ case 12:
+ /* 0x;SEESIL */
+ sprintf(buf, "SIN-LIST: ");
+ p = buf + 10;
+ for (i = 0; i < 3; i++)
+ if (card->sil[ch - 1] & (1 << i))
+ p += sprintf(p, "%02d", bit2si[i]);
+ isdnloop_fake(card, buf, ch + 1);
+ break;
+ case 13:
+ /* 0x;SILC */
+ card->sil[ch - 1] = 0;
+ break;
+ case 14:
+ /* 00;FV2ON */
+ break;
+ case 15:
+ /* 00;FV2OFF */
+ break;
+ }
+}
+
+/*
+ * Put command-strings into the of the 'card'. In reality, execute them
+ * right in place by calling isdnloop_parse_cmd(). Also copy every
+ * command to the read message ringbuffer, preceeding it with a '>'.
+ * These mesagges can be read at /dev/isdnctrl.
+ *
+ * Parameter:
+ * buf = pointer to command buffer.
+ * len = length of buffer data.
+ * user = flag: 1 = called form userlevel, 0 called from kernel.
+ * card = pointer to card struct.
+ * Return:
+ * number of bytes transferred (currently always equals len).
+ */
+static int
+isdnloop_writecmd(const u_char * buf, int len, int user, isdnloop_card * card)
+{
+ int xcount = 0;
+ int ocount = 1;
+ isdn_ctrl cmd;
+
+ while (len) {
+ int count = len;
+ u_char *p;
+ u_char msg[0x100];
+
+ if (count > 255)
+ count = 255;
+ if (user) {
+ if (copy_from_user(msg, buf, count))
+ return -EFAULT;
+ } else
+ memcpy(msg, buf, count);
+ isdnloop_putmsg(card, '>');
+ for (p = msg; count > 0; count--, p++) {
+ len--;
+ xcount++;
+ isdnloop_putmsg(card, *p);
+ card->omsg[card->optr] = *p;
+ if (*p == '\n') {
+ card->omsg[card->optr] = '\0';
+ card->optr = 0;
+ isdnloop_parse_cmd(card);
+ if (len) {
+ isdnloop_putmsg(card, '>');
+ ocount++;
+ }
+ } else {
+ if (card->optr < 59)
+ card->optr++;
+ }
+ ocount++;
+ }
+ }
+ cmd.command = ISDN_STAT_STAVAIL;
+ cmd.driver = card->myid;
+ cmd.arg = ocount;
+ card->interface.statcallb(&cmd);
+ return xcount;
+}
+
+/*
+ * Delete card's pending timers, send STOP to linklevel
+ */
+static void
+isdnloop_stopcard(isdnloop_card * card)
+{
+ unsigned long flags;
+ isdn_ctrl cmd;
+
+ spin_lock_irqsave(&card->isdnloop_lock, flags);
+ if (card->flags & ISDNLOOP_FLAGS_RUNNING) {
+ card->flags &= ~ISDNLOOP_FLAGS_RUNNING;
+ del_timer(&card->st_timer);
+ del_timer(&card->rb_timer);
+ del_timer(&card->c_timer[0]);
+ del_timer(&card->c_timer[1]);
+ cmd.command = ISDN_STAT_STOP;
+ cmd.driver = card->myid;
+ card->interface.statcallb(&cmd);
+ }
+ spin_unlock_irqrestore(&card->isdnloop_lock, flags);
+}
+
+/*
+ * Stop all cards before unload.
+ */
+static void
+isdnloop_stopallcards(void)
+{
+ isdnloop_card *p = cards;
+
+ while (p) {
+ isdnloop_stopcard(p);
+ p = p->next;
+ }
+}
+
+/*
+ * Start a 'card'. Simulate card's boot message and set the phone
+ * number(s) of the virtual 'S0-Interface'. Install D-channel
+ * poll timer.
+ *
+ * Parameter:
+ * card = pointer to card struct.
+ * sdefp = pointer to struct holding ioctl parameters.
+ * Return:
+ * 0 on success, -E??? otherwise.
+ */
+static int
+isdnloop_start(isdnloop_card * card, isdnloop_sdef * sdefp)
+{
+ unsigned long flags;
+ isdnloop_sdef sdef;
+ int i;
+
+ if (card->flags & ISDNLOOP_FLAGS_RUNNING)
+ return -EBUSY;
+ if (copy_from_user((char *) &sdef, (char *) sdefp, sizeof(sdef)))
+ return -EFAULT;
+ spin_lock_irqsave(&card->isdnloop_lock, flags);
+ switch (sdef.ptype) {
+ case ISDN_PTYPE_EURO:
+ if (isdnloop_fake(card, "DRV1.23EC-Q.931-CAPI-CNS-BASIS-20.02.96",
+ -1)) {
+ spin_unlock_irqrestore(&card->isdnloop_lock, flags);
+ return -ENOMEM;
+ }
+ card->sil[0] = card->sil[1] = 4;
+ if (isdnloop_fake(card, "TEI OK", 0)) {
+ spin_unlock_irqrestore(&card->isdnloop_lock, flags);
+ return -ENOMEM;
+ }
+ for (i = 0; i < 3; i++)
+ strcpy(card->s0num[i], sdef.num[i]);
+ break;
+ case ISDN_PTYPE_1TR6:
+ if (isdnloop_fake(card, "DRV1.04TC-1TR6-CAPI-CNS-BASIS-29.11.95",
+ -1)) {
+ spin_unlock_irqrestore(&card->isdnloop_lock, flags);
+ return -ENOMEM;
+ }
+ card->sil[0] = card->sil[1] = 4;
+ if (isdnloop_fake(card, "TEI OK", 0)) {
+ spin_unlock_irqrestore(&card->isdnloop_lock, flags);
+ return -ENOMEM;
+ }
+ strcpy(card->s0num[0], sdef.num[0]);
+ card->s0num[1][0] = '\0';
+ card->s0num[2][0] = '\0';
+ break;
+ default:
+ spin_unlock_irqrestore(&card->isdnloop_lock, flags);
+ printk(KERN_WARNING "isdnloop: Illegal D-channel protocol %d\n",
+ sdef.ptype);
+ return -EINVAL;
+ }
+ init_timer(&card->st_timer);
+ card->st_timer.expires = jiffies + ISDNLOOP_TIMER_DCREAD;
+ card->st_timer.function = isdnloop_polldchan;
+ card->st_timer.data = (unsigned long) card;
+ add_timer(&card->st_timer);
+ card->flags |= ISDNLOOP_FLAGS_RUNNING;
+ spin_unlock_irqrestore(&card->isdnloop_lock, flags);
+ return 0;
+}
+
+/*
+ * Main handler for commands sent by linklevel.
+ */
+static int
+isdnloop_command(isdn_ctrl * c, isdnloop_card * card)
+{
+ ulong a;
+ int i;
+ char cbuf[60];
+ isdn_ctrl cmd;
+ isdnloop_cdef cdef;
+
+ switch (c->command) {
+ case ISDN_CMD_IOCTL:
+ memcpy(&a, c->parm.num, sizeof(ulong));
+ switch (c->arg) {
+ case ISDNLOOP_IOCTL_DEBUGVAR:
+ return (ulong) card;
+ case ISDNLOOP_IOCTL_STARTUP:
+ if (!access_ok(VERIFY_READ, (void *) a, sizeof(isdnloop_sdef)))
+ return -EFAULT;
+ return (isdnloop_start(card, (isdnloop_sdef *) a));
+ break;
+ case ISDNLOOP_IOCTL_ADDCARD:
+ if (copy_from_user((char *)&cdef,
+ (char *)a,
+ sizeof(cdef)))
+ return -EFAULT;
+ return (isdnloop_addcard(cdef.id1));
+ break;
+ case ISDNLOOP_IOCTL_LEASEDCFG:
+ if (a) {
+ if (!card->leased) {
+ card->leased = 1;
+ while (card->ptype == ISDN_PTYPE_UNKNOWN)
+ schedule_timeout_interruptible(10);
+ schedule_timeout_interruptible(10);
+ sprintf(cbuf, "00;FV2ON\n01;EAZ1\n02;EAZ2\n");
+ i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+ printk(KERN_INFO
+ "isdnloop: (%s) Leased-line mode enabled\n",
+ CID);
+ cmd.command = ISDN_STAT_RUN;
+ cmd.driver = card->myid;
+ cmd.arg = 0;
+ card->interface.statcallb(&cmd);
+ }
+ } else {
+ if (card->leased) {
+ card->leased = 0;
+ sprintf(cbuf, "00;FV2OFF\n");
+ i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+ printk(KERN_INFO
+ "isdnloop: (%s) Leased-line mode disabled\n",
+ CID);
+ cmd.command = ISDN_STAT_RUN;
+ cmd.driver = card->myid;
+ cmd.arg = 0;
+ card->interface.statcallb(&cmd);
+ }
+ }
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case ISDN_CMD_DIAL:
+ if (!(card->flags & ISDNLOOP_FLAGS_RUNNING))
+ return -ENODEV;
+ if (card->leased)
+ break;
+ if ((c->arg & 255) < ISDNLOOP_BCH) {
+ char *p;
+ char dial[50];
+ char dcode[4];
+
+ a = c->arg;
+ p = c->parm.setup.phone;
+ if (*p == 's' || *p == 'S') {
+ /* Dial for SPV */
+ p++;
+ strcpy(dcode, "SCA");
+ } else
+ /* Normal Dial */
+ strcpy(dcode, "CAL");
+ strcpy(dial, p);
+ sprintf(cbuf, "%02d;D%s_R%s,%02d,%02d,%s\n", (int) (a + 1),
+ dcode, dial, c->parm.setup.si1,
+ c->parm.setup.si2, c->parm.setup.eazmsn);
+ i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+ }
+ break;
+ case ISDN_CMD_ACCEPTD:
+ if (!(card->flags & ISDNLOOP_FLAGS_RUNNING))
+ return -ENODEV;
+ if (c->arg < ISDNLOOP_BCH) {
+ a = c->arg + 1;
+ cbuf[0] = 0;
+ switch (card->l2_proto[a - 1]) {
+ case ISDN_PROTO_L2_X75I:
+ sprintf(cbuf, "%02d;BX75\n", (int) a);
+ break;
+#ifdef CONFIG_ISDN_X25
+ case ISDN_PROTO_L2_X25DTE:
+ sprintf(cbuf, "%02d;BX2T\n", (int) a);
+ break;
+ case ISDN_PROTO_L2_X25DCE:
+ sprintf(cbuf, "%02d;BX2C\n", (int) a);
+ break;
+#endif
+ case ISDN_PROTO_L2_HDLC:
+ sprintf(cbuf, "%02d;BTRA\n", (int) a);
+ break;
+ }
+ if (strlen(cbuf))
+ i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+ sprintf(cbuf, "%02d;DCON_R\n", (int) a);
+ i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+ }
+ break;
+ case ISDN_CMD_ACCEPTB:
+ if (!(card->flags & ISDNLOOP_FLAGS_RUNNING))
+ return -ENODEV;
+ if (c->arg < ISDNLOOP_BCH) {
+ a = c->arg + 1;
+ switch (card->l2_proto[a - 1]) {
+ case ISDN_PROTO_L2_X75I:
+ sprintf(cbuf, "%02d;BCON_R,BX75\n", (int) a);
+ break;
+#ifdef CONFIG_ISDN_X25
+ case ISDN_PROTO_L2_X25DTE:
+ sprintf(cbuf, "%02d;BCON_R,BX2T\n", (int) a);
+ break;
+ case ISDN_PROTO_L2_X25DCE:
+ sprintf(cbuf, "%02d;BCON_R,BX2C\n", (int) a);
+ break;
+#endif
+ case ISDN_PROTO_L2_HDLC:
+ sprintf(cbuf, "%02d;BCON_R,BTRA\n", (int) a);
+ break;
+ default:
+ sprintf(cbuf, "%02d;BCON_R\n", (int) a);
+ }
+ printk(KERN_DEBUG "isdnloop writecmd '%s'\n", cbuf);
+ i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+ break;
+ case ISDN_CMD_HANGUP:
+ if (!(card->flags & ISDNLOOP_FLAGS_RUNNING))
+ return -ENODEV;
+ if (c->arg < ISDNLOOP_BCH) {
+ a = c->arg + 1;
+ sprintf(cbuf, "%02d;BDIS_R\n%02d;DDIS_R\n", (int) a, (int) a);
+ i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+ }
+ break;
+ case ISDN_CMD_SETEAZ:
+ if (!(card->flags & ISDNLOOP_FLAGS_RUNNING))
+ return -ENODEV;
+ if (card->leased)
+ break;
+ if (c->arg < ISDNLOOP_BCH) {
+ a = c->arg + 1;
+ if (card->ptype == ISDN_PTYPE_EURO) {
+ sprintf(cbuf, "%02d;MS%s%s\n", (int) a,
+ c->parm.num[0] ? "N" : "ALL", c->parm.num);
+ } else
+ sprintf(cbuf, "%02d;EAZ%s\n", (int) a,
+ c->parm.num[0] ? c->parm.num : (u_char *) "0123456789");
+ i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+ }
+ break;
+ case ISDN_CMD_CLREAZ:
+ if (!(card->flags & ISDNLOOP_FLAGS_RUNNING))
+ return -ENODEV;
+ if (card->leased)
+ break;
+ if (c->arg < ISDNLOOP_BCH) {
+ a = c->arg + 1;
+ if (card->ptype == ISDN_PTYPE_EURO)
+ sprintf(cbuf, "%02d;MSNC\n", (int) a);
+ else
+ sprintf(cbuf, "%02d;EAZC\n", (int) a);
+ i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+ }
+ break;
+ case ISDN_CMD_SETL2:
+ if (!(card->flags & ISDNLOOP_FLAGS_RUNNING))
+ return -ENODEV;
+ if ((c->arg & 255) < ISDNLOOP_BCH) {
+ a = c->arg;
+ switch (a >> 8) {
+ case ISDN_PROTO_L2_X75I:
+ sprintf(cbuf, "%02d;BX75\n", (int) (a & 255) + 1);
+ break;
+#ifdef CONFIG_ISDN_X25
+ case ISDN_PROTO_L2_X25DTE:
+ sprintf(cbuf, "%02d;BX2T\n", (int) (a & 255) + 1);
+ break;
+ case ISDN_PROTO_L2_X25DCE:
+ sprintf(cbuf, "%02d;BX2C\n", (int) (a & 255) + 1);
+ break;
+#endif
+ case ISDN_PROTO_L2_HDLC:
+ sprintf(cbuf, "%02d;BTRA\n", (int) (a & 255) + 1);
+ break;
+ case ISDN_PROTO_L2_TRANS:
+ sprintf(cbuf, "%02d;BTRA\n", (int) (a & 255) + 1);
+ break;
+ default:
+ return -EINVAL;
+ }
+ i = isdnloop_writecmd(cbuf, strlen(cbuf), 0, card);
+ card->l2_proto[a & 255] = (a >> 8);
+ }
+ break;
+ case ISDN_CMD_SETL3:
+ if (!(card->flags & ISDNLOOP_FLAGS_RUNNING))
+ return -ENODEV;
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Find card with given driverId
+ */
+static inline isdnloop_card *
+isdnloop_findcard(int driverid)
+{
+ isdnloop_card *p = cards;
+
+ while (p) {
+ if (p->myid == driverid)
+ return p;
+ p = p->next;
+ }
+ return (isdnloop_card *) 0;
+}
+
+/*
+ * Wrapper functions for interface to linklevel
+ */
+static int
+if_command(isdn_ctrl * c)
+{
+ isdnloop_card *card = isdnloop_findcard(c->driver);
+
+ if (card)
+ return (isdnloop_command(c, card));
+ printk(KERN_ERR
+ "isdnloop: if_command called with invalid driverId!\n");
+ return -ENODEV;
+}
+
+static int
+if_writecmd(const u_char __user *buf, int len, int id, int channel)
+{
+ isdnloop_card *card = isdnloop_findcard(id);
+
+ if (card) {
+ if (!(card->flags & ISDNLOOP_FLAGS_RUNNING))
+ return -ENODEV;
+ return (isdnloop_writecmd(buf, len, 1, card));
+ }
+ printk(KERN_ERR
+ "isdnloop: if_writecmd called with invalid driverId!\n");
+ return -ENODEV;
+}
+
+static int
+if_readstatus(u_char __user *buf, int len, int id, int channel)
+{
+ isdnloop_card *card = isdnloop_findcard(id);
+
+ if (card) {
+ if (!(card->flags & ISDNLOOP_FLAGS_RUNNING))
+ return -ENODEV;
+ return (isdnloop_readstatus(buf, len, card));
+ }
+ printk(KERN_ERR
+ "isdnloop: if_readstatus called with invalid driverId!\n");
+ return -ENODEV;
+}
+
+static int
+if_sendbuf(int id, int channel, int ack, struct sk_buff *skb)
+{
+ isdnloop_card *card = isdnloop_findcard(id);
+
+ if (card) {
+ if (!(card->flags & ISDNLOOP_FLAGS_RUNNING))
+ return -ENODEV;
+ /* ack request stored in skb scratch area */
+ *(skb->head) = ack;
+ return (isdnloop_sendbuf(channel, skb, card));
+ }
+ printk(KERN_ERR
+ "isdnloop: if_sendbuf called with invalid driverId!\n");
+ return -ENODEV;
+}
+
+/*
+ * Allocate a new card-struct, initialize it
+ * link it into cards-list and register it at linklevel.
+ */
+static isdnloop_card *
+isdnloop_initcard(char *id)
+{
+ isdnloop_card *card;
+ int i;
+
+ if (!(card = kzalloc(sizeof(isdnloop_card), GFP_KERNEL))) {
+ printk(KERN_WARNING
+ "isdnloop: (%s) Could not allocate card-struct.\n", id);
+ return (isdnloop_card *) 0;
+ }
+ card->interface.owner = THIS_MODULE;
+ card->interface.channels = ISDNLOOP_BCH;
+ card->interface.hl_hdrlen = 1; /* scratch area for storing ack flag*/
+ card->interface.maxbufsize = 4000;
+ card->interface.command = if_command;
+ card->interface.writebuf_skb = if_sendbuf;
+ card->interface.writecmd = if_writecmd;
+ card->interface.readstat = if_readstatus;
+ card->interface.features = ISDN_FEATURE_L2_X75I |
+#ifdef CONFIG_ISDN_X25
+ ISDN_FEATURE_L2_X25DTE |
+ ISDN_FEATURE_L2_X25DCE |
+#endif
+ ISDN_FEATURE_L2_HDLC |
+ ISDN_FEATURE_L3_TRANS |
+ ISDN_FEATURE_P_UNKNOWN;
+ card->ptype = ISDN_PTYPE_UNKNOWN;
+ strlcpy(card->interface.id, id, sizeof(card->interface.id));
+ card->msg_buf_write = card->msg_buf;
+ card->msg_buf_read = card->msg_buf;
+ card->msg_buf_end = &card->msg_buf[sizeof(card->msg_buf) - 1];
+ for (i = 0; i < ISDNLOOP_BCH; i++) {
+ card->l2_proto[i] = ISDN_PROTO_L2_X75I;
+ skb_queue_head_init(&card->bqueue[i]);
+ }
+ skb_queue_head_init(&card->dqueue);
+ spin_lock_init(&card->isdnloop_lock);
+ card->next = cards;
+ cards = card;
+ if (!register_isdn(&card->interface)) {
+ cards = cards->next;
+ printk(KERN_WARNING
+ "isdnloop: Unable to register %s\n", id);
+ kfree(card);
+ return (isdnloop_card *) 0;
+ }
+ card->myid = card->interface.channels;
+ return card;
+}
+
+static int
+isdnloop_addcard(char *id1)
+{
+ isdnloop_card *card;
+
+ if (!(card = isdnloop_initcard(id1))) {
+ return -EIO;
+ }
+ printk(KERN_INFO
+ "isdnloop: (%s) virtual card added\n",
+ card->interface.id);
+ return 0;
+}
+
+static int __init
+isdnloop_init(void)
+{
+ char *p;
+ char rev[10];
+
+ if ((p = strchr(revision, ':'))) {
+ strcpy(rev, p + 1);
+ p = strchr(rev, '$');
+ *p = 0;
+ } else
+ strcpy(rev, " ??? ");
+ printk(KERN_NOTICE "isdnloop-ISDN-driver Rev%s\n", rev);
+
+ if (isdnloop_id)
+ return (isdnloop_addcard(isdnloop_id));
+
+ return 0;
+}
+
+static void __exit
+isdnloop_exit(void)
+{
+ isdn_ctrl cmd;
+ isdnloop_card *card = cards;
+ isdnloop_card *last;
+ int i;
+
+ isdnloop_stopallcards();
+ while (card) {
+ cmd.command = ISDN_STAT_UNLOAD;
+ cmd.driver = card->myid;
+ card->interface.statcallb(&cmd);
+ for (i = 0; i < ISDNLOOP_BCH; i++)
+ isdnloop_free_queue(card, i);
+ card = card->next;
+ }
+ card = cards;
+ while (card) {
+ last = card;
+ skb_queue_purge(&card->dqueue);
+ card = card->next;
+ kfree(last);
+ }
+ printk(KERN_NOTICE "isdnloop-ISDN-driver unloaded\n");
+}
+
+module_init(isdnloop_init);
+module_exit(isdnloop_exit);
diff --git a/drivers/isdn/isdnloop/isdnloop.h b/drivers/isdn/isdnloop/isdnloop.h
new file mode 100644
index 0000000..0d458a8
--- /dev/null
+++ b/drivers/isdn/isdnloop/isdnloop.h
@@ -0,0 +1,112 @@
+/* $Id: isdnloop.h,v 1.5.6.3 2001/09/23 22:24:56 kai Exp $
+ *
+ * Loopback lowlevel module for testing of linklevel.
+ *
+ * Copyright 1997 by Fritz Elfert (fritz@isdn4linux.de)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef isdnloop_h
+#define isdnloop_h
+
+#define ISDNLOOP_IOCTL_DEBUGVAR 0
+#define ISDNLOOP_IOCTL_ADDCARD 1
+#define ISDNLOOP_IOCTL_LEASEDCFG 2
+#define ISDNLOOP_IOCTL_STARTUP 3
+
+/* Struct for adding new cards */
+typedef struct isdnloop_cdef {
+ char id1[10];
+} isdnloop_cdef;
+
+/* Struct for configuring cards */
+typedef struct isdnloop_sdef {
+ int ptype;
+ char num[3][20];
+} isdnloop_sdef;
+
+#if defined(__KERNEL__) || defined(__DEBUGVAR__)
+
+#ifdef __KERNEL__
+/* Kernel includes */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/major.h>
+#include <asm/io.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/isdnif.h>
+
+#endif /* __KERNEL__ */
+
+#define ISDNLOOP_FLAGS_B1ACTIVE 1 /* B-Channel-1 is open */
+#define ISDNLOOP_FLAGS_B2ACTIVE 2 /* B-Channel-2 is open */
+#define ISDNLOOP_FLAGS_RUNNING 4 /* Cards driver activated */
+#define ISDNLOOP_FLAGS_RBTIMER 8 /* scheduling of B-Channel-poll */
+#define ISDNLOOP_TIMER_BCREAD 1 /* B-Channel poll-cycle */
+#define ISDNLOOP_TIMER_DCREAD (HZ/2) /* D-Channel poll-cycle */
+#define ISDNLOOP_TIMER_ALERTWAIT (10*HZ) /* Alert timeout */
+#define ISDNLOOP_MAX_SQUEUE 65536 /* Max. outstanding send-data */
+#define ISDNLOOP_BCH 2 /* channels per card */
+
+/*
+ * Per card driver data
+ */
+typedef struct isdnloop_card {
+ struct isdnloop_card *next; /* Pointer to next device struct */
+ struct isdnloop_card
+ *rcard[ISDNLOOP_BCH]; /* Pointer to 'remote' card */
+ int rch[ISDNLOOP_BCH]; /* 'remote' channel */
+ int myid; /* Driver-Nr. assigned by linklevel */
+ int leased; /* Flag: This Adapter is connected */
+ /* to a leased line */
+ int sil[ISDNLOOP_BCH]; /* SI's to listen for */
+ char eazlist[ISDNLOOP_BCH][11];
+ /* EAZ's to listen for */
+ char s0num[3][20]; /* 1TR6 base-number or MSN's */
+ unsigned short flags; /* Statusflags */
+ int ptype; /* Protocol type (1TR6 or Euro) */
+ struct timer_list st_timer; /* Timer for Status-Polls */
+ struct timer_list rb_timer; /* Timer for B-Channel-Polls */
+ struct timer_list
+ c_timer[ISDNLOOP_BCH]; /* Timer for Alerting */
+ int l2_proto[ISDNLOOP_BCH]; /* Current layer-2-protocol */
+ isdn_if interface; /* Interface to upper layer */
+ int iptr; /* Index to imsg-buffer */
+ char imsg[60]; /* Internal buf for status-parsing */
+ int optr; /* Index to omsg-buffer */
+ char omsg[60]; /* Internal buf for cmd-parsing */
+ char msg_buf[2048]; /* Buffer for status-messages */
+ char *msg_buf_write; /* Writepointer for statusbuffer */
+ char *msg_buf_read; /* Readpointer for statusbuffer */
+ char *msg_buf_end; /* Pointer to end of statusbuffer */
+ int sndcount[ISDNLOOP_BCH]; /* Byte-counters for B-Ch.-send */
+ struct sk_buff_head
+ bqueue[ISDNLOOP_BCH]; /* B-Channel queues */
+ struct sk_buff_head dqueue; /* D-Channel queue */
+ spinlock_t isdnloop_lock;
+} isdnloop_card;
+
+/*
+ * Main driver data
+ */
+#ifdef __KERNEL__
+static isdnloop_card *cards = (isdnloop_card *) 0;
+#endif /* __KERNEL__ */
+
+/* Utility-Macros */
+
+#define CID (card->interface.id)
+
+#endif /* defined(__KERNEL__) || defined(__DEBUGVAR__) */
+#endif /* isdnloop_h */
diff --git a/drivers/isdn/mISDN/Kconfig b/drivers/isdn/mISDN/Kconfig
new file mode 100644
index 0000000..4938355
--- /dev/null
+++ b/drivers/isdn/mISDN/Kconfig
@@ -0,0 +1,44 @@
+#
+# modularer ISDN driver
+#
+
+menuconfig MISDN
+ tristate "Modular ISDN driver"
+ help
+ Enable support for the modular ISDN driver.
+
+if MISDN != n
+
+config MISDN_DSP
+ tristate "Digital Audio Processing of transparent data"
+ depends on MISDN
+ 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
+ echo cancelation, 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.eversberg.eu
+ and get more informations about this module and it's usage.
+ If unsure, say 'N'.
+
+config MISDN_L1OIP
+ tristate "ISDN over IP tunnel"
+ depends on MISDN
+ help
+ Enable support for ISDN over IP tunnel.
+
+ It features:
+ - dynamic IP exchange, if one or both peers have dynamic IPs
+ - BRI (S0) and PRI (S2M) interface
+ - layer 1 control via network keepalive frames
+ - direct tunneling of physical interface via IP
+
+ NOTE: This protocol is called 'Layer 1 over IP' and is not
+ compatible with ISDNoIP (Agfeo) or TDMoIP. Protocol description is
+ provided in the source code.
+
+source "drivers/isdn/hardware/mISDN/Kconfig"
+
+endif #MISDN
diff --git a/drivers/isdn/mISDN/Makefile b/drivers/isdn/mISDN/Makefile
new file mode 100644
index 0000000..1cb5e63
--- /dev/null
+++ b/drivers/isdn/mISDN/Makefile
@@ -0,0 +1,13 @@
+#
+# Makefile for the modular ISDN driver
+#
+
+obj-$(CONFIG_MISDN) += mISDN_core.o
+obj-$(CONFIG_MISDN_DSP) += mISDN_dsp.o
+obj-$(CONFIG_MISDN_L1OIP) += l1oip.o
+
+# multi objects
+
+mISDN_core-objs := core.o fsm.o socket.o hwchannel.o stack.o layer1.o layer2.o tei.o timerdev.o
+mISDN_dsp-objs := dsp_core.o dsp_cmx.o dsp_tones.o dsp_dtmf.o dsp_audio.o dsp_blowfish.o dsp_pipeline.o dsp_hwec.o
+l1oip-objs := l1oip_core.o l1oip_codec.o
diff --git a/drivers/isdn/mISDN/core.c b/drivers/isdn/mISDN/core.c
new file mode 100644
index 0000000..3306817
--- /dev/null
+++ b/drivers/isdn/mISDN/core.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright 2008 by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/stddef.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/mISDNif.h>
+#include "core.h"
+
+static u_int debug;
+
+MODULE_AUTHOR("Karsten Keil");
+MODULE_LICENSE("GPL");
+module_param(debug, uint, S_IRUGO | S_IWUSR);
+
+static LIST_HEAD(devices);
+DEFINE_RWLOCK(device_lock);
+static u64 device_ids;
+#define MAX_DEVICE_ID 63
+
+static LIST_HEAD(Bprotocols);
+DEFINE_RWLOCK(bp_lock);
+
+struct mISDNdevice
+*get_mdevice(u_int id)
+{
+ struct mISDNdevice *dev;
+
+ read_lock(&device_lock);
+ list_for_each_entry(dev, &devices, D.list)
+ if (dev->id == id) {
+ read_unlock(&device_lock);
+ return dev;
+ }
+ read_unlock(&device_lock);
+ return NULL;
+}
+
+int
+get_mdevice_count(void)
+{
+ struct mISDNdevice *dev;
+ int cnt = 0;
+
+ read_lock(&device_lock);
+ list_for_each_entry(dev, &devices, D.list)
+ cnt++;
+ read_unlock(&device_lock);
+ return cnt;
+}
+
+static int
+get_free_devid(void)
+{
+ u_int i;
+
+ for (i = 0; i <= MAX_DEVICE_ID; i++)
+ if (!test_and_set_bit(i, (u_long *)&device_ids))
+ return i;
+ return -1;
+}
+
+int
+mISDN_register_device(struct mISDNdevice *dev, char *name)
+{
+ u_long flags;
+ int err;
+
+ dev->id = get_free_devid();
+ if (dev->id < 0)
+ return -EBUSY;
+ if (name && name[0])
+ strcpy(dev->name, name);
+ else
+ sprintf(dev->name, "mISDN%d", dev->id);
+ if (debug & DEBUG_CORE)
+ printk(KERN_DEBUG "mISDN_register %s %d\n",
+ dev->name, dev->id);
+ err = create_stack(dev);
+ if (err)
+ return err;
+ write_lock_irqsave(&device_lock, flags);
+ list_add_tail(&dev->D.list, &devices);
+ write_unlock_irqrestore(&device_lock, flags);
+ return 0;
+}
+EXPORT_SYMBOL(mISDN_register_device);
+
+void
+mISDN_unregister_device(struct mISDNdevice *dev) {
+ u_long flags;
+
+ if (debug & DEBUG_CORE)
+ printk(KERN_DEBUG "mISDN_unregister %s %d\n",
+ dev->name, dev->id);
+ write_lock_irqsave(&device_lock, flags);
+ list_del(&dev->D.list);
+ write_unlock_irqrestore(&device_lock, flags);
+ test_and_clear_bit(dev->id, (u_long *)&device_ids);
+ delete_stack(dev);
+}
+EXPORT_SYMBOL(mISDN_unregister_device);
+
+u_int
+get_all_Bprotocols(void)
+{
+ struct Bprotocol *bp;
+ u_int m = 0;
+
+ read_lock(&bp_lock);
+ list_for_each_entry(bp, &Bprotocols, list)
+ m |= bp->Bprotocols;
+ read_unlock(&bp_lock);
+ return m;
+}
+
+struct Bprotocol *
+get_Bprotocol4mask(u_int m)
+{
+ struct Bprotocol *bp;
+
+ read_lock(&bp_lock);
+ list_for_each_entry(bp, &Bprotocols, list)
+ if (bp->Bprotocols & m) {
+ read_unlock(&bp_lock);
+ return bp;
+ }
+ read_unlock(&bp_lock);
+ return NULL;
+}
+
+struct Bprotocol *
+get_Bprotocol4id(u_int id)
+{
+ u_int m;
+
+ if (id < ISDN_P_B_START || id > 63) {
+ printk(KERN_WARNING "%s id not in range %d\n",
+ __func__, id);
+ return NULL;
+ }
+ m = 1 << (id & ISDN_P_B_MASK);
+ return get_Bprotocol4mask(m);
+}
+
+int
+mISDN_register_Bprotocol(struct Bprotocol *bp)
+{
+ u_long flags;
+ struct Bprotocol *old;
+
+ if (debug & DEBUG_CORE)
+ printk(KERN_DEBUG "%s: %s/%x\n", __func__,
+ bp->name, bp->Bprotocols);
+ old = get_Bprotocol4mask(bp->Bprotocols);
+ if (old) {
+ printk(KERN_WARNING
+ "register duplicate protocol old %s/%x new %s/%x\n",
+ old->name, old->Bprotocols, bp->name, bp->Bprotocols);
+ return -EBUSY;
+ }
+ write_lock_irqsave(&bp_lock, flags);
+ list_add_tail(&bp->list, &Bprotocols);
+ write_unlock_irqrestore(&bp_lock, flags);
+ return 0;
+}
+EXPORT_SYMBOL(mISDN_register_Bprotocol);
+
+void
+mISDN_unregister_Bprotocol(struct Bprotocol *bp)
+{
+ u_long flags;
+
+ if (debug & DEBUG_CORE)
+ printk(KERN_DEBUG "%s: %s/%x\n", __func__, bp->name,
+ bp->Bprotocols);
+ write_lock_irqsave(&bp_lock, flags);
+ list_del(&bp->list);
+ write_unlock_irqrestore(&bp_lock, flags);
+}
+EXPORT_SYMBOL(mISDN_unregister_Bprotocol);
+
+int
+mISDNInit(void)
+{
+ int err;
+
+ printk(KERN_INFO "Modular ISDN core version %d.%d.%d\n",
+ MISDN_MAJOR_VERSION, MISDN_MINOR_VERSION, MISDN_RELEASE);
+ mISDN_initstack(&debug);
+ err = mISDN_inittimer(&debug);
+ if (err)
+ goto error;
+ err = l1_init(&debug);
+ if (err) {
+ mISDN_timer_cleanup();
+ goto error;
+ }
+ err = Isdnl2_Init(&debug);
+ if (err) {
+ mISDN_timer_cleanup();
+ l1_cleanup();
+ goto error;
+ }
+ err = misdn_sock_init(&debug);
+ if (err) {
+ mISDN_timer_cleanup();
+ l1_cleanup();
+ Isdnl2_cleanup();
+ }
+error:
+ return err;
+}
+
+void mISDN_cleanup(void)
+{
+ misdn_sock_cleanup();
+ mISDN_timer_cleanup();
+ l1_cleanup();
+ Isdnl2_cleanup();
+
+ if (!list_empty(&devices))
+ printk(KERN_ERR "%s devices still registered\n", __func__);
+
+ if (!list_empty(&Bprotocols))
+ printk(KERN_ERR "%s Bprotocols still registered\n", __func__);
+ printk(KERN_DEBUG "mISDNcore unloaded\n");
+}
+
+module_init(mISDNInit);
+module_exit(mISDN_cleanup);
+
diff --git a/drivers/isdn/mISDN/core.h b/drivers/isdn/mISDN/core.h
new file mode 100644
index 0000000..7da7233
--- /dev/null
+++ b/drivers/isdn/mISDN/core.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2008 by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+#ifndef mISDN_CORE_H
+#define mISDN_CORE_H
+
+extern struct mISDNdevice *get_mdevice(u_int);
+extern int get_mdevice_count(void);
+
+/* stack status flag */
+#define mISDN_STACK_ACTION_MASK 0x0000ffff
+#define mISDN_STACK_COMMAND_MASK 0x000f0000
+#define mISDN_STACK_STATUS_MASK 0xfff00000
+/* action bits 0-15 */
+#define mISDN_STACK_WORK 0
+#define mISDN_STACK_SETUP 1
+#define mISDN_STACK_CLEARING 2
+#define mISDN_STACK_RESTART 3
+#define mISDN_STACK_WAKEUP 4
+#define mISDN_STACK_ABORT 15
+/* command bits 16-19 */
+#define mISDN_STACK_STOPPED 16
+#define mISDN_STACK_INIT 17
+#define mISDN_STACK_THREADSTART 18
+/* status bits 20-31 */
+#define mISDN_STACK_BCHANNEL 20
+#define mISDN_STACK_ACTIVE 29
+#define mISDN_STACK_RUNNING 30
+#define mISDN_STACK_KILLED 31
+
+
+/* manager options */
+#define MGR_OPT_USER 24
+#define MGR_OPT_NETWORK 25
+
+extern int connect_Bstack(struct mISDNdevice *, struct mISDNchannel *,
+ u_int, struct sockaddr_mISDN *);
+extern int connect_layer1(struct mISDNdevice *, struct mISDNchannel *,
+ u_int, struct sockaddr_mISDN *);
+extern int create_l2entity(struct mISDNdevice *, struct mISDNchannel *,
+ u_int, struct sockaddr_mISDN *);
+
+extern int create_stack(struct mISDNdevice *);
+extern int create_teimanager(struct mISDNdevice *);
+extern void delete_teimanager(struct mISDNchannel *);
+extern void delete_channel(struct mISDNchannel *);
+extern void delete_stack(struct mISDNdevice *);
+extern void mISDN_initstack(u_int *);
+extern int misdn_sock_init(u_int *);
+extern void misdn_sock_cleanup(void);
+extern void add_layer2(struct mISDNchannel *, struct mISDNstack *);
+extern void __add_layer2(struct mISDNchannel *, struct mISDNstack *);
+
+extern u_int get_all_Bprotocols(void);
+struct Bprotocol *get_Bprotocol4mask(u_int);
+struct Bprotocol *get_Bprotocol4id(u_int);
+
+extern int mISDN_inittimer(u_int *);
+extern void mISDN_timer_cleanup(void);
+
+extern int l1_init(u_int *);
+extern void l1_cleanup(void);
+extern int Isdnl2_Init(u_int *);
+extern void Isdnl2_cleanup(void);
+
+#endif
diff --git a/drivers/isdn/mISDN/dsp.h b/drivers/isdn/mISDN/dsp.h
new file mode 100644
index 0000000..6c3fed6
--- /dev/null
+++ b/drivers/isdn/mISDN/dsp.h
@@ -0,0 +1,263 @@
+/*
+ * Audio support data for ISDN4Linux.
+ *
+ * Copyright 2002/2003 by Andreas Eversberg (jolly@eversberg.eu)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#define DEBUG_DSP_CTRL 0x0001
+#define DEBUG_DSP_CORE 0x0002
+#define DEBUG_DSP_DTMF 0x0004
+#define DEBUG_DSP_CMX 0x0010
+#define DEBUG_DSP_TONE 0x0020
+#define DEBUG_DSP_BLOWFISH 0x0040
+#define DEBUG_DSP_DELAY 0x0100
+#define DEBUG_DSP_DTMFCOEFF 0x8000 /* heavy output */
+
+/* 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)
+
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+
+#include "dsp_ecdis.h"
+
+extern int dsp_options;
+extern int dsp_debug;
+extern int dsp_poll;
+extern int dsp_tics;
+extern spinlock_t dsp_lock;
+extern struct work_struct dsp_workq;
+extern u32 dsp_poll_diff; /* calculated fix-comma corrected poll value */
+
+/***************
+ * 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_law_tables(void);
+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 dsp_silence;
+
+
+/*************
+ * cmx stuff *
+ *************/
+
+#define MAX_POLL 256 /* maximum number of send-chunks */
+
+#define CMX_BUFF_SIZE 0x8000 /* must be 2**n (0x1000 about 1/2 second) */
+#define CMX_BUFF_HALF 0x4000 /* CMX_BUFF_SIZE / 2 */
+#define CMX_BUFF_MASK 0x7fff /* CMX_BUFF_SIZE - 1 */
+
+/* how many seconds will we check the lowest delay until the jitter buffer
+ is reduced by that delay */
+#define MAX_SECONDS_JITTER_CHECK 5
+
+extern struct timer_list dsp_spl_tl;
+extern u32 dsp_spl_jiffies;
+
+/* 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;
+struct dsp_conf_member {
+ struct list_head list;
+ struct dsp *dsp;
+};
+
+/* the list of all conferences */
+struct dsp_conf {
+ 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 */
+ /* note: if both unset, has only one member */
+};
+
+
+/**************
+ * DTMF stuff *
+ **************/
+
+#define DSP_DTMF_NPOINTS 102
+
+#define ECHOCAN_BUFLEN (4*128)
+
+struct dsp_dtmf {
+ int treshold; /* above this is dtmf (square of) */
+ 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 */
+};
+
+
+/******************
+ * pipeline stuff *
+ ******************/
+struct dsp_pipeline {
+ rwlock_t lock;
+ struct list_head list;
+ int inuse;
+};
+
+/***************
+ * tones stuff *
+ ***************/
+
+struct dsp_tone {
+ 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;
+};
+
+/*****************
+ * general stuff *
+ *****************/
+
+struct dsp {
+ struct list_head list;
+ struct mISDNchannel ch;
+ struct mISDNchannel *up;
+ unsigned char name[64];
+ int b_active;
+ int echo; /* echo is enabled */
+ int rx_disabled; /* what the user wants */
+ int rx_is_off; /* what the card is */
+ int tx_mix;
+ struct dsp_tone tone;
+ struct dsp_dtmf dtmf;
+ int tx_volume, rx_volume;
+
+ /* queue for sending frames */
+ struct work_struct workq;
+ struct sk_buff_head sendq;
+ int hdlc; /* if mode is hdlc */
+ int data_pending; /* currently an unconfirmed frame */
+
+ /* conference stuff */
+ u32 conf_id;
+ struct dsp_conf *conf;
+ struct dsp_conf_member
+ *member;
+
+ /* buffer stuff */
+ int rx_W; /* current write pos for data without timestamp */
+ int rx_R; /* current read pos for transmit clock */
+ int rx_init; /* if set, pointers will be adjusted first */
+ int tx_W; /* current write pos for transmit data */
+ int tx_R; /* current read pos for transmit clock */
+ int rx_delay[MAX_SECONDS_JITTER_CHECK];
+ int tx_delay[MAX_SECONDS_JITTER_CHECK];
+ u8 tx_buff[CMX_BUFF_SIZE];
+ u8 rx_buff[CMX_BUFF_SIZE];
+ int last_tx; /* if set, we transmitted last poll interval */
+ int cmx_delay; /* initial delay of buffers,
+ or 0 for dynamic jitter buffer */
+ int tx_dejitter; /* if set, dejitter tx buffer */
+ int tx_data; /* enables tx-data of CMX to upper layer */
+
+ /* hardware stuff */
+ struct dsp_features features;
+ int features_rx_off; /* set if rx_off is featured */
+ 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;
+
+ struct dsp_pipeline
+ pipeline;
+};
+
+/* functions */
+
+extern void dsp_change_volume(struct sk_buff *skb, int volume);
+
+extern struct list_head dsp_ilist;
+extern struct list_head conf_ilist;
+extern void dsp_cmx_debug(struct dsp *dsp);
+extern void dsp_cmx_hardware(struct dsp_conf *conf, struct dsp *dsp);
+extern int dsp_cmx_conf(struct dsp *dsp, u32 conf_id);
+extern void dsp_cmx_receive(struct dsp *dsp, struct sk_buff *skb);
+extern void dsp_cmx_hdlc(struct dsp *dsp, struct sk_buff *skb);
+extern void dsp_cmx_send(void *arg);
+extern void dsp_cmx_transmit(struct dsp *dsp, struct sk_buff *skb);
+extern int dsp_cmx_del_conf_member(struct dsp *dsp);
+extern int dsp_cmx_del_conf(struct dsp_conf *conf);
+
+extern void dsp_dtmf_goertzel_init(struct dsp *dsp);
+extern void dsp_dtmf_hardware(struct dsp *dsp);
+extern u8 *dsp_dtmf_goertzel_decode(struct dsp *dsp, u8 *data, int len,
+ int fmt);
+
+extern int dsp_tone(struct dsp *dsp, int tone);
+extern void dsp_tone_copy(struct dsp *dsp, u8 *data, int len);
+extern void dsp_tone_timeout(void *arg);
+
+extern void dsp_bf_encrypt(struct dsp *dsp, u8 *data, int len);
+extern void dsp_bf_decrypt(struct dsp *dsp, u8 *data, int len);
+extern int dsp_bf_init(struct dsp *dsp, const u8 *key, unsigned int keylen);
+extern void dsp_bf_cleanup(struct dsp *dsp);
+
+extern int dsp_pipeline_module_init(void);
+extern void dsp_pipeline_module_exit(void);
+extern int dsp_pipeline_init(struct dsp_pipeline *pipeline);
+extern void dsp_pipeline_destroy(struct dsp_pipeline *pipeline);
+extern int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg);
+extern void dsp_pipeline_process_tx(struct dsp_pipeline *pipeline, u8 *data,
+ int len);
+extern void dsp_pipeline_process_rx(struct dsp_pipeline *pipeline, u8 *data,
+ int len);
+
diff --git a/drivers/isdn/mISDN/dsp_audio.c b/drivers/isdn/mISDN/dsp_audio.c
new file mode 100644
index 0000000..1c2dd56
--- /dev/null
+++ b/drivers/isdn/mISDN/dsp_audio.c
@@ -0,0 +1,434 @@
+/*
+ * Audio support data for mISDN_dsp.
+ *
+ * Copyright 2002/2003 by Andreas Eversberg (jolly@eversberg.eu)
+ * Rewritten by Peter
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/mISDNif.h>
+#include <linux/mISDNdsp.h>
+#include "core.h"
+#include "dsp.h"
+
+/* ulaw[unsigned char] -> signed 16-bit */
+s32 dsp_audio_ulaw_to_s32[256];
+/* alaw[unsigned char] -> signed 16-bit */
+s32 dsp_audio_alaw_to_s32[256];
+
+s32 *dsp_audio_law_to_s32;
+EXPORT_SYMBOL(dsp_audio_law_to_s32);
+
+/* signed 16-bit -> law */
+u8 dsp_audio_s16_to_law[65536];
+EXPORT_SYMBOL(dsp_audio_s16_to_law);
+
+/* alaw -> ulaw */
+u8 dsp_audio_alaw_to_ulaw[256];
+/* ulaw -> alaw */
+u8 dsp_audio_ulaw_to_alaw[256];
+u8 dsp_silence;
+
+
+/*****************************************************
+ * generate table for conversion of s16 to alaw/ulaw *
+ *****************************************************/
+
+#define AMI_MASK 0x55
+
+static inline unsigned char linear2alaw(short int linear)
+{
+ int mask;
+ int seg;
+ int pcm_val;
+ static int seg_end[8] = {
+ 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF
+ };
+
+ pcm_val = linear;
+ if (pcm_val >= 0) {
+ /* Sign (7th) bit = 1 */
+ mask = AMI_MASK | 0x80;
+ } else {
+ /* Sign bit = 0 */
+ mask = AMI_MASK;
+ pcm_val = -pcm_val;
+ }
+
+ /* Convert the scaled magnitude to segment number. */
+ for (seg = 0; seg < 8; seg++) {
+ if (pcm_val <= seg_end[seg])
+ break;
+ }
+ /* Combine the sign, segment, and quantization bits. */
+ return ((seg << 4) |
+ ((pcm_val >> ((seg) ? (seg + 3) : 4)) & 0x0F)) ^ mask;
+}
+
+
+static inline short int alaw2linear(unsigned char alaw)
+{
+ int i;
+ int seg;
+
+ alaw ^= AMI_MASK;
+ i = ((alaw & 0x0F) << 4) + 8 /* rounding error */;
+ seg = (((int) alaw & 0x70) >> 4);
+ if (seg)
+ i = (i + 0x100) << (seg - 1);
+ return (short int) ((alaw & 0x80) ? i : -i);
+}
+
+static inline short int ulaw2linear(unsigned char ulaw)
+{
+ short mu, e, f, y;
+ static short etab[] = {0, 132, 396, 924, 1980, 4092, 8316, 16764};
+
+ mu = 255 - ulaw;
+ e = (mu & 0x70) / 16;
+ f = mu & 0x0f;
+ y = f * (1 << (e + 3));
+ y += etab[e];
+ if (mu & 0x80)
+ y = -y;
+ return y;
+}
+
+#define BIAS 0x84 /*!< define the add-in bias for 16 bit samples */
+
+static unsigned char linear2ulaw(short sample)
+{
+ static int exp_lut[256] = {
+ 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+ 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7};
+ int sign, exponent, mantissa;
+ unsigned char ulawbyte;
+
+ /* Get the sample into sign-magnitude. */
+ sign = (sample >> 8) & 0x80; /* set aside the sign */
+ if (sign != 0)
+ sample = -sample; /* get magnitude */
+
+ /* Convert from 16 bit linear to ulaw. */
+ sample = sample + BIAS;
+ exponent = exp_lut[(sample >> 7) & 0xFF];
+ mantissa = (sample >> (exponent + 3)) & 0x0F;
+ ulawbyte = ~(sign | (exponent << 4) | mantissa);
+
+ return ulawbyte;
+}
+
+static int reverse_bits(int i)
+{
+ int z, j;
+ z = 0;
+
+ for (j = 0; j < 8; j++) {
+ if ((i & (1 << j)) != 0)
+ z |= 1 << (7 - j);
+ }
+ return z;
+}
+
+
+void dsp_audio_generate_law_tables(void)
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ dsp_audio_alaw_to_s32[i] = alaw2linear(reverse_bits(i));
+
+ for (i = 0; i < 256; i++)
+ dsp_audio_ulaw_to_s32[i] = ulaw2linear(reverse_bits(i));
+
+ for (i = 0; i < 256; i++) {
+ dsp_audio_alaw_to_ulaw[i] =
+ linear2ulaw(dsp_audio_alaw_to_s32[i]);
+ dsp_audio_ulaw_to_alaw[i] =
+ linear2alaw(dsp_audio_ulaw_to_s32[i]);
+ }
+}
+
+void
+dsp_audio_generate_s2law_table(void)
+{
+ int i;
+
+ if (dsp_options & DSP_OPT_ULAW) {
+ /* generating ulaw-table */
+ for (i = -32768; i < 32768; i++) {
+ dsp_audio_s16_to_law[i & 0xffff] =
+ reverse_bits(linear2ulaw(i));
+ }
+ } else {
+ /* generating alaw-table */
+ for (i = -32768; i < 32768; i++) {
+ dsp_audio_s16_to_law[i & 0xffff] =
+ reverse_bits(linear2alaw(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, k;
+ u8 spl;
+ u8 sorted_alaw[256];
+
+ /* generate alaw table, sorted by the linear value */
+ for (i = 0; i < 256; i++) {
+ j = 0;
+ for (k = 0; k < 256; k++) {
+ if (dsp_audio_alaw_to_s32[k]
+ < dsp_audio_alaw_to_s32[i]) {
+ j++;
+ }
+ }
+ sorted_alaw[j] = i;
+ }
+
+ /* generate tabels */
+ for (i = 0; i < 256; i++) {
+ /* 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 */
+ for (j = 0; j < 256; j++) {
+ if (sorted_alaw[j] == spl)
+ break;
+ }
+ /* write 7-bit audio value */
+ dsp_audio_law2seven[i] = j >> 1;
+ }
+ for (i = 0; i < 128; i++) {
+ spl = sorted_alaw[i << 1];
+ if (dsp_options & DSP_OPT_ULAW)
+ spl = dsp_audio_alaw_to_ulaw[spl];
+ dsp_audio_seven2law[i] = spl;
+ }
+}
+
+
+/* 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;
+ int num[] = { 110, 125, 150, 175, 200, 300, 400, 500 };
+ int denum[] = { 100, 100, 100, 100, 100, 100, 100, 100 };
+
+ i = 0;
+ while (i < 256) {
+ dsp_audio_reduce8[i] = dsp_audio_s16_to_law[
+ (dsp_audio_law_to_s32[i] * denum[7] / num[7]) & 0xffff];
+ dsp_audio_reduce7[i] = dsp_audio_s16_to_law[
+ (dsp_audio_law_to_s32[i] * denum[6] / num[6]) & 0xffff];
+ dsp_audio_reduce6[i] = dsp_audio_s16_to_law[
+ (dsp_audio_law_to_s32[i] * denum[5] / num[5]) & 0xffff];
+ dsp_audio_reduce5[i] = dsp_audio_s16_to_law[
+ (dsp_audio_law_to_s32[i] * denum[4] / num[4]) & 0xffff];
+ dsp_audio_reduce4[i] = dsp_audio_s16_to_law[
+ (dsp_audio_law_to_s32[i] * denum[3] / num[3]) & 0xffff];
+ dsp_audio_reduce3[i] = dsp_audio_s16_to_law[
+ (dsp_audio_law_to_s32[i] * denum[2] / num[2]) & 0xffff];
+ dsp_audio_reduce2[i] = dsp_audio_s16_to_law[
+ (dsp_audio_law_to_s32[i] * denum[1] / num[1]) & 0xffff];
+ dsp_audio_reduce1[i] = dsp_audio_s16_to_law[
+ (dsp_audio_law_to_s32[i] * denum[0] / num[0]) & 0xffff];
+ sample = dsp_audio_law_to_s32[i] * num[0] / denum[0];
+ 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] * num[1] / denum[1];
+ 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] * num[2] / denum[2];
+ 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] * num[3] / denum[3];
+ 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] * num[4] / denum[4];
+ 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] * num[5] / denum[5];
+ 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] * num[6] / denum[6];
+ 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] * num[7] / denum[7];
+ 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 --git a/drivers/isdn/mISDN/dsp_biquad.h b/drivers/isdn/mISDN/dsp_biquad.h
new file mode 100644
index 0000000..038191b
--- /dev/null
+++ b/drivers/isdn/mISDN/dsp_biquad.h
@@ -0,0 +1,65 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * biquad.h - General telephony bi-quad section routines (currently this just
+ * handles canonic/type 2 form)
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2001 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * 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 of the License, 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.
+ *
+ */
+
+struct biquad2_state {
+ int32_t gain;
+ int32_t a1;
+ int32_t a2;
+ int32_t b1;
+ int32_t b2;
+
+ int32_t z1;
+ int32_t z2;
+};
+
+static inline void biquad2_init(struct biquad2_state *bq,
+ int32_t gain, int32_t a1, int32_t a2, int32_t b1, int32_t b2)
+{
+ bq->gain = gain;
+ bq->a1 = a1;
+ bq->a2 = a2;
+ bq->b1 = b1;
+ bq->b2 = b2;
+
+ bq->z1 = 0;
+ bq->z2 = 0;
+}
+
+static inline int16_t biquad2(struct biquad2_state *bq, int16_t sample)
+{
+ int32_t y;
+ int32_t z0;
+
+ z0 = sample*bq->gain + bq->z1*bq->a1 + bq->z2*bq->a2;
+ y = z0 + bq->z1*bq->b1 + bq->z2*bq->b2;
+
+ bq->z2 = bq->z1;
+ bq->z1 = z0 >> 15;
+ y >>= 15;
+ return y;
+}
diff --git a/drivers/isdn/mISDN/dsp_blowfish.c b/drivers/isdn/mISDN/dsp_blowfish.c
new file mode 100644
index 0000000..18e411e
--- /dev/null
+++ b/drivers/isdn/mISDN/dsp_blowfish.c
@@ -0,0 +1,672 @@
+/*
+ * Blowfish encryption/decryption for mISDN_dsp.
+ *
+ * Copyright Andreas Eversberg (jolly@eversberg.eu)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/mISDNif.h>
+#include <linux/mISDNdsp.h>
+#include "core.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) do { b ^= P[n]; a ^= bf_F(b); } while (0)
+#define DROUND(a, b, n) do { a ^= bf_F(b); b ^= P[n]; } while (0)
+
+
+/*
+ * encrypt isdn data frame
+ * every block with 9 samples is encrypted
+ */
+void
+dsp_bf_encrypt(struct dsp *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]];
+ nibble = dsp_audio_law2seven[bf_data_in[4]];
+ yr = nibble;
+ 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(struct dsp *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 */
+ nibble = bf_crypt_inring[j++ & 15]; /* bit7 = 0 */
+ yr = nibble;
+ 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(struct dsp *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] = dsp_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(struct dsp *dsp)
+{
+ dsp->bf_enable = 0;
+}
diff --git a/drivers/isdn/mISDN/dsp_cmx.c b/drivers/isdn/mISDN/dsp_cmx.c
new file mode 100644
index 0000000..c2f51cc
--- /dev/null
+++ b/drivers/isdn/mISDN/dsp_cmx.c
@@ -0,0 +1,1886 @@
+/*
+ * Audio crossconnecting/conferrencing (hardware level).
+ *
+ * Copyright 2002 by Andreas Eversberg (jolly@eversberg.eu)
+ *
+ * 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 struct dsp_conf which has one or more members in a chain
+ * of struct dsp_conf_member.
+ *
+ * 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 struct dsp_conf entry.
+ */
+
+/*
+ * HOW THE CMX WORKS:
+ *
+ * There are 3 types of interaction: One member is alone, in this case only
+ * data flow from upper to lower layer 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.
+ * - Dejittering and clock generation.
+ *
+ * There are 2 buffers:
+ *
+ *
+ * RX-Buffer
+ * R W
+ * | |
+ * ----------------+-------------+-------------------
+ *
+ * The rx-buffer is a ring buffer used to store the received data for each
+ * individual member. This is only the case if data needs to be dejittered
+ * or in case of a conference where different clocks require reclocking.
+ * The transmit-clock (R) will read the buffer.
+ * If the clock overruns the write-pointer, we will have a buffer underrun.
+ * If the write pointer always has a certain distance from the transmit-
+ * clock, we will have a delay. The delay will dynamically be increased and
+ * reduced.
+ *
+ *
+ * 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.
+ * Additionally a dynamic dejittering can be enabled. this allows data from
+ * user space that have jitter and different clock source.
+ *
+ *
+ * Clock:
+ *
+ * A Clock is not required, if the data source has exactly one clock. In this
+ * case the data source is forwarded to the destination.
+ *
+ * A Clock is required, because the data source
+ * - has multiple clocks.
+ * - has no usable clock due to jitter or packet loss (VoIP).
+ * In this case the system's clock is used. The clock resolution depends on
+ * the jiffie resolution.
+ *
+ * If a member joins a conference:
+ *
+ * - If a member joins, its rx_buff is set to silence and change read pointer
+ * to transmit clock.
+ *
+ * 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.
+ * The procedure of transmit data to card is cmx_send.
+ *
+ *
+ * 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 <linux/delay.h>
+#include <linux/mISDNif.h>
+#include <linux/mISDNdsp.h>
+#include "core.h"
+#include "dsp.h"
+/*
+ * debugging of multi party conference,
+ * by using conference even with two members
+ */
+
+/* #define CMX_CONF_DEBUG */
+
+/*#define CMX_DEBUG * massive read/write pointer output */
+/*#define CMX_TX_DEBUG * massive read/write on tx-buffer with content */
+
+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;
+}
+
+/*
+ * debug cmx memory structure
+ */
+void
+dsp_cmx_debug(struct dsp *dsp)
+{
+ struct dsp_conf *conf;
+ struct dsp_conf_member *member;
+ struct dsp *odsp;
+
+ printk(KERN_DEBUG "-----Current DSP\n");
+ list_for_each_entry(odsp, &dsp_ilist, list) {
+ printk(KERN_DEBUG "* %s echo=%d txmix=%d",
+ odsp->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_ilist, list) {
+ printk(KERN_DEBUG "* Conf %d (%p)\n", conf->id, 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->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 struct dsp_conf *
+dsp_cmx_search_conf(u32 id)
+{
+ struct dsp_conf *conf;
+
+ if (!id) {
+ printk(KERN_WARNING "%s: conference ID is 0.\n", __func__);
+ return NULL;
+ }
+
+ /* search conference */
+ list_for_each_entry(conf, &conf_ilist, list)
+ if (conf->id == id)
+ return conf;
+
+ return NULL;
+}
+
+
+/*
+ * add member to conference
+ */
+static int
+dsp_cmx_add_conf_member(struct dsp *dsp, struct dsp_conf *conf)
+{
+ struct dsp_conf_member *member;
+
+ if (!conf || !dsp) {
+ printk(KERN_WARNING "%s: conf or dsp is 0.\n", __func__);
+ return -EINVAL;
+ }
+ if (dsp->member) {
+ printk(KERN_WARNING "%s: dsp is already member in a conf.\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (dsp->conf) {
+ printk(KERN_WARNING "%s: dsp is already in a conf.\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ member = kzalloc(sizeof(struct dsp_conf_member), GFP_ATOMIC);
+ if (!member) {
+ printk(KERN_ERR "kmalloc struct dsp_conf_member failed\n");
+ return -ENOMEM;
+ }
+ member->dsp = dsp;
+ /* clear rx buffer */
+ memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff));
+ dsp->rx_init = 1; /* rx_W and rx_R will be adjusted on first frame */
+ dsp->rx_W = 0;
+ dsp->rx_R = 0;
+
+ list_add_tail(&member->list, &conf->mlist);
+
+ dsp->conf = conf;
+ dsp->member = member;
+
+ return 0;
+}
+
+
+/*
+ * del member from conference
+ */
+int
+dsp_cmx_del_conf_member(struct dsp *dsp)
+{
+ struct dsp_conf_member *member;
+
+ if (!dsp) {
+ printk(KERN_WARNING "%s: dsp is 0.\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (!dsp->conf) {
+ printk(KERN_WARNING "%s: dsp is not in a conf.\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (list_empty(&dsp->conf->mlist)) {
+ printk(KERN_WARNING "%s: dsp has linked an empty conf.\n",
+ __func__);
+ 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;
+ kfree(member);
+ return 0;
+ }
+ }
+ printk(KERN_WARNING
+ "%s: dsp is not present in its own conf_meber list.\n",
+ __func__);
+
+ return -EINVAL;
+}
+
+
+/*
+ * new conference
+ */
+static struct dsp_conf
+*dsp_cmx_new_conf(u32 id)
+{
+ struct dsp_conf *conf;
+
+ if (!id) {
+ printk(KERN_WARNING "%s: id is 0.\n",
+ __func__);
+ return NULL;
+ }
+
+ conf = kzalloc(sizeof(struct dsp_conf), GFP_ATOMIC);
+ if (!conf) {
+ printk(KERN_ERR "kmalloc struct dsp_conf failed\n");
+ return NULL;
+ }
+ INIT_LIST_HEAD(&conf->mlist);
+ conf->id = id;
+
+ list_add_tail(&conf->list, &conf_ilist);
+
+ return conf;
+}
+
+
+/*
+ * del conference
+ */
+int
+dsp_cmx_del_conf(struct dsp_conf *conf)
+{
+ if (!conf) {
+ printk(KERN_WARNING "%s: conf is null.\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (!list_empty(&conf->mlist)) {
+ printk(KERN_WARNING "%s: conf not empty.\n",
+ __func__);
+ return -EINVAL;
+ }
+ list_del(&conf->list);
+ kfree(conf);
+
+ return 0;
+}
+
+
+/*
+ * send HW message to hfc card
+ */
+static void
+dsp_cmx_hw_message(struct dsp *dsp, u32 message, u32 param1, u32 param2,
+ u32 param3, u32 param4)
+{
+ struct mISDN_ctrl_req cq;
+
+ memset(&cq, 0, sizeof(cq));
+ cq.op = message;
+ cq.p1 = param1 | (param2 << 8);
+ cq.p2 = param3 | (param4 << 8);
+ if (dsp->ch.peer)
+ dsp->ch.peer->ctrl(dsp->ch.peer, CONTROL_CHANNEL, &cq);
+}
+
+
+/*
+ * 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(struct dsp_conf *conf, struct dsp *dsp)
+{
+ struct dsp_conf_member *member, *nextm;
+ struct dsp *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) */
+ if (!conf) {
+ if (!dsp)
+ return;
+ if (dsp_debug & DEBUG_DSP_CMX)
+ printk(KERN_DEBUG "%s checking dsp %s\n",
+ __func__, dsp->name);
+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", __func__,
+ dsp->name, dsp->hfc_conf);
+ dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_CONF_SPLIT,
+ 0, 0, 0, 0);
+ dsp->hfc_conf = -1;
+ }
+ /* process hw echo */
+ if (dsp->features.pcm_banks < 1)
+ return;
+ 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",
+ __func__, dsp->name,
+ dsp->pcm_slot_tx, dsp->pcm_slot_rx);
+ dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_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; /* 2 means loop */
+ dsp->pcm_bank_rx = 2;
+ if (dsp_debug & DEBUG_DSP_CMX)
+ printk(KERN_DEBUG
+ "%s refresh %s for echo using slot %d\n",
+ __func__, dsp->name,
+ dsp->pcm_slot_tx);
+ dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_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_ilist, list) {
+ if (finddsp->features.pcm_id == dsp->features.pcm_id) {
+ if (finddsp->pcm_slot_rx >= 0 &&
+ finddsp->pcm_slot_rx < sizeof(freeslots))
+ freeslots[finddsp->pcm_slot_rx] = 0;
+ if (finddsp->pcm_slot_tx >= 0 &&
+ finddsp->pcm_slot_tx < sizeof(freeslots))
+ freeslots[finddsp->pcm_slot_tx] = 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",
+ __func__);
+ /* 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",
+ __func__, dsp->name, dsp->pcm_slot_tx);
+ dsp_cmx_hw_message(dsp, MISDN_CTRL_HFC_PCM_CONN,
+ dsp->pcm_slot_tx, 2, dsp->pcm_slot_rx, 2);
+ return;
+ }
+
+ /* conf gets updated (all members) */
+ if (dsp_debug & DEBUG_DSP_CMX)
+ printk(KERN_DEBUG "%s checking conference %d\n",
+ __func__, conf->id);
+
+ if (list_empty(&conf->mlist)) {
+ printk(KERN_ERR "%s: conference whithout members\n",
+ __func__);
+ return;
+ }
+ member = list_entry(conf->mlist.next, struct dsp_conf_member, 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", __func__,
+ member->dsp->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",
+ __func__,
+ dsp->name,
+ dsp->hfc_conf);
+ dsp_cmx_hw_message(dsp,
+ MISDN_CTRL_HFC_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",
+ __func__,
+ dsp->name,
+ dsp->pcm_slot_tx,
+ dsp->pcm_slot_rx);
+ dsp_cmx_hw_message(dsp,
+ MISDN_CTRL_HFC_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", __func__,
+ member->dsp->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",
+ __func__, member->dsp->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",
+ __func__, member->dsp->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",
+ __func__, member->dsp->name);
+ goto conf_software;
+ }
+ /* check if tx-data turned on */
+ if (member->dsp->tx_data) {
+ if (dsp_debug & DEBUG_DSP_CMX)
+ printk(KERN_DEBUG
+ "%s dsp %s cannot form a conf, because "
+ "tx_data is turned on\n",
+ __func__, member->dsp->name);
+ goto conf_software;
+ }
+ /* check if pipeline exists */
+ if (member->dsp->pipeline.inuse) {
+ if (dsp_debug & DEBUG_DSP_CMX)
+ printk(KERN_DEBUG
+ "%s dsp %s cannot form a conf, because "
+ "pipeline exists\n", __func__,
+ member->dsp->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",
+ __func__, member->dsp->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",
+ __func__, member->dsp->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",
+ __func__, member->dsp->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", __func__, conf->id);
+ conf->hardware = 0;
+ conf->software = 0;
+ member = list_entry(conf->mlist.next, struct dsp_conf_member,
+ 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, struct dsp_conf_member,
+ list);
+ nextm = list_entry(member->list.next, struct dsp_conf_member,
+ 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",
+ __func__, member->dsp->name,
+ member->dsp->hfc_conf);
+ dsp_cmx_hw_message(member->dsp,
+ MISDN_CTRL_HFC_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",
+ __func__, nextm->dsp->name,
+ nextm->dsp->hfc_conf);
+ dsp_cmx_hw_message(nextm->dsp,
+ MISDN_CTRL_HFC_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.hfc_id !=
+ nextm->dsp->features.hfc_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",
+ __func__,
+ member->dsp->name,
+ nextm->dsp->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_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_rx <
+ sizeof(freeslots))
+ freeslots[dsp->pcm_slot_tx] = 0;
+ if (dsp->pcm_slot_tx >= 0 &&
+ dsp->pcm_slot_tx <
+ sizeof(freeslots))
+ freeslots[dsp->pcm_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", __func__,
+ member->dsp->name,
+ nextm->dsp->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",
+ __func__,
+ member->dsp->name,
+ nextm->dsp->name,
+ member->dsp->pcm_slot_tx);
+ dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_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, MISDN_CTRL_HFC_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", __func__,
+ member->dsp->name,
+ nextm->dsp->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_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_rx <
+ sizeof(freeslots))
+ freeslots[dsp->pcm_slot_tx] = 0;
+ if (dsp->pcm_slot_tx >= 0 &&
+ dsp->pcm_slot_tx <
+ sizeof(freeslots))
+ freeslots[dsp->pcm_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", __func__,
+ member->dsp->name,
+ nextm->dsp->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",
+ __func__,
+ member->dsp->name,
+ nextm->dsp->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", __func__,
+ member->dsp->name,
+ nextm->dsp->name,
+ member->dsp->pcm_slot_tx,
+ member->dsp->pcm_slot_rx);
+ dsp_cmx_hw_message(member->dsp, MISDN_CTRL_HFC_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, MISDN_CTRL_HFC_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",
+ __func__, conf->id);
+ goto conf_software;
+ }
+
+ /* for more than two members.. */
+
+ /* in case of hdlc, we change to software */
+ if (dsp->hdlc)
+ 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_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_tx <
+ sizeof(freeslots))
+ freeslots[dsp->pcm_slot_tx] = 0;
+ if (dsp->pcm_slot_rx >= 0 &&
+ dsp->pcm_slot_rx <
+ sizeof(freeslots))
+ freeslots[dsp->pcm_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",
+ __func__, 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", __func__,
+ member->dsp->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, MISDN_CTRL_HFC_PCM_CONN,
+ i, 2, i, 2);
+ dsp_cmx_hw_message(member->dsp,
+ MISDN_CTRL_HFC_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_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",
+ __func__, 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(struct dsp *dsp, u32 conf_id)
+{
+ int err;
+ struct dsp_conf *conf;
+ struct dsp_conf_member *member;
+
+ /* 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;
+ } else if (!list_empty(&conf->mlist)) {
+ member = list_entry(conf->mlist.next, struct dsp_conf_member,
+ list);
+ if (dsp->hdlc && !member->dsp->hdlc) {
+ if (dsp_debug & DEBUG_DSP_CMX)
+ printk(KERN_DEBUG
+ "cannot join transparent conference.\n");
+ return -EINVAL;
+ }
+ if (!dsp->hdlc && member->dsp->hdlc) {
+ if (dsp_debug & DEBUG_DSP_CMX)
+ printk(KERN_DEBUG
+ "cannot join hdlc conference.\n");
+ 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(struct dsp *dsp, struct sk_buff *skb)
+{
+ u8 *d, *p;
+ int len = skb->len;
+ struct mISDNhead *hh = mISDN_HEAD_P(skb);
+ int w, i, ii;
+
+ /* check if we have sompen */
+ if (len < 1)
+ return;
+
+ /* half of the buffer should be larger than maximum packet size */
+ if (len >= CMX_BUFF_HALF) {
+ 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;
+ }
+
+ /*
+ * initialize pointers if not already -
+ * also add delay if requested by PH_SIGNAL
+ */
+ if (dsp->rx_init) {
+ dsp->rx_init = 0;
+ if (dsp->features.unordered) {
+ dsp->rx_R = (hh->id & CMX_BUFF_MASK);
+ dsp->rx_W = (dsp->rx_R + dsp->cmx_delay)
+ & CMX_BUFF_MASK;
+ } else {
+ dsp->rx_R = 0;
+ dsp->rx_W = dsp->cmx_delay;
+ }
+ }
+ /* if frame contains time code, write directly */
+ if (dsp->features.unordered) {
+ dsp->rx_W = (hh->id & CMX_BUFF_MASK);
+ /* printk(KERN_DEBUG "%s %08x\n", dsp->name, hh->id); */
+ }
+ /*
+ * if we underrun (or maybe overrun),
+ * we set our new read pointer, and write silence to buffer
+ */
+ if (((dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK) >= CMX_BUFF_HALF) {
+ if (dsp_debug & DEBUG_DSP_CMX)
+ printk(KERN_DEBUG
+ "cmx_receive(dsp=%lx): UNDERRUN (or overrun the "
+ "maximum delay), adjusting read pointer! "
+ "(inst %s)\n", (u_long)dsp, dsp->name);
+ /* flush buffer */
+ if (dsp->features.unordered) {
+ dsp->rx_R = (hh->id & CMX_BUFF_MASK);
+ dsp->rx_W = (dsp->rx_R + dsp->cmx_delay)
+ & CMX_BUFF_MASK;
+ } else {
+ dsp->rx_R = 0;
+ dsp->rx_W = dsp->cmx_delay;
+ }
+ memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff));
+ }
+ /* if we have reached double delay, jump back to middle */
+ if (dsp->cmx_delay)
+ if (((dsp->rx_W - dsp->rx_R) & CMX_BUFF_MASK) >=
+ (dsp->cmx_delay << 1)) {
+ if (dsp_debug & DEBUG_DSP_CMX)
+ printk(KERN_DEBUG
+ "cmx_receive(dsp=%lx): OVERRUN (because "
+ "twice the delay is reached), adjusting "
+ "read pointer! (inst %s)\n",
+ (u_long)dsp, dsp->name);
+ /* flush buffer */
+ if (dsp->features.unordered) {
+ dsp->rx_R = (hh->id & CMX_BUFF_MASK);
+ dsp->rx_W = (dsp->rx_R + dsp->cmx_delay)
+ & CMX_BUFF_MASK;
+ } else {
+ dsp->rx_R = 0;
+ dsp->rx_W = dsp->cmx_delay;
+ }
+ memset(dsp->rx_buff, dsp_silence, sizeof(dsp->rx_buff));
+ }
+
+ /* show where to write */
+#ifdef CMX_DEBUG
+ printk(KERN_DEBUG
+ "cmx_receive(dsp=%lx): rx_R(dsp)=%05x rx_W(dsp)=%05x len=%d %s\n",
+ (u_long)dsp, dsp->rx_R, dsp->rx_W, len, dsp->name);
+#endif
+
+ /* write data into rx_buffer */
+ p = skb->data;
+ d = dsp->rx_buff;
+ w = dsp->rx_W;
+ i = 0;
+ ii = len;
+ while (i < ii) {
+ d[w++ & CMX_BUFF_MASK] = *p++;
+ i++;
+ }
+
+ /* increase write-pointer */
+ dsp->rx_W = ((dsp->rx_W+len) & CMX_BUFF_MASK);
+}
+
+
+/*
+ * send (mixed) audio data to card and control jitter
+ */
+static void
+dsp_cmx_send_member(struct dsp *dsp, int len, s32 *c, int members)
+{
+ struct dsp_conf *conf = dsp->conf;
+ struct dsp *member, *other;
+ register s32 sample;
+ u8 *d, *p, *q, *o_q;
+ struct sk_buff *nskb, *txskb;
+ int r, rr, t, tt, o_r, o_rr;
+ int preload = 0;
+ struct mISDNhead *hh, *thh;
+
+ /* don't process if: */
+ if (!dsp->b_active) { /* if not active */
+ dsp->last_tx = 0;
+ return;
+ }
+ if (dsp->pcm_slot_tx >= 0 && /* connected to pcm slot */
+ dsp->tx_R == dsp->tx_W && /* AND no tx-data */
+ !(dsp->tone.tone && dsp->tone.software)) { /* AND not soft tones */
+ dsp->last_tx = 0;
+ return;
+ }
+
+#ifdef CMX_DEBUG
+ printk(KERN_DEBUG
+ "SEND members=%d dsp=%s, conf=%p, rx_R=%05x rx_W=%05x\n",
+ members, dsp->name, conf, dsp->rx_R, dsp->rx_W);
+#endif
+
+ /* preload if we have delay set */
+ if (dsp->cmx_delay && !dsp->last_tx) {
+ preload = len;
+ if (preload < 128)
+ preload = 128;
+ }
+
+ /* PREPARE RESULT */
+ nskb = mI_alloc_skb(len + preload, GFP_ATOMIC);
+ if (!nskb) {
+ printk(KERN_ERR
+ "FATAL ERROR in mISDN_dsp.o: cannot alloc %d bytes\n",
+ len + preload);
+ return;
+ }
+ hh = mISDN_HEAD_P(nskb);
+ hh->prim = PH_DATA_REQ;
+ hh->id = 0;
+ dsp->last_tx = 1;
+
+ /* set pointers, indexes and stuff */
+ member = dsp;
+ p = dsp->tx_buff; /* transmit data */
+ q = dsp->rx_buff; /* received data */
+ d = skb_put(nskb, preload + len); /* result */
+ t = dsp->tx_R; /* tx-pointers */
+ tt = dsp->tx_W;
+ r = dsp->rx_R; /* rx-pointers */
+ rr = (r + len) & CMX_BUFF_MASK;
+
+ /* preload with silence, if required */
+ if (preload) {
+ memset(d, dsp_silence, preload);
+ d += preload;
+ }
+
+ /* PROCESS TONES/TX-DATA ONLY */
+ if (dsp->tone.tone && dsp->tone.software) {
+ /* -> copy tone */
+ dsp_tone_copy(dsp, d, len);
+ dsp->tx_R = 0; /* clear tx buffer */
+ dsp->tx_W = 0;
+ goto send_packet;
+ }
+ /* if we have tx-data but do not use mixing */
+ if (!dsp->tx_mix && t != tt) {
+ /* -> send tx-data and continue when not enough */
+#ifdef CMX_TX_DEBUG
+ sprintf(debugbuf, "TX sending (%04x-%04x)%p: ", t, tt, p);
+#endif
+ while (r != rr && t != tt) {
+#ifdef CMX_TX_DEBUG
+ if (strlen(debugbuf) < 48)
+ sprintf(debugbuf+strlen(debugbuf), " %02x", p[t]);
+#endif
+ *d++ = p[t]; /* write tx_buff */
+ t = (t+1) & CMX_BUFF_MASK;
+ r = (r+1) & CMX_BUFF_MASK;
+ }
+ if (r == rr) {
+ dsp->tx_R = t;
+#ifdef CMX_TX_DEBUG
+ printk(KERN_DEBUG "%s\n", debugbuf);
+#endif
+ goto send_packet;
+ }
+ }
+#ifdef CMX_TX_DEBUG
+ printk(KERN_DEBUG "%s\n", debugbuf);
+#endif
+
+ /* PROCESS DATA (one member / no conf) */
+ if (!conf || members <= 1) {
+ /* -> 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, dsp_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->tx_R = t;
+ goto send_packet;
+ }
+ /* PROCESS DATA (two members) */
+#ifdef CMX_CONF_DEBUG
+ if (0) {
+#else
+ if (members == 2) {
+#endif
+ /* "other" becomes other party */
+ other = (list_entry(conf->mlist.next,
+ struct dsp_conf_member, list))->dsp;
+ if (other == member)
+ other = (list_entry(conf->mlist.prev,
+ struct dsp_conf_member, list))->dsp;
+ o_q = other->rx_buff; /* received data */
+ o_rr = (other->rx_R + len) & CMX_BUFF_MASK;
+ /* end of rx-pointer */
+ o_r = (o_rr - rr + r) & CMX_BUFF_MASK;
+ /* start rx-pointer at current read position*/
+ /* -> if echo is NOT enabled */
+ if (!dsp->echo) {
+ /*
+ * -> copy other member's rx-data,
+ * if tx-data is available, mix
+ */
+ while (o_r != o_rr && t != tt) {
+ *d++ = dsp_audio_mix_law[(p[t]<<8)|o_q[o_r]];
+ t = (t+1) & CMX_BUFF_MASK;
+ o_r = (o_r+1) & CMX_BUFF_MASK;
+ }
+ while (o_r != o_rr) {
+ *d++ = o_q[o_r];
+ o_r = (o_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[q[r]] +
+ dsp_audio_law_to_s32[o_q[o_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;
+ o_r = (o_r+1) & CMX_BUFF_MASK;
+ }
+ while (r != rr) {
+ *d++ = dsp_audio_mix_law[(q[r]<<8)|o_q[o_r]];
+ r = (r+1) & CMX_BUFF_MASK;
+ o_r = (o_r+1) & CMX_BUFF_MASK;
+ }
+ }
+ dsp->tx_R = t;
+ goto send_packet;
+ }
+#ifdef DSP_NEVER_DEFINED
+ }
+#endif
+ /* PROCESS DATA (three or more members) */
+ /* -> 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++ -
+ 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++ - 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++;
+ 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++;
+ 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->tx_R = t;
+ goto send_packet;
+
+send_packet:
+ /*
+ * send tx-data if enabled - don't filter,
+ * becuase we want what we send, not what we filtered
+ */
+ if (dsp->tx_data) {
+ /* PREPARE RESULT */
+ txskb = mI_alloc_skb(len, GFP_ATOMIC);
+ if (!txskb) {
+ printk(KERN_ERR
+ "FATAL ERROR in mISDN_dsp.o: "
+ "cannot alloc %d bytes\n", len);
+ } else {
+ thh = mISDN_HEAD_P(txskb);
+ thh->prim = DL_DATA_REQ;
+ thh->id = 0;
+ memcpy(skb_put(txskb, len), nskb->data+preload, len);
+ /* queue (trigger later) */
+ skb_queue_tail(&dsp->sendq, txskb);
+ }
+ }
+ /* adjust volume */
+ if (dsp->tx_volume)
+ dsp_change_volume(nskb, dsp->tx_volume);
+ /* pipeline */
+ if (dsp->pipeline.inuse)
+ dsp_pipeline_process_tx(&dsp->pipeline, nskb->data, nskb->len);
+ /* crypt */
+ if (dsp->bf_enable)
+ dsp_bf_encrypt(dsp, nskb->data, nskb->len);
+ /* queue and trigger */
+ skb_queue_tail(&dsp->sendq, nskb);
+ schedule_work(&dsp->workq);
+}
+
+u32 samplecount;
+struct timer_list dsp_spl_tl;
+u32 dsp_spl_jiffies; /* calculate the next time to fire */
+u32 dsp_start_jiffies; /* jiffies at the time, the calculation begins */
+struct timeval dsp_start_tv; /* time at start of calculation */
+
+void
+dsp_cmx_send(void *arg)
+{
+ struct dsp_conf *conf;
+ struct dsp_conf_member *member;
+ struct dsp *dsp;
+ int mustmix, members;
+ s32 mixbuffer[MAX_POLL+100], *c;
+ u8 *p, *q;
+ int r, rr;
+ int jittercheck = 0, delay, i;
+ u_long flags;
+ struct timeval tv;
+ u32 elapsed;
+ s16 length;
+
+ /* lock */
+ spin_lock_irqsave(&dsp_lock, flags);
+
+ if (!dsp_start_tv.tv_sec) {
+ do_gettimeofday(&dsp_start_tv);
+ length = dsp_poll;
+ } else {
+ do_gettimeofday(&tv);
+ elapsed = ((tv.tv_sec - dsp_start_tv.tv_sec) * 8000)
+ + ((s32)(tv.tv_usec / 125) - (dsp_start_tv.tv_usec / 125));
+ dsp_start_tv.tv_sec = tv.tv_sec;
+ dsp_start_tv.tv_usec = tv.tv_usec;
+ length = elapsed;
+ }
+ if (length > MAX_POLL + 100)
+ length = MAX_POLL + 100;
+/* printk(KERN_DEBUG "len=%d dsp_count=0x%x.%04x dsp_poll_diff=0x%x.%04x\n",
+ length, dsp_count >> 16, dsp_count & 0xffff, dsp_poll_diff >> 16,
+ dsp_poll_diff & 0xffff);
+ */
+
+ /*
+ * check if jitter needs to be checked
+ * (this is about every second = 8192 samples)
+ */
+ samplecount += length;
+ if ((samplecount & 8191) < length)
+ jittercheck = 1;
+
+ /* loop all members that do not require conference mixing */
+ list_for_each_entry(dsp, &dsp_ilist, list) {
+ if (dsp->hdlc)
+ continue;
+ conf = dsp->conf;
+ mustmix = 0;
+ members = 0;
+ if (conf) {
+ members = count_list_member(&conf->mlist);
+#ifdef CMX_CONF_DEBUG
+ if (conf->software && members > 1)
+#else
+ if (conf->software && members > 2)
+#endif
+ mustmix = 1;
+ }
+
+ /* transmission required */
+ if (!mustmix) {
+ dsp_cmx_send_member(dsp, length, mixbuffer, members);
+
+ /*
+ * unused mixbuffer is given to prevent a
+ * potential null-pointer-bug
+ */
+ }
+ }
+
+ /* loop all members that require conference mixing */
+ list_for_each_entry(conf, &conf_ilist, list) {
+ /* count members and check hardware */
+ members = count_list_member(&conf->mlist);
+#ifdef CMX_CONF_DEBUG
+ if (conf->software && members > 1) {
+#else
+ if (conf->software && members > 2) {
+#endif
+ /* check for hdlc conf */
+ member = list_entry(conf->mlist.next,
+ struct dsp_conf_member, list);
+ if (member->dsp->hdlc)
+ continue;
+ /* mix all data */
+ memset(mixbuffer, 0, length*sizeof(s32));
+ list_for_each_entry(member, &conf->mlist, list) {
+ dsp = member->dsp;
+ /* get range of data to mix */
+ c = mixbuffer;
+ q = dsp->rx_buff;
+ r = dsp->rx_R;
+ rr = (r + length) & CMX_BUFF_MASK;
+ /* add member's data */
+ while (r != rr) {
+ *c++ += dsp_audio_law_to_s32[q[r]];
+ r = (r+1) & CMX_BUFF_MASK;
+ }
+ }
+
+ /* process each member */
+ list_for_each_entry(member, &conf->mlist, list) {
+ /* transmission */
+ dsp_cmx_send_member(member->dsp, length,
+ mixbuffer, members);
+ }
+ }
+ }
+
+ /* delete rx-data, increment buffers, change pointers */
+ list_for_each_entry(dsp, &dsp_ilist, list) {
+ if (dsp->hdlc)
+ continue;
+ p = dsp->rx_buff;
+ q = dsp->tx_buff;
+ r = dsp->rx_R;
+ /* move receive pointer when receiving */
+ if (!dsp->rx_is_off) {
+ rr = (r + length) & CMX_BUFF_MASK;
+ /* delete rx-data */
+ while (r != rr) {
+ p[r] = dsp_silence;
+ r = (r+1) & CMX_BUFF_MASK;
+ }
+ /* increment rx-buffer pointer */
+ dsp->rx_R = r; /* write incremented read pointer */
+ }
+
+ /* check current rx_delay */
+ delay = (dsp->rx_W-dsp->rx_R) & CMX_BUFF_MASK;
+ if (delay >= CMX_BUFF_HALF)
+ delay = 0; /* will be the delay before next write */
+ /* check for lower delay */
+ if (delay < dsp->rx_delay[0])
+ dsp->rx_delay[0] = delay;
+ /* check current tx_delay */
+ delay = (dsp->tx_W-dsp->tx_R) & CMX_BUFF_MASK;
+ if (delay >= CMX_BUFF_HALF)
+ delay = 0; /* will be the delay before next write */
+ /* check for lower delay */
+ if (delay < dsp->tx_delay[0])
+ dsp->tx_delay[0] = delay;
+ if (jittercheck) {
+ /* find the lowest of all rx_delays */
+ delay = dsp->rx_delay[0];
+ i = 1;
+ while (i < MAX_SECONDS_JITTER_CHECK) {
+ if (delay > dsp->rx_delay[i])
+ delay = dsp->rx_delay[i];
+ i++;
+ }
+ /*
+ * remove rx_delay only if we have delay AND we
+ * have not preset cmx_delay
+ */
+ if (delay && !dsp->cmx_delay) {
+ if (dsp_debug & DEBUG_DSP_CMX)
+ printk(KERN_DEBUG
+ "%s lowest rx_delay of %d bytes for"
+ " dsp %s are now removed.\n",
+ __func__, delay,
+ dsp->name);
+ r = dsp->rx_R;
+ rr = (r + delay) & CMX_BUFF_MASK;
+ /* delete rx-data */
+ while (r != rr) {
+ p[r] = dsp_silence;
+ r = (r+1) & CMX_BUFF_MASK;
+ }
+ /* increment rx-buffer pointer */
+ dsp->rx_R = r;
+ /* write incremented read pointer */
+ }
+ /* find the lowest of all tx_delays */
+ delay = dsp->tx_delay[0];
+ i = 1;
+ while (i < MAX_SECONDS_JITTER_CHECK) {
+ if (delay > dsp->tx_delay[i])
+ delay = dsp->tx_delay[i];
+ i++;
+ }
+ /*
+ * remove delay only if we have delay AND we
+ * have enabled tx_dejitter
+ */
+ if (delay && dsp->tx_dejitter) {
+ if (dsp_debug & DEBUG_DSP_CMX)
+ printk(KERN_DEBUG
+ "%s lowest tx_delay of %d bytes for"
+ " dsp %s are now removed.\n",
+ __func__, delay,
+ dsp->name);
+ r = dsp->tx_R;
+ rr = (r + delay) & CMX_BUFF_MASK;
+ /* delete tx-data */
+ while (r != rr) {
+ q[r] = dsp_silence;
+ r = (r+1) & CMX_BUFF_MASK;
+ }
+ /* increment rx-buffer pointer */
+ dsp->tx_R = r;
+ /* write incremented read pointer */
+ }
+ /* scroll up delays */
+ i = MAX_SECONDS_JITTER_CHECK - 1;
+ while (i) {
+ dsp->rx_delay[i] = dsp->rx_delay[i-1];
+ dsp->tx_delay[i] = dsp->tx_delay[i-1];
+ i--;
+ }
+ dsp->tx_delay[0] = CMX_BUFF_HALF; /* (infinite) delay */
+ dsp->rx_delay[0] = CMX_BUFF_HALF; /* (infinite) delay */
+ }
+ }
+
+ /* if next event would be in the past ... */
+ if ((s32)(dsp_spl_jiffies+dsp_tics-jiffies) <= 0)
+ dsp_spl_jiffies = jiffies + 1;
+ else
+ dsp_spl_jiffies += dsp_tics;
+
+ dsp_spl_tl.expires = dsp_spl_jiffies;
+ add_timer(&dsp_spl_tl);
+
+ /* unlock */
+ spin_unlock_irqrestore(&dsp_lock, flags);
+}
+
+/*
+ * audio data is transmitted from upper layer to the dsp
+ */
+void
+dsp_cmx_transmit(struct dsp *dsp, struct sk_buff *skb)
+{
+ u_int w, ww;
+ u8 *d, *p;
+ int space; /* todo: , l = skb->len; */
+#ifdef CMX_TX_DEBUG
+ char debugbuf[256] = "";
+#endif
+
+ /* check if there is enough space, and then copy */
+ w = dsp->tx_W;
+ ww = dsp->tx_R;
+ 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->tx_W = ww;
+
+ /* show current buffer */
+#ifdef CMX_DEBUG
+ printk(KERN_DEBUG
+ "cmx_transmit(dsp=%lx) %d bytes to 0x%x-0x%x. %s\n",
+ (u_long)dsp, (ww-w)&CMX_BUFF_MASK, w, ww, dsp->name);
+#endif
+
+ /* copy transmit data to tx-buffer */
+#ifdef CMX_TX_DEBUG
+ sprintf(debugbuf, "TX getting (%04x-%04x)%p: ", w, ww, p);
+#endif
+ while (w != ww) {
+#ifdef CMX_TX_DEBUG
+ if (strlen(debugbuf) < 48)
+ sprintf(debugbuf+strlen(debugbuf), " %02x", *d);
+#endif
+ p[w] = *d++;
+ w = (w+1) & CMX_BUFF_MASK;
+ }
+#ifdef CMX_TX_DEBUG
+ printk(KERN_DEBUG "%s\n", debugbuf);
+#endif
+
+}
+
+/*
+ * hdlc data is received from card and sent to all members.
+ */
+void
+dsp_cmx_hdlc(struct dsp *dsp, struct sk_buff *skb)
+{
+ struct sk_buff *nskb = NULL;
+ struct dsp_conf_member *member;
+ struct mISDNhead *hh;
+
+ /* not if not active */
+ if (!dsp->b_active)
+ return;
+
+ /* check if we have sompen */
+ if (skb->len < 1)
+ return;
+
+ /* no conf */
+ if (!dsp->conf) {
+ /* in case of hardware (echo) */
+ if (dsp->pcm_slot_tx >= 0)
+ return;
+ if (dsp->echo)
+ nskb = skb_clone(skb, GFP_ATOMIC);
+ if (nskb) {
+ hh = mISDN_HEAD_P(nskb);
+ hh->prim = PH_DATA_REQ;
+ hh->id = 0;
+ skb_queue_tail(&dsp->sendq, nskb);
+ schedule_work(&dsp->workq);
+ }
+ return;
+ }
+ /* in case of hardware conference */
+ if (dsp->conf->hardware)
+ return;
+ list_for_each_entry(member, &dsp->conf->mlist, list) {
+ if (dsp->echo || member->dsp != dsp) {
+ nskb = skb_clone(skb, GFP_ATOMIC);
+ if (nskb) {
+ hh = mISDN_HEAD_P(nskb);
+ hh->prim = PH_DATA_REQ;
+ hh->id = 0;
+ skb_queue_tail(&member->dsp->sendq, nskb);
+ schedule_work(&member->dsp->workq);
+ }
+ }
+ }
+}
+
+
diff --git a/drivers/isdn/mISDN/dsp_core.c b/drivers/isdn/mISDN/dsp_core.c
new file mode 100644
index 0000000..2f10ed8
--- /dev/null
+++ b/drivers/isdn/mISDN/dsp_core.c
@@ -0,0 +1,1191 @@
+/*
+ * Author Andreas Eversberg (jolly@eversberg.eu)
+ * 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 (clocking)
+ * - (4) echo generation for delay test
+ * - (5) volume control
+ * - (6) disable receive data
+ * - (7) pipeline
+ * - (8) encryption/decryption
+ *
+ * Look:
+ * TX RX
+ * ------upper layer------
+ * | ^
+ * | |(6)
+ * v |
+ * +-----+-------------+-----+
+ * |(3)(4) |
+ * | CMX |
+ * | |
+ * | +-------------+
+ * | | ^
+ * | | |
+ * |+---------+| +----+----+
+ * ||(1) || |(2) |
+ * || || | |
+ * || Tones || | DTMF |
+ * || || | |
+ * || || | |
+ * |+----+----+| +----+----+
+ * +-----+-----+ ^
+ * | |
+ * v |
+ * +----+----+ +----+----+
+ * |(5) | |(5) |
+ * | | | |
+ * |TX Volume| |RX Volume|
+ * | | | |
+ * | | | |
+ * +----+----+ +----+----+
+ * | ^
+ * | |
+ * v |
+ * +----+-------------+----+
+ * |(7) |
+ * | |
+ * | Pipeline Processing |
+ * | |
+ * | |
+ * +----+-------------+----+
+ * | ^
+ * | |
+ * v |
+ * +----+----+ +----+----+
+ * |(8) | |(8) |
+ * | | | |
+ * | 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.
+ *
+ * Echo Cancellation: Sometimes we like to cancel echo from the interface.
+ * Note that a VoIP call may not have echo caused by the IP phone. The echo
+ * is generated by the telephone line connected to it. Because the delay
+ * is high, it becomes an echo. RESULT: Echo Cachelation is required if
+ * both echo AND delay is applied to an interface.
+ * Remember that software CMX always generates a more or less delay.
+ *
+ * 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. This lock MUST lock irq, because it
+ * must lock timer events by DSP poll timer.
+ * When data is ready to be transmitted down, the data is queued and sent
+ * outside lock and timer event.
+ * PH_CONTROL must not change any settings, join or split conference members
+ * during process of data.
+ *
+ * HDLC:
+ *
+ * It works quite the same as transparent, except that HDLC data is forwarded
+ * to all other conference members if no hardware bridging is possible.
+ * Send data will be writte to sendq. Sendq will be sent if confirm is received.
+ * Conference cannot join, if one member is not hdlc.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/mISDNif.h>
+#include <linux/mISDNdsp.h>
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include "core.h"
+#include "dsp.h"
+
+const char *mISDN_dsp_revision = "2.0";
+
+static int debug;
+static int options;
+static int poll;
+static int dtmfthreshold = 100;
+
+MODULE_AUTHOR("Andreas Eversberg");
+module_param(debug, uint, S_IRUGO | S_IWUSR);
+module_param(options, uint, S_IRUGO | S_IWUSR);
+module_param(poll, uint, S_IRUGO | S_IWUSR);
+module_param(dtmfthreshold, uint, S_IRUGO | S_IWUSR);
+MODULE_LICENSE("GPL");
+
+/*int spinnest = 0;*/
+
+spinlock_t dsp_lock; /* global dsp lock */
+struct list_head dsp_ilist;
+struct list_head conf_ilist;
+int dsp_debug;
+int dsp_options;
+int dsp_poll, dsp_tics;
+
+/* check if rx may be turned off or must be turned on */
+static void
+dsp_rx_off_member(struct dsp *dsp)
+{
+ struct mISDN_ctrl_req cq;
+ int rx_off = 1;
+
+ if (!dsp->features_rx_off)
+ return;
+
+ /* not disabled */
+ if (!dsp->rx_disabled)
+ rx_off = 0;
+ /* software dtmf */
+ else if (dsp->dtmf.software)
+ rx_off = 0;
+ /* echo in software */
+ else if (dsp->echo && dsp->pcm_slot_tx < 0)
+ rx_off = 0;
+ /* bridge in software */
+ else if (dsp->conf) {
+ if (dsp->conf->software)
+ rx_off = 0;
+ }
+
+ if (rx_off == dsp->rx_is_off)
+ return;
+
+ if (!dsp->ch.peer) {
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: no peer, no rx_off\n",
+ __func__);
+ return;
+ }
+ cq.op = MISDN_CTRL_RX_OFF;
+ cq.p1 = rx_off;
+ if (dsp->ch.peer->ctrl(dsp->ch.peer, CONTROL_CHANNEL, &cq)) {
+ printk(KERN_DEBUG "%s: 2nd CONTROL_CHANNEL failed\n",
+ __func__);
+ return;
+ }
+ dsp->rx_is_off = rx_off;
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: %s set rx_off = %d\n",
+ __func__, dsp->name, rx_off);
+}
+static void
+dsp_rx_off(struct dsp *dsp)
+{
+ struct dsp_conf_member *member;
+
+ if (dsp_options & DSP_OPT_NOHARDWARE)
+ return;
+
+ /* no conf */
+ if (!dsp->conf) {
+ dsp_rx_off_member(dsp);
+ return;
+ }
+ /* check all members in conf */
+ list_for_each_entry(member, &dsp->conf->mlist, list) {
+ dsp_rx_off_member(member->dsp);
+ }
+}
+
+static int
+dsp_control_req(struct dsp *dsp, struct mISDNhead *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", __func__);
+ 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->hdlc) {
+ ret = -EINVAL;
+ break;
+ }
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: start dtmf\n", __func__);
+ if (len == sizeof(int)) {
+ printk(KERN_NOTICE "changing DTMF Threshold "
+ "to %d\n", *((int *)data));
+ dsp->dtmf.treshold = (*(int *)data) * 10000;
+ }
+ /* init goertzel */
+ dsp_dtmf_goertzel_init(dsp);
+
+ /* check dtmf hardware */
+ dsp_dtmf_hardware(dsp);
+ break;
+ case DTMF_TONE_STOP: /* turn off DTMF */
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: stop dtmf\n", __func__);
+ dsp->dtmf.hardware = 0;
+ dsp->dtmf.software = 0;
+ break;
+ case DSP_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",
+ __func__, *((u32 *)data));
+ ret = dsp_cmx_conf(dsp, *((u32 *)data));
+ /* dsp_cmx_hardware will also be called here */
+ dsp_rx_off(dsp);
+ if (dsp_debug & DEBUG_DSP_CMX)
+ dsp_cmx_debug(dsp);
+ break;
+ case DSP_CONF_SPLIT: /* remove from conference */
+conf_split:
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: release conference\n", __func__);
+ ret = dsp_cmx_conf(dsp, 0);
+ /* dsp_cmx_hardware will also be called here */
+ if (dsp_debug & DEBUG_DSP_CMX)
+ dsp_cmx_debug(dsp);
+ dsp_rx_off(dsp);
+ break;
+ case DSP_TONE_PATT_ON: /* play tone */
+ if (dsp->hdlc) {
+ ret = -EINVAL;
+ break;
+ }
+ if (len < sizeof(int)) {
+ ret = -EINVAL;
+ break;
+ }
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: turn tone 0x%x on\n",
+ __func__, *((int *)skb->data));
+ ret = dsp_tone(dsp, *((int *)data));
+ if (!ret) {
+ dsp_cmx_hardware(dsp->conf, dsp);
+ dsp_rx_off(dsp);
+ }
+ if (!dsp->tone.tone)
+ goto tone_off;
+ break;
+ case DSP_TONE_PATT_OFF: /* stop tone */
+ if (dsp->hdlc) {
+ ret = -EINVAL;
+ break;
+ }
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: turn tone off\n", __func__);
+ dsp_tone(dsp, 0);
+ dsp_cmx_hardware(dsp->conf, dsp);
+ dsp_rx_off(dsp);
+ /* reset tx buffers (user space data) */
+tone_off:
+ dsp->rx_W = 0;
+ dsp->rx_R = 0;
+ break;
+ case DSP_VOL_CHANGE_TX: /* change volume */
+ if (dsp->hdlc) {
+ ret = -EINVAL;
+ break;
+ }
+ if (len < sizeof(int)) {
+ ret = -EINVAL;
+ break;
+ }
+ dsp->tx_volume = *((int *)data);
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: change tx vol to %d\n",
+ __func__, dsp->tx_volume);
+ dsp_cmx_hardware(dsp->conf, dsp);
+ dsp_dtmf_hardware(dsp);
+ dsp_rx_off(dsp);
+ break;
+ case DSP_VOL_CHANGE_RX: /* change volume */
+ if (dsp->hdlc) {
+ ret = -EINVAL;
+ break;
+ }
+ if (len < sizeof(int)) {
+ ret = -EINVAL;
+ break;
+ }
+ dsp->rx_volume = *((int *)data);
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: change rx vol to %d\n",
+ __func__, dsp->tx_volume);
+ dsp_cmx_hardware(dsp->conf, dsp);
+ dsp_dtmf_hardware(dsp);
+ dsp_rx_off(dsp);
+ break;
+ case DSP_ECHO_ON: /* enable echo */
+ dsp->echo = 1; /* soft echo */
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: enable cmx-echo\n", __func__);
+ dsp_cmx_hardware(dsp->conf, dsp);
+ dsp_rx_off(dsp);
+ if (dsp_debug & DEBUG_DSP_CMX)
+ dsp_cmx_debug(dsp);
+ break;
+ case DSP_ECHO_OFF: /* disable echo */
+ dsp->echo = 0;
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: disable cmx-echo\n", __func__);
+ dsp_cmx_hardware(dsp->conf, dsp);
+ dsp_rx_off(dsp);
+ if (dsp_debug & DEBUG_DSP_CMX)
+ dsp_cmx_debug(dsp);
+ break;
+ case DSP_RECEIVE_ON: /* enable receive to user space */
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: enable receive to user "
+ "space\n", __func__);
+ dsp->rx_disabled = 0;
+ dsp_rx_off(dsp);
+ break;
+ case DSP_RECEIVE_OFF: /* disable receive to user space */
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: disable receive to "
+ "user space\n", __func__);
+ dsp->rx_disabled = 1;
+ dsp_rx_off(dsp);
+ break;
+ case DSP_MIX_ON: /* enable mixing of tx data */
+ if (dsp->hdlc) {
+ ret = -EINVAL;
+ break;
+ }
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: enable mixing of "
+ "tx-data with conf mebers\n", __func__);
+ dsp->tx_mix = 1;
+ dsp_cmx_hardware(dsp->conf, dsp);
+ dsp_rx_off(dsp);
+ if (dsp_debug & DEBUG_DSP_CMX)
+ dsp_cmx_debug(dsp);
+ break;
+ case DSP_MIX_OFF: /* disable mixing of tx data */
+ if (dsp->hdlc) {
+ ret = -EINVAL;
+ break;
+ }
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: disable mixing of "
+ "tx-data with conf mebers\n", __func__);
+ dsp->tx_mix = 0;
+ dsp_cmx_hardware(dsp->conf, dsp);
+ dsp_rx_off(dsp);
+ if (dsp_debug & DEBUG_DSP_CMX)
+ dsp_cmx_debug(dsp);
+ break;
+ case DSP_TXDATA_ON: /* enable txdata */
+ dsp->tx_data = 1;
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: enable tx-data\n", __func__);
+ dsp_cmx_hardware(dsp->conf, dsp);
+ dsp_rx_off(dsp);
+ if (dsp_debug & DEBUG_DSP_CMX)
+ dsp_cmx_debug(dsp);
+ break;
+ case DSP_TXDATA_OFF: /* disable txdata */
+ dsp->tx_data = 0;
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: disable tx-data\n", __func__);
+ dsp_cmx_hardware(dsp->conf, dsp);
+ dsp_rx_off(dsp);
+ if (dsp_debug & DEBUG_DSP_CMX)
+ dsp_cmx_debug(dsp);
+ break;
+ case DSP_DELAY: /* use delay algorithm instead of dynamic
+ jitter algorithm */
+ if (dsp->hdlc) {
+ ret = -EINVAL;
+ break;
+ }
+ if (len < sizeof(int)) {
+ ret = -EINVAL;
+ break;
+ }
+ dsp->cmx_delay = (*((int *)data)) << 3;
+ /* miliseconds to samples */
+ if (dsp->cmx_delay >= (CMX_BUFF_HALF>>1))
+ /* clip to half of maximum usable buffer
+ (half of half buffer) */
+ dsp->cmx_delay = (CMX_BUFF_HALF>>1) - 1;
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: use delay algorithm to "
+ "compensate jitter (%d samples)\n",
+ __func__, dsp->cmx_delay);
+ break;
+ case DSP_JITTER: /* use dynamic jitter algorithm instead of
+ delay algorithm */
+ if (dsp->hdlc) {
+ ret = -EINVAL;
+ break;
+ }
+ dsp->cmx_delay = 0;
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: use jitter algorithm to "
+ "compensate jitter\n", __func__);
+ break;
+ case DSP_TX_DEJITTER: /* use dynamic jitter algorithm for tx-buffer */
+ if (dsp->hdlc) {
+ ret = -EINVAL;
+ break;
+ }
+ dsp->tx_dejitter = 1;
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: use dejitter on TX "
+ "buffer\n", __func__);
+ break;
+ case DSP_TX_DEJ_OFF: /* use tx-buffer without dejittering*/
+ if (dsp->hdlc) {
+ ret = -EINVAL;
+ break;
+ }
+ dsp->tx_dejitter = 0;
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: use TX buffer without "
+ "dejittering\n", __func__);
+ break;
+ case DSP_PIPELINE_CFG:
+ if (dsp->hdlc) {
+ ret = -EINVAL;
+ break;
+ }
+ if (len > 0 && ((char *)data)[len - 1]) {
+ printk(KERN_DEBUG "%s: pipeline config string "
+ "is not NULL terminated!\n", __func__);
+ ret = -EINVAL;
+ } else {
+ dsp->pipeline.inuse = 1;
+ dsp_cmx_hardware(dsp->conf, dsp);
+ ret = dsp_pipeline_build(&dsp->pipeline,
+ len > 0 ? (char *)data : NULL);
+ dsp_cmx_hardware(dsp->conf, dsp);
+ dsp_rx_off(dsp);
+ }
+ break;
+ case DSP_BF_ENABLE_KEY: /* turn blowfish on */
+ if (dsp->hdlc) {
+ ret = -EINVAL;
+ break;
+ }
+ 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", __func__);
+ ret = dsp_bf_init(dsp, (u8 *)data, len);
+ /* set new cont */
+ if (!ret)
+ cont = DSP_BF_ACCEPT;
+ else
+ cont = DSP_BF_REJECT;
+ /* send indication if it worked to set it */
+ nskb = _alloc_mISDN_skb(PH_CONTROL_IND, MISDN_ID_ANY,
+ sizeof(int), &cont, GFP_ATOMIC);
+ if (nskb) {
+ if (dsp->up) {
+ if (dsp->up->send(dsp->up, nskb))
+ dev_kfree_skb(nskb);
+ } else
+ dev_kfree_skb(nskb);
+ }
+ if (!ret) {
+ dsp_cmx_hardware(dsp->conf, dsp);
+ dsp_dtmf_hardware(dsp);
+ dsp_rx_off(dsp);
+ }
+ break;
+ case DSP_BF_DISABLE: /* turn blowfish off */
+ if (dsp->hdlc) {
+ ret = -EINVAL;
+ break;
+ }
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: turn blowfish off\n", __func__);
+ dsp_bf_cleanup(dsp);
+ dsp_cmx_hardware(dsp->conf, dsp);
+ dsp_dtmf_hardware(dsp);
+ dsp_rx_off(dsp);
+ break;
+ default:
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: ctrl req %x unhandled\n",
+ __func__, cont);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static void
+get_features(struct mISDNchannel *ch)
+{
+ struct dsp *dsp = container_of(ch, struct dsp, ch);
+ struct mISDN_ctrl_req cq;
+
+ if (dsp_options & DSP_OPT_NOHARDWARE)
+ return;
+ if (!ch->peer) {
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: no peer, no features\n",
+ __func__);
+ return;
+ }
+ memset(&cq, 0, sizeof(cq));
+ cq.op = MISDN_CTRL_GETOP;
+ if (ch->peer->ctrl(ch->peer, CONTROL_CHANNEL, &cq) < 0) {
+ printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n",
+ __func__);
+ return;
+ }
+ if (cq.op & MISDN_CTRL_RX_OFF)
+ dsp->features_rx_off = 1;
+ if ((cq.op & MISDN_CTRL_HW_FEATURES_OP)) {
+ cq.op = MISDN_CTRL_HW_FEATURES;
+ *((u_long *)&cq.p1) = (u_long)&dsp->features;
+ if (ch->peer->ctrl(ch->peer, CONTROL_CHANNEL, &cq)) {
+ printk(KERN_DEBUG "%s: 2nd CONTROL_CHANNEL failed\n",
+ __func__);
+ }
+ } else
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: features not supported for %s\n",
+ __func__, dsp->name);
+}
+
+static int
+dsp_function(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+ struct dsp *dsp = container_of(ch, struct dsp, ch);
+ struct mISDNhead *hh;
+ int ret = 0;
+ u8 *digits;
+ int cont;
+ struct sk_buff *nskb;
+ u_long flags;
+
+ hh = mISDN_HEAD_P(skb);
+ switch (hh->prim) {
+ /* FROM DOWN */
+ case (PH_DATA_CNF):
+ dsp->data_pending = 0;
+ /* trigger next hdlc frame, if any */
+ if (dsp->hdlc) {
+ spin_lock_irqsave(&dsp_lock, flags);
+ if (dsp->b_active)
+ schedule_work(&dsp->workq);
+ spin_unlock_irqrestore(&dsp_lock, flags);
+ }
+ break;
+ case (PH_DATA_IND):
+ case (DL_DATA_IND):
+ if (skb->len < 1) {
+ ret = -EINVAL;
+ break;
+ }
+ if (dsp->rx_is_off) {
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: rx-data during rx_off"
+ " for %s\n",
+ __func__, dsp->name);
+ }
+ if (dsp->hdlc) {
+ /* hdlc */
+ spin_lock_irqsave(&dsp_lock, flags);
+ dsp_cmx_hdlc(dsp, skb);
+ spin_unlock_irqrestore(&dsp_lock, flags);
+ if (dsp->rx_disabled) {
+ /* if receive is not allowed */
+ break;
+ }
+ hh->prim = DL_DATA_IND;
+ if (dsp->up)
+ return dsp->up->send(dsp->up, skb);
+ break;
+ }
+
+ /* decrypt if enabled */
+ if (dsp->bf_enable)
+ dsp_bf_decrypt(dsp, skb->data, skb->len);
+ /* pipeline */
+ if (dsp->pipeline.inuse)
+ dsp_pipeline_process_rx(&dsp->pipeline, skb->data,
+ skb->len);
+ /* change volume if requested */
+ if (dsp->rx_volume)
+ dsp_change_volume(skb, dsp->rx_volume);
+
+ /* 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);
+ while (*digits) {
+ if (dsp_debug & DEBUG_DSP_DTMF)
+ printk(KERN_DEBUG "%s: digit"
+ "(%c) to layer %s\n",
+ __func__, *digits, dsp->name);
+ cont = DTMF_TONE_VAL | *digits;
+ nskb = _alloc_mISDN_skb(PH_CONTROL_IND,
+ MISDN_ID_ANY, sizeof(int), &cont,
+ GFP_ATOMIC);
+ if (nskb) {
+ if (dsp->up) {
+ if (dsp->up->send(
+ dsp->up, nskb))
+ dev_kfree_skb(nskb);
+ } else
+ dev_kfree_skb(nskb);
+ }
+ digits++;
+ }
+ }
+ /* we need to process receive data if software */
+ spin_lock_irqsave(&dsp_lock, flags);
+ if (dsp->pcm_slot_tx < 0 && dsp->pcm_slot_rx < 0) {
+ /* process data from card at cmx */
+ dsp_cmx_receive(dsp, skb);
+ }
+ spin_unlock_irqrestore(&dsp_lock, flags);
+
+ if (dsp->rx_disabled) {
+ /* if receive is not allowed */
+ break;
+ }
+ hh->prim = DL_DATA_IND;
+ if (dsp->up)
+ return dsp->up->send(dsp->up, skb);
+ break;
+ case (PH_CONTROL_IND):
+ if (dsp_debug & DEBUG_DSP_DTMFCOEFF)
+ printk(KERN_DEBUG "%s: PH_CONTROL INDICATION "
+ "received: %x (len %d) %s\n", __func__,
+ hh->id, skb->len, dsp->name);
+ switch (hh->id) {
+ case (DTMF_HFC_COEF): /* getting coefficients */
+ if (!dsp->dtmf.hardware) {
+ if (dsp_debug & DEBUG_DSP_DTMFCOEFF)
+ printk(KERN_DEBUG "%s: ignoring DTMF "
+ "coefficients from HFC\n",
+ __func__);
+ break;
+ }
+ digits = dsp_dtmf_goertzel_decode(dsp, skb->data,
+ skb->len, 2);
+ while (*digits) {
+ int k;
+ struct sk_buff *nskb;
+ if (dsp_debug & DEBUG_DSP_DTMF)
+ printk(KERN_DEBUG "%s: digit"
+ "(%c) to layer %s\n",
+ __func__, *digits, dsp->name);
+ k = *digits | DTMF_TONE_VAL;
+ nskb = _alloc_mISDN_skb(PH_CONTROL_IND,
+ MISDN_ID_ANY, sizeof(int), &k,
+ GFP_ATOMIC);
+ if (nskb) {
+ if (dsp->up) {
+ if (dsp->up->send(
+ dsp->up, nskb))
+ dev_kfree_skb(nskb);
+ } else
+ dev_kfree_skb(nskb);
+ }
+ digits++;
+ }
+ break;
+ case (HFC_VOL_CHANGE_TX): /* change volume */
+ if (skb->len != sizeof(int)) {
+ ret = -EINVAL;
+ break;
+ }
+ spin_lock_irqsave(&dsp_lock, flags);
+ dsp->tx_volume = *((int *)skb->data);
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: change tx volume to "
+ "%d\n", __func__, dsp->tx_volume);
+ dsp_cmx_hardware(dsp->conf, dsp);
+ dsp_dtmf_hardware(dsp);
+ dsp_rx_off(dsp);
+ spin_unlock_irqrestore(&dsp_lock, flags);
+ break;
+ default:
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: ctrl ind %x unhandled "
+ "%s\n", __func__, hh->id, dsp->name);
+ ret = -EINVAL;
+ }
+ break;
+ case (PH_ACTIVATE_IND):
+ case (PH_ACTIVATE_CNF):
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: b_channel is now active %s\n",
+ __func__, dsp->name);
+ /* bchannel now active */
+ spin_lock_irqsave(&dsp_lock, flags);
+ dsp->b_active = 1;
+ dsp->data_pending = 0;
+ dsp->rx_init = 1;
+ /* rx_W and rx_R will be adjusted on first frame */
+ dsp->rx_W = 0;
+ dsp->rx_R = 0;
+ memset(dsp->rx_buff, 0, sizeof(dsp->rx_buff));
+ dsp_cmx_hardware(dsp->conf, dsp);
+ dsp_dtmf_hardware(dsp);
+ dsp_rx_off(dsp);
+ spin_unlock_irqrestore(&dsp_lock, flags);
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: done with activation, sending "
+ "confirm to user space. %s\n", __func__,
+ dsp->name);
+ /* send activation to upper layer */
+ hh->prim = DL_ESTABLISH_CNF;
+ if (dsp->up)
+ return dsp->up->send(dsp->up, skb);
+ break;
+ case (PH_DEACTIVATE_IND):
+ case (PH_DEACTIVATE_CNF):
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: b_channel is now inactive %s\n",
+ __func__, dsp->name);
+ /* bchannel now inactive */
+ spin_lock_irqsave(&dsp_lock, flags);
+ dsp->b_active = 0;
+ dsp->data_pending = 0;
+ dsp_cmx_hardware(dsp->conf, dsp);
+ dsp_rx_off(dsp);
+ spin_unlock_irqrestore(&dsp_lock, flags);
+ hh->prim = DL_RELEASE_CNF;
+ if (dsp->up)
+ return dsp->up->send(dsp->up, skb);
+ break;
+ /* FROM UP */
+ case (DL_DATA_REQ):
+ case (PH_DATA_REQ):
+ if (skb->len < 1) {
+ ret = -EINVAL;
+ break;
+ }
+ if (dsp->hdlc) {
+ /* hdlc */
+ spin_lock_irqsave(&dsp_lock, flags);
+ if (dsp->b_active) {
+ skb_queue_tail(&dsp->sendq, skb);
+ schedule_work(&dsp->workq);
+ }
+ spin_unlock_irqrestore(&dsp_lock, flags);
+ return 0;
+ }
+ /* send data to tx-buffer (if no tone is played) */
+ if (!dsp->tone.tone) {
+ spin_lock_irqsave(&dsp_lock, flags);
+ dsp_cmx_transmit(dsp, skb);
+ spin_unlock_irqrestore(&dsp_lock, flags);
+ }
+ break;
+ case (PH_CONTROL_REQ):
+ spin_lock_irqsave(&dsp_lock, flags);
+ ret = dsp_control_req(dsp, hh, skb);
+ spin_unlock_irqrestore(&dsp_lock, flags);
+ break;
+ case (DL_ESTABLISH_REQ):
+ case (PH_ACTIVATE_REQ):
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: activating b_channel %s\n",
+ __func__, dsp->name);
+ if (dsp->dtmf.hardware || dsp->dtmf.software)
+ dsp_dtmf_goertzel_init(dsp);
+ get_features(ch);
+ /* send ph_activate */
+ hh->prim = PH_ACTIVATE_REQ;
+ if (ch->peer)
+ return ch->recv(ch->peer, skb);
+ break;
+ case (DL_RELEASE_REQ):
+ case (PH_DEACTIVATE_REQ):
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: releasing b_channel %s\n",
+ __func__, dsp->name);
+ spin_lock_irqsave(&dsp_lock, flags);
+ dsp->tone.tone = 0;
+ dsp->tone.hardware = 0;
+ dsp->tone.software = 0;
+ if (timer_pending(&dsp->tone.tl))
+ del_timer(&dsp->tone.tl);
+ if (dsp->conf)
+ dsp_cmx_conf(dsp, 0); /* dsp_cmx_hardware will also be
+ called here */
+ skb_queue_purge(&dsp->sendq);
+ spin_unlock_irqrestore(&dsp_lock, flags);
+ hh->prim = PH_DEACTIVATE_REQ;
+ if (ch->peer)
+ return ch->recv(ch->peer, skb);
+ break;
+ default:
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: msg %x unhandled %s\n",
+ __func__, hh->prim, dsp->name);
+ ret = -EINVAL;
+ }
+ if (!ret)
+ dev_kfree_skb(skb);
+ return ret;
+}
+
+static int
+dsp_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+ struct dsp *dsp = container_of(ch, struct dsp, ch);
+ u_long flags;
+ int err = 0;
+
+ if (debug & DEBUG_DSP_CTRL)
+ printk(KERN_DEBUG "%s:(%x)\n", __func__, cmd);
+
+ switch (cmd) {
+ case OPEN_CHANNEL:
+ break;
+ case CLOSE_CHANNEL:
+ if (dsp->ch.peer)
+ dsp->ch.peer->ctrl(dsp->ch.peer, CLOSE_CHANNEL, NULL);
+
+ /* wait until workqueue has finished,
+ * must lock here, or we may hit send-process currently
+ * queueing. */
+ spin_lock_irqsave(&dsp_lock, flags);
+ dsp->b_active = 0;
+ spin_unlock_irqrestore(&dsp_lock, flags);
+ /* MUST not be locked, because it waits until queue is done. */
+ cancel_work_sync(&dsp->workq);
+ spin_lock_irqsave(&dsp_lock, flags);
+ if (timer_pending(&dsp->tone.tl))
+ del_timer(&dsp->tone.tl);
+ skb_queue_purge(&dsp->sendq);
+ if (dsp_debug & DEBUG_DSP_CTRL)
+ printk(KERN_DEBUG "%s: releasing member %s\n",
+ __func__, dsp->name);
+ dsp->b_active = 0;
+ dsp_cmx_conf(dsp, 0); /* dsp_cmx_hardware will also be called
+ here */
+ dsp_pipeline_destroy(&dsp->pipeline);
+
+ if (dsp_debug & DEBUG_DSP_CTRL)
+ printk(KERN_DEBUG "%s: remove & destroy object %s\n",
+ __func__, dsp->name);
+ list_del(&dsp->list);
+ spin_unlock_irqrestore(&dsp_lock, flags);
+
+ if (dsp_debug & DEBUG_DSP_CTRL)
+ printk(KERN_DEBUG "%s: dsp instance released\n",
+ __func__);
+ vfree(dsp);
+ module_put(THIS_MODULE);
+ break;
+ }
+ return err;
+}
+
+static void
+dsp_send_bh(struct work_struct *work)
+{
+ struct dsp *dsp = container_of(work, struct dsp, workq);
+ struct sk_buff *skb;
+ struct mISDNhead *hh;
+
+ if (dsp->hdlc && dsp->data_pending)
+ return; /* wait until data has been acknowledged */
+
+ /* send queued data */
+ while ((skb = skb_dequeue(&dsp->sendq))) {
+ /* in locked date, we must have still data in queue */
+ if (dsp->data_pending) {
+ if (dsp_debug & DEBUG_DSP_CORE)
+ printk(KERN_DEBUG "%s: fifo full %s, this is "
+ "no bug!\n", __func__, dsp->name);
+ /* flush transparent data, if not acked */
+ dev_kfree_skb(skb);
+ continue;
+ }
+ hh = mISDN_HEAD_P(skb);
+ if (hh->prim == DL_DATA_REQ) {
+ /* send packet up */
+ if (dsp->up) {
+ if (dsp->up->send(dsp->up, skb))
+ dev_kfree_skb(skb);
+ } else
+ dev_kfree_skb(skb);
+ } else {
+ /* send packet down */
+ if (dsp->ch.peer) {
+ dsp->data_pending = 1;
+ if (dsp->ch.recv(dsp->ch.peer, skb)) {
+ dev_kfree_skb(skb);
+ dsp->data_pending = 0;
+ }
+ } else
+ dev_kfree_skb(skb);
+ }
+ }
+}
+
+static int
+dspcreate(struct channel_req *crq)
+{
+ struct dsp *ndsp;
+ u_long flags;
+
+ if (crq->protocol != ISDN_P_B_L2DSP
+ && crq->protocol != ISDN_P_B_L2DSPHDLC)
+ return -EPROTONOSUPPORT;
+ ndsp = vmalloc(sizeof(struct dsp));
+ if (!ndsp) {
+ printk(KERN_ERR "%s: vmalloc struct dsp failed\n", __func__);
+ return -ENOMEM;
+ }
+ memset(ndsp, 0, sizeof(struct dsp));
+ if (dsp_debug & DEBUG_DSP_CTRL)
+ printk(KERN_DEBUG "%s: creating new dsp instance\n", __func__);
+
+ /* default enabled */
+ INIT_WORK(&ndsp->workq, (void *)dsp_send_bh);
+ skb_queue_head_init(&ndsp->sendq);
+ ndsp->ch.send = dsp_function;
+ ndsp->ch.ctrl = dsp_ctrl;
+ ndsp->up = crq->ch;
+ crq->ch = &ndsp->ch;
+ if (crq->protocol == ISDN_P_B_L2DSP) {
+ crq->protocol = ISDN_P_B_RAW;
+ ndsp->hdlc = 0;
+ } else {
+ crq->protocol = ISDN_P_B_HDLC;
+ ndsp->hdlc = 1;
+ }
+ if (!try_module_get(THIS_MODULE))
+ printk(KERN_WARNING "%s:cannot get module\n",
+ __func__);
+
+ sprintf(ndsp->name, "DSP_C%x(0x%p)",
+ ndsp->up->st->dev->id + 1, ndsp);
+ /* set frame size to start */
+ 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 tone timer */
+ ndsp->tone.tl.function = (void *)dsp_tone_timeout;
+ ndsp->tone.tl.data = (long) ndsp;
+ init_timer(&ndsp->tone.tl);
+
+ if (dtmfthreshold < 20 || dtmfthreshold > 500)
+ dtmfthreshold = 200;
+ ndsp->dtmf.treshold = dtmfthreshold*10000;
+
+ /* init pipeline append to list */
+ spin_lock_irqsave(&dsp_lock, flags);
+ dsp_pipeline_init(&ndsp->pipeline);
+ list_add_tail(&ndsp->list, &dsp_ilist);
+ spin_unlock_irqrestore(&dsp_lock, flags);
+
+ return 0;
+}
+
+
+static struct Bprotocol DSP = {
+ .Bprotocols = (1 << (ISDN_P_B_L2DSP & ISDN_P_B_MASK))
+ | (1 << (ISDN_P_B_L2DSPHDLC & ISDN_P_B_MASK)),
+ .name = "dsp",
+ .create = dspcreate
+};
+
+static int dsp_init(void)
+{
+ int err;
+ int tics;
+
+ printk(KERN_INFO "DSP modul %s\n", mISDN_dsp_revision);
+
+ dsp_options = options;
+ dsp_debug = debug;
+
+ /* set packet size */
+ dsp_poll = poll;
+ if (dsp_poll) {
+ if (dsp_poll > MAX_POLL) {
+ printk(KERN_ERR "%s: Wrong poll value (%d), use %d "
+ "maximum.\n", __func__, poll, MAX_POLL);
+ err = -EINVAL;
+ return err;
+ }
+ if (dsp_poll < 8) {
+ printk(KERN_ERR "%s: Wrong poll value (%d), use 8 "
+ "minimum.\n", __func__, dsp_poll);
+ err = -EINVAL;
+ return err;
+ }
+ dsp_tics = poll * HZ / 8000;
+ if (dsp_tics * 8000 != poll * HZ) {
+ printk(KERN_INFO "mISDN_dsp: Cannot clock every %d "
+ "samples (0,125 ms). It is not a multiple of "
+ "%d HZ.\n", poll, HZ);
+ err = -EINVAL;
+ return err;
+ }
+ } else {
+ poll = 8;
+ while (poll <= MAX_POLL) {
+ tics = poll * HZ / 8000;
+ if (tics * 8000 == poll * HZ) {
+ dsp_tics = tics;
+ dsp_poll = poll;
+ if (poll >= 64)
+ break;
+ }
+ poll++;
+ }
+ }
+ if (dsp_poll == 0) {
+ printk(KERN_INFO "mISDN_dsp: There is no multiple of kernel "
+ "clock that equals exactly the duration of 8-256 "
+ "samples. (Choose kernel clock speed like 100, 250, "
+ "300, 1000)\n");
+ err = -EINVAL;
+ return err;
+ }
+ printk(KERN_INFO "mISDN_dsp: DSP clocks every %d samples. This equals "
+ "%d jiffies.\n", dsp_poll, dsp_tics);
+
+ spin_lock_init(&dsp_lock);
+ INIT_LIST_HEAD(&dsp_ilist);
+ INIT_LIST_HEAD(&conf_ilist);
+
+ /* init conversion tables */
+ dsp_audio_generate_law_tables();
+ dsp_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();
+
+ err = dsp_pipeline_module_init();
+ if (err) {
+ printk(KERN_ERR "mISDN_dsp: Can't initialize pipeline, "
+ "error(%d)\n", err);
+ return err;
+ }
+
+ err = mISDN_register_Bprotocol(&DSP);
+ if (err) {
+ printk(KERN_ERR "Can't register %s error(%d)\n", DSP.name, err);
+ return err;
+ }
+
+ /* set sample timer */
+ dsp_spl_tl.function = (void *)dsp_cmx_send;
+ dsp_spl_tl.data = 0;
+ init_timer(&dsp_spl_tl);
+ dsp_spl_tl.expires = jiffies + dsp_tics;
+ dsp_spl_jiffies = dsp_spl_tl.expires;
+ add_timer(&dsp_spl_tl);
+
+ return 0;
+}
+
+
+static void dsp_cleanup(void)
+{
+ mISDN_unregister_Bprotocol(&DSP);
+
+ if (timer_pending(&dsp_spl_tl))
+ del_timer(&dsp_spl_tl);
+
+ if (!list_empty(&dsp_ilist)) {
+ printk(KERN_ERR "mISDN_dsp: Audio DSP object inst list not "
+ "empty.\n");
+ }
+ if (!list_empty(&conf_ilist)) {
+ printk(KERN_ERR "mISDN_dsp: Conference list not empty. Not "
+ "all memory freed.\n");
+ }
+
+ dsp_pipeline_module_exit();
+}
+
+module_init(dsp_init);
+module_exit(dsp_cleanup);
+
diff --git a/drivers/isdn/mISDN/dsp_dtmf.c b/drivers/isdn/mISDN/dsp_dtmf.c
new file mode 100644
index 0000000..efc371c
--- /dev/null
+++ b/drivers/isdn/mISDN/dsp_dtmf.c
@@ -0,0 +1,303 @@
+/*
+ * DTMF decoder.
+ *
+ * Copyright by Andreas Eversberg (jolly@eversberg.eu)
+ * 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 <linux/mISDNif.h>
+#include <linux/mISDNdsp.h>
+#include "core.h"
+#include "dsp.h"
+
+#define NCOEFF 8 /* number of frequencies to be analyzed */
+
+/* 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(struct dsp *dsp)
+{
+ dsp->dtmf.size = 0;
+ dsp->dtmf.lastwhat = '\0';
+ dsp->dtmf.lastdigit = '\0';
+ dsp->dtmf.count = 0;
+}
+
+/* check for hardware or software features
+ */
+void dsp_dtmf_hardware(struct dsp *dsp)
+{
+ int hardware = 1;
+
+ if (!dsp->features.hfc_dtmf)
+ hardware = 0;
+
+ /* check for volume change */
+ if (dsp->tx_volume) {
+ if (dsp_debug & DEBUG_DSP_DTMF)
+ printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, "
+ "because tx_volume is changed\n",
+ __func__, dsp->name);
+ hardware = 0;
+ }
+ if (dsp->rx_volume) {
+ if (dsp_debug & DEBUG_DSP_DTMF)
+ printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, "
+ "because rx_volume is changed\n",
+ __func__, dsp->name);
+ hardware = 0;
+ }
+ /* check if encryption is enabled */
+ if (dsp->bf_enable) {
+ if (dsp_debug & DEBUG_DSP_DTMF)
+ printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, "
+ "because encryption is enabled\n",
+ __func__, dsp->name);
+ hardware = 0;
+ }
+ /* check if pipeline exists */
+ if (dsp->pipeline.inuse) {
+ if (dsp_debug & DEBUG_DSP_DTMF)
+ printk(KERN_DEBUG "%s dsp %s cannot do hardware DTMF, "
+ "because pipeline exists.\n",
+ __func__, dsp->name);
+ hardware = 0;
+ }
+
+ dsp->dtmf.hardware = hardware;
+ dsp->dtmf.software = !hardware;
+}
+
+
+/*************************************************************
+ * 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(struct dsp *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 has 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 < DSP_DTMF_NPOINTS && len) {
+ buf[size++] = dsp_audio_law_to_s32[*data++];
+ len--;
+ }
+ break;
+
+ case 2: /* HFC coefficients */
+ default:
+ if (len < 64) {
+ if (len > 0)
+ printk(KERN_ERR "%s: coefficients have invalid "
+ "size. (is=%d < must=%d)\n",
+ __func__, 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 = 0;
+ sk1 = 0;
+ 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] > dsp->dtmf.treshold) {
+ 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 = -1;
+ 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 = -1;
+ 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)
+ < sizeof(dsp->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 --git a/drivers/isdn/mISDN/dsp_ecdis.h b/drivers/isdn/mISDN/dsp_ecdis.h
new file mode 100644
index 0000000..8a20af4
--- /dev/null
+++ b/drivers/isdn/mISDN/dsp_ecdis.h
@@ -0,0 +1,110 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * ec_disable_detector.h - A detector which should eventually meet the
+ * G.164/G.165 requirements for detecting the
+ * 2100Hz echo cancellor disable tone.
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2001 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * 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 of the License, 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 "dsp_biquad.h"
+
+struct ec_disable_detector_state {
+ struct biquad2_state notch;
+ int notch_level;
+ int channel_level;
+ int tone_present;
+ int tone_cycle_duration;
+ int good_cycles;
+ int hit;
+};
+
+
+#define FALSE 0
+#define TRUE (!FALSE)
+
+static inline void
+echo_can_disable_detector_init(struct ec_disable_detector_state *det)
+{
+ /* Elliptic notch */
+ /* This is actually centred at 2095Hz, but gets the balance we want, due
+ to the asymmetric walls of the notch */
+ biquad2_init(&det->notch,
+ (int32_t) (-0.7600000*32768.0),
+ (int32_t) (-0.1183852*32768.0),
+ (int32_t) (-0.5104039*32768.0),
+ (int32_t) (0.1567596*32768.0),
+ (int32_t) (1.0000000*32768.0));
+
+ det->channel_level = 0;
+ det->notch_level = 0;
+ det->tone_present = FALSE;
+ det->tone_cycle_duration = 0;
+ det->good_cycles = 0;
+ det->hit = 0;
+}
+/*- End of function --------------------------------------------------------*/
+
+static inline int
+echo_can_disable_detector_update(struct ec_disable_detector_state *det,
+int16_t amp)
+{
+ int16_t notched;
+
+ notched = biquad2(&det->notch, amp);
+ /* Estimate the overall energy in the channel, and the energy in
+ the notch (i.e. overall channel energy - tone energy => noise).
+ Use abs instead of multiply for speed (is it really faster?).
+ Damp the overall energy a little more for a stable result.
+ Damp the notch energy a little less, so we don't damp out the
+ blip every time the phase reverses */
+ det->channel_level += ((abs(amp) - det->channel_level) >> 5);
+ det->notch_level += ((abs(notched) - det->notch_level) >> 4);
+ if (det->channel_level > 280) {
+ /* There is adequate energy in the channel.
+ Is it mostly at 2100Hz? */
+ if (det->notch_level*6 < det->channel_level) {
+ /* The notch says yes, so we have the tone. */
+ if (!det->tone_present) {
+ /* Do we get a kick every 450+-25ms? */
+ if (det->tone_cycle_duration >= 425*8
+ && det->tone_cycle_duration <= 475*8) {
+ det->good_cycles++;
+ if (det->good_cycles > 2)
+ det->hit = TRUE;
+ }
+ det->tone_cycle_duration = 0;
+ }
+ det->tone_present = TRUE;
+ } else
+ det->tone_present = FALSE;
+ det->tone_cycle_duration++;
+ } else {
+ det->tone_present = FALSE;
+ det->tone_cycle_duration = 0;
+ det->good_cycles = 0;
+ }
+ return det->hit;
+}
+/*- End of function --------------------------------------------------------*/
+/*- End of file ------------------------------------------------------------*/
diff --git a/drivers/isdn/mISDN/dsp_hwec.c b/drivers/isdn/mISDN/dsp_hwec.c
new file mode 100644
index 0000000..eb892d9
--- /dev/null
+++ b/drivers/isdn/mISDN/dsp_hwec.c
@@ -0,0 +1,138 @@
+/*
+ * dsp_hwec.c:
+ * builtin mISDN dsp pipeline element for enabling the hw echocanceller
+ *
+ * Copyright (C) 2007, Nadi Sarrar
+ *
+ * Nadi Sarrar <nadi@beronet.com>
+ *
+ * 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 of the License, 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., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/mISDNdsp.h>
+#include <linux/mISDNif.h>
+#include "core.h"
+#include "dsp.h"
+#include "dsp_hwec.h"
+
+static struct mISDN_dsp_element_arg args[] = {
+ { "deftaps", "128", "Set the number of taps of cancellation." },
+};
+
+static struct mISDN_dsp_element dsp_hwec_p = {
+ .name = "hwec",
+ .new = NULL,
+ .free = NULL,
+ .process_tx = NULL,
+ .process_rx = NULL,
+ .num_args = sizeof(args) / sizeof(struct mISDN_dsp_element_arg),
+ .args = args,
+};
+struct mISDN_dsp_element *dsp_hwec = &dsp_hwec_p;
+
+void dsp_hwec_enable(struct dsp *dsp, const char *arg)
+{
+ int deftaps = 128,
+ len;
+ struct mISDN_ctrl_req cq;
+
+ if (!dsp) {
+ printk(KERN_ERR "%s: failed to enable hwec: dsp is NULL\n",
+ __func__);
+ return;
+ }
+
+ if (!arg)
+ goto _do;
+
+ len = strlen(arg);
+ if (!len)
+ goto _do;
+
+ {
+ char _dup[len + 1];
+ char *dup, *tok, *name, *val;
+ int tmp;
+
+ strcpy(_dup, arg);
+ dup = _dup;
+
+ while ((tok = strsep(&dup, ","))) {
+ if (!strlen(tok))
+ continue;
+ name = strsep(&tok, "=");
+ val = tok;
+
+ if (!val)
+ continue;
+
+ if (!strcmp(name, "deftaps")) {
+ if (sscanf(val, "%d", &tmp) == 1)
+ deftaps = tmp;
+ }
+ }
+ }
+
+_do:
+ printk(KERN_DEBUG "%s: enabling hwec with deftaps=%d\n",
+ __func__, deftaps);
+ memset(&cq, 0, sizeof(cq));
+ cq.op = MISDN_CTRL_HFC_ECHOCAN_ON;
+ cq.p1 = deftaps;
+ if (!dsp->ch.peer->ctrl(&dsp->ch, CONTROL_CHANNEL, &cq)) {
+ printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n",
+ __func__);
+ return;
+ }
+}
+
+void dsp_hwec_disable(struct dsp *dsp)
+{
+ struct mISDN_ctrl_req cq;
+
+ if (!dsp) {
+ printk(KERN_ERR "%s: failed to disable hwec: dsp is NULL\n",
+ __func__);
+ return;
+ }
+
+ printk(KERN_DEBUG "%s: disabling hwec\n", __func__);
+ memset(&cq, 0, sizeof(cq));
+ cq.op = MISDN_CTRL_HFC_ECHOCAN_OFF;
+ if (!dsp->ch.peer->ctrl(&dsp->ch, CONTROL_CHANNEL, &cq)) {
+ printk(KERN_DEBUG "%s: CONTROL_CHANNEL failed\n",
+ __func__);
+ return;
+ }
+}
+
+int dsp_hwec_init(void)
+{
+ mISDN_dsp_element_register(dsp_hwec);
+
+ return 0;
+}
+
+void dsp_hwec_exit(void)
+{
+ mISDN_dsp_element_unregister(dsp_hwec);
+}
+
diff --git a/drivers/isdn/mISDN/dsp_hwec.h b/drivers/isdn/mISDN/dsp_hwec.h
new file mode 100644
index 0000000..eebe80c
--- /dev/null
+++ b/drivers/isdn/mISDN/dsp_hwec.h
@@ -0,0 +1,10 @@
+/*
+ * dsp_hwec.h
+ */
+
+extern struct mISDN_dsp_element *dsp_hwec;
+extern void dsp_hwec_enable(struct dsp *dsp, const char *arg);
+extern void dsp_hwec_disable(struct dsp *dsp);
+extern int dsp_hwec_init(void);
+extern void dsp_hwec_exit(void);
+
diff --git a/drivers/isdn/mISDN/dsp_pipeline.c b/drivers/isdn/mISDN/dsp_pipeline.c
new file mode 100644
index 0000000..850260a
--- /dev/null
+++ b/drivers/isdn/mISDN/dsp_pipeline.c
@@ -0,0 +1,348 @@
+/*
+ * dsp_pipeline.c: pipelined audio processing
+ *
+ * Copyright (C) 2007, Nadi Sarrar
+ *
+ * Nadi Sarrar <nadi@beronet.com>
+ *
+ * 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 of the License, 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., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * The full GNU General Public License is included in this distribution in the
+ * file called LICENSE.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/string.h>
+#include <linux/mISDNif.h>
+#include <linux/mISDNdsp.h>
+#include "dsp.h"
+#include "dsp_hwec.h"
+
+/* uncomment for debugging */
+/*#define PIPELINE_DEBUG*/
+
+struct dsp_pipeline_entry {
+ struct mISDN_dsp_element *elem;
+ void *p;
+ struct list_head list;
+};
+struct dsp_element_entry {
+ struct mISDN_dsp_element *elem;
+ struct device dev;
+ struct list_head list;
+};
+
+static LIST_HEAD(dsp_elements);
+
+/* sysfs */
+static struct class *elements_class;
+
+static ssize_t
+attr_show_args(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct mISDN_dsp_element *elem = dev_get_drvdata(dev);
+ ssize_t len = 0;
+ int i = 0;
+
+ *buf = 0;
+ for (; i < elem->num_args; ++i)
+ len = sprintf(buf, "%sName: %s\n%s%s%sDescription: %s\n"
+ "\n", buf,
+ elem->args[i].name,
+ elem->args[i].def ? "Default: " : "",
+ elem->args[i].def ? elem->args[i].def : "",
+ elem->args[i].def ? "\n" : "",
+ elem->args[i].desc);
+
+ return len;
+}
+
+static struct device_attribute element_attributes[] = {
+ __ATTR(args, 0444, attr_show_args, NULL),
+};
+
+int mISDN_dsp_element_register(struct mISDN_dsp_element *elem)
+{
+ struct dsp_element_entry *entry;
+ int ret, i;
+
+ if (!elem)
+ return -EINVAL;
+
+ entry = kzalloc(sizeof(struct dsp_element_entry), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ entry->elem = elem;
+
+ entry->dev.class = elements_class;
+ dev_set_drvdata(&entry->dev, elem);
+ snprintf(entry->dev.bus_id, BUS_ID_SIZE, elem->name);
+ ret = device_register(&entry->dev);
+ if (ret) {
+ printk(KERN_ERR "%s: failed to register %s\n",
+ __func__, elem->name);
+ goto err1;
+ }
+
+ for (i = 0; i < (sizeof(element_attributes)
+ / sizeof(struct device_attribute)); ++i)
+ ret = device_create_file(&entry->dev,
+ &element_attributes[i]);
+ if (ret) {
+ printk(KERN_ERR "%s: failed to create device file\n",
+ __func__);
+ goto err2;
+ }
+
+ list_add_tail(&entry->list, &dsp_elements);
+
+ printk(KERN_DEBUG "%s: %s registered\n", __func__, elem->name);
+
+ return 0;
+
+err2:
+ device_unregister(&entry->dev);
+err1:
+ kfree(entry);
+ return ret;
+}
+EXPORT_SYMBOL(mISDN_dsp_element_register);
+
+void mISDN_dsp_element_unregister(struct mISDN_dsp_element *elem)
+{
+ struct dsp_element_entry *entry, *n;
+
+ if (!elem)
+ return;
+
+ list_for_each_entry_safe(entry, n, &dsp_elements, list)
+ if (entry->elem == elem) {
+ list_del(&entry->list);
+ device_unregister(&entry->dev);
+ kfree(entry);
+ printk(KERN_DEBUG "%s: %s unregistered\n",
+ __func__, elem->name);
+ return;
+ }
+ printk(KERN_ERR "%s: element %s not in list.\n", __func__, elem->name);
+}
+EXPORT_SYMBOL(mISDN_dsp_element_unregister);
+
+int dsp_pipeline_module_init(void)
+{
+ elements_class = class_create(THIS_MODULE, "dsp_pipeline");
+ if (IS_ERR(elements_class))
+ return PTR_ERR(elements_class);
+
+#ifdef PIPELINE_DEBUG
+ printk(KERN_DEBUG "%s: dsp pipeline module initialized\n", __func__);
+#endif
+
+ dsp_hwec_init();
+
+ return 0;
+}
+
+void dsp_pipeline_module_exit(void)
+{
+ struct dsp_element_entry *entry, *n;
+
+ dsp_hwec_exit();
+
+ class_destroy(elements_class);
+
+ list_for_each_entry_safe(entry, n, &dsp_elements, list) {
+ list_del(&entry->list);
+ printk(KERN_WARNING "%s: element was still registered: %s\n",
+ __func__, entry->elem->name);
+ kfree(entry);
+ }
+
+ printk(KERN_DEBUG "%s: dsp pipeline module exited\n", __func__);
+}
+
+int dsp_pipeline_init(struct dsp_pipeline *pipeline)
+{
+ if (!pipeline)
+ return -EINVAL;
+
+ INIT_LIST_HEAD(&pipeline->list);
+
+#ifdef PIPELINE_DEBUG
+ printk(KERN_DEBUG "%s: dsp pipeline ready\n", __func__);
+#endif
+
+ return 0;
+}
+
+static inline void _dsp_pipeline_destroy(struct dsp_pipeline *pipeline)
+{
+ struct dsp_pipeline_entry *entry, *n;
+
+ list_for_each_entry_safe(entry, n, &pipeline->list, list) {
+ list_del(&entry->list);
+ if (entry->elem == dsp_hwec)
+ dsp_hwec_disable(container_of(pipeline, struct dsp,
+ pipeline));
+ else
+ entry->elem->free(entry->p);
+ kfree(entry);
+ }
+}
+
+void dsp_pipeline_destroy(struct dsp_pipeline *pipeline)
+{
+
+ if (!pipeline)
+ return;
+
+ _dsp_pipeline_destroy(pipeline);
+
+#ifdef PIPELINE_DEBUG
+ printk(KERN_DEBUG "%s: dsp pipeline destroyed\n", __func__);
+#endif
+}
+
+int dsp_pipeline_build(struct dsp_pipeline *pipeline, const char *cfg)
+{
+ int len, incomplete = 0, found = 0;
+ char *dup, *tok, *name, *args;
+ struct dsp_element_entry *entry, *n;
+ struct dsp_pipeline_entry *pipeline_entry;
+ struct mISDN_dsp_element *elem;
+
+ if (!pipeline)
+ return -EINVAL;
+
+ if (!list_empty(&pipeline->list))
+ _dsp_pipeline_destroy(pipeline);
+
+ if (!cfg)
+ return 0;
+
+ len = strlen(cfg);
+ if (!len)
+ return 0;
+
+ dup = kmalloc(len + 1, GFP_KERNEL);
+ if (!dup)
+ return 0;
+ strcpy(dup, cfg);
+ while ((tok = strsep(&dup, "|"))) {
+ if (!strlen(tok))
+ continue;
+ name = strsep(&tok, "(");
+ args = strsep(&tok, ")");
+ if (args && !*args)
+ args = 0;
+
+ list_for_each_entry_safe(entry, n, &dsp_elements, list)
+ if (!strcmp(entry->elem->name, name)) {
+ elem = entry->elem;
+
+ pipeline_entry = kmalloc(sizeof(struct
+ dsp_pipeline_entry), GFP_KERNEL);
+ if (!pipeline_entry) {
+ printk(KERN_DEBUG "%s: failed to add "
+ "entry to pipeline: %s (out of "
+ "memory)\n", __func__, elem->name);
+ incomplete = 1;
+ goto _out;
+ }
+ pipeline_entry->elem = elem;
+
+ if (elem == dsp_hwec) {
+ /* This is a hack to make the hwec
+ available as a pipeline module */
+ dsp_hwec_enable(container_of(pipeline,
+ struct dsp, pipeline), args);
+ list_add_tail(&pipeline_entry->list,
+ &pipeline->list);
+ } else {
+ pipeline_entry->p = elem->new(args);
+ if (pipeline_entry->p) {
+ list_add_tail(&pipeline_entry->
+ list, &pipeline->list);
+#ifdef PIPELINE_DEBUG
+ printk(KERN_DEBUG "%s: created "
+ "instance of %s%s%s\n",
+ __func__, name, args ?
+ " with args " : "", args ?
+ args : "");
+#endif
+ } else {
+ printk(KERN_DEBUG "%s: failed "
+ "to add entry to pipeline: "
+ "%s (new() returned NULL)\n",
+ __func__, elem->name);
+ kfree(pipeline_entry);
+ incomplete = 1;
+ }
+ }
+ found = 1;
+ break;
+ }
+
+ if (found)
+ found = 0;
+ else {
+ printk(KERN_DEBUG "%s: element not found, skipping: "
+ "%s\n", __func__, name);
+ incomplete = 1;
+ }
+ }
+
+_out:
+ if (!list_empty(&pipeline->list))
+ pipeline->inuse = 1;
+ else
+ pipeline->inuse = 0;
+
+#ifdef PIPELINE_DEBUG
+ printk(KERN_DEBUG "%s: dsp pipeline built%s: %s\n",
+ __func__, incomplete ? " incomplete" : "", cfg);
+#endif
+ kfree(dup);
+ return 0;
+}
+
+void dsp_pipeline_process_tx(struct dsp_pipeline *pipeline, u8 *data, int len)
+{
+ struct dsp_pipeline_entry *entry;
+
+ if (!pipeline)
+ return;
+
+ list_for_each_entry(entry, &pipeline->list, list)
+ if (entry->elem->process_tx)
+ entry->elem->process_tx(entry->p, data, len);
+}
+
+void dsp_pipeline_process_rx(struct dsp_pipeline *pipeline, u8 *data, int len)
+{
+ struct dsp_pipeline_entry *entry;
+
+ if (!pipeline)
+ return;
+
+ list_for_each_entry_reverse(entry, &pipeline->list, list)
+ if (entry->elem->process_rx)
+ entry->elem->process_rx(entry->p, data, len);
+}
+
+
diff --git a/drivers/isdn/mISDN/dsp_tones.c b/drivers/isdn/mISDN/dsp_tones.c
new file mode 100644
index 0000000..23dd0dd
--- /dev/null
+++ b/drivers/isdn/mISDN/dsp_tones.c
@@ -0,0 +1,551 @@
+/*
+ * Audio support data for ISDN4Linux.
+ *
+ * Copyright Andreas Eversberg (jolly@eversberg.eu)
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/mISDNif.h>
+#include <linux/mISDNdsp.h>
+#include "core.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(struct dsp *dsp, u8 *data, int len)
+{
+ int index, count, start, num;
+ struct pattern *pat;
+ struct dsp_tone *tone = &dsp->tone;
+
+ /* if we have no tone, we copy silence */
+ if (!tone->tone) {
+ memset(data, dsp_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", __func__, 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(struct dsp *dsp, u8 *sample, int len)
+{
+ struct sk_buff *nskb;
+
+ /* unlocking is not required, because we don't expect a response */
+ nskb = _alloc_mISDN_skb(PH_CONTROL_REQ,
+ (len)?HFC_SPL_LOOP_ON:HFC_SPL_LOOP_OFF, len, sample,
+ GFP_ATOMIC);
+ if (nskb) {
+ if (dsp->ch.peer) {
+ if (dsp->ch.recv(dsp->ch.peer, nskb))
+ dev_kfree_skb(nskb);
+ } else
+ dev_kfree_skb(nskb);
+ }
+}
+
+
+/*****************
+ * timer expires *
+ *****************/
+void
+dsp_tone_timeout(void *arg)
+{
+ struct dsp *dsp = arg;
+ struct dsp_tone *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(struct dsp *dsp, int tone)
+{
+ struct pattern *pat;
+ int i;
+ struct dsp_tone *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",
+ __func__, 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 --git a/drivers/isdn/mISDN/fsm.c b/drivers/isdn/mISDN/fsm.c
new file mode 100644
index 0000000..b5d6553
--- /dev/null
+++ b/drivers/isdn/mISDN/fsm.c
@@ -0,0 +1,183 @@
+/*
+ * finite state machine implementation
+ *
+ * Author Karsten Keil <kkeil@novell.com>
+ *
+ * Thanks to Jan den Ouden
+ * Fritz Elfert
+ * Copyright 2008 by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include "fsm.h"
+
+#define FSM_TIMER_DEBUG 0
+
+void
+mISDN_FsmNew(struct Fsm *fsm,
+ struct FsmNode *fnlist, int fncount)
+{
+ int i;
+
+ fsm->jumpmatrix = kzalloc(sizeof(FSMFNPTR) * fsm->state_count *
+ fsm->event_count, GFP_KERNEL);
+
+ 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: %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;
+}
+EXPORT_SYMBOL(mISDN_FsmNew);
+
+void
+mISDN_FsmFree(struct Fsm *fsm)
+{
+ kfree((void *) fsm->jumpmatrix);
+}
+EXPORT_SYMBOL(mISDN_FsmFree);
+
+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 1;
+ }
+}
+EXPORT_SYMBOL(mISDN_FsmEvent);
+
+void
+mISDN_FsmChangeState(struct FsmInst *fi, int newstate)
+{
+ fi->state = newstate;
+ if (fi->debug)
+ fi->printdebug(fi, "ChangeState %s",
+ fi->fsm->strState[newstate]);
+}
+EXPORT_SYMBOL(mISDN_FsmChangeState);
+
+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);
+}
+EXPORT_SYMBOL(mISDN_FsmInitTimer);
+
+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);
+}
+EXPORT_SYMBOL(mISDN_FsmDelTimer);
+
+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)) {
+ if (ft->fi->debug) {
+ 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;
+}
+EXPORT_SYMBOL(mISDN_FsmAddTimer);
+
+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_FsmRestartTimer);
diff --git a/drivers/isdn/mISDN/fsm.h b/drivers/isdn/mISDN/fsm.h
new file mode 100644
index 0000000..928f5be
--- /dev/null
+++ b/drivers/isdn/mISDN/fsm.h
@@ -0,0 +1,67 @@
+/*
+ *
+ * Author Karsten Keil <kkeil@novell.com>
+ *
+ * Thanks to Jan den Ouden
+ * Fritz Elfert
+ * Copyright 2008 by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+#ifndef _MISDN_FSM_H
+#define _MISDN_FSM_H
+
+#include <linux/timer.h>
+
+/* 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 --git a/drivers/isdn/mISDN/hwchannel.c b/drivers/isdn/mISDN/hwchannel.c
new file mode 100644
index 0000000..2596fba
--- /dev/null
+++ b/drivers/isdn/mISDN/hwchannel.c
@@ -0,0 +1,365 @@
+/*
+ *
+ * Author Karsten Keil <kkeil@novell.com>
+ *
+ * Copyright 2008 by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/mISDNhw.h>
+
+static void
+dchannel_bh(struct work_struct *ws)
+{
+ struct dchannel *dch = container_of(ws, struct dchannel, workq);
+ struct sk_buff *skb;
+ int err;
+
+ if (test_and_clear_bit(FLG_RECVQUEUE, &dch->Flags)) {
+ while ((skb = skb_dequeue(&dch->rqueue))) {
+ if (likely(dch->dev.D.peer)) {
+ err = dch->dev.D.recv(dch->dev.D.peer, skb);
+ if (err)
+ dev_kfree_skb(skb);
+ } else
+ dev_kfree_skb(skb);
+ }
+ }
+ if (test_and_clear_bit(FLG_PHCHANGE, &dch->Flags)) {
+ if (dch->phfunc)
+ dch->phfunc(dch);
+ }
+}
+
+static void
+bchannel_bh(struct work_struct *ws)
+{
+ struct bchannel *bch = container_of(ws, struct bchannel, workq);
+ struct sk_buff *skb;
+ int err;
+
+ if (test_and_clear_bit(FLG_RECVQUEUE, &bch->Flags)) {
+ while ((skb = skb_dequeue(&bch->rqueue))) {
+ if (bch->rcount >= 64)
+ printk(KERN_WARNING "B-channel %p receive "
+ "queue if full, but empties...\n", bch);
+ bch->rcount--;
+ if (likely(bch->ch.peer)) {
+ err = bch->ch.recv(bch->ch.peer, skb);
+ if (err)
+ dev_kfree_skb(skb);
+ } else
+ dev_kfree_skb(skb);
+ }
+ }
+}
+
+int
+mISDN_initdchannel(struct dchannel *ch, int maxlen, void *phf)
+{
+ test_and_set_bit(FLG_HDLC, &ch->Flags);
+ ch->maxlen = maxlen;
+ ch->hw = NULL;
+ ch->rx_skb = NULL;
+ ch->tx_skb = NULL;
+ ch->tx_idx = 0;
+ ch->phfunc = phf;
+ skb_queue_head_init(&ch->squeue);
+ skb_queue_head_init(&ch->rqueue);
+ INIT_LIST_HEAD(&ch->dev.bchannels);
+ INIT_WORK(&ch->workq, dchannel_bh);
+ return 0;
+}
+EXPORT_SYMBOL(mISDN_initdchannel);
+
+int
+mISDN_initbchannel(struct bchannel *ch, int maxlen)
+{
+ ch->Flags = 0;
+ ch->maxlen = maxlen;
+ ch->hw = NULL;
+ ch->rx_skb = NULL;
+ ch->tx_skb = NULL;
+ ch->tx_idx = 0;
+ skb_queue_head_init(&ch->rqueue);
+ ch->rcount = 0;
+ ch->next_skb = NULL;
+ INIT_WORK(&ch->workq, bchannel_bh);
+ return 0;
+}
+EXPORT_SYMBOL(mISDN_initbchannel);
+
+int
+mISDN_freedchannel(struct dchannel *ch)
+{
+ if (ch->tx_skb) {
+ dev_kfree_skb(ch->tx_skb);
+ ch->tx_skb = NULL;
+ }
+ if (ch->rx_skb) {
+ dev_kfree_skb(ch->rx_skb);
+ ch->rx_skb = NULL;
+ }
+ skb_queue_purge(&ch->squeue);
+ skb_queue_purge(&ch->rqueue);
+ flush_scheduled_work();
+ return 0;
+}
+EXPORT_SYMBOL(mISDN_freedchannel);
+
+int
+mISDN_freebchannel(struct bchannel *ch)
+{
+ if (ch->tx_skb) {
+ dev_kfree_skb(ch->tx_skb);
+ ch->tx_skb = NULL;
+ }
+ if (ch->rx_skb) {
+ dev_kfree_skb(ch->rx_skb);
+ ch->rx_skb = NULL;
+ }
+ if (ch->next_skb) {
+ dev_kfree_skb(ch->next_skb);
+ ch->next_skb = NULL;
+ }
+ skb_queue_purge(&ch->rqueue);
+ ch->rcount = 0;
+ flush_scheduled_work();
+ return 0;
+}
+EXPORT_SYMBOL(mISDN_freebchannel);
+
+static inline u_int
+get_sapi_tei(u_char *p)
+{
+ u_int sapi, tei;
+
+ sapi = *p >> 2;
+ tei = p[1] >> 1;
+ return sapi | (tei << 8);
+}
+
+void
+recv_Dchannel(struct dchannel *dch)
+{
+ struct mISDNhead *hh;
+
+ if (dch->rx_skb->len < 2) { /* at least 2 for sapi / tei */
+ dev_kfree_skb(dch->rx_skb);
+ dch->rx_skb = NULL;
+ return;
+ }
+ hh = mISDN_HEAD_P(dch->rx_skb);
+ hh->prim = PH_DATA_IND;
+ hh->id = get_sapi_tei(dch->rx_skb->data);
+ skb_queue_tail(&dch->rqueue, dch->rx_skb);
+ dch->rx_skb = NULL;
+ schedule_event(dch, FLG_RECVQUEUE);
+}
+EXPORT_SYMBOL(recv_Dchannel);
+
+void
+recv_Bchannel(struct bchannel *bch)
+{
+ struct mISDNhead *hh;
+
+ hh = mISDN_HEAD_P(bch->rx_skb);
+ hh->prim = PH_DATA_IND;
+ hh->id = MISDN_ID_ANY;
+ if (bch->rcount >= 64) {
+ dev_kfree_skb(bch->rx_skb);
+ bch->rx_skb = NULL;
+ return;
+ }
+ bch->rcount++;
+ skb_queue_tail(&bch->rqueue, bch->rx_skb);
+ bch->rx_skb = NULL;
+ schedule_event(bch, FLG_RECVQUEUE);
+}
+EXPORT_SYMBOL(recv_Bchannel);
+
+void
+recv_Dchannel_skb(struct dchannel *dch, struct sk_buff *skb)
+{
+ skb_queue_tail(&dch->rqueue, skb);
+ schedule_event(dch, FLG_RECVQUEUE);
+}
+EXPORT_SYMBOL(recv_Dchannel_skb);
+
+void
+recv_Bchannel_skb(struct bchannel *bch, struct sk_buff *skb)
+{
+ if (bch->rcount >= 64) {
+ dev_kfree_skb(skb);
+ return;
+ }
+ bch->rcount++;
+ skb_queue_tail(&bch->rqueue, skb);
+ schedule_event(bch, FLG_RECVQUEUE);
+}
+EXPORT_SYMBOL(recv_Bchannel_skb);
+
+static void
+confirm_Dsend(struct dchannel *dch)
+{
+ struct sk_buff *skb;
+
+ skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(dch->tx_skb),
+ 0, NULL, GFP_ATOMIC);
+ if (!skb) {
+ printk(KERN_ERR "%s: no skb id %x\n", __func__,
+ mISDN_HEAD_ID(dch->tx_skb));
+ return;
+ }
+ skb_queue_tail(&dch->rqueue, skb);
+ schedule_event(dch, FLG_RECVQUEUE);
+}
+
+int
+get_next_dframe(struct dchannel *dch)
+{
+ dch->tx_idx = 0;
+ dch->tx_skb = skb_dequeue(&dch->squeue);
+ if (dch->tx_skb) {
+ confirm_Dsend(dch);
+ return 1;
+ }
+ dch->tx_skb = NULL;
+ test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
+ return 0;
+}
+EXPORT_SYMBOL(get_next_dframe);
+
+void
+confirm_Bsend(struct bchannel *bch)
+{
+ struct sk_buff *skb;
+
+ if (bch->rcount >= 64)
+ return;
+ skb = _alloc_mISDN_skb(PH_DATA_CNF, mISDN_HEAD_ID(bch->tx_skb),
+ 0, NULL, GFP_ATOMIC);
+ if (!skb) {
+ printk(KERN_ERR "%s: no skb id %x\n", __func__,
+ mISDN_HEAD_ID(bch->tx_skb));
+ return;
+ }
+ bch->rcount++;
+ skb_queue_tail(&bch->rqueue, skb);
+ schedule_event(bch, FLG_RECVQUEUE);
+}
+EXPORT_SYMBOL(confirm_Bsend);
+
+int
+get_next_bframe(struct bchannel *bch)
+{
+ bch->tx_idx = 0;
+ if (test_bit(FLG_TX_NEXT, &bch->Flags)) {
+ bch->tx_skb = bch->next_skb;
+ if (bch->tx_skb) {
+ bch->next_skb = NULL;
+ test_and_clear_bit(FLG_TX_NEXT, &bch->Flags);
+ if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
+ confirm_Bsend(bch); /* not for transparent */
+ return 1;
+ } else {
+ test_and_clear_bit(FLG_TX_NEXT, &bch->Flags);
+ printk(KERN_WARNING "B TX_NEXT without skb\n");
+ }
+ }
+ bch->tx_skb = NULL;
+ test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
+ return 0;
+}
+EXPORT_SYMBOL(get_next_bframe);
+
+void
+queue_ch_frame(struct mISDNchannel *ch, u_int pr, int id, struct sk_buff *skb)
+{
+ struct mISDNhead *hh;
+
+ if (!skb) {
+ _queue_data(ch, pr, id, 0, NULL, GFP_ATOMIC);
+ } else {
+ if (ch->peer) {
+ hh = mISDN_HEAD_P(skb);
+ hh->prim = pr;
+ hh->id = id;
+ if (!ch->recv(ch->peer, skb))
+ return;
+ }
+ dev_kfree_skb(skb);
+ }
+}
+EXPORT_SYMBOL(queue_ch_frame);
+
+int
+dchannel_senddata(struct dchannel *ch, struct sk_buff *skb)
+{
+ /* check oversize */
+ if (skb->len <= 0) {
+ printk(KERN_WARNING "%s: skb too small\n", __func__);
+ return -EINVAL;
+ }
+ if (skb->len > ch->maxlen) {
+ printk(KERN_WARNING "%s: skb too large(%d/%d)\n",
+ __func__, skb->len, ch->maxlen);
+ return -EINVAL;
+ }
+ /* HW lock must be obtained */
+ if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) {
+ skb_queue_tail(&ch->squeue, skb);
+ return 0;
+ } else {
+ /* write to fifo */
+ ch->tx_skb = skb;
+ ch->tx_idx = 0;
+ return 1;
+ }
+}
+EXPORT_SYMBOL(dchannel_senddata);
+
+int
+bchannel_senddata(struct bchannel *ch, struct sk_buff *skb)
+{
+
+ /* check oversize */
+ if (skb->len <= 0) {
+ printk(KERN_WARNING "%s: skb too small\n", __func__);
+ return -EINVAL;
+ }
+ if (skb->len > ch->maxlen) {
+ printk(KERN_WARNING "%s: skb too large(%d/%d)\n",
+ __func__, skb->len, ch->maxlen);
+ return -EINVAL;
+ }
+ /* HW lock must be obtained */
+ /* check for pending next_skb */
+ if (ch->next_skb) {
+ printk(KERN_WARNING
+ "%s: next_skb exist ERROR (skb->len=%d next_skb->len=%d)\n",
+ __func__, skb->len, ch->next_skb->len);
+ return -EBUSY;
+ }
+ if (test_and_set_bit(FLG_TX_BUSY, &ch->Flags)) {
+ test_and_set_bit(FLG_TX_NEXT, &ch->Flags);
+ ch->next_skb = skb;
+ return 0;
+ } else {
+ /* write to fifo */
+ ch->tx_skb = skb;
+ ch->tx_idx = 0;
+ return 1;
+ }
+}
+EXPORT_SYMBOL(bchannel_senddata);
diff --git a/drivers/isdn/mISDN/l1oip.h b/drivers/isdn/mISDN/l1oip.h
new file mode 100644
index 0000000..a23d575
--- /dev/null
+++ b/drivers/isdn/mISDN/l1oip.h
@@ -0,0 +1,91 @@
+/*
+ * see notice in l1oip.c
+ */
+
+/* debugging */
+#define DEBUG_L1OIP_INIT 0x00010000
+#define DEBUG_L1OIP_SOCKET 0x00020000
+#define DEBUG_L1OIP_MGR 0x00040000
+#define DEBUG_L1OIP_MSG 0x00080000
+
+/* enable to disorder received bchannels by sequence 2143658798... */
+/*
+#define REORDER_DEBUG
+*/
+
+/* frames */
+#define L1OIP_MAX_LEN 2048 /* max packet size form l2 */
+#define L1OIP_MAX_PERFRAME 1400 /* max data size in one frame */
+
+
+/* timers */
+#define L1OIP_KEEPALIVE 15
+#define L1OIP_TIMEOUT 65
+
+
+/* socket */
+#define L1OIP_DEFAULTPORT 931
+
+
+/* channel structure */
+struct l1oip_chan {
+ struct dchannel *dch;
+ struct bchannel *bch;
+ u32 tx_counter; /* counts xmit bytes/packets */
+ u32 rx_counter; /* counts recv bytes/packets */
+ u32 codecstate; /* used by codec to save data */
+#ifdef REORDER_DEBUG
+ int disorder_flag;
+ struct sk_buff *disorder_skb;
+ u32 disorder_cnt;
+#endif
+};
+
+
+/* card structure */
+struct l1oip {
+ struct list_head list;
+
+ /* card */
+ int registered; /* if registered with mISDN */
+ char name[MISDN_MAX_IDLEN];
+ int idx; /* card index */
+ int pri; /* 1=pri, 0=bri */
+ int d_idx; /* current dchannel number */
+ int b_num; /* number of bchannels */
+ u32 id; /* id of connection */
+ int ondemand; /* if transmis. is on demand */
+ int bundle; /* bundle channels in one frm */
+ int codec; /* codec to use for transmis. */
+ int limit; /* limit number of bchannels */
+
+ /* timer */
+ struct timer_list keep_tl;
+ struct timer_list timeout_tl;
+ int timeout_on;
+ struct work_struct workq;
+
+ /* socket */
+ struct socket *socket; /* if set, socket is created */
+ struct completion socket_complete;/* completion of sock thread */
+ struct task_struct *socket_thread;
+ spinlock_t socket_lock; /* access sock outside thread */
+ u32 remoteip; /* if all set, ip is assigned */
+ u16 localport; /* must always be set */
+ u16 remoteport; /* must always be set */
+ struct sockaddr_in sin_local; /* local socket name */
+ struct sockaddr_in sin_remote; /* remote socket name */
+ struct msghdr sendmsg; /* ip message to send */
+ struct iovec sendiov; /* iov for message */
+
+ /* frame */
+ struct l1oip_chan chan[128]; /* channel instances */
+};
+
+extern int l1oip_law_to_4bit(u8 *data, int len, u8 *result, u32 *state);
+extern int l1oip_4bit_to_law(u8 *data, int len, u8 *result);
+extern int l1oip_alaw_to_ulaw(u8 *data, int len, u8 *result);
+extern int l1oip_ulaw_to_alaw(u8 *data, int len, u8 *result);
+extern void l1oip_4bit_free(void);
+extern int l1oip_4bit_alloc(int ulaw);
+
diff --git a/drivers/isdn/mISDN/l1oip_codec.c b/drivers/isdn/mISDN/l1oip_codec.c
new file mode 100644
index 0000000..a2dc457
--- /dev/null
+++ b/drivers/isdn/mISDN/l1oip_codec.c
@@ -0,0 +1,374 @@
+/*
+
+ * l1oip_codec.c generic codec using lookup table
+ * -> conversion from a-Law to u-Law
+ * -> conversion from u-Law to a-Law
+ * -> compression by reducing the number of sample resolution to 4
+ *
+ * NOTE: It is not compatible with any standard codec like ADPCM.
+ *
+ * Author Andreas Eversberg (jolly@eversberg.eu)
+ *
+ * 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.
+
+ */
+
+/*
+
+How the codec works:
+--------------------
+
+The volume is increased to increase the dynamic range of the audio signal.
+Each sample is converted to a-LAW with only 16 steps of level resolution.
+A pair of two samples are stored in one byte.
+
+The first byte is stored in the upper bits, the second byte is stored in the
+lower bits.
+
+To speed up compression and decompression, two lookup tables are formed:
+
+- 16 bits index for two samples (law encoded) with 8 bit compressed result.
+- 8 bits index for one compressed data with 16 bits decompressed result.
+
+NOTE: The bytes are handled as they are law-encoded.
+
+*/
+
+#include <linux/vmalloc.h>
+#include <linux/mISDNif.h>
+#include "core.h"
+
+/* definitions of codec. don't use calculations, code may run slower. */
+
+static u8 *table_com;
+static u16 *table_dec;
+
+
+/* alaw -> ulaw */
+static u8 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 */
+static u8 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
+};
+
+/* alaw -> 4bit compression */
+static u8 alaw_to_4bit[256] = {
+ 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+ 0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
+ 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+ 0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
+ 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+ 0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
+ 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+ 0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
+ 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+ 0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
+ 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0d, 0x02,
+ 0x0e, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
+ 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+ 0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
+ 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+ 0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
+ 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+ 0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
+ 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+ 0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
+ 0x0e, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
+ 0x0d, 0x02, 0x08, 0x07, 0x0f, 0x01, 0x0a, 0x05,
+ 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+ 0x0d, 0x02, 0x09, 0x07, 0x0f, 0x00, 0x0b, 0x04,
+ 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+ 0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
+ 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+ 0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
+ 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+ 0x0d, 0x02, 0x08, 0x07, 0x0f, 0x00, 0x0b, 0x04,
+ 0x0e, 0x01, 0x0a, 0x05, 0x0f, 0x00, 0x0c, 0x03,
+ 0x0d, 0x02, 0x09, 0x06, 0x0f, 0x00, 0x0b, 0x04,
+};
+
+/* 4bit -> alaw decompression */
+static u8 _4bit_to_alaw[16] = {
+ 0x5d, 0x51, 0xd9, 0xd7, 0x5f, 0x53, 0xa3, 0x4b,
+ 0x2a, 0x3a, 0x22, 0x2e, 0x26, 0x56, 0x20, 0x2c,
+};
+
+/* ulaw -> 4bit compression */
+static u8 ulaw_to_4bit[256] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05,
+ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05,
+ 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
+ 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+ 0x0f, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e,
+ 0x0e, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
+ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a,
+ 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09,
+ 0x09, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+};
+
+/* 4bit -> ulaw decompression */
+static u8 _4bit_to_ulaw[16] = {
+ 0x11, 0x21, 0x31, 0x40, 0x4e, 0x5c, 0x68, 0x71,
+ 0xfe, 0xef, 0xe7, 0xdb, 0xcd, 0xbf, 0xaf, 0x9f,
+};
+
+
+/*
+ * Compresses data to the result buffer
+ * The result size must be at least half of the input buffer.
+ * The number of samples also must be even!
+ */
+int
+l1oip_law_to_4bit(u8 *data, int len, u8 *result, u32 *state)
+{
+ int ii, i = 0, o = 0;
+
+ if (!len)
+ return 0;
+
+ /* send saved byte and first input byte */
+ if (*state) {
+ *result++ = table_com[(((*state)<<8)&0xff00) | (*data++)];
+ len--;
+ o++;
+ }
+
+ ii = len >> 1;
+
+ while (i < ii) {
+ *result++ = table_com[(data[0]<<8) | (data[1])];
+ data += 2;
+ i++;
+ o++;
+ }
+
+ /* if len has an odd number, we save byte for next call */
+ if (len & 1)
+ *state = 0x100 + *data;
+ else
+ *state = 0;
+
+ return o;
+}
+
+/* Decompress data to the result buffer
+ * The result size must be the number of sample in packet. (2 * input data)
+ * The number of samples in the result are even!
+ */
+int
+l1oip_4bit_to_law(u8 *data, int len, u8 *result)
+{
+ int i = 0;
+ u16 r;
+
+ while (i < len) {
+ r = table_dec[*data++];
+ *result++ = r>>8;
+ *result++ = r;
+ i++;
+ }
+
+ return len << 1;
+}
+
+
+/*
+ * law conversion
+ */
+int
+l1oip_alaw_to_ulaw(u8 *data, int len, u8 *result)
+{
+ int i = 0;
+
+ while (i < len) {
+ *result++ = alaw_to_ulaw[*data++];
+ i++;
+ }
+
+ return len;
+}
+
+int
+l1oip_ulaw_to_alaw(u8 *data, int len, u8 *result)
+{
+ int i = 0;
+
+ while (i < len) {
+ *result++ = ulaw_to_alaw[*data++];
+ i++;
+ }
+
+ return len;
+}
+
+
+/*
+ * generate/free compression and decompression table
+ */
+void
+l1oip_4bit_free(void)
+{
+ if (table_dec)
+ vfree(table_dec);
+ if (table_com)
+ vfree(table_com);
+ table_com = NULL;
+ table_dec = NULL;
+}
+
+int
+l1oip_4bit_alloc(int ulaw)
+{
+ int i1, i2, c, sample;
+
+ /* in case, it is called again */
+ if (table_dec)
+ return 0;
+
+ /* alloc conversion tables */
+ table_com = vmalloc(65536);
+ table_dec = vmalloc(512);
+ if (!table_com | !table_dec) {
+ l1oip_4bit_free();
+ return -ENOMEM;
+ }
+ memset(table_com, 0, 65536);
+ memset(table_dec, 0, 512);
+ /* generate compression table */
+ i1 = 0;
+ while (i1 < 256) {
+ if (ulaw)
+ c = ulaw_to_4bit[i1];
+ else
+ c = alaw_to_4bit[i1];
+ i2 = 0;
+ while (i2 < 256) {
+ table_com[(i1<<8) | i2] |= (c<<4);
+ table_com[(i2<<8) | i1] |= c;
+ i2++;
+ }
+ i1++;
+ }
+
+ /* generate decompression table */
+ i1 = 0;
+ while (i1 < 16) {
+ if (ulaw)
+ sample = _4bit_to_ulaw[i1];
+ else
+ sample = _4bit_to_alaw[i1];
+ i2 = 0;
+ while (i2 < 16) {
+ table_dec[(i1<<4) | i2] |= (sample<<8);
+ table_dec[(i2<<4) | i1] |= sample;
+ i2++;
+ }
+ i1++;
+ }
+
+ return 0;
+}
+
+
diff --git a/drivers/isdn/mISDN/l1oip_core.c b/drivers/isdn/mISDN/l1oip_core.c
new file mode 100644
index 0000000..e42150a
--- /dev/null
+++ b/drivers/isdn/mISDN/l1oip_core.c
@@ -0,0 +1,1516 @@
+/*
+
+ * l1oip.c low level driver for tunneling layer 1 over IP
+ *
+ * NOTE: It is not compatible with TDMoIP nor "ISDN over IP".
+ *
+ * Author Andreas Eversberg (jolly@eversberg.eu)
+ *
+ * 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.
+ *
+ */
+
+/* module parameters:
+ * type:
+ Value 1 = BRI
+ Value 2 = PRI
+ Value 3 = BRI (multi channel frame, not supported yet)
+ Value 4 = PRI (multi channel frame, not supported yet)
+ A multi channel frame reduces overhead to a single frame for all
+ b-channels, but increases delay.
+ (NOTE: Multi channel frames are not implemented yet.)
+
+ * codec:
+ Value 0 = transparent (default)
+ Value 1 = transfer ALAW
+ Value 2 = transfer ULAW
+ Value 3 = transfer generic 4 bit compression.
+
+ * ulaw:
+ 0 = we use a-Law (default)
+ 1 = we use u-Law
+
+ * limit:
+ limitation of B-channels to control bandwidth (1...126)
+ BRI: 1 or 2
+ PRI: 1-30, 31-126 (126, because dchannel ist not counted here)
+ Also limited ressources are used for stack, resulting in less channels.
+ It is possible to have more channels than 30 in PRI mode, this must
+ be supported by the application.
+
+ * ip:
+ byte representation of remote ip address (127.0.0.1 -> 127,0,0,1)
+ If not given or four 0, no remote address is set.
+ For multiple interfaces, concat ip addresses. (127,0,0,1,127,0,0,1)
+
+ * port:
+ port number (local interface)
+ If not given or 0, port 931 is used for fist instance, 932 for next...
+ For multiple interfaces, different ports must be given.
+
+ * remoteport:
+ port number (remote interface)
+ If not given or 0, remote port equals local port
+ For multiple interfaces on equal sites, different ports must be given.
+
+ * ondemand:
+ 0 = fixed (always transmit packets, even when remote side timed out)
+ 1 = on demand (only transmit packets, when remote side is detected)
+ the default is 0
+ NOTE: ID must also be set for on demand.
+
+ * id:
+ optional value to identify frames. This value must be equal on both
+ peers and should be random. If omitted or 0, no ID is transmitted.
+
+ * debug:
+ NOTE: only one debug value must be given for all cards
+ enable debugging (see l1oip.h for debug options)
+
+
+Special mISDN controls:
+
+ op = MISDN_CTRL_SETPEER*
+ p1 = bytes 0-3 : remote IP address in network order (left element first)
+ p2 = bytes 1-2 : remote port in network order (high byte first)
+ optional:
+ p2 = bytes 3-4 : local port in network order (high byte first)
+
+ op = MISDN_CTRL_UNSETPEER*
+
+ * Use l1oipctrl for comfortable setting or removing ip address.
+ (Layer 1 Over IP CTRL)
+
+
+L1oIP-Protocol
+--------------
+
+Frame Header:
+
+ 7 6 5 4 3 2 1 0
++---------------+
+|Ver|T|I|Coding |
++---------------+
+| ID byte 3 * |
++---------------+
+| ID byte 2 * |
++---------------+
+| ID byte 1 * |
++---------------+
+| ID byte 0 * |
++---------------+
+|M| Channel |
++---------------+
+| Length * |
++---------------+
+| Time Base MSB |
++---------------+
+| Time Base LSB |
++---------------+
+| Data.... |
+
+...
+
+| |
++---------------+
+|M| Channel |
++---------------+
+| Length * |
++---------------+
+| Time Base MSB |
++---------------+
+| Time Base LSB |
++---------------+
+| Data.... |
+
+...
+
+
+* Only included in some cases.
+
+- Ver = Version
+If version is missmatch, the frame must be ignored.
+
+- T = Type of interface
+Must be 0 for S0 or 1 for E1.
+
+- I = Id present
+If bit is set, four ID bytes are included in frame.
+
+- ID = Connection ID
+Additional ID to prevent Denial of Service attacs. Also it prevents hijacking
+connections with dynamic IP. The ID should be random and must not be 0.
+
+- Coding = Type of codec
+Must be 0 for no transcoding. Also for D-channel and other HDLC frames.
+ 1 and 2 are reserved for explicitly use of a-LAW or u-LAW codec.
+ 3 is used for generic table compressor.
+
+- M = More channels to come. If this flag is 1, the following byte contains
+the length of the channel data. After the data block, the next channel will
+be defined. The flag for the last channel block (or if only one channel is
+transmitted), must be 0 and no length is given.
+
+- Channel = Channel number
+0 reserved
+1-3 channel data for S0 (3 is D-channel)
+1-31 channel data for E1 (16 is D-channel)
+32-127 channel data for extended E1 (16 is D-channel)
+
+- The length is used if the M-flag is 1. It is used to find the next channel
+inside frame.
+NOTE: A value of 0 equals 256 bytes of data.
+ -> For larger data blocks, a single frame must be used.
+ -> For larger streams, a single frame or multiple blocks with same channel ID
+ must be used.
+
+- Time Base = Timestamp of first sample in frame
+The "Time Base" is used to rearange packets and to detect packet loss.
+The 16 bits are sent in network order (MSB first) and count 1/8000 th of a
+second. This causes a wrap arround each 8,192 seconds. There is no requirement
+for the initial "Time Base", but 0 should be used for the first packet.
+In case of HDLC data, this timestamp counts the packet or byte number.
+
+
+Two Timers:
+
+After initialisation, a timer of 15 seconds is started. Whenever a packet is
+transmitted, the timer is reset to 15 seconds again. If the timer expires, an
+empty packet is transmitted. This keep the connection alive.
+
+When a valid packet is received, a timer 65 seconds is started. The interface
+become ACTIVE. If the timer expires, the interface becomes INACTIVE.
+
+
+Dynamic IP handling:
+
+To allow dynamic IP, the ID must be non 0. In this case, any packet with the
+correct port number and ID will be accepted. If the remote side changes its IP
+the new IP is used for all transmitted packets until it changes again.
+
+
+On Demand:
+
+If the ondemand parameter is given, the remote IP is set to 0 on timeout.
+This will stop keepalive traffic to remote. If the remote is online again,
+traffic will continue to the remote address. This is usefull for road warriors.
+This feature only works with ID set, otherwhise it is highly unsecure.
+
+
+Socket and Thread
+-----------------
+
+The complete socket opening and closing is done by a thread.
+When the thread opened a socket, the hc->socket descriptor is set. Whenever a
+packet shall be sent to the socket, the hc->socket must be checked wheter not
+NULL. To prevent change in socket descriptor, the hc->socket_lock must be used.
+To change the socket, a recall of l1oip_socket_open() will safely kill the
+socket process and create a new one.
+
+*/
+
+#define L1OIP_VERSION 0 /* 0...3 */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mISDNif.h>
+#include <linux/mISDNhw.h>
+#include <linux/mISDNdsp.h>
+#include <linux/init.h>
+#include <linux/in.h>
+#include <linux/inet.h>
+#include <linux/workqueue.h>
+#include <linux/kthread.h>
+#include <net/sock.h>
+#include "core.h"
+#include "l1oip.h"
+
+static const char *l1oip_revision = "2.00";
+
+static int l1oip_cnt;
+static spinlock_t l1oip_lock;
+static struct list_head l1oip_ilist;
+
+#define MAX_CARDS 16
+static u_int type[MAX_CARDS];
+static u_int codec[MAX_CARDS];
+static u_int ip[MAX_CARDS*4];
+static u_int port[MAX_CARDS];
+static u_int remoteport[MAX_CARDS];
+static u_int ondemand[MAX_CARDS];
+static u_int limit[MAX_CARDS];
+static u_int id[MAX_CARDS];
+static int debug;
+static int ulaw;
+
+MODULE_AUTHOR("Andreas Eversberg");
+MODULE_LICENSE("GPL");
+module_param_array(type, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(codec, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(ip, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(port, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(remoteport, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(ondemand, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(limit, uint, NULL, S_IRUGO | S_IWUSR);
+module_param_array(id, uint, NULL, S_IRUGO | S_IWUSR);
+module_param(ulaw, uint, S_IRUGO | S_IWUSR);
+module_param(debug, uint, S_IRUGO | S_IWUSR);
+
+/*
+ * send a frame via socket, if open and restart timer
+ */
+static int
+l1oip_socket_send(struct l1oip *hc, u8 localcodec, u8 channel, u32 chanmask,
+ u16 timebase, u8 *buf, int len)
+{
+ u8 *p;
+ int multi = 0;
+ u8 frame[len+32];
+ struct socket *socket = NULL;
+ mm_segment_t oldfs;
+
+ if (debug & DEBUG_L1OIP_MSG)
+ printk(KERN_DEBUG "%s: sending data to socket (len = %d)\n",
+ __func__, len);
+
+ p = frame;
+
+ /* restart timer */
+ if ((int)(hc->keep_tl.expires-jiffies) < 5*HZ) {
+ del_timer(&hc->keep_tl);
+ hc->keep_tl.expires = jiffies + L1OIP_KEEPALIVE*HZ;
+ add_timer(&hc->keep_tl);
+ } else
+ hc->keep_tl.expires = jiffies + L1OIP_KEEPALIVE*HZ;
+
+ if (debug & DEBUG_L1OIP_MSG)
+ printk(KERN_DEBUG "%s: resetting timer\n", __func__);
+
+ /* drop if we have no remote ip or port */
+ if (!hc->sin_remote.sin_addr.s_addr || !hc->sin_remote.sin_port) {
+ if (debug & DEBUG_L1OIP_MSG)
+ printk(KERN_DEBUG "%s: dropping frame, because remote "
+ "IP is not set.\n", __func__);
+ return len;
+ }
+
+ /* assemble frame */
+ *p++ = (L1OIP_VERSION<<6) /* version and coding */
+ | (hc->pri?0x20:0x00) /* type */
+ | (hc->id?0x10:0x00) /* id */
+ | localcodec;
+ if (hc->id) {
+ *p++ = hc->id>>24; /* id */
+ *p++ = hc->id>>16;
+ *p++ = hc->id>>8;
+ *p++ = hc->id;
+ }
+ *p++ = (multi == 1)?0x80:0x00 + channel; /* m-flag, channel */
+ if (multi == 1)
+ *p++ = len; /* length */
+ *p++ = timebase>>8; /* time base */
+ *p++ = timebase;
+
+ if (buf && len) { /* add data to frame */
+ if (localcodec == 1 && ulaw)
+ l1oip_ulaw_to_alaw(buf, len, p);
+ else if (localcodec == 2 && !ulaw)
+ l1oip_alaw_to_ulaw(buf, len, p);
+ else if (localcodec == 3)
+ len = l1oip_law_to_4bit(buf, len, p,
+ &hc->chan[channel].codecstate);
+ else
+ memcpy(p, buf, len);
+ }
+ len += p - frame;
+
+ /* check for socket in safe condition */
+ spin_lock(&hc->socket_lock);
+ if (!hc->socket) {
+ spin_unlock(&hc->socket_lock);
+ return 0;
+ }
+ /* seize socket */
+ socket = hc->socket;
+ hc->socket = NULL;
+ spin_unlock(&hc->socket_lock);
+ /* send packet */
+ if (debug & DEBUG_L1OIP_MSG)
+ printk(KERN_DEBUG "%s: sending packet to socket (len "
+ "= %d)\n", __func__, len);
+ hc->sendiov.iov_base = frame;
+ hc->sendiov.iov_len = len;
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ len = sock_sendmsg(socket, &hc->sendmsg, len);
+ set_fs(oldfs);
+ /* give socket back */
+ hc->socket = socket; /* no locking required */
+
+ return len;
+}
+
+
+/*
+ * receive channel data from socket
+ */
+static void
+l1oip_socket_recv(struct l1oip *hc, u8 remotecodec, u8 channel, u16 timebase,
+ u8 *buf, int len)
+{
+ struct sk_buff *nskb;
+ struct bchannel *bch;
+ struct dchannel *dch;
+ u8 *p;
+ u32 rx_counter;
+
+ if (len == 0) {
+ if (debug & DEBUG_L1OIP_MSG)
+ printk(KERN_DEBUG "%s: received empty keepalive data, "
+ "ignoring\n", __func__);
+ return;
+ }
+
+ if (debug & DEBUG_L1OIP_MSG)
+ printk(KERN_DEBUG "%s: received data, sending to mISDN (%d)\n",
+ __func__, len);
+
+ if (channel < 1 || channel > 127) {
+ printk(KERN_WARNING "%s: packet error - channel %d out of "
+ "range\n", __func__, channel);
+ return;
+ }
+ dch = hc->chan[channel].dch;
+ bch = hc->chan[channel].bch;
+ if (!dch && !bch) {
+ printk(KERN_WARNING "%s: packet error - channel %d not in "
+ "stack\n", __func__, channel);
+ return;
+ }
+
+ /* prepare message */
+ nskb = mI_alloc_skb((remotecodec == 3)?(len<<1):len, GFP_ATOMIC);
+ if (!nskb) {
+ printk(KERN_ERR "%s: No mem for skb.\n", __func__);
+ return;
+ }
+ p = skb_put(nskb, (remotecodec == 3)?(len<<1):len);
+
+ if (remotecodec == 1 && ulaw)
+ l1oip_alaw_to_ulaw(buf, len, p);
+ else if (remotecodec == 2 && !ulaw)
+ l1oip_ulaw_to_alaw(buf, len, p);
+ else if (remotecodec == 3)
+ len = l1oip_4bit_to_law(buf, len, p);
+ else
+ memcpy(p, buf, len);
+
+ /* send message up */
+ if (dch && len >= 2) {
+ dch->rx_skb = nskb;
+ recv_Dchannel(dch);
+ }
+ if (bch) {
+ /* expand 16 bit sequence number to 32 bit sequence number */
+ rx_counter = hc->chan[channel].rx_counter;
+ if (((s16)(timebase - rx_counter)) >= 0) {
+ /* time has changed forward */
+ if (timebase >= (rx_counter & 0xffff))
+ rx_counter =
+ (rx_counter & 0xffff0000) | timebase;
+ else
+ rx_counter = ((rx_counter & 0xffff0000)+0x10000)
+ | timebase;
+ } else {
+ /* time has changed backwards */
+ if (timebase < (rx_counter & 0xffff))
+ rx_counter =
+ (rx_counter & 0xffff0000) | timebase;
+ else
+ rx_counter = ((rx_counter & 0xffff0000)-0x10000)
+ | timebase;
+ }
+ hc->chan[channel].rx_counter = rx_counter;
+
+#ifdef REORDER_DEBUG
+ if (hc->chan[channel].disorder_flag) {
+ struct sk_buff *skb;
+ int cnt;
+ skb = hc->chan[channel].disorder_skb;
+ hc->chan[channel].disorder_skb = nskb;
+ nskb = skb;
+ cnt = hc->chan[channel].disorder_cnt;
+ hc->chan[channel].disorder_cnt = rx_counter;
+ rx_counter = cnt;
+ }
+ hc->chan[channel].disorder_flag ^= 1;
+ if (nskb)
+#endif
+ queue_ch_frame(&bch->ch, PH_DATA_IND, rx_counter, nskb);
+ }
+}
+
+
+/*
+ * parse frame and extract channel data
+ */
+static void
+l1oip_socket_parse(struct l1oip *hc, struct sockaddr_in *sin, u8 *buf, int len)
+{
+ u32 id;
+ u8 channel;
+ u8 remotecodec;
+ u16 timebase;
+ int m, mlen;
+ int len_start = len; /* initial frame length */
+ struct dchannel *dch = hc->chan[hc->d_idx].dch;
+
+ if (debug & DEBUG_L1OIP_MSG)
+ printk(KERN_DEBUG "%s: received frame, parsing... (%d)\n",
+ __func__, len);
+
+ /* check lenght */
+ if (len < 1+1+2) {
+ printk(KERN_WARNING "%s: packet error - length %d below "
+ "4 bytes\n", __func__, len);
+ return;
+ }
+
+ /* check version */
+ if (((*buf)>>6) != L1OIP_VERSION) {
+ printk(KERN_WARNING "%s: packet error - unknown version %d\n",
+ __func__, buf[0]>>6);
+ return;
+ }
+
+ /* check type */
+ if (((*buf)&0x20) && !hc->pri) {
+ printk(KERN_WARNING "%s: packet error - received E1 packet "
+ "on S0 interface\n", __func__);
+ return;
+ }
+ if (!((*buf)&0x20) && hc->pri) {
+ printk(KERN_WARNING "%s: packet error - received S0 packet "
+ "on E1 interface\n", __func__);
+ return;
+ }
+
+ /* get id flag */
+ id = (*buf>>4)&1;
+
+ /* check coding */
+ remotecodec = (*buf) & 0x0f;
+ if (remotecodec > 3) {
+ printk(KERN_WARNING "%s: packet error - remotecodec %d "
+ "unsupported\n", __func__, remotecodec);
+ return;
+ }
+ buf++;
+ len--;
+
+ /* check id */
+ if (id) {
+ if (!hc->id) {
+ printk(KERN_WARNING "%s: packet error - packet has id "
+ "0x%x, but we have not\n", __func__, id);
+ return;
+ }
+ if (len < 4) {
+ printk(KERN_WARNING "%s: packet error - packet too "
+ "short for ID value\n", __func__);
+ return;
+ }
+ id = (*buf++) << 24;
+ id += (*buf++) << 16;
+ id += (*buf++) << 8;
+ id += (*buf++);
+ len -= 4;
+
+ if (id != hc->id) {
+ printk(KERN_WARNING "%s: packet error - ID mismatch, "
+ "got 0x%x, we 0x%x\n",
+ __func__, id, hc->id);
+ return;
+ }
+ } else {
+ if (hc->id) {
+ printk(KERN_WARNING "%s: packet error - packet has no "
+ "ID, but we have\n", __func__);
+ return;
+ }
+ }
+
+multiframe:
+ if (len < 1) {
+ printk(KERN_WARNING "%s: packet error - packet too short, "
+ "channel expected at position %d.\n",
+ __func__, len-len_start+1);
+ return;
+ }
+
+ /* get channel and multiframe flag */
+ channel = *buf&0x7f;
+ m = *buf >> 7;
+ buf++;
+ len--;
+
+ /* check length on multiframe */
+ if (m) {
+ if (len < 1) {
+ printk(KERN_WARNING "%s: packet error - packet too "
+ "short, length expected at position %d.\n",
+ __func__, len_start-len-1);
+ return;
+ }
+
+ mlen = *buf++;
+ len--;
+ if (mlen == 0)
+ mlen = 256;
+ if (len < mlen+3) {
+ printk(KERN_WARNING "%s: packet error - length %d at "
+ "position %d exceeds total length %d.\n",
+ __func__, mlen, len_start-len-1, len_start);
+ return;
+ }
+ if (len == mlen+3) {
+ printk(KERN_WARNING "%s: packet error - length %d at "
+ "position %d will not allow additional "
+ "packet.\n",
+ __func__, mlen, len_start-len+1);
+ return;
+ }
+ } else
+ mlen = len-2; /* single frame, substract timebase */
+
+ if (len < 2) {
+ printk(KERN_WARNING "%s: packet error - packet too short, time "
+ "base expected at position %d.\n",
+ __func__, len-len_start+1);
+ return;
+ }
+
+ /* get time base */
+ timebase = (*buf++) << 8;
+ timebase |= (*buf++);
+ len -= 2;
+
+ /* if inactive, we send up a PH_ACTIVATE and activate */
+ if (!test_bit(FLG_ACTIVE, &dch->Flags)) {
+ if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET))
+ printk(KERN_DEBUG "%s: interface become active due to "
+ "received packet\n", __func__);
+ test_and_set_bit(FLG_ACTIVE, &dch->Flags);
+ _queue_data(&dch->dev.D, PH_ACTIVATE_IND, MISDN_ID_ANY, 0,
+ NULL, GFP_ATOMIC);
+ }
+
+ /* distribute packet */
+ l1oip_socket_recv(hc, remotecodec, channel, timebase, buf, mlen);
+ buf += mlen;
+ len -= mlen;
+
+ /* multiframe */
+ if (m)
+ goto multiframe;
+
+ /* restart timer */
+ if ((int)(hc->timeout_tl.expires-jiffies) < 5*HZ || !hc->timeout_on) {
+ hc->timeout_on = 1;
+ del_timer(&hc->timeout_tl);
+ hc->timeout_tl.expires = jiffies + L1OIP_TIMEOUT*HZ;
+ add_timer(&hc->timeout_tl);
+ } else /* only adjust timer */
+ hc->timeout_tl.expires = jiffies + L1OIP_TIMEOUT*HZ;
+
+ /* if ip or source port changes */
+ if ((hc->sin_remote.sin_addr.s_addr != sin->sin_addr.s_addr)
+ || (hc->sin_remote.sin_port != sin->sin_port)) {
+ if (debug & DEBUG_L1OIP_SOCKET)
+ printk(KERN_DEBUG "%s: remote address changes from "
+ "0x%08x to 0x%08x (port %d to %d)\n", __func__,
+ ntohl(hc->sin_remote.sin_addr.s_addr),
+ ntohl(sin->sin_addr.s_addr),
+ ntohs(hc->sin_remote.sin_port),
+ ntohs(sin->sin_port));
+ hc->sin_remote.sin_addr.s_addr = sin->sin_addr.s_addr;
+ hc->sin_remote.sin_port = sin->sin_port;
+ }
+}
+
+
+/*
+ * socket stuff
+ */
+static int
+l1oip_socket_thread(void *data)
+{
+ struct l1oip *hc = (struct l1oip *)data;
+ int ret = 0;
+ struct msghdr msg;
+ struct iovec iov;
+ mm_segment_t oldfs;
+ struct sockaddr_in sin_rx;
+ unsigned char recvbuf[1500];
+ int recvlen;
+ struct socket *socket = NULL;
+ DECLARE_COMPLETION(wait);
+
+ /* make daemon */
+ allow_signal(SIGTERM);
+
+ /* create socket */
+ if (sock_create(PF_INET, SOCK_DGRAM, IPPROTO_UDP, &socket)) {
+ printk(KERN_ERR "%s: Failed to create socket.\n", __func__);
+ return -EIO;
+ }
+
+ /* set incoming address */
+ hc->sin_local.sin_family = AF_INET;
+ hc->sin_local.sin_addr.s_addr = INADDR_ANY;
+ hc->sin_local.sin_port = htons((unsigned short)hc->localport);
+
+ /* set outgoing address */
+ hc->sin_remote.sin_family = AF_INET;
+ hc->sin_remote.sin_addr.s_addr = htonl(hc->remoteip);
+ hc->sin_remote.sin_port = htons((unsigned short)hc->remoteport);
+
+ /* bind to incomming port */
+ if (socket->ops->bind(socket, (struct sockaddr *)&hc->sin_local,
+ sizeof(hc->sin_local))) {
+ printk(KERN_ERR "%s: Failed to bind socket to port %d.\n",
+ __func__, hc->localport);
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ /* check sk */
+ if (socket->sk == NULL) {
+ printk(KERN_ERR "%s: socket->sk == NULL\n", __func__);
+ ret = -EIO;
+ goto fail;
+ }
+
+ /* build receive message */
+ msg.msg_name = &sin_rx;
+ msg.msg_namelen = sizeof(sin_rx);
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ /* build send message */
+ hc->sendmsg.msg_name = &hc->sin_remote;
+ hc->sendmsg.msg_namelen = sizeof(hc->sin_remote);
+ hc->sendmsg.msg_control = NULL;
+ hc->sendmsg.msg_controllen = 0;
+ hc->sendmsg.msg_iov = &hc->sendiov;
+ hc->sendmsg.msg_iovlen = 1;
+
+ /* give away socket */
+ spin_lock(&hc->socket_lock);
+ hc->socket = socket;
+ spin_unlock(&hc->socket_lock);
+
+ /* read loop */
+ if (debug & DEBUG_L1OIP_SOCKET)
+ printk(KERN_DEBUG "%s: socket created and open\n",
+ __func__);
+ while (!signal_pending(current)) {
+ iov.iov_base = recvbuf;
+ iov.iov_len = sizeof(recvbuf);
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ recvlen = sock_recvmsg(socket, &msg, sizeof(recvbuf), 0);
+ set_fs(oldfs);
+ if (recvlen > 0) {
+ l1oip_socket_parse(hc, &sin_rx, recvbuf, recvlen);
+ } else {
+ if (debug & DEBUG_L1OIP_SOCKET)
+ printk(KERN_WARNING "%s: broken pipe on socket\n",
+ __func__);
+ }
+ }
+
+ /* get socket back, check first if in use, maybe by send function */
+ spin_lock(&hc->socket_lock);
+ /* if hc->socket is NULL, it is in use until it is given back */
+ while (!hc->socket) {
+ spin_unlock(&hc->socket_lock);
+ schedule_timeout(HZ/10);
+ spin_lock(&hc->socket_lock);
+ }
+ hc->socket = NULL;
+ spin_unlock(&hc->socket_lock);
+
+ if (debug & DEBUG_L1OIP_SOCKET)
+ printk(KERN_DEBUG "%s: socket thread terminating\n",
+ __func__);
+
+fail:
+ /* close socket */
+ if (socket)
+ sock_release(socket);
+
+ /* if we got killed, signal completion */
+ complete(&hc->socket_complete);
+ hc->socket_thread = NULL; /* show termination of thread */
+
+ if (debug & DEBUG_L1OIP_SOCKET)
+ printk(KERN_DEBUG "%s: socket thread terminated\n",
+ __func__);
+ return ret;
+}
+
+static void
+l1oip_socket_close(struct l1oip *hc)
+{
+ /* kill thread */
+ if (hc->socket_thread) {
+ if (debug & DEBUG_L1OIP_SOCKET)
+ printk(KERN_DEBUG "%s: socket thread exists, "
+ "killing...\n", __func__);
+ send_sig(SIGTERM, hc->socket_thread, 0);
+ wait_for_completion(&hc->socket_complete);
+ }
+}
+
+static int
+l1oip_socket_open(struct l1oip *hc)
+{
+ /* in case of reopen, we need to close first */
+ l1oip_socket_close(hc);
+
+ init_completion(&hc->socket_complete);
+
+ /* create receive process */
+ hc->socket_thread = kthread_run(l1oip_socket_thread, hc, "l1oip_%s",
+ hc->name);
+ if (IS_ERR(hc->socket_thread)) {
+ int err = PTR_ERR(hc->socket_thread);
+ printk(KERN_ERR "%s: Failed (%d) to create socket process.\n",
+ __func__, err);
+ hc->socket_thread = NULL;
+ sock_release(hc->socket);
+ return err;
+ }
+ if (debug & DEBUG_L1OIP_SOCKET)
+ printk(KERN_DEBUG "%s: socket thread created\n", __func__);
+
+ return 0;
+}
+
+
+static void
+l1oip_send_bh(struct work_struct *work)
+{
+ struct l1oip *hc = container_of(work, struct l1oip, workq);
+
+ if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET))
+ printk(KERN_DEBUG "%s: keepalive timer expired, sending empty "
+ "frame on dchannel\n", __func__);
+
+ /* send an empty l1oip frame at D-channel */
+ l1oip_socket_send(hc, 0, hc->d_idx, 0, 0, NULL, 0);
+}
+
+
+/*
+ * timer stuff
+ */
+static void
+l1oip_keepalive(void *data)
+{
+ struct l1oip *hc = (struct l1oip *)data;
+
+ schedule_work(&hc->workq);
+}
+
+static void
+l1oip_timeout(void *data)
+{
+ struct l1oip *hc = (struct l1oip *)data;
+ struct dchannel *dch = hc->chan[hc->d_idx].dch;
+
+ if (debug & DEBUG_L1OIP_MSG)
+ printk(KERN_DEBUG "%s: timeout timer expired, turn layer one "
+ "down.\n", __func__);
+
+ hc->timeout_on = 0; /* state that timer must be initialized next time */
+
+ /* if timeout, we send up a PH_DEACTIVATE and deactivate */
+ if (test_bit(FLG_ACTIVE, &dch->Flags)) {
+ if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET))
+ printk(KERN_DEBUG "%s: interface become deactivated "
+ "due to timeout\n", __func__);
+ test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
+ _queue_data(&dch->dev.D, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0,
+ NULL, GFP_ATOMIC);
+ }
+
+ /* if we have ondemand set, we remove ip address */
+ if (hc->ondemand) {
+ if (debug & DEBUG_L1OIP_MSG)
+ printk(KERN_DEBUG "%s: on demand causes ip address to "
+ "be removed\n", __func__);
+ hc->sin_remote.sin_addr.s_addr = 0;
+ }
+}
+
+
+/*
+ * message handling
+ */
+static int
+handle_dmsg(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+ struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D);
+ struct dchannel *dch = container_of(dev, struct dchannel, dev);
+ struct l1oip *hc = dch->hw;
+ struct mISDNhead *hh = mISDN_HEAD_P(skb);
+ int ret = -EINVAL;
+ int l, ll;
+ unsigned char *p;
+
+ switch (hh->prim) {
+ case PH_DATA_REQ:
+ if (skb->len < 1) {
+ printk(KERN_WARNING "%s: skb too small\n",
+ __func__);
+ break;
+ }
+ if (skb->len > MAX_DFRAME_LEN_L1 || skb->len > L1OIP_MAX_LEN) {
+ printk(KERN_WARNING "%s: skb too large\n",
+ __func__);
+ break;
+ }
+ /* send frame */
+ p = skb->data;
+ l = skb->len;
+ while (l) {
+ ll = (l < L1OIP_MAX_PERFRAME)?l:L1OIP_MAX_PERFRAME;
+ l1oip_socket_send(hc, 0, dch->slot, 0,
+ hc->chan[dch->slot].tx_counter++, p, ll);
+ p += ll;
+ l -= ll;
+ }
+ skb_trim(skb, 0);
+ queue_ch_frame(ch, PH_DATA_CNF, hh->id, skb);
+ return 0;
+ case PH_ACTIVATE_REQ:
+ if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET))
+ printk(KERN_DEBUG "%s: PH_ACTIVATE channel %d (1..%d)\n"
+ , __func__, dch->slot, hc->b_num+1);
+ skb_trim(skb, 0);
+ if (test_bit(FLG_ACTIVE, &dch->Flags))
+ queue_ch_frame(ch, PH_ACTIVATE_IND, hh->id, skb);
+ else
+ queue_ch_frame(ch, PH_DEACTIVATE_IND, hh->id, skb);
+ return 0;
+ case PH_DEACTIVATE_REQ:
+ if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET))
+ printk(KERN_DEBUG "%s: PH_DEACTIVATE channel %d "
+ "(1..%d)\n", __func__, dch->slot,
+ hc->b_num+1);
+ skb_trim(skb, 0);
+ if (test_bit(FLG_ACTIVE, &dch->Flags))
+ queue_ch_frame(ch, PH_ACTIVATE_IND, hh->id, skb);
+ else
+ queue_ch_frame(ch, PH_DEACTIVATE_IND, hh->id, skb);
+ return 0;
+ }
+ if (!ret)
+ dev_kfree_skb(skb);
+ return ret;
+}
+
+static int
+channel_dctrl(struct dchannel *dch, struct mISDN_ctrl_req *cq)
+{
+ int ret = 0;
+ struct l1oip *hc = dch->hw;
+
+ switch (cq->op) {
+ case MISDN_CTRL_GETOP:
+ cq->op = MISDN_CTRL_SETPEER | MISDN_CTRL_UNSETPEER;
+ break;
+ case MISDN_CTRL_SETPEER:
+ hc->remoteip = (u32)cq->p1;
+ hc->remoteport = cq->p2 & 0xffff;
+ hc->localport = cq->p2 >> 16;
+ if (!hc->remoteport)
+ hc->remoteport = hc->localport;
+ if (debug & DEBUG_L1OIP_SOCKET)
+ printk(KERN_DEBUG "%s: got new ip address from user "
+ "space.\n", __func__);
+ l1oip_socket_open(hc);
+ break;
+ case MISDN_CTRL_UNSETPEER:
+ if (debug & DEBUG_L1OIP_SOCKET)
+ printk(KERN_DEBUG "%s: removing ip address.\n",
+ __func__);
+ hc->remoteip = 0;
+ l1oip_socket_open(hc);
+ break;
+ default:
+ printk(KERN_WARNING "%s: unknown Op %x\n",
+ __func__, cq->op);
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int
+open_dchannel(struct l1oip *hc, struct dchannel *dch, struct channel_req *rq)
+{
+ if (debug & DEBUG_HW_OPEN)
+ printk(KERN_DEBUG "%s: dev(%d) open from %p\n", __func__,
+ dch->dev.id, __builtin_return_address(0));
+ if (rq->protocol == ISDN_P_NONE)
+ return -EINVAL;
+ if ((dch->dev.D.protocol != ISDN_P_NONE) &&
+ (dch->dev.D.protocol != rq->protocol)) {
+ if (debug & DEBUG_HW_OPEN)
+ printk(KERN_WARNING "%s: change protocol %x to %x\n",
+ __func__, dch->dev.D.protocol, rq->protocol);
+ }
+ if (dch->dev.D.protocol != rq->protocol)
+ dch->dev.D.protocol = rq->protocol;
+
+ if (test_bit(FLG_ACTIVE, &dch->Flags)) {
+ _queue_data(&dch->dev.D, PH_ACTIVATE_IND, MISDN_ID_ANY,
+ 0, NULL, GFP_KERNEL);
+ }
+ rq->ch = &dch->dev.D;
+ if (!try_module_get(THIS_MODULE))
+ printk(KERN_WARNING "%s:cannot get module\n", __func__);
+ return 0;
+}
+
+static int
+open_bchannel(struct l1oip *hc, struct dchannel *dch, struct channel_req *rq)
+{
+ struct bchannel *bch;
+ int ch;
+
+ if (!test_channelmap(rq->adr.channel, dch->dev.channelmap))
+ return -EINVAL;
+ if (rq->protocol == ISDN_P_NONE)
+ return -EINVAL;
+ ch = rq->adr.channel; /* BRI: 1=B1 2=B2 PRI: 1..15,17.. */
+ bch = hc->chan[ch].bch;
+ if (!bch) {
+ printk(KERN_ERR "%s:internal error ch %d has no bch\n",
+ __func__, ch);
+ return -EINVAL;
+ }
+ if (test_and_set_bit(FLG_OPEN, &bch->Flags))
+ return -EBUSY; /* b-channel can be only open once */
+ bch->ch.protocol = rq->protocol;
+ rq->ch = &bch->ch;
+ if (!try_module_get(THIS_MODULE))
+ printk(KERN_WARNING "%s:cannot get module\n", __func__);
+ return 0;
+}
+
+static int
+l1oip_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+ struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D);
+ struct dchannel *dch = container_of(dev, struct dchannel, dev);
+ struct l1oip *hc = dch->hw;
+ struct channel_req *rq;
+ int err = 0;
+
+ if (dch->debug & DEBUG_HW)
+ printk(KERN_DEBUG "%s: cmd:%x %p\n",
+ __func__, cmd, arg);
+ switch (cmd) {
+ case OPEN_CHANNEL:
+ rq = arg;
+ switch (rq->protocol) {
+ case ISDN_P_TE_S0:
+ case ISDN_P_NT_S0:
+ if (hc->pri) {
+ err = -EINVAL;
+ break;
+ }
+ err = open_dchannel(hc, dch, rq);
+ break;
+ case ISDN_P_TE_E1:
+ case ISDN_P_NT_E1:
+ if (!hc->pri) {
+ err = -EINVAL;
+ break;
+ }
+ err = open_dchannel(hc, dch, rq);
+ break;
+ default:
+ err = open_bchannel(hc, dch, rq);
+ }
+ break;
+ case CLOSE_CHANNEL:
+ if (debug & DEBUG_HW_OPEN)
+ printk(KERN_DEBUG "%s: dev(%d) close from %p\n",
+ __func__, dch->dev.id,
+ __builtin_return_address(0));
+ module_put(THIS_MODULE);
+ break;
+ case CONTROL_CHANNEL:
+ err = channel_dctrl(dch, arg);
+ break;
+ default:
+ if (dch->debug & DEBUG_HW)
+ printk(KERN_DEBUG "%s: unknown command %x\n",
+ __func__, cmd);
+ err = -EINVAL;
+ }
+ return err;
+}
+
+static int
+handle_bmsg(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+ struct bchannel *bch = container_of(ch, struct bchannel, ch);
+ struct l1oip *hc = bch->hw;
+ int ret = -EINVAL;
+ struct mISDNhead *hh = mISDN_HEAD_P(skb);
+ int l, ll, i;
+ unsigned char *p;
+
+ switch (hh->prim) {
+ case PH_DATA_REQ:
+ if (skb->len <= 0) {
+ printk(KERN_WARNING "%s: skb too small\n",
+ __func__);
+ break;
+ }
+ if (skb->len > MAX_DFRAME_LEN_L1 || skb->len > L1OIP_MAX_LEN) {
+ printk(KERN_WARNING "%s: skb too large\n",
+ __func__);
+ break;
+ }
+ /* check for AIS / ulaw-silence */
+ p = skb->data;
+ l = skb->len;
+ for (i = 0; i < l; i++) {
+ if (*p++ != 0xff)
+ break;
+ }
+ if (i == l) {
+ if (debug & DEBUG_L1OIP_MSG)
+ printk(KERN_DEBUG "%s: got AIS, not sending, "
+ "but counting\n", __func__);
+ hc->chan[bch->slot].tx_counter += l;
+ skb_trim(skb, 0);
+ queue_ch_frame(ch, PH_DATA_CNF, hh->id, skb);
+ return 0;
+ }
+ /* check for silence */
+ p = skb->data;
+ l = skb->len;
+ for (i = 0; i < l; i++) {
+ if (*p++ != 0x2a)
+ break;
+ }
+ if (i == l) {
+ if (debug & DEBUG_L1OIP_MSG)
+ printk(KERN_DEBUG "%s: got silence, not sending"
+ ", but counting\n", __func__);
+ hc->chan[bch->slot].tx_counter += l;
+ skb_trim(skb, 0);
+ queue_ch_frame(ch, PH_DATA_CNF, hh->id, skb);
+ return 0;
+ }
+
+ /* send frame */
+ p = skb->data;
+ l = skb->len;
+ while (l) {
+ ll = (l < L1OIP_MAX_PERFRAME)?l:L1OIP_MAX_PERFRAME;
+ l1oip_socket_send(hc, hc->codec, bch->slot, 0,
+ hc->chan[bch->slot].tx_counter, p, ll);
+ hc->chan[bch->slot].tx_counter += ll;
+ p += ll;
+ l -= ll;
+ }
+ skb_trim(skb, 0);
+ queue_ch_frame(ch, PH_DATA_CNF, hh->id, skb);
+ return 0;
+ case PH_ACTIVATE_REQ:
+ if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET))
+ printk(KERN_DEBUG "%s: PH_ACTIVATE channel %d (1..%d)\n"
+ , __func__, bch->slot, hc->b_num+1);
+ hc->chan[bch->slot].codecstate = 0;
+ test_and_set_bit(FLG_ACTIVE, &bch->Flags);
+ skb_trim(skb, 0);
+ queue_ch_frame(ch, PH_ACTIVATE_IND, hh->id, skb);
+ return 0;
+ case PH_DEACTIVATE_REQ:
+ if (debug & (DEBUG_L1OIP_MSG|DEBUG_L1OIP_SOCKET))
+ printk(KERN_DEBUG "%s: PH_DEACTIVATE channel %d "
+ "(1..%d)\n", __func__, bch->slot,
+ hc->b_num+1);
+ test_and_clear_bit(FLG_ACTIVE, &bch->Flags);
+ skb_trim(skb, 0);
+ queue_ch_frame(ch, PH_DEACTIVATE_IND, hh->id, skb);
+ return 0;
+ }
+ if (!ret)
+ dev_kfree_skb(skb);
+ return ret;
+}
+
+static int
+channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
+{
+ int ret = 0;
+ struct dsp_features *features =
+ (struct dsp_features *)(*((u_long *)&cq->p1));
+
+ switch (cq->op) {
+ case MISDN_CTRL_GETOP:
+ cq->op = MISDN_CTRL_HW_FEATURES_OP;
+ break;
+ case MISDN_CTRL_HW_FEATURES: /* fill features structure */
+ if (debug & DEBUG_L1OIP_MSG)
+ printk(KERN_DEBUG "%s: HW_FEATURE request\n",
+ __func__);
+ /* create confirm */
+ features->unclocked = 1;
+ features->unordered = 1;
+ break;
+ default:
+ printk(KERN_WARNING "%s: unknown Op %x\n",
+ __func__, cq->op);
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int
+l1oip_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+ struct bchannel *bch = container_of(ch, struct bchannel, ch);
+ int err = -EINVAL;
+
+ if (bch->debug & DEBUG_HW)
+ printk(KERN_DEBUG "%s: cmd:%x %p\n",
+ __func__, cmd, arg);
+ switch (cmd) {
+ case CLOSE_CHANNEL:
+ test_and_clear_bit(FLG_OPEN, &bch->Flags);
+ test_and_clear_bit(FLG_ACTIVE, &bch->Flags);
+ ch->protocol = ISDN_P_NONE;
+ ch->peer = NULL;
+ module_put(THIS_MODULE);
+ err = 0;
+ break;
+ case CONTROL_CHANNEL:
+ err = channel_bctrl(bch, arg);
+ break;
+ default:
+ printk(KERN_WARNING "%s: unknown prim(%x)\n",
+ __func__, cmd);
+ }
+ return err;
+}
+
+
+/*
+ * cleanup module and stack
+ */
+static void
+release_card(struct l1oip *hc)
+{
+ int ch;
+
+ if (timer_pending(&hc->keep_tl))
+ del_timer(&hc->keep_tl);
+
+ if (timer_pending(&hc->timeout_tl))
+ del_timer(&hc->timeout_tl);
+
+ if (hc->socket_thread)
+ l1oip_socket_close(hc);
+
+ if (hc->registered && hc->chan[hc->d_idx].dch)
+ mISDN_unregister_device(&hc->chan[hc->d_idx].dch->dev);
+ for (ch = 0; ch < 128; ch++) {
+ if (hc->chan[ch].dch) {
+ mISDN_freedchannel(hc->chan[ch].dch);
+ kfree(hc->chan[ch].dch);
+ }
+ if (hc->chan[ch].bch) {
+ mISDN_freebchannel(hc->chan[ch].bch);
+ kfree(hc->chan[ch].bch);
+#ifdef REORDER_DEBUG
+ if (hc->chan[ch].disorder_skb)
+ dev_kfree_skb(hc->chan[ch].disorder_skb);
+#endif
+ }
+ }
+
+ spin_lock(&l1oip_lock);
+ list_del(&hc->list);
+ spin_unlock(&l1oip_lock);
+
+ kfree(hc);
+}
+
+static void
+l1oip_cleanup(void)
+{
+ struct l1oip *hc, *next;
+
+ list_for_each_entry_safe(hc, next, &l1oip_ilist, list)
+ release_card(hc);
+
+ l1oip_4bit_free();
+}
+
+
+/*
+ * module and stack init
+ */
+static int
+init_card(struct l1oip *hc, int pri, int bundle)
+{
+ struct dchannel *dch;
+ struct bchannel *bch;
+ int ret;
+ int i, ch;
+
+ spin_lock_init(&hc->socket_lock);
+ hc->idx = l1oip_cnt;
+ hc->pri = pri;
+ hc->d_idx = pri?16:3;
+ hc->b_num = pri?30:2;
+ hc->bundle = bundle;
+ if (hc->pri)
+ sprintf(hc->name, "l1oip-e1.%d", l1oip_cnt + 1);
+ else
+ sprintf(hc->name, "l1oip-s0.%d", l1oip_cnt + 1);
+
+ switch (codec[l1oip_cnt]) {
+ case 0: /* as is */
+ case 1: /* alaw */
+ case 2: /* ulaw */
+ case 3: /* 4bit */
+ break;
+ default:
+ printk(KERN_ERR "Codec(%d) not supported.\n",
+ codec[l1oip_cnt]);
+ return -EINVAL;
+ }
+ hc->codec = codec[l1oip_cnt];
+ if (debug & DEBUG_L1OIP_INIT)
+ printk(KERN_DEBUG "%s: using codec %d\n",
+ __func__, hc->codec);
+
+ if (id[l1oip_cnt] == 0) {
+ printk(KERN_WARNING "Warning: No 'id' value given or "
+ "0, this is highly unsecure. Please use 32 "
+ "bit randmom number 0x...\n");
+ }
+ hc->id = id[l1oip_cnt];
+ if (debug & DEBUG_L1OIP_INIT)
+ printk(KERN_DEBUG "%s: using id 0x%x\n", __func__, hc->id);
+
+ hc->ondemand = ondemand[l1oip_cnt];
+ if (hc->ondemand && !hc->id) {
+ printk(KERN_ERR "%s: ondemand option only allowed in "
+ "conjunction with non 0 ID\n", __func__);
+ return -EINVAL;
+ }
+
+ if (limit[l1oip_cnt])
+ hc->b_num = limit[l1oip_cnt];
+ if (!pri && hc->b_num > 2) {
+ printk(KERN_ERR "Maximum limit for BRI interface is 2 "
+ "channels.\n");
+ return -EINVAL;
+ }
+ if (pri && hc->b_num > 126) {
+ printk(KERN_ERR "Maximum limit for PRI interface is 126 "
+ "channels.\n");
+ return -EINVAL;
+ }
+ if (pri && hc->b_num > 30) {
+ printk(KERN_WARNING "Maximum limit for BRI interface is 30 "
+ "channels.\n");
+ printk(KERN_WARNING "Your selection of %d channels must be "
+ "supported by application.\n", hc->limit);
+ }
+
+ hc->remoteip = ip[l1oip_cnt<<2] << 24
+ | ip[(l1oip_cnt<<2)+1] << 16
+ | ip[(l1oip_cnt<<2)+2] << 8
+ | ip[(l1oip_cnt<<2)+3];
+ hc->localport = port[l1oip_cnt]?:(L1OIP_DEFAULTPORT+l1oip_cnt);
+ if (remoteport[l1oip_cnt])
+ hc->remoteport = remoteport[l1oip_cnt];
+ else
+ hc->remoteport = hc->localport;
+ if (debug & DEBUG_L1OIP_INIT)
+ printk(KERN_DEBUG "%s: using local port %d remote ip "
+ "%d.%d.%d.%d port %d ondemand %d\n", __func__,
+ hc->localport, hc->remoteip >> 24,
+ (hc->remoteip >> 16) & 0xff,
+ (hc->remoteip >> 8) & 0xff, hc->remoteip & 0xff,
+ hc->remoteport, hc->ondemand);
+
+ dch = kzalloc(sizeof(struct dchannel), GFP_KERNEL);
+ if (!dch)
+ return -ENOMEM;
+ dch->debug = debug;
+ mISDN_initdchannel(dch, MAX_DFRAME_LEN_L1, NULL);
+ dch->hw = hc;
+ if (pri)
+ dch->dev.Dprotocols = (1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1);
+ else
+ dch->dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0);
+ dch->dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
+ (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
+ dch->dev.D.send = handle_dmsg;
+ dch->dev.D.ctrl = l1oip_dctrl;
+ dch->dev.nrbchan = hc->b_num;
+ dch->slot = hc->d_idx;
+ hc->chan[hc->d_idx].dch = dch;
+ i = 1;
+ for (ch = 0; ch < dch->dev.nrbchan; ch++) {
+ if (ch == 15)
+ i++;
+ bch = kzalloc(sizeof(struct bchannel), GFP_KERNEL);
+ if (!bch) {
+ printk(KERN_ERR "%s: no memory for bchannel\n",
+ __func__);
+ return -ENOMEM;
+ }
+ bch->nr = i + ch;
+ bch->slot = i + ch;
+ bch->debug = debug;
+ mISDN_initbchannel(bch, MAX_DATA_MEM);
+ bch->hw = hc;
+ bch->ch.send = handle_bmsg;
+ bch->ch.ctrl = l1oip_bctrl;
+ bch->ch.nr = i + ch;
+ list_add(&bch->ch.list, &dch->dev.bchannels);
+ hc->chan[i + ch].bch = bch;
+ set_channelmap(bch->nr, dch->dev.channelmap);
+ }
+ ret = mISDN_register_device(&dch->dev, hc->name);
+ if (ret)
+ return ret;
+ hc->registered = 1;
+
+ if (debug & DEBUG_L1OIP_INIT)
+ printk(KERN_DEBUG "%s: Setting up network card(%d)\n",
+ __func__, l1oip_cnt + 1);
+ ret = l1oip_socket_open(hc);
+ if (ret)
+ return ret;
+
+ hc->keep_tl.function = (void *)l1oip_keepalive;
+ hc->keep_tl.data = (ulong)hc;
+ init_timer(&hc->keep_tl);
+ hc->keep_tl.expires = jiffies + 2*HZ; /* two seconds first time */
+ add_timer(&hc->keep_tl);
+
+ hc->timeout_tl.function = (void *)l1oip_timeout;
+ hc->timeout_tl.data = (ulong)hc;
+ init_timer(&hc->timeout_tl);
+ hc->timeout_on = 0; /* state that we have timer off */
+
+ return 0;
+}
+
+static int __init
+l1oip_init(void)
+{
+ int pri, bundle;
+ struct l1oip *hc;
+ int ret;
+
+ printk(KERN_INFO "mISDN: Layer-1-over-IP driver Rev. %s\n",
+ l1oip_revision);
+
+ INIT_LIST_HEAD(&l1oip_ilist);
+ spin_lock_init(&l1oip_lock);
+
+ if (l1oip_4bit_alloc(ulaw))
+ return -ENOMEM;
+
+ l1oip_cnt = 0;
+ while (type[l1oip_cnt] && l1oip_cnt < MAX_CARDS) {
+ switch (type[l1oip_cnt] & 0xff) {
+ case 1:
+ pri = 0;
+ bundle = 0;
+ break;
+ case 2:
+ pri = 1;
+ bundle = 0;
+ break;
+ case 3:
+ pri = 0;
+ bundle = 1;
+ break;
+ case 4:
+ pri = 1;
+ bundle = 1;
+ break;
+ default:
+ printk(KERN_ERR "Card type(%d) not supported.\n",
+ type[l1oip_cnt] & 0xff);
+ l1oip_cleanup();
+ return -EINVAL;
+ }
+
+ if (debug & DEBUG_L1OIP_INIT)
+ printk(KERN_DEBUG "%s: interface %d is %s with %s.\n",
+ __func__, l1oip_cnt, pri?"PRI":"BRI",
+ bundle?"bundled IP packet for all B-channels"
+ :"seperate IP packets for every B-channel");
+
+ hc = kzalloc(sizeof(struct l1oip), GFP_ATOMIC);
+ if (!hc) {
+ printk(KERN_ERR "No kmem for L1-over-IP driver.\n");
+ l1oip_cleanup();
+ return -ENOMEM;
+ }
+ INIT_WORK(&hc->workq, (void *)l1oip_send_bh);
+
+ spin_lock(&l1oip_lock);
+ list_add_tail(&hc->list, &l1oip_ilist);
+ spin_unlock(&l1oip_lock);
+
+ ret = init_card(hc, pri, bundle);
+ if (ret) {
+ l1oip_cleanup();
+ return ret;
+ }
+
+ l1oip_cnt++;
+ }
+ printk(KERN_INFO "%d virtual devices registered\n", l1oip_cnt);
+ return 0;
+}
+
+module_init(l1oip_init);
+module_exit(l1oip_cleanup);
+
diff --git a/drivers/isdn/mISDN/layer1.c b/drivers/isdn/mISDN/layer1.c
new file mode 100644
index 0000000..fced1a2
--- /dev/null
+++ b/drivers/isdn/mISDN/layer1.c
@@ -0,0 +1,403 @@
+/*
+ *
+ * Author Karsten Keil <kkeil@novell.com>
+ *
+ * Copyright 2008 by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+
+#include <linux/module.h>
+#include <linux/mISDNhw.h>
+#include "layer1.h"
+#include "fsm.h"
+
+static int *debug;
+
+struct layer1 {
+ u_long Flags;
+ struct FsmInst l1m;
+ struct FsmTimer timer;
+ int delay;
+ struct dchannel *dch;
+ dchannel_l1callback *dcb;
+};
+
+#define TIMER3_VALUE 7000
+
+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",
+};
+
+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, ...)
+{
+ struct layer1 *l1 = fi->userdata;
+ va_list va;
+
+ va_start(va, fmt);
+ printk(KERN_DEBUG "%s: ", l1->dch->dev.name);
+ vprintk(fmt, va);
+ printk("\n");
+ va_end(va);
+}
+
+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)
+{
+ struct layer1 *l1 = fi->userdata;
+
+ mISDN_FsmChangeState(fi, ST_L1_F3);
+ if (test_bit(FLG_L1_ACTIVATING, &l1->Flags))
+ l1->dcb(l1->dch, HW_POWERUP_REQ);
+}
+
+static void
+l1_deact_req_s(struct FsmInst *fi, int event, void *arg)
+{
+ struct layer1 *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)
+{
+ struct layer1 *l1 = fi->userdata;
+
+ if (test_bit(FLG_L1_ACTIVATING, &l1->Flags)) {
+ mISDN_FsmChangeState(fi, ST_L1_F4);
+ l1->dcb(l1->dch, INFO3_P8);
+ } 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)
+{
+ struct layer1 *l1 = fi->userdata;
+
+ mISDN_FsmChangeState(fi, ST_L1_F6);
+ l1->dcb(l1->dch, INFO3_P8);
+}
+
+static void
+l1_info4_ind(struct FsmInst *fi, int event, void *arg)
+{
+ struct layer1 *l1 = fi->userdata;
+
+ mISDN_FsmChangeState(fi, ST_L1_F7);
+ l1->dcb(l1->dch, INFO3_P8);
+ 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)
+{
+ struct layer1 *l1 = fi->userdata;
+
+ 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))
+ l1->dcb(l1->dch, HW_D_NOBLOCKED);
+ l1->dcb(l1->dch, PH_DEACTIVATE_IND);
+ }
+ if (l1->l1m.state != ST_L1_F6) {
+ mISDN_FsmChangeState(fi, ST_L1_F3);
+ l1->dcb(l1->dch, HW_POWERUP_REQ);
+ }
+}
+
+static void
+l1_timer_act(struct FsmInst *fi, int event, void *arg)
+{
+ struct layer1 *l1 = fi->userdata;
+
+ test_and_clear_bit(FLG_L1_ACTTIMER, &l1->Flags);
+ test_and_set_bit(FLG_L1_ACTIVATED, &l1->Flags);
+ l1->dcb(l1->dch, PH_ACTIVATE_IND);
+}
+
+static void
+l1_timer_deact(struct FsmInst *fi, int event, void *arg)
+{
+ struct layer1 *l1 = fi->userdata;
+
+ 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))
+ l1->dcb(l1->dch, HW_D_NOBLOCKED);
+ l1->dcb(l1->dch, PH_DEACTIVATE_IND);
+ l1->dcb(l1->dch, HW_DEACT_REQ);
+}
+
+static void
+l1_activate_s(struct FsmInst *fi, int event, void *arg)
+{
+ struct layer1 *l1 = fi->userdata;
+
+ mISDN_FsmRestartTimer(&l1->timer, TIMER3_VALUE, EV_TIMER3, NULL, 2);
+ test_and_set_bit(FLG_L1_T3RUN, &l1->Flags);
+ l1->dcb(l1->dch, HW_RESET_REQ);
+}
+
+static void
+l1_activate_no(struct FsmInst *fi, int event, void *arg)
+{
+ struct layer1 *l1 = fi->userdata;
+
+ 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))
+ l1->dcb(l1->dch, HW_D_NOBLOCKED);
+ l1->dcb(l1->dch, PH_DEACTIVATE_IND);
+ }
+}
+
+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},
+};
+
+static void
+release_l1(struct layer1 *l1) {
+ mISDN_FsmDelTimer(&l1->timer, 0);
+ if (l1->dch)
+ l1->dch->l1 = NULL;
+ module_put(THIS_MODULE);
+ kfree(l1);
+}
+
+int
+l1_event(struct layer1 *l1, u_int event)
+{
+ int err = 0;
+
+ if (!l1)
+ return -EINVAL;
+ switch (event) {
+ case HW_RESET_IND:
+ mISDN_FsmEvent(&l1->l1m, EV_RESET_IND, NULL);
+ break;
+ case HW_DEACT_IND:
+ mISDN_FsmEvent(&l1->l1m, EV_DEACT_IND, NULL);
+ break;
+ case HW_POWERUP_IND:
+ mISDN_FsmEvent(&l1->l1m, EV_POWER_UP, NULL);
+ break;
+ case HW_DEACT_CNF:
+ mISDN_FsmEvent(&l1->l1m, EV_DEACT_CNF, NULL);
+ break;
+ case ANYSIGNAL:
+ mISDN_FsmEvent(&l1->l1m, EV_ANYSIG_IND, NULL);
+ break;
+ case LOSTFRAMING:
+ mISDN_FsmEvent(&l1->l1m, EV_ANYSIG_IND, NULL);
+ break;
+ case INFO2:
+ mISDN_FsmEvent(&l1->l1m, EV_INFO2_IND, NULL);
+ break;
+ case INFO4_P8:
+ mISDN_FsmEvent(&l1->l1m, EV_INFO4_IND, NULL);
+ break;
+ case INFO4_P10:
+ mISDN_FsmEvent(&l1->l1m, EV_INFO4_IND, NULL);
+ break;
+ case PH_ACTIVATE_REQ:
+ if (test_bit(FLG_L1_ACTIVATED, &l1->Flags))
+ l1->dcb(l1->dch, PH_ACTIVATE_IND);
+ else {
+ test_and_set_bit(FLG_L1_ACTIVATING, &l1->Flags);
+ mISDN_FsmEvent(&l1->l1m, EV_PH_ACTIVATE, NULL);
+ }
+ break;
+ case CLOSE_CHANNEL:
+ release_l1(l1);
+ break;
+ default:
+ if (*debug & DEBUG_L1)
+ printk(KERN_DEBUG "%s %x unhandled\n",
+ __func__, event);
+ err = -EINVAL;
+ }
+ return err;
+}
+EXPORT_SYMBOL(l1_event);
+
+int
+create_l1(struct dchannel *dch, dchannel_l1callback *dcb) {
+ struct layer1 *nl1;
+
+ nl1 = kzalloc(sizeof(struct layer1), GFP_ATOMIC);
+ if (!nl1) {
+ printk(KERN_ERR "kmalloc struct layer1 failed\n");
+ return -ENOMEM;
+ }
+ nl1->l1m.fsm = &l1fsm_s;
+ nl1->l1m.state = ST_L1_F3;
+ nl1->Flags = 0;
+ nl1->l1m.debug = *debug & DEBUG_L1_FSM;
+ nl1->l1m.userdata = nl1;
+ nl1->l1m.userint = 0;
+ nl1->l1m.printdebug = l1m_debug;
+ nl1->dch = dch;
+ nl1->dcb = dcb;
+ mISDN_FsmInitTimer(&nl1->l1m, &nl1->timer);
+ __module_get(THIS_MODULE);
+ dch->l1 = nl1;
+ return 0;
+}
+EXPORT_SYMBOL(create_l1);
+
+int
+l1_init(u_int *deb)
+{
+ debug = deb;
+ 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, ARRAY_SIZE(L1SFnList));
+ return 0;
+}
+
+void
+l1_cleanup(void)
+{
+ mISDN_FsmFree(&l1fsm_s);
+}
diff --git a/drivers/isdn/mISDN/layer1.h b/drivers/isdn/mISDN/layer1.h
new file mode 100644
index 0000000..9c8125f
--- /dev/null
+++ b/drivers/isdn/mISDN/layer1.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * Layer 1 defines
+ *
+ * Copyright 2008 by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+#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
+
diff --git a/drivers/isdn/mISDN/layer2.c b/drivers/isdn/mISDN/layer2.c
new file mode 100644
index 0000000..a7915a1
--- /dev/null
+++ b/drivers/isdn/mISDN/layer2.c
@@ -0,0 +1,2216 @@
+/*
+ *
+ * Author Karsten Keil <kkeil@novell.com>
+ *
+ * Copyright 2008 by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+#include "fsm.h"
+#include "layer2.h"
+
+static int *debug;
+
+static
+struct Fsm l2fsm = {NULL, 0, 0, NULL, NULL};
+
+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",
+};
+
+static void
+l2m_debug(struct FsmInst *fi, char *fmt, ...)
+{
+ struct layer2 *l2 = fi->userdata;
+ va_list va;
+
+ if (!(*debug & DEBUG_L2_FSM))
+ return;
+ va_start(va, fmt);
+ printk(KERN_DEBUG "l2 (tei %d): ", l2->tei);
+ vprintk(fmt, va);
+ printk("\n");
+ va_end(va);
+}
+
+inline u_int
+l2headersize(struct layer2 *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(struct layer2 *l2)
+{
+ return test_bit(FLG_LAPD, &l2->flag) ? 2 : 1;
+}
+
+static u_int
+l2_newid(struct layer2 *l2)
+{
+ u_int id;
+
+ id = l2->next_id++;
+ if (id == 0x7fff)
+ l2->next_id = 1;
+ id <<= 16;
+ id |= l2->tei << 8;
+ id |= l2->sapi;
+ return id;
+}
+
+static void
+l2up(struct layer2 *l2, u_int prim, struct sk_buff *skb)
+{
+ int err;
+
+ if (!l2->up)
+ return;
+ mISDN_HEAD_PRIM(skb) = prim;
+ mISDN_HEAD_ID(skb) = (l2->ch.nr << 16) | l2->ch.addr;
+ err = l2->up->send(l2->up, skb);
+ if (err) {
+ printk(KERN_WARNING "%s: err=%d\n", __func__, err);
+ dev_kfree_skb(skb);
+ }
+}
+
+static void
+l2up_create(struct layer2 *l2, u_int prim, int len, void *arg)
+{
+ struct sk_buff *skb;
+ struct mISDNhead *hh;
+ int err;
+
+ if (!l2->up)
+ return;
+ skb = mI_alloc_skb(len, GFP_ATOMIC);
+ if (!skb)
+ return;
+ hh = mISDN_HEAD_P(skb);
+ hh->prim = prim;
+ hh->id = (l2->ch.nr << 16) | l2->ch.addr;
+ if (len)
+ memcpy(skb_put(skb, len), arg, len);
+ err = l2->up->send(l2->up, skb);
+ if (err) {
+ printk(KERN_WARNING "%s: err=%d\n", __func__, err);
+ dev_kfree_skb(skb);
+ }
+}
+
+static int
+l2down_skb(struct layer2 *l2, struct sk_buff *skb) {
+ int ret;
+
+ ret = l2->ch.recv(l2->ch.peer, skb);
+ if (ret && (*debug & DEBUG_L2_RECV))
+ printk(KERN_DEBUG "l2down_skb: ret(%d)\n", ret);
+ return ret;
+}
+
+static int
+l2down_raw(struct layer2 *l2, struct sk_buff *skb)
+{
+ struct mISDNhead *hh = mISDN_HEAD_P(skb);
+
+ if (hh->prim == PH_DATA_REQ) {
+ if (test_and_set_bit(FLG_L1_NOTREADY, &l2->flag)) {
+ skb_queue_tail(&l2->down_queue, skb);
+ return 0;
+ }
+ l2->down_id = mISDN_HEAD_ID(skb);
+ }
+ return l2down_skb(l2, skb);
+}
+
+static int
+l2down(struct layer2 *l2, u_int prim, u_int id, struct sk_buff *skb)
+{
+ struct mISDNhead *hh = mISDN_HEAD_P(skb);
+
+ hh->prim = prim;
+ hh->id = id;
+ return l2down_raw(l2, skb);
+}
+
+static int
+l2down_create(struct layer2 *l2, u_int prim, u_int id, int len, void *arg)
+{
+ struct sk_buff *skb;
+ int err;
+ struct mISDNhead *hh;
+
+ skb = mI_alloc_skb(len, GFP_ATOMIC);
+ if (!skb)
+ return -ENOMEM;
+ hh = mISDN_HEAD_P(skb);
+ hh->prim = prim;
+ hh->id = id;
+ if (len)
+ memcpy(skb_put(skb, len), arg, len);
+ err = l2down_raw(l2, skb);
+ if (err)
+ dev_kfree_skb(skb);
+ return err;
+}
+
+static int
+ph_data_confirm(struct layer2 *l2, struct mISDNhead *hh, struct sk_buff *skb) {
+ struct sk_buff *nskb = skb;
+ int ret = -EAGAIN;
+
+ if (test_bit(FLG_L1_NOTREADY, &l2->flag)) {
+ if (hh->id == l2->down_id) {
+ nskb = skb_dequeue(&l2->down_queue);
+ if (nskb) {
+ l2->down_id = mISDN_HEAD_ID(nskb);
+ if (l2down_skb(l2, nskb)) {
+ dev_kfree_skb(nskb);
+ l2->down_id = MISDN_ID_NONE;
+ }
+ } else
+ l2->down_id = MISDN_ID_NONE;
+ if (ret) {
+ dev_kfree_skb(skb);
+ ret = 0;
+ }
+ if (l2->down_id == MISDN_ID_NONE) {
+ test_and_clear_bit(FLG_L1_NOTREADY, &l2->flag);
+ mISDN_FsmEvent(&l2->l2m, EV_L2_ACK_PULL, NULL);
+ }
+ }
+ }
+ if (!test_and_set_bit(FLG_L1_NOTREADY, &l2->flag)) {
+ nskb = skb_dequeue(&l2->down_queue);
+ if (nskb) {
+ l2->down_id = mISDN_HEAD_ID(nskb);
+ if (l2down_skb(l2, nskb)) {
+ dev_kfree_skb(nskb);
+ l2->down_id = MISDN_ID_NONE;
+ test_and_clear_bit(FLG_L1_NOTREADY, &l2->flag);
+ }
+ } else
+ test_and_clear_bit(FLG_L1_NOTREADY, &l2->flag);
+ }
+ return ret;
+}
+
+static int
+l2mgr(struct layer2 *l2, u_int prim, void *arg) {
+ long c = (long)arg;
+
+ printk(KERN_WARNING
+ "l2mgr: addr:%x prim %x %c\n", l2->id, prim, (char)c);
+ if (test_bit(FLG_LAPD, &l2->flag) &&
+ !test_bit(FLG_FIXED_TEI, &l2->flag)) {
+ switch (c) {
+ case 'C':
+ case 'D':
+ case 'G':
+ case 'H':
+ l2_tei(l2, prim, (u_long)arg);
+ break;
+ }
+ }
+ return 0;
+}
+
+static void
+set_peer_busy(struct layer2 *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(struct layer2 *l2) {
+ if (test_and_clear_bit(FLG_PEER_BUSY, &l2->flag))
+ test_and_clear_bit(FLG_L2BLOCK, &l2->flag);
+}
+
+static void
+InitWin(struct layer2 *l2)
+{
+ int i;
+
+ for (i = 0; i < MAX_WINDOW; i++)
+ l2->windowar[i] = NULL;
+}
+
+static int
+freewin(struct layer2 *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(struct layer2 *l2)
+{
+ int cnt = freewin(l2);
+
+ if (cnt)
+ printk(KERN_WARNING
+ "isdnl2 freed %d skbuffs in release\n", cnt);
+}
+
+inline unsigned int
+cansend(struct layer2 *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(struct layer2 *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(struct layer2 *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;
+ }
+}
+
+static inline void
+enqueue_super(struct layer2 *l2, struct sk_buff *skb)
+{
+ if (l2down(l2, PH_DATA_REQ, l2_newid(l2), skb))
+ dev_kfree_skb(skb);
+}
+
+static inline void
+enqueue_ui(struct layer2 *l2, struct sk_buff *skb)
+{
+ if (l2->tm)
+ l2_tei(l2, MDL_STATUS_UI_IND, 0);
+ if (l2down(l2, PH_DATA_REQ, l2_newid(l2), 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, struct layer2 *l2)
+{
+ if (test_bit(FLG_MOD128, &l2->flag))
+ return data[0] == RR;
+ else
+ return (data[0] & 0xf) == 1;
+}
+
+inline int
+IsSFrame(u_char *data, struct layer2 *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, struct layer2 *l2)
+{
+ u_char d = data[0] & ~0x10;
+
+ return test_bit(FLG_MOD128, &l2->flag) ? d == SABME : d == SABM;
+}
+
+inline int
+IsREJ(u_char *data, struct layer2 *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, struct layer2 *l2)
+{
+ return test_bit(FLG_MOD128, &l2->flag) ?
+ data[0] == RNR : (data[0] & 0xf) == RNR;
+}
+
+int
+iframe_error(struct layer2 *l2, struct sk_buff *skb)
+{
+ u_int i;
+ int rsp = *skb->data & 0x2;
+
+ i = l2addrsize(l2) + (test_bit(FLG_MOD128, &l2->flag) ? 2 : 1);
+ 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(struct layer2 *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(struct layer2 *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(struct layer2 *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(struct layer2 *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 (*debug & DEBUG_L2)
+ 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 (*debug & DEBUG_L2)
+ l2m_debug(&l2->l2m,
+ "FRMR information %2x %2x %2x",
+ datap[0], datap[1], datap[2]);
+ }
+ return 0;
+}
+
+static unsigned int
+legalnr(struct layer2 *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(struct layer2 *l2, unsigned int nr)
+{
+ struct sk_buff *skb;
+
+ 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;
+ }
+ skb = skb_dequeue(&l2->tmp_queue);
+ while (skb) {
+ dev_kfree_skb(skb);
+ skb = skb_dequeue(&l2->tmp_queue);
+ }
+}
+
+static void
+send_uframe(struct layer2 *l2, struct sk_buff *skb, u_char cmd, u_char cr)
+{
+ u_char tmp[MAX_L2HEADER_LEN];
+ int i;
+
+ i = sethdraddr(l2, tmp, cr);
+ tmp[i++] = cmd;
+ if (skb)
+ skb_trim(skb, 0);
+ else {
+ skb = mI_alloc_skb(i, GFP_ATOMIC);
+ if (!skb) {
+ printk(KERN_WARNING "%s: can't alloc skbuff\n",
+ __func__);
+ return;
+ }
+ }
+ memcpy(skb_put(skb, i), tmp, i);
+ enqueue_super(l2, skb);
+}
+
+
+inline u_char
+get_PollFlag(struct layer2 *l2, struct sk_buff *skb)
+{
+ return skb->data[l2addrsize(l2)] & 0x10;
+}
+
+inline u_char
+get_PollFlagFree(struct layer2 *l2, struct sk_buff *skb)
+{
+ u_char PF;
+
+ PF = get_PollFlag(l2, skb);
+ dev_kfree_skb(skb);
+ return PF;
+}
+
+inline void
+start_t200(struct layer2 *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(struct layer2 *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(struct layer2 *l2, int i)
+{
+ if (test_and_clear_bit(FLG_T200_RUN, &l2->flag))
+ mISDN_FsmDelTimer(&l2->t200, i);
+}
+
+inline void
+st5_dl_release_l2l3(struct layer2 *l2)
+{
+ int pr;
+
+ if (test_and_clear_bit(FLG_PEND_REL, &l2->flag))
+ pr = DL_RELEASE_CNF;
+ else
+ pr = DL_RELEASE_IND;
+ l2up_create(l2, pr, 0, NULL);
+}
+
+inline void
+lapb_dl_release_l2l3(struct layer2 *l2, int f)
+{
+ if (test_bit(FLG_LAPB, &l2->flag))
+ l2down_create(l2, PH_DEACTIVATE_REQ, l2_newid(l2), 0, NULL);
+ l2up_create(l2, f, 0, NULL);
+}
+
+static void
+establishlink(struct FsmInst *fi)
+{
+ struct layer2 *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;
+ struct layer2 *l2 = fi->userdata;
+
+ if (get_PollFlagFree(l2, skb))
+ l2mgr(l2, MDL_ERROR_IND, (void *) 'C');
+ else
+ l2mgr(l2, MDL_ERROR_IND, (void *) 'D');
+
+}
+
+static void
+l2_mdl_error_dm(struct FsmInst *fi, int event, void *arg)
+{
+ struct sk_buff *skb = arg;
+ struct layer2 *l2 = fi->userdata;
+
+ if (get_PollFlagFree(l2, skb))
+ l2mgr(l2, MDL_ERROR_IND, (void *) 'B');
+ else {
+ l2mgr(l2, MDL_ERROR_IND, (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;
+ struct layer2 *l2 = fi->userdata;
+
+ if (get_PollFlagFree(l2, skb))
+ l2mgr(l2, MDL_ERROR_IND, (void *) 'B');
+ else
+ l2mgr(l2, MDL_ERROR_IND, (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)
+{
+ struct layer2 *l2 = fi->userdata;
+
+ mISDN_FsmChangeState(fi, ST_L2_3);
+ dev_kfree_skb((struct sk_buff *)arg);
+ l2_tei(l2, MDL_ASSIGN_IND, 0);
+}
+
+static void
+l2_queue_ui_assign(struct FsmInst *fi, int event, void *arg)
+{
+ struct layer2 *l2 = fi->userdata;
+ struct sk_buff *skb = arg;
+
+ skb_queue_tail(&l2->ui_queue, skb);
+ mISDN_FsmChangeState(fi, ST_L2_2);
+ l2_tei(l2, MDL_ASSIGN_IND, 0);
+}
+
+static void
+l2_queue_ui(struct FsmInst *fi, int event, void *arg)
+{
+ struct layer2 *l2 = fi->userdata;
+ struct sk_buff *skb = arg;
+
+ skb_queue_tail(&l2->ui_queue, skb);
+}
+
+static void
+tx_ui(struct layer2 *l2)
+{
+ struct sk_buff *skb;
+ u_char header[MAX_L2HEADER_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)
+{
+ struct layer2 *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)
+{
+ struct layer2 *l2 = fi->userdata;
+ struct sk_buff *skb = arg;
+
+ skb_pull(skb, l2headersize(l2, 1));
+/*
+ * in states 1-3 for broadcast
+ */
+
+ if (l2->tm)
+ l2_tei(l2, MDL_STATUS_UI_IND, 0);
+ l2up(l2, DL_UNITDATA_IND, skb);
+}
+
+static void
+l2_establish(struct FsmInst *fi, int event, void *arg)
+{
+ struct sk_buff *skb = arg;
+ struct layer2 *l2 = fi->userdata;
+
+ 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;
+ struct layer2 *l2 = fi->userdata;
+
+ skb_queue_purge(&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;
+ struct layer2 *l2 = fi->userdata;
+
+ skb_queue_purge(&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)
+{
+ struct layer2 *l2 = fi->userdata;
+ struct sk_buff *skb = arg;
+
+ skb_trim(skb, 0);
+ l2up(l2, DL_RELEASE_CNF, skb);
+}
+
+static void
+l2_pend_rel(struct FsmInst *fi, int event, void *arg)
+{
+ struct sk_buff *skb = arg;
+ struct layer2 *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)
+{
+ struct layer2 *l2 = fi->userdata;
+ struct sk_buff *skb = arg;
+
+ skb_queue_purge(&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)
+{
+ struct layer2 *l2 = fi->userdata;
+ struct sk_buff *skb = arg;
+
+ l2->vs = 0;
+ l2->va = 0;
+ l2->vr = 0;
+ l2->sow = 0;
+ 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);
+ l2up(l2, DL_ESTABLISH_IND, skb);
+ if (l2->tm)
+ l2_tei(l2, MDL_STATUS_UP_IND, 0);
+}
+
+static void
+l2_send_UA(struct FsmInst *fi, int event, void *arg)
+{
+ struct layer2 *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)
+{
+ struct layer2 *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)
+{
+ struct layer2 *l2 = fi->userdata;
+ struct sk_buff *skb = arg;
+ int est = 0;
+
+ send_uframe(l2, skb, UA | get_PollFlag(l2, skb), RSP);
+
+ l2mgr(l2, MDL_ERROR_IND, (void *) 'F');
+
+ if (l2->vs != l2->va) {
+ skb_queue_purge(&l2->i_queue);
+ est = 1;
+ }
+
+ clear_exception(l2);
+ l2->vs = 0;
+ l2->va = 0;
+ l2->vr = 0;
+ l2->sow = 0;
+ 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_IND, 0, NULL);
+/* mISDN_queue_data(&l2->inst, l2->inst.id | MSG_BROADCAST,
+ * MGR_SHORTSTATUS | INDICATION, SSTATUS_L2_ESTABLISHED,
+ * 0, NULL, 0);
+ */
+ 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)
+{
+ struct layer2 *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);
+ skb_queue_purge(&l2->i_queue);
+ freewin(l2);
+ lapb_dl_release_l2l3(l2, DL_RELEASE_IND);
+ if (l2->tm)
+ l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+}
+
+static void
+l2_connected(struct FsmInst *fi, int event, void *arg)
+{
+ struct layer2 *l2 = fi->userdata;
+ struct sk_buff *skb = arg;
+ int pr = -1;
+
+ 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_CNF;
+ } else if (l2->vs != l2->va) {
+ skb_queue_purge(&l2->i_queue);
+ pr = DL_ESTABLISH_IND;
+ }
+ stop_t200(l2, 5);
+ l2->vr = 0;
+ l2->vs = 0;
+ l2->va = 0;
+ l2->sow = 0;
+ 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, NULL);
+
+ if (skb_queue_len(&l2->i_queue) && cansend(l2))
+ mISDN_FsmEvent(fi, EV_L2_ACK_PULL, NULL);
+
+ if (l2->tm)
+ l2_tei(l2, MDL_STATUS_UP_IND, 0);
+}
+
+static void
+l2_released(struct FsmInst *fi, int event, void *arg)
+{
+ struct layer2 *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, DL_RELEASE_CNF);
+ mISDN_FsmChangeState(fi, ST_L2_4);
+ if (l2->tm)
+ l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+}
+
+static void
+l2_reestablish(struct FsmInst *fi, int event, void *arg)
+{
+ struct layer2 *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)
+{
+ struct layer2 *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))
+ skb_queue_purge(&l2->i_queue);
+ if (test_bit(FLG_LAPB, &l2->flag))
+ l2down_create(l2, PH_DEACTIVATE_REQ,
+ l2_newid(l2), 0, NULL);
+ st5_dl_release_l2l3(l2);
+ mISDN_FsmChangeState(fi, ST_L2_4);
+ if (l2->tm)
+ l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+ }
+}
+
+static void
+l2_st6_dm_release(struct FsmInst *fi, int event, void *arg)
+{
+ struct layer2 *l2 = fi->userdata;
+ struct sk_buff *skb = arg;
+
+ if (get_PollFlagFree(l2, skb)) {
+ stop_t200(l2, 8);
+ lapb_dl_release_l2l3(l2, DL_RELEASE_CNF);
+ mISDN_FsmChangeState(fi, ST_L2_4);
+ if (l2->tm)
+ l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+ }
+}
+
+void
+enquiry_cr(struct layer2 *l2, u_char typ, u_char cr, u_char pf)
+{
+ struct sk_buff *skb;
+ u_char tmp[MAX_L2HEADER_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);
+ skb = mI_alloc_skb(i, GFP_ATOMIC);
+ if (!skb) {
+ 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(struct layer2 *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(struct layer2 *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)
+{
+ struct layer2 *l2 = fi->userdata;
+
+ l2mgr(l2, MDL_ERROR_IND, (void *) 'J');
+ establishlink(fi);
+ test_and_clear_bit(FLG_L3_INIT, &l2->flag);
+}
+
+static void
+invoke_retransmission(struct layer2 *l2, unsigned int nr)
+{
+ u_int p1;
+
+ 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",
+ __func__, p1);
+ l2->windowar[p1] = NULL;
+ }
+ mISDN_FsmEvent(&l2->l2m, EV_L2_ACK_PULL, NULL);
+ }
+}
+
+static void
+l2_st7_got_super(struct FsmInst *fi, int event, void *arg)
+{
+ struct layer2 *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_IND, (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)
+{
+ struct layer2 *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)
+{
+ struct layer2 *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)
+{
+ struct layer2 *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)
+{
+ struct layer2 *l2 = fi->userdata;
+ struct sk_buff *skb = arg;
+ int PollFlag, i;
+ u_int ns, nr;
+
+ 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 {
+ 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);
+ if (PollFlag)
+ enquiry_response(l2);
+ else
+ test_and_set_bit(FLG_ACK_PEND, &l2->flag);
+ skb_pull(skb, l2headersize(l2, 0));
+ l2up(l2, DL_DATA_IND, skb);
+ } else {
+ /* n(s)!=v(r) */
+ 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)
+{
+ struct layer2 *l2 = fi->userdata;
+ u_int info;
+
+ l2->tei = (signed char)(long)arg;
+ set_channel_address(&l2->ch, l2->sapi, l2->tei);
+ info = DL_INFO_L2_CONNECT;
+ l2up_create(l2, DL_INFORMATION_IND, sizeof(info), &info);
+ 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)
+{
+ struct layer2 *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);
+ skb_queue_purge(&l2->i_queue);
+ l2mgr(l2, MDL_ERROR_IND, (void *) 'G');
+ if (test_bit(FLG_LAPB, &l2->flag))
+ l2down_create(l2, PH_DEACTIVATE_REQ,
+ l2_newid(l2), 0, NULL);
+ st5_dl_release_l2l3(l2);
+ if (l2->tm)
+ l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+ } 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)
+{
+ struct layer2 *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_IND, (void *) 'H');
+ lapb_dl_release_l2l3(l2, DL_RELEASE_CNF);
+ if (l2->tm)
+ l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+ } 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)
+{
+ struct layer2 *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)
+{
+ struct layer2 *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_IND, (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)
+{
+ struct layer2 *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)
+{
+ struct layer2 *l2 = fi->userdata;
+ struct sk_buff *skb, *nskb, *oskb;
+ u_char header[MAX_L2HEADER_LEN];
+ u_int i, p1;
+
+ if (!cansend(l2))
+ return;
+
+ skb = skb_dequeue(&l2->i_queue);
+ if (!skb)
+ return;
+
+ 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;
+ }
+
+ 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 = mI_alloc_skb(oskb->len + i, GFP_ATOMIC);
+ if (!nskb) {
+ dev_kfree_skb(oskb);
+ printk(KERN_WARNING "%s: no skb mem\n", __func__);
+ 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, l2_newid(l2), 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)
+{
+ struct layer2 *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)
+{
+ struct layer2 *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_IND, (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)
+{
+ struct layer2 *l2 = fi->userdata;
+
+ skb_queue_purge(&l2->ui_queue);
+ l2->tei = GROUP_TEI;
+ mISDN_FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_st3_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+ struct layer2 *l2 = fi->userdata;
+
+ skb_queue_purge(&l2->ui_queue);
+ l2->tei = GROUP_TEI;
+ l2up_create(l2, DL_RELEASE_IND, 0, NULL);
+ mISDN_FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_st5_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+ struct layer2 *l2 = fi->userdata;
+
+ skb_queue_purge(&l2->i_queue);
+ skb_queue_purge(&l2->ui_queue);
+ freewin(l2);
+ l2->tei = GROUP_TEI;
+ stop_t200(l2, 17);
+ st5_dl_release_l2l3(l2);
+ mISDN_FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_st6_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+ struct layer2 *l2 = fi->userdata;
+
+ skb_queue_purge(&l2->ui_queue);
+ l2->tei = GROUP_TEI;
+ stop_t200(l2, 18);
+ l2up_create(l2, DL_RELEASE_IND, 0, NULL);
+ mISDN_FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_tei_remove(struct FsmInst *fi, int event, void *arg)
+{
+ struct layer2 *l2 = fi->userdata;
+
+ skb_queue_purge(&l2->i_queue);
+ skb_queue_purge(&l2->ui_queue);
+ freewin(l2);
+ l2->tei = GROUP_TEI;
+ stop_t200(l2, 17);
+ mISDN_FsmDelTimer(&l2->t203, 19);
+ l2up_create(l2, DL_RELEASE_IND, 0, NULL);
+/* mISDN_queue_data(&l2->inst, l2->inst.id | MSG_BROADCAST,
+ * MGR_SHORTSTATUS_IND, SSTATUS_L2_RELEASED,
+ * 0, NULL, 0);
+ */
+ mISDN_FsmChangeState(fi, ST_L2_1);
+}
+
+static void
+l2_st14_persistant_da(struct FsmInst *fi, int event, void *arg)
+{
+ struct layer2 *l2 = fi->userdata;
+ struct sk_buff *skb = arg;
+
+ skb_queue_purge(&l2->i_queue);
+ skb_queue_purge(&l2->ui_queue);
+ if (test_and_clear_bit(FLG_ESTAB_PEND, &l2->flag))
+ l2up(l2, DL_RELEASE_IND, skb);
+ else
+ dev_kfree_skb(skb);
+}
+
+static void
+l2_st5_persistant_da(struct FsmInst *fi, int event, void *arg)
+{
+ struct layer2 *l2 = fi->userdata;
+ struct sk_buff *skb = arg;
+
+ skb_queue_purge(&l2->i_queue);
+ skb_queue_purge(&l2->ui_queue);
+ freewin(l2);
+ stop_t200(l2, 19);
+ st5_dl_release_l2l3(l2);
+ mISDN_FsmChangeState(fi, ST_L2_4);
+ if (l2->tm)
+ l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+ dev_kfree_skb(skb);
+}
+
+static void
+l2_st6_persistant_da(struct FsmInst *fi, int event, void *arg)
+{
+ struct layer2 *l2 = fi->userdata;
+ struct sk_buff *skb = arg;
+
+ skb_queue_purge(&l2->ui_queue);
+ stop_t200(l2, 20);
+ l2up(l2, DL_RELEASE_CNF, skb);
+ mISDN_FsmChangeState(fi, ST_L2_4);
+ if (l2->tm)
+ l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+}
+
+static void
+l2_persistant_da(struct FsmInst *fi, int event, void *arg)
+{
+ struct layer2 *l2 = fi->userdata;
+ struct sk_buff *skb = arg;
+
+ skb_queue_purge(&l2->i_queue);
+ skb_queue_purge(&l2->ui_queue);
+ freewin(l2);
+ stop_t200(l2, 19);
+ mISDN_FsmDelTimer(&l2->t203, 19);
+ l2up(l2, DL_RELEASE_IND, skb);
+ mISDN_FsmChangeState(fi, ST_L2_4);
+ if (l2->tm)
+ l2_tei(l2, MDL_STATUS_DOWN_IND, 0);
+}
+
+static void
+l2_set_own_busy(struct FsmInst *fi, int event, void *arg)
+{
+ struct layer2 *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)
+{
+ struct layer2 *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)
+{
+ struct layer2 *l2 = fi->userdata;
+
+ l2mgr(l2, MDL_ERROR_IND, arg);
+}
+
+static void
+l2_frame_error_reest(struct FsmInst *fi, int event, void *arg)
+{
+ struct layer2 *l2 = fi->userdata;
+
+ l2mgr(l2, MDL_ERROR_IND, 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(struct layer2 *l2, struct mISDNhead *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)) { /* Maybe not needed */
+ 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) {
+ /* not our bussiness
+ * printk(KERN_DEBUG "%s: sapi %d/%d sapi mismatch\n",
+ * __func__,
+ * psapi, l2->sapi);
+ */
+ dev_kfree_skb(skb);
+ return 0;
+ }
+ if ((ptei != l2->tei) && (ptei != GROUP_TEI)) {
+ /* not our bussiness
+ * printk(KERN_DEBUG "%s: tei %d/%d sapi %d mismatch\n",
+ * __func__,
+ * ptei, l2->tei, psapi);
+ */
+ dev_kfree_skb(skb);
+ return 0;
+ }
+ } else
+ datap += l;
+ if (!(*datap & 1)) { /* I-Frame */
+ c = iframe_error(l2, skb);
+ if (!c)
+ ret = mISDN_FsmEvent(&l2->l2m, EV_L2_I, skb);
+ } else if (IsSFrame(datap, l2)) { /* S-Frame */
+ c = super_error(l2, skb);
+ if (!c)
+ ret = mISDN_FsmEvent(&l2->l2m, EV_L2_SUPER, skb);
+ } else if (IsUI(datap)) {
+ c = UI_error(l2, skb);
+ if (!c)
+ ret = mISDN_FsmEvent(&l2->l2m, EV_L2_UI, skb);
+ } else if (IsSABME(datap, l2)) {
+ c = unnum_error(l2, skb, CMD);
+ if (!c)
+ ret = mISDN_FsmEvent(&l2->l2m, EV_L2_SABME, skb);
+ } else if (IsUA(datap)) {
+ c = unnum_error(l2, skb, RSP);
+ if (!c)
+ ret = mISDN_FsmEvent(&l2->l2m, EV_L2_UA, skb);
+ } else if (IsDISC(datap)) {
+ c = unnum_error(l2, skb, CMD);
+ if (!c)
+ ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DISC, skb);
+ } else if (IsDM(datap)) {
+ c = unnum_error(l2, skb, RSP);
+ if (!c)
+ ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DM, skb);
+ } else if (IsFRMR(datap)) {
+ c = FRMR_error(l2, skb);
+ if (!c)
+ 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
+l2_send(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+ struct layer2 *l2 = container_of(ch, struct layer2, ch);
+ struct mISDNhead *hh = mISDN_HEAD_P(skb);
+ int ret = -EINVAL;
+
+ if (*debug & DEBUG_L2_RECV)
+ printk(KERN_DEBUG "%s: prim(%x) id(%x) tei(%d)\n",
+ __func__, hh->prim, hh->id, l2->tei);
+ switch (hh->prim) {
+ case PH_DATA_IND:
+ ret = ph_data_indication(l2, hh, skb);
+ break;
+ case PH_DATA_CNF:
+ ret = ph_data_confirm(l2, hh, skb);
+ break;
+ case PH_ACTIVATE_IND:
+ test_and_set_bit(FLG_L1_ACTIV, &l2->flag);
+ l2up_create(l2, MPH_ACTIVATE_IND, 0, NULL);
+ if (test_and_clear_bit(FLG_ESTAB_PEND, &l2->flag))
+ ret = mISDN_FsmEvent(&l2->l2m,
+ EV_L2_DL_ESTABLISH_REQ, skb);
+ break;
+ case PH_DEACTIVATE_IND:
+ test_and_clear_bit(FLG_L1_ACTIV, &l2->flag);
+ l2up_create(l2, MPH_DEACTIVATE_IND, 0, NULL);
+ ret = mISDN_FsmEvent(&l2->l2m, EV_L1_DEACTIVATE, skb);
+ break;
+ case MPH_INFORMATION_IND:
+ if (!l2->up)
+ break;
+ ret = l2->up->send(l2->up, skb);
+ break;
+ case DL_DATA_REQ:
+ ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_DATA, skb);
+ break;
+ case DL_UNITDATA_REQ:
+ ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_UNITDATA, skb);
+ break;
+ case DL_ESTABLISH_REQ:
+ if (test_bit(FLG_LAPB, &l2->flag))
+ test_and_set_bit(FLG_ORIG, &l2->flag);
+ 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_REQ, l2_newid(l2),
+ skb);
+ }
+ break;
+ case DL_RELEASE_REQ:
+ if (test_bit(FLG_LAPB, &l2->flag))
+ l2down_create(l2, PH_DEACTIVATE_REQ,
+ l2_newid(l2), 0, NULL);
+ ret = mISDN_FsmEvent(&l2->l2m, EV_L2_DL_RELEASE_REQ,
+ skb);
+ break;
+ default:
+ if (*debug & DEBUG_L2)
+ l2m_debug(&l2->l2m, "l2 unknown pr %04x",
+ hh->prim);
+ }
+ if (ret) {
+ dev_kfree_skb(skb);
+ ret = 0;
+ }
+ return ret;
+}
+
+int
+tei_l2(struct layer2 *l2, u_int cmd, u_long arg)
+{
+ int ret = -EINVAL;
+
+ if (*debug & DEBUG_L2_TEI)
+ printk(KERN_DEBUG "%s: cmd(%x)\n", __func__, cmd);
+ switch (cmd) {
+ case (MDL_ASSIGN_REQ):
+ ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ASSIGN, (void *)arg);
+ break;
+ case (MDL_REMOVE_REQ):
+ ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_REMOVE, NULL);
+ break;
+ case (MDL_ERROR_IND):
+ ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ERROR, NULL);
+ break;
+ case (MDL_ERROR_RSP):
+ /* ETS 300-125 5.3.2.1 Test: TC13010 */
+ printk(KERN_NOTICE "MDL_ERROR|REQ (tei_l2)\n");
+ ret = mISDN_FsmEvent(&l2->l2m, EV_L2_MDL_ERROR, NULL);
+ break;
+ }
+ return ret;
+}
+
+static void
+release_l2(struct layer2 *l2)
+{
+ mISDN_FsmDelTimer(&l2->t200, 21);
+ mISDN_FsmDelTimer(&l2->t203, 16);
+ skb_queue_purge(&l2->i_queue);
+ skb_queue_purge(&l2->ui_queue);
+ skb_queue_purge(&l2->down_queue);
+ ReleaseWin(l2);
+ if (test_bit(FLG_LAPD, &l2->flag)) {
+ TEIrelease(l2);
+ if (l2->ch.st)
+ l2->ch.st->dev->D.ctrl(&l2->ch.st->dev->D,
+ CLOSE_CHANNEL, NULL);
+ }
+ kfree(l2);
+}
+
+static int
+l2_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+ struct layer2 *l2 = container_of(ch, struct layer2, ch);
+ u_int info;
+
+ if (*debug & DEBUG_L2_CTRL)
+ printk(KERN_DEBUG "%s:(%x)\n", __func__, cmd);
+
+ switch (cmd) {
+ case OPEN_CHANNEL:
+ if (test_bit(FLG_LAPD, &l2->flag)) {
+ set_channel_address(&l2->ch, l2->sapi, l2->tei);
+ info = DL_INFO_L2_CONNECT;
+ l2up_create(l2, DL_INFORMATION_IND,
+ sizeof(info), &info);
+ }
+ break;
+ case CLOSE_CHANNEL:
+ if (l2->ch.peer)
+ l2->ch.peer->ctrl(l2->ch.peer, CLOSE_CHANNEL, NULL);
+ release_l2(l2);
+ break;
+ }
+ return 0;
+}
+
+struct layer2 *
+create_l2(struct mISDNchannel *ch, u_int protocol, u_long options, u_long arg)
+{
+ struct layer2 *l2;
+ struct channel_req rq;
+
+ l2 = kzalloc(sizeof(struct layer2), GFP_KERNEL);
+ if (!l2) {
+ printk(KERN_ERR "kzalloc layer2 failed\n");
+ return NULL;
+ }
+ l2->next_id = 1;
+ l2->down_id = MISDN_ID_NONE;
+ l2->up = ch;
+ l2->ch.st = ch->st;
+ l2->ch.send = l2_send;
+ l2->ch.ctrl = l2_ctrl;
+ switch (protocol) {
+ case ISDN_P_LAPD_NT:
+ test_and_set_bit(FLG_LAPD, &l2->flag);
+ test_and_set_bit(FLG_LAPD_NET, &l2->flag);
+ test_and_set_bit(FLG_MOD128, &l2->flag);
+ l2->sapi = 0;
+ l2->maxlen = MAX_DFRAME_LEN;
+ if (test_bit(OPTION_L2_PMX, &options))
+ l2->window = 7;
+ else
+ l2->window = 1;
+ if (test_bit(OPTION_L2_PTP, &options))
+ test_and_set_bit(FLG_PTP, &l2->flag);
+ if (test_bit(OPTION_L2_FIXEDTEI, &options))
+ test_and_set_bit(FLG_FIXED_TEI, &l2->flag);
+ l2->tei = (u_int)arg;
+ l2->T200 = 1000;
+ l2->N200 = 3;
+ l2->T203 = 10000;
+ if (test_bit(OPTION_L2_PMX, &options))
+ rq.protocol = ISDN_P_NT_E1;
+ else
+ rq.protocol = ISDN_P_NT_S0;
+ rq.adr.channel = 0;
+ l2->ch.st->dev->D.ctrl(&l2->ch.st->dev->D, OPEN_CHANNEL, &rq);
+ break;
+ case ISDN_P_LAPD_TE:
+ test_and_set_bit(FLG_LAPD, &l2->flag);
+ test_and_set_bit(FLG_MOD128, &l2->flag);
+ test_and_set_bit(FLG_ORIG, &l2->flag);
+ l2->sapi = 0;
+ l2->maxlen = MAX_DFRAME_LEN;
+ if (test_bit(OPTION_L2_PMX, &options))
+ l2->window = 7;
+ else
+ l2->window = 1;
+ if (test_bit(OPTION_L2_PTP, &options))
+ test_and_set_bit(FLG_PTP, &l2->flag);
+ if (test_bit(OPTION_L2_FIXEDTEI, &options))
+ test_and_set_bit(FLG_FIXED_TEI, &l2->flag);
+ l2->tei = (u_int)arg;
+ l2->T200 = 1000;
+ l2->N200 = 3;
+ l2->T203 = 10000;
+ if (test_bit(OPTION_L2_PMX, &options))
+ rq.protocol = ISDN_P_TE_E1;
+ else
+ rq.protocol = ISDN_P_TE_S0;
+ rq.adr.channel = 0;
+ l2->ch.st->dev->D.ctrl(&l2->ch.st->dev->D, OPEN_CHANNEL, &rq);
+ break;
+ case ISDN_P_B_X75SLP:
+ test_and_set_bit(FLG_LAPB, &l2->flag);
+ l2->window = 7;
+ l2->maxlen = MAX_DATA_SIZE;
+ l2->T200 = 1000;
+ l2->N200 = 4;
+ l2->T203 = 5000;
+ l2->addr.A = 3;
+ l2->addr.B = 1;
+ break;
+ default:
+ printk(KERN_ERR "layer2 create failed prt %x\n",
+ protocol);
+ kfree(l2);
+ return NULL;
+ }
+ skb_queue_head_init(&l2->i_queue);
+ skb_queue_head_init(&l2->ui_queue);
+ skb_queue_head_init(&l2->down_queue);
+ skb_queue_head_init(&l2->tmp_queue);
+ InitWin(l2);
+ l2->l2m.fsm = &l2fsm;
+ if (test_bit(FLG_LAPB, &l2->flag) ||
+ test_bit(FLG_PTP, &l2->flag) ||
+ test_bit(FLG_LAPD_NET, &l2->flag))
+ l2->l2m.state = ST_L2_4;
+ else
+ l2->l2m.state = ST_L2_1;
+ l2->l2m.debug = *debug;
+ l2->l2m.userdata = l2;
+ l2->l2m.userint = 0;
+ l2->l2m.printdebug = l2m_debug;
+
+ mISDN_FsmInitTimer(&l2->l2m, &l2->t200);
+ mISDN_FsmInitTimer(&l2->l2m, &l2->t203);
+ return l2;
+}
+
+static int
+x75create(struct channel_req *crq)
+{
+ struct layer2 *l2;
+
+ if (crq->protocol != ISDN_P_B_X75SLP)
+ return -EPROTONOSUPPORT;
+ l2 = create_l2(crq->ch, crq->protocol, 0, 0);
+ if (!l2)
+ return -ENOMEM;
+ crq->ch = &l2->ch;
+ crq->protocol = ISDN_P_B_HDLC;
+ return 0;
+}
+
+static struct Bprotocol X75SLP = {
+ .Bprotocols = (1 << (ISDN_P_B_X75SLP & ISDN_P_B_MASK)),
+ .name = "X75SLP",
+ .create = x75create
+};
+
+int
+Isdnl2_Init(u_int *deb)
+{
+ debug = deb;
+ mISDN_register_Bprotocol(&X75SLP);
+ l2fsm.state_count = L2_STATE_COUNT;
+ l2fsm.event_count = L2_EVENT_COUNT;
+ l2fsm.strEvent = strL2Event;
+ l2fsm.strState = strL2State;
+ mISDN_FsmNew(&l2fsm, L2FnList, ARRAY_SIZE(L2FnList));
+ TEIInit(deb);
+ return 0;
+}
+
+void
+Isdnl2_cleanup(void)
+{
+ mISDN_unregister_Bprotocol(&X75SLP);
+ TEIFree();
+ mISDN_FsmFree(&l2fsm);
+}
+
diff --git a/drivers/isdn/mISDN/layer2.h b/drivers/isdn/mISDN/layer2.h
new file mode 100644
index 0000000..6293f80
--- /dev/null
+++ b/drivers/isdn/mISDN/layer2.h
@@ -0,0 +1,140 @@
+/*
+ * Layer 2 defines
+ *
+ * Copyright 2008 by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/mISDNif.h>
+#include <linux/skbuff.h>
+#include "fsm.h"
+
+#define MAX_WINDOW 8
+
+struct manager {
+ struct mISDNchannel ch;
+ struct mISDNchannel bcast;
+ u_long options;
+ struct list_head layer2;
+ rwlock_t lock;
+ struct FsmInst deact;
+ struct FsmTimer datimer;
+ struct sk_buff_head sendq;
+ struct mISDNchannel *up;
+ u_int nextid;
+ u_int lastid;
+};
+
+struct teimgr {
+ int ri;
+ int rcnt;
+ struct FsmInst tei_m;
+ struct FsmTimer timer;
+ int tval, nval;
+ struct layer2 *l2;
+ struct manager *mgr;
+};
+
+struct laddr {
+ u_char A;
+ u_char B;
+};
+
+struct layer2 {
+ struct list_head list;
+ struct mISDNchannel ch;
+ u_long flag;
+ int id;
+ struct mISDNchannel *up;
+ signed char sapi;
+ signed char tei;
+ struct laddr addr;
+ u_int maxlen;
+ struct teimgr *tm;
+ u_int vs, va, vr;
+ int rc;
+ u_int window;
+ u_int sow;
+ struct FsmInst l2m;
+ struct FsmTimer t200, t203;
+ int T200, N200, T203;
+ u_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;
+};
+
+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)
+
+extern struct layer2 *create_l2(struct mISDNchannel *, u_int,
+ u_long, u_long);
+extern int tei_l2(struct layer2 *, u_int, u_long arg);
+
+
+/* from tei.c */
+extern int l2_tei(struct layer2 *, u_int, u_long arg);
+extern void TEIrelease(struct layer2 *);
+extern int TEIInit(u_int *);
+extern void TEIFree(void);
+
+#define MAX_L2HEADER_LEN 4
+
+#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_NOTREADY 17
+#define FLG_LAPD_NET 18
diff --git a/drivers/isdn/mISDN/socket.c b/drivers/isdn/mISDN/socket.c
new file mode 100644
index 0000000..e5a20f9
--- /dev/null
+++ b/drivers/isdn/mISDN/socket.c
@@ -0,0 +1,781 @@
+/*
+ *
+ * Author Karsten Keil <kkeil@novell.com>
+ *
+ * Copyright 2008 by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/mISDNif.h>
+#include "core.h"
+
+static int *debug;
+
+static struct proto mISDN_proto = {
+ .name = "misdn",
+ .owner = THIS_MODULE,
+ .obj_size = sizeof(struct mISDN_sock)
+};
+
+#define _pms(sk) ((struct mISDN_sock *)sk)
+
+static struct mISDN_sock_list data_sockets = {
+ .lock = __RW_LOCK_UNLOCKED(data_sockets.lock)
+};
+
+static struct mISDN_sock_list base_sockets = {
+ .lock = __RW_LOCK_UNLOCKED(base_sockets.lock)
+};
+
+#define L2_HEADER_LEN 4
+
+static inline struct sk_buff *
+_l2_alloc_skb(unsigned int len, gfp_t gfp_mask)
+{
+ struct sk_buff *skb;
+
+ skb = alloc_skb(len + L2_HEADER_LEN, gfp_mask);
+ if (likely(skb))
+ skb_reserve(skb, L2_HEADER_LEN);
+ return skb;
+}
+
+static void
+mISDN_sock_link(struct mISDN_sock_list *l, struct sock *sk)
+{
+ write_lock_bh(&l->lock);
+ sk_add_node(sk, &l->head);
+ write_unlock_bh(&l->lock);
+}
+
+static void mISDN_sock_unlink(struct mISDN_sock_list *l, struct sock *sk)
+{
+ write_lock_bh(&l->lock);
+ sk_del_node_init(sk);
+ write_unlock_bh(&l->lock);
+}
+
+static int
+mISDN_send(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+ struct mISDN_sock *msk;
+ int err;
+
+ msk = container_of(ch, struct mISDN_sock, ch);
+ if (*debug & DEBUG_SOCKET)
+ printk(KERN_DEBUG "%s len %d %p\n", __func__, skb->len, skb);
+ if (msk->sk.sk_state == MISDN_CLOSED)
+ return -EUNATCH;
+ __net_timestamp(skb);
+ err = sock_queue_rcv_skb(&msk->sk, skb);
+ if (err)
+ printk(KERN_WARNING "%s: error %d\n", __func__, err);
+ return err;
+}
+
+static int
+mISDN_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+ struct mISDN_sock *msk;
+
+ msk = container_of(ch, struct mISDN_sock, ch);
+ if (*debug & DEBUG_SOCKET)
+ printk(KERN_DEBUG "%s(%p, %x, %p)\n", __func__, ch, cmd, arg);
+ switch (cmd) {
+ case CLOSE_CHANNEL:
+ msk->sk.sk_state = MISDN_CLOSED;
+ break;
+ }
+ return 0;
+}
+
+static inline void
+mISDN_sock_cmsg(struct sock *sk, struct msghdr *msg, struct sk_buff *skb)
+{
+ struct timeval tv;
+
+ if (_pms(sk)->cmask & MISDN_TIME_STAMP) {
+ skb_get_timestamp(skb, &tv);
+ put_cmsg(msg, SOL_MISDN, MISDN_TIME_STAMP, sizeof(tv), &tv);
+ }
+}
+
+static int
+mISDN_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *msg, size_t len, int flags)
+{
+ struct sk_buff *skb;
+ struct sock *sk = sock->sk;
+ struct sockaddr_mISDN *maddr;
+
+ int copied, err;
+
+ if (*debug & DEBUG_SOCKET)
+ printk(KERN_DEBUG "%s: len %d, flags %x ch.nr %d, proto %x\n",
+ __func__, (int)len, flags, _pms(sk)->ch.nr,
+ sk->sk_protocol);
+ if (flags & (MSG_OOB))
+ return -EOPNOTSUPP;
+
+ if (sk->sk_state == MISDN_CLOSED)
+ return 0;
+
+ skb = skb_recv_datagram(sk, flags, flags & MSG_DONTWAIT, &err);
+ if (!skb)
+ return err;
+
+ if (msg->msg_namelen >= sizeof(struct sockaddr_mISDN)) {
+ msg->msg_namelen = sizeof(struct sockaddr_mISDN);
+ maddr = (struct sockaddr_mISDN *)msg->msg_name;
+ maddr->family = AF_ISDN;
+ maddr->dev = _pms(sk)->dev->id;
+ if ((sk->sk_protocol == ISDN_P_LAPD_TE) ||
+ (sk->sk_protocol == ISDN_P_LAPD_NT)) {
+ maddr->channel = (mISDN_HEAD_ID(skb) >> 16) & 0xff;
+ maddr->tei = (mISDN_HEAD_ID(skb) >> 8) & 0xff;
+ maddr->sapi = mISDN_HEAD_ID(skb) & 0xff;
+ } else {
+ maddr->channel = _pms(sk)->ch.nr;
+ maddr->sapi = _pms(sk)->ch.addr & 0xFF;
+ maddr->tei = (_pms(sk)->ch.addr >> 8) & 0xFF;
+ }
+ } else {
+ if (msg->msg_namelen)
+ printk(KERN_WARNING "%s: too small namelen %d\n",
+ __func__, msg->msg_namelen);
+ msg->msg_namelen = 0;
+ }
+
+ copied = skb->len + MISDN_HEADER_LEN;
+ if (len < copied) {
+ if (flags & MSG_PEEK)
+ atomic_dec(&skb->users);
+ else
+ skb_queue_head(&sk->sk_receive_queue, skb);
+ return -ENOSPC;
+ }
+ memcpy(skb_push(skb, MISDN_HEADER_LEN), mISDN_HEAD_P(skb),
+ MISDN_HEADER_LEN);
+
+ err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
+
+ mISDN_sock_cmsg(sk, msg, skb);
+
+ skb_free_datagram(sk, skb);
+
+ return err ? : copied;
+}
+
+static int
+mISDN_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *msg, size_t len)
+{
+ struct sock *sk = sock->sk;
+ struct sk_buff *skb;
+ int err = -ENOMEM;
+ struct sockaddr_mISDN *maddr;
+
+ if (*debug & DEBUG_SOCKET)
+ printk(KERN_DEBUG "%s: len %d flags %x ch %d proto %x\n",
+ __func__, (int)len, msg->msg_flags, _pms(sk)->ch.nr,
+ sk->sk_protocol);
+
+ if (msg->msg_flags & MSG_OOB)
+ return -EOPNOTSUPP;
+
+ if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_NOSIGNAL|MSG_ERRQUEUE))
+ return -EINVAL;
+
+ if (len < MISDN_HEADER_LEN)
+ return -EINVAL;
+
+ if (sk->sk_state != MISDN_BOUND)
+ return -EBADFD;
+
+ lock_sock(sk);
+
+ skb = _l2_alloc_skb(len, GFP_KERNEL);
+ if (!skb)
+ goto done;
+
+ if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {
+ err = -EFAULT;
+ goto drop;
+ }
+
+ memcpy(mISDN_HEAD_P(skb), skb->data, MISDN_HEADER_LEN);
+ skb_pull(skb, MISDN_HEADER_LEN);
+
+ if (msg->msg_namelen >= sizeof(struct sockaddr_mISDN)) {
+ /* if we have a address, we use it */
+ maddr = (struct sockaddr_mISDN *)msg->msg_name;
+ mISDN_HEAD_ID(skb) = maddr->channel;
+ } else { /* use default for L2 messages */
+ if ((sk->sk_protocol == ISDN_P_LAPD_TE) ||
+ (sk->sk_protocol == ISDN_P_LAPD_NT))
+ mISDN_HEAD_ID(skb) = _pms(sk)->ch.nr;
+ }
+
+ if (*debug & DEBUG_SOCKET)
+ printk(KERN_DEBUG "%s: ID:%x\n",
+ __func__, mISDN_HEAD_ID(skb));
+
+ err = -ENODEV;
+ if (!_pms(sk)->ch.peer ||
+ (err = _pms(sk)->ch.recv(_pms(sk)->ch.peer, skb)))
+ goto drop;
+
+ err = len;
+
+done:
+ release_sock(sk);
+ return err;
+
+drop:
+ kfree_skb(skb);
+ goto done;
+}
+
+static int
+data_sock_release(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+
+ if (*debug & DEBUG_SOCKET)
+ printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk);
+ if (!sk)
+ return 0;
+ switch (sk->sk_protocol) {
+ case ISDN_P_TE_S0:
+ case ISDN_P_NT_S0:
+ case ISDN_P_TE_E1:
+ case ISDN_P_NT_E1:
+ if (sk->sk_state == MISDN_BOUND)
+ delete_channel(&_pms(sk)->ch);
+ else
+ mISDN_sock_unlink(&data_sockets, sk);
+ break;
+ case ISDN_P_LAPD_TE:
+ case ISDN_P_LAPD_NT:
+ case ISDN_P_B_RAW:
+ case ISDN_P_B_HDLC:
+ case ISDN_P_B_X75SLP:
+ case ISDN_P_B_L2DTMF:
+ case ISDN_P_B_L2DSP:
+ case ISDN_P_B_L2DSPHDLC:
+ delete_channel(&_pms(sk)->ch);
+ mISDN_sock_unlink(&data_sockets, sk);
+ break;
+ }
+
+ lock_sock(sk);
+
+ sock_orphan(sk);
+ skb_queue_purge(&sk->sk_receive_queue);
+
+ release_sock(sk);
+ sock_put(sk);
+
+ return 0;
+}
+
+static int
+data_sock_ioctl_bound(struct sock *sk, unsigned int cmd, void __user *p)
+{
+ struct mISDN_ctrl_req cq;
+ int err = -EINVAL, val;
+ struct mISDNchannel *bchan, *next;
+
+ lock_sock(sk);
+ if (!_pms(sk)->dev) {
+ err = -ENODEV;
+ goto done;
+ }
+ switch (cmd) {
+ case IMCTRLREQ:
+ if (copy_from_user(&cq, p, sizeof(cq))) {
+ err = -EFAULT;
+ break;
+ }
+ if ((sk->sk_protocol & ~ISDN_P_B_MASK) == ISDN_P_B_START) {
+ list_for_each_entry_safe(bchan, next,
+ &_pms(sk)->dev->bchannels, list) {
+ if (bchan->nr == cq.channel) {
+ err = bchan->ctrl(bchan,
+ CONTROL_CHANNEL, &cq);
+ break;
+ }
+ }
+ } else
+ err = _pms(sk)->dev->D.ctrl(&_pms(sk)->dev->D,
+ CONTROL_CHANNEL, &cq);
+ if (err)
+ break;
+ if (copy_to_user(p, &cq, sizeof(cq)))
+ err = -EFAULT;
+ break;
+ case IMCLEAR_L2:
+ if (sk->sk_protocol != ISDN_P_LAPD_NT) {
+ err = -EINVAL;
+ break;
+ }
+ if (get_user(val, (int __user *)p)) {
+ err = -EFAULT;
+ break;
+ }
+ err = _pms(sk)->dev->teimgr->ctrl(_pms(sk)->dev->teimgr,
+ CONTROL_CHANNEL, &val);
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+done:
+ release_sock(sk);
+ return err;
+}
+
+static int
+data_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ int err = 0, id;
+ struct sock *sk = sock->sk;
+ struct mISDNdevice *dev;
+ struct mISDNversion ver;
+
+ switch (cmd) {
+ case IMGETVERSION:
+ ver.major = MISDN_MAJOR_VERSION;
+ ver.minor = MISDN_MINOR_VERSION;
+ ver.release = MISDN_RELEASE;
+ if (copy_to_user((void __user *)arg, &ver, sizeof(ver)))
+ err = -EFAULT;
+ break;
+ case IMGETCOUNT:
+ id = get_mdevice_count();
+ if (put_user(id, (int __user *)arg))
+ err = -EFAULT;
+ break;
+ case IMGETDEVINFO:
+ if (get_user(id, (int __user *)arg)) {
+ err = -EFAULT;
+ break;
+ }
+ dev = get_mdevice(id);
+ if (dev) {
+ struct mISDN_devinfo di;
+
+ di.id = dev->id;
+ di.Dprotocols = dev->Dprotocols;
+ di.Bprotocols = dev->Bprotocols | get_all_Bprotocols();
+ di.protocol = dev->D.protocol;
+ memcpy(di.channelmap, dev->channelmap,
+ sizeof(di.channelmap));
+ di.nrbchan = dev->nrbchan;
+ strcpy(di.name, dev->name);
+ if (copy_to_user((void __user *)arg, &di, sizeof(di)))
+ err = -EFAULT;
+ } else
+ err = -ENODEV;
+ break;
+ default:
+ if (sk->sk_state == MISDN_BOUND)
+ err = data_sock_ioctl_bound(sk, cmd,
+ (void __user *)arg);
+ else
+ err = -ENOTCONN;
+ }
+ return err;
+}
+
+static int data_sock_setsockopt(struct socket *sock, int level, int optname,
+ char __user *optval, int len)
+{
+ struct sock *sk = sock->sk;
+ int err = 0, opt = 0;
+
+ if (*debug & DEBUG_SOCKET)
+ printk(KERN_DEBUG "%s(%p, %d, %x, %p, %d)\n", __func__, sock,
+ level, optname, optval, len);
+
+ lock_sock(sk);
+
+ switch (optname) {
+ case MISDN_TIME_STAMP:
+ if (get_user(opt, (int __user *)optval)) {
+ err = -EFAULT;
+ break;
+ }
+
+ if (opt)
+ _pms(sk)->cmask |= MISDN_TIME_STAMP;
+ else
+ _pms(sk)->cmask &= ~MISDN_TIME_STAMP;
+ break;
+ default:
+ err = -ENOPROTOOPT;
+ break;
+ }
+ release_sock(sk);
+ return err;
+}
+
+static int data_sock_getsockopt(struct socket *sock, int level, int optname,
+ char __user *optval, int __user *optlen)
+{
+ struct sock *sk = sock->sk;
+ int len, opt;
+
+ if (get_user(len, optlen))
+ return -EFAULT;
+
+ switch (optname) {
+ case MISDN_TIME_STAMP:
+ if (_pms(sk)->cmask & MISDN_TIME_STAMP)
+ opt = 1;
+ else
+ opt = 0;
+
+ if (put_user(opt, optval))
+ return -EFAULT;
+ break;
+ default:
+ return -ENOPROTOOPT;
+ }
+
+ return 0;
+}
+
+static int
+data_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
+{
+ struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr;
+ struct sock *sk = sock->sk;
+ int err = 0;
+
+ if (*debug & DEBUG_SOCKET)
+ printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk);
+ if (addr_len != sizeof(struct sockaddr_mISDN))
+ return -EINVAL;
+ if (!maddr || maddr->family != AF_ISDN)
+ return -EINVAL;
+
+ lock_sock(sk);
+
+ if (_pms(sk)->dev) {
+ err = -EALREADY;
+ goto done;
+ }
+ _pms(sk)->dev = get_mdevice(maddr->dev);
+ if (!_pms(sk)->dev) {
+ err = -ENODEV;
+ goto done;
+ }
+ _pms(sk)->ch.send = mISDN_send;
+ _pms(sk)->ch.ctrl = mISDN_ctrl;
+
+ switch (sk->sk_protocol) {
+ case ISDN_P_TE_S0:
+ case ISDN_P_NT_S0:
+ case ISDN_P_TE_E1:
+ case ISDN_P_NT_E1:
+ mISDN_sock_unlink(&data_sockets, sk);
+ err = connect_layer1(_pms(sk)->dev, &_pms(sk)->ch,
+ sk->sk_protocol, maddr);
+ if (err)
+ mISDN_sock_link(&data_sockets, sk);
+ break;
+ case ISDN_P_LAPD_TE:
+ case ISDN_P_LAPD_NT:
+ err = create_l2entity(_pms(sk)->dev, &_pms(sk)->ch,
+ sk->sk_protocol, maddr);
+ break;
+ case ISDN_P_B_RAW:
+ case ISDN_P_B_HDLC:
+ case ISDN_P_B_X75SLP:
+ case ISDN_P_B_L2DTMF:
+ case ISDN_P_B_L2DSP:
+ case ISDN_P_B_L2DSPHDLC:
+ err = connect_Bstack(_pms(sk)->dev, &_pms(sk)->ch,
+ sk->sk_protocol, maddr);
+ break;
+ default:
+ err = -EPROTONOSUPPORT;
+ }
+ if (err)
+ goto done;
+ sk->sk_state = MISDN_BOUND;
+ _pms(sk)->ch.protocol = sk->sk_protocol;
+
+done:
+ release_sock(sk);
+ return err;
+}
+
+static int
+data_sock_getname(struct socket *sock, struct sockaddr *addr,
+ int *addr_len, int peer)
+{
+ struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr;
+ struct sock *sk = sock->sk;
+
+ if (!_pms(sk)->dev)
+ return -EBADFD;
+
+ lock_sock(sk);
+
+ *addr_len = sizeof(*maddr);
+ maddr->dev = _pms(sk)->dev->id;
+ maddr->channel = _pms(sk)->ch.nr;
+ maddr->sapi = _pms(sk)->ch.addr & 0xff;
+ maddr->tei = (_pms(sk)->ch.addr >> 8) & 0xff;
+ release_sock(sk);
+ return 0;
+}
+
+static const struct proto_ops data_sock_ops = {
+ .family = PF_ISDN,
+ .owner = THIS_MODULE,
+ .release = data_sock_release,
+ .ioctl = data_sock_ioctl,
+ .bind = data_sock_bind,
+ .getname = data_sock_getname,
+ .sendmsg = mISDN_sock_sendmsg,
+ .recvmsg = mISDN_sock_recvmsg,
+ .poll = datagram_poll,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = data_sock_setsockopt,
+ .getsockopt = data_sock_getsockopt,
+ .connect = sock_no_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .mmap = sock_no_mmap
+};
+
+static int
+data_sock_create(struct net *net, struct socket *sock, int protocol)
+{
+ struct sock *sk;
+
+ if (sock->type != SOCK_DGRAM)
+ return -ESOCKTNOSUPPORT;
+
+ sk = sk_alloc(net, PF_ISDN, GFP_KERNEL, &mISDN_proto);
+ if (!sk)
+ return -ENOMEM;
+
+ sock_init_data(sock, sk);
+
+ sock->ops = &data_sock_ops;
+ sock->state = SS_UNCONNECTED;
+ sock_reset_flag(sk, SOCK_ZAPPED);
+
+ sk->sk_protocol = protocol;
+ sk->sk_state = MISDN_OPEN;
+ mISDN_sock_link(&data_sockets, sk);
+
+ return 0;
+}
+
+static int
+base_sock_release(struct socket *sock)
+{
+ struct sock *sk = sock->sk;
+
+ printk(KERN_DEBUG "%s(%p) sk=%p\n", __func__, sock, sk);
+ if (!sk)
+ return 0;
+
+ mISDN_sock_unlink(&base_sockets, sk);
+ sock_orphan(sk);
+ sock_put(sk);
+
+ return 0;
+}
+
+static int
+base_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+ int err = 0, id;
+ struct mISDNdevice *dev;
+ struct mISDNversion ver;
+
+ switch (cmd) {
+ case IMGETVERSION:
+ ver.major = MISDN_MAJOR_VERSION;
+ ver.minor = MISDN_MINOR_VERSION;
+ ver.release = MISDN_RELEASE;
+ if (copy_to_user((void __user *)arg, &ver, sizeof(ver)))
+ err = -EFAULT;
+ break;
+ case IMGETCOUNT:
+ id = get_mdevice_count();
+ if (put_user(id, (int __user *)arg))
+ err = -EFAULT;
+ break;
+ case IMGETDEVINFO:
+ if (get_user(id, (int __user *)arg)) {
+ err = -EFAULT;
+ break;
+ }
+ dev = get_mdevice(id);
+ if (dev) {
+ struct mISDN_devinfo di;
+
+ di.id = dev->id;
+ di.Dprotocols = dev->Dprotocols;
+ di.Bprotocols = dev->Bprotocols | get_all_Bprotocols();
+ di.protocol = dev->D.protocol;
+ memcpy(di.channelmap, dev->channelmap,
+ sizeof(di.channelmap));
+ di.nrbchan = dev->nrbchan;
+ strcpy(di.name, dev->name);
+ if (copy_to_user((void __user *)arg, &di, sizeof(di)))
+ err = -EFAULT;
+ } else
+ err = -ENODEV;
+ break;
+ default:
+ err = -EINVAL;
+ }
+ return err;
+}
+
+static int
+base_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
+{
+ struct sockaddr_mISDN *maddr = (struct sockaddr_mISDN *) addr;
+ struct sock *sk = sock->sk;
+ int err = 0;
+
+ if (!maddr || maddr->family != AF_ISDN)
+ return -EINVAL;
+
+ lock_sock(sk);
+
+ if (_pms(sk)->dev) {
+ err = -EALREADY;
+ goto done;
+ }
+
+ _pms(sk)->dev = get_mdevice(maddr->dev);
+ if (!_pms(sk)->dev) {
+ err = -ENODEV;
+ goto done;
+ }
+ sk->sk_state = MISDN_BOUND;
+
+done:
+ release_sock(sk);
+ return err;
+}
+
+static const struct proto_ops base_sock_ops = {
+ .family = PF_ISDN,
+ .owner = THIS_MODULE,
+ .release = base_sock_release,
+ .ioctl = base_sock_ioctl,
+ .bind = base_sock_bind,
+ .getname = sock_no_getname,
+ .sendmsg = sock_no_sendmsg,
+ .recvmsg = sock_no_recvmsg,
+ .poll = sock_no_poll,
+ .listen = sock_no_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = sock_no_setsockopt,
+ .getsockopt = sock_no_getsockopt,
+ .connect = sock_no_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = sock_no_accept,
+ .mmap = sock_no_mmap
+};
+
+
+static int
+base_sock_create(struct net *net, struct socket *sock, int protocol)
+{
+ struct sock *sk;
+
+ if (sock->type != SOCK_RAW)
+ return -ESOCKTNOSUPPORT;
+
+ sk = sk_alloc(net, PF_ISDN, GFP_KERNEL, &mISDN_proto);
+ if (!sk)
+ return -ENOMEM;
+
+ sock_init_data(sock, sk);
+ sock->ops = &base_sock_ops;
+ sock->state = SS_UNCONNECTED;
+ sock_reset_flag(sk, SOCK_ZAPPED);
+ sk->sk_protocol = protocol;
+ sk->sk_state = MISDN_OPEN;
+ mISDN_sock_link(&base_sockets, sk);
+
+ return 0;
+}
+
+static int
+mISDN_sock_create(struct net *net, struct socket *sock, int proto)
+{
+ int err = -EPROTONOSUPPORT;
+
+ switch (proto) {
+ case ISDN_P_BASE:
+ err = base_sock_create(net, sock, proto);
+ break;
+ case ISDN_P_TE_S0:
+ case ISDN_P_NT_S0:
+ case ISDN_P_TE_E1:
+ case ISDN_P_NT_E1:
+ case ISDN_P_LAPD_TE:
+ case ISDN_P_LAPD_NT:
+ case ISDN_P_B_RAW:
+ case ISDN_P_B_HDLC:
+ case ISDN_P_B_X75SLP:
+ case ISDN_P_B_L2DTMF:
+ case ISDN_P_B_L2DSP:
+ case ISDN_P_B_L2DSPHDLC:
+ err = data_sock_create(net, sock, proto);
+ break;
+ default:
+ return err;
+ }
+
+ return err;
+}
+
+static struct
+net_proto_family mISDN_sock_family_ops = {
+ .owner = THIS_MODULE,
+ .family = PF_ISDN,
+ .create = mISDN_sock_create,
+};
+
+int
+misdn_sock_init(u_int *deb)
+{
+ int err;
+
+ debug = deb;
+ err = sock_register(&mISDN_sock_family_ops);
+ if (err)
+ printk(KERN_ERR "%s: error(%d)\n", __func__, err);
+ return err;
+}
+
+void
+misdn_sock_cleanup(void)
+{
+ sock_unregister(PF_ISDN);
+}
+
diff --git a/drivers/isdn/mISDN/stack.c b/drivers/isdn/mISDN/stack.c
new file mode 100644
index 0000000..54cfddc
--- /dev/null
+++ b/drivers/isdn/mISDN/stack.c
@@ -0,0 +1,674 @@
+/*
+ *
+ * Author Karsten Keil <kkeil@novell.com>
+ *
+ * Copyright 2008 by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/mISDNif.h>
+#include <linux/kthread.h>
+#include "core.h"
+
+static u_int *debug;
+
+static inline void
+_queue_message(struct mISDNstack *st, struct sk_buff *skb)
+{
+ struct mISDNhead *hh = mISDN_HEAD_P(skb);
+
+ if (*debug & DEBUG_QUEUE_FUNC)
+ printk(KERN_DEBUG "%s prim(%x) id(%x) %p\n",
+ __func__, hh->prim, hh->id, skb);
+ skb_queue_tail(&st->msgq, skb);
+ if (likely(!test_bit(mISDN_STACK_STOPPED, &st->status))) {
+ test_and_set_bit(mISDN_STACK_WORK, &st->status);
+ wake_up_interruptible(&st->workq);
+ }
+}
+
+int
+mISDN_queue_message(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+ _queue_message(ch->st, skb);
+ return 0;
+}
+
+static struct mISDNchannel *
+get_channel4id(struct mISDNstack *st, u_int id)
+{
+ struct mISDNchannel *ch;
+
+ mutex_lock(&st->lmutex);
+ list_for_each_entry(ch, &st->layer2, list) {
+ if (id == ch->nr)
+ goto unlock;
+ }
+ ch = NULL;
+unlock:
+ mutex_unlock(&st->lmutex);
+ return ch;
+}
+
+static void
+send_socklist(struct mISDN_sock_list *sl, struct sk_buff *skb)
+{
+ struct hlist_node *node;
+ struct sock *sk;
+ struct sk_buff *cskb = NULL;
+
+ read_lock(&sl->lock);
+ sk_for_each(sk, node, &sl->head) {
+ if (sk->sk_state != MISDN_BOUND)
+ continue;
+ if (!cskb)
+ cskb = skb_copy(skb, GFP_KERNEL);
+ if (!cskb) {
+ printk(KERN_WARNING "%s no skb\n", __func__);
+ break;
+ }
+ if (!sock_queue_rcv_skb(sk, cskb))
+ cskb = NULL;
+ }
+ read_unlock(&sl->lock);
+ if (cskb)
+ dev_kfree_skb(cskb);
+}
+
+static void
+send_layer2(struct mISDNstack *st, struct sk_buff *skb)
+{
+ struct sk_buff *cskb;
+ struct mISDNhead *hh = mISDN_HEAD_P(skb);
+ struct mISDNchannel *ch;
+ int ret;
+
+ if (!st)
+ return;
+ mutex_lock(&st->lmutex);
+ if ((hh->id & MISDN_ID_ADDR_MASK) == MISDN_ID_ANY) { /* L2 for all */
+ list_for_each_entry(ch, &st->layer2, list) {
+ if (list_is_last(&ch->list, &st->layer2)) {
+ cskb = skb;
+ skb = NULL;
+ } else {
+ cskb = skb_copy(skb, GFP_KERNEL);
+ }
+ if (cskb) {
+ ret = ch->send(ch, cskb);
+ if (ret) {
+ if (*debug & DEBUG_SEND_ERR)
+ printk(KERN_DEBUG
+ "%s ch%d prim(%x) addr(%x)"
+ " err %d\n",
+ __func__, ch->nr,
+ hh->prim, ch->addr, ret);
+ dev_kfree_skb(cskb);
+ }
+ } else {
+ printk(KERN_WARNING "%s ch%d addr %x no mem\n",
+ __func__, ch->nr, ch->addr);
+ goto out;
+ }
+ }
+ } else {
+ list_for_each_entry(ch, &st->layer2, list) {
+ if ((hh->id & MISDN_ID_ADDR_MASK) == ch->addr) {
+ ret = ch->send(ch, skb);
+ if (!ret)
+ skb = NULL;
+ goto out;
+ }
+ }
+ ret = st->dev->teimgr->ctrl(st->dev->teimgr, CHECK_DATA, skb);
+ if (!ret)
+ skb = NULL;
+ else if (*debug & DEBUG_SEND_ERR)
+ printk(KERN_DEBUG
+ "%s ch%d mgr prim(%x) addr(%x) err %d\n",
+ __func__, ch->nr, hh->prim, ch->addr, ret);
+ }
+out:
+ mutex_unlock(&st->lmutex);
+ if (skb)
+ dev_kfree_skb(skb);
+}
+
+static inline int
+send_msg_to_layer(struct mISDNstack *st, struct sk_buff *skb)
+{
+ struct mISDNhead *hh = mISDN_HEAD_P(skb);
+ struct mISDNchannel *ch;
+ int lm;
+
+ lm = hh->prim & MISDN_LAYERMASK;
+ if (*debug & DEBUG_QUEUE_FUNC)
+ printk(KERN_DEBUG "%s prim(%x) id(%x) %p\n",
+ __func__, hh->prim, hh->id, skb);
+ if (lm == 0x1) {
+ if (!hlist_empty(&st->l1sock.head)) {
+ __net_timestamp(skb);
+ send_socklist(&st->l1sock, skb);
+ }
+ return st->layer1->send(st->layer1, skb);
+ } else if (lm == 0x2) {
+ if (!hlist_empty(&st->l1sock.head))
+ send_socklist(&st->l1sock, skb);
+ send_layer2(st, skb);
+ return 0;
+ } else if (lm == 0x4) {
+ ch = get_channel4id(st, hh->id);
+ if (ch)
+ return ch->send(ch, skb);
+ else
+ printk(KERN_WARNING
+ "%s: dev(%s) prim(%x) id(%x) no channel\n",
+ __func__, st->dev->name, hh->prim, hh->id);
+ } else if (lm == 0x8) {
+ WARN_ON(lm == 0x8);
+ ch = get_channel4id(st, hh->id);
+ if (ch)
+ return ch->send(ch, skb);
+ else
+ printk(KERN_WARNING
+ "%s: dev(%s) prim(%x) id(%x) no channel\n",
+ __func__, st->dev->name, hh->prim, hh->id);
+ } else {
+ /* broadcast not handled yet */
+ printk(KERN_WARNING "%s: dev(%s) prim %x not delivered\n",
+ __func__, st->dev->name, hh->prim);
+ }
+ return -ESRCH;
+}
+
+static void
+do_clear_stack(struct mISDNstack *st)
+{
+}
+
+static int
+mISDNStackd(void *data)
+{
+ struct mISDNstack *st = data;
+ int err = 0;
+
+#ifdef CONFIG_SMP
+ lock_kernel();
+#endif
+ sigfillset(&current->blocked);
+#ifdef CONFIG_SMP
+ unlock_kernel();
+#endif
+ if (*debug & DEBUG_MSG_THREAD)
+ printk(KERN_DEBUG "mISDNStackd %s started\n", st->dev->name);
+
+ if (st->notify != NULL) {
+ complete(st->notify);
+ st->notify = NULL;
+ }
+
+ for (;;) {
+ struct sk_buff *skb;
+
+ if (unlikely(test_bit(mISDN_STACK_STOPPED, &st->status))) {
+ test_and_clear_bit(mISDN_STACK_WORK, &st->status);
+ test_and_clear_bit(mISDN_STACK_RUNNING, &st->status);
+ } else
+ test_and_set_bit(mISDN_STACK_RUNNING, &st->status);
+ while (test_bit(mISDN_STACK_WORK, &st->status)) {
+ skb = skb_dequeue(&st->msgq);
+ if (!skb) {
+ test_and_clear_bit(mISDN_STACK_WORK,
+ &st->status);
+ /* test if a race happens */
+ skb = skb_dequeue(&st->msgq);
+ if (!skb)
+ continue;
+ test_and_set_bit(mISDN_STACK_WORK,
+ &st->status);
+ }
+#ifdef MISDN_MSG_STATS
+ st->msg_cnt++;
+#endif
+ err = send_msg_to_layer(st, skb);
+ if (unlikely(err)) {
+ if (*debug & DEBUG_SEND_ERR)
+ printk(KERN_DEBUG
+ "%s: %s prim(%x) id(%x) "
+ "send call(%d)\n",
+ __func__, st->dev->name,
+ mISDN_HEAD_PRIM(skb),
+ mISDN_HEAD_ID(skb), err);
+ dev_kfree_skb(skb);
+ continue;
+ }
+ if (unlikely(test_bit(mISDN_STACK_STOPPED,
+ &st->status))) {
+ test_and_clear_bit(mISDN_STACK_WORK,
+ &st->status);
+ test_and_clear_bit(mISDN_STACK_RUNNING,
+ &st->status);
+ break;
+ }
+ }
+ if (test_bit(mISDN_STACK_CLEARING, &st->status)) {
+ test_and_set_bit(mISDN_STACK_STOPPED, &st->status);
+ test_and_clear_bit(mISDN_STACK_RUNNING, &st->status);
+ do_clear_stack(st);
+ test_and_clear_bit(mISDN_STACK_CLEARING, &st->status);
+ test_and_set_bit(mISDN_STACK_RESTART, &st->status);
+ }
+ if (test_and_clear_bit(mISDN_STACK_RESTART, &st->status)) {
+ test_and_clear_bit(mISDN_STACK_STOPPED, &st->status);
+ test_and_set_bit(mISDN_STACK_RUNNING, &st->status);
+ if (!skb_queue_empty(&st->msgq))
+ test_and_set_bit(mISDN_STACK_WORK,
+ &st->status);
+ }
+ if (test_bit(mISDN_STACK_ABORT, &st->status))
+ break;
+ if (st->notify != NULL) {
+ complete(st->notify);
+ st->notify = NULL;
+ }
+#ifdef MISDN_MSG_STATS
+ st->sleep_cnt++;
+#endif
+ test_and_clear_bit(mISDN_STACK_ACTIVE, &st->status);
+ wait_event_interruptible(st->workq, (st->status &
+ mISDN_STACK_ACTION_MASK));
+ if (*debug & DEBUG_MSG_THREAD)
+ printk(KERN_DEBUG "%s: %s wake status %08lx\n",
+ __func__, st->dev->name, st->status);
+ test_and_set_bit(mISDN_STACK_ACTIVE, &st->status);
+
+ test_and_clear_bit(mISDN_STACK_WAKEUP, &st->status);
+
+ if (test_bit(mISDN_STACK_STOPPED, &st->status)) {
+ test_and_clear_bit(mISDN_STACK_RUNNING, &st->status);
+#ifdef MISDN_MSG_STATS
+ st->stopped_cnt++;
+#endif
+ }
+ }
+#ifdef MISDN_MSG_STATS
+ printk(KERN_DEBUG "mISDNStackd daemon for %s proceed %d "
+ "msg %d sleep %d stopped\n",
+ st->dev->name, st->msg_cnt, st->sleep_cnt, st->stopped_cnt);
+ printk(KERN_DEBUG
+ "mISDNStackd daemon for %s utime(%ld) stime(%ld)\n",
+ st->dev->name, st->thread->utime, st->thread->stime);
+ printk(KERN_DEBUG
+ "mISDNStackd daemon for %s nvcsw(%ld) nivcsw(%ld)\n",
+ st->dev->name, st->thread->nvcsw, st->thread->nivcsw);
+ printk(KERN_DEBUG "mISDNStackd daemon for %s killed now\n",
+ st->dev->name);
+#endif
+ test_and_set_bit(mISDN_STACK_KILLED, &st->status);
+ test_and_clear_bit(mISDN_STACK_RUNNING, &st->status);
+ test_and_clear_bit(mISDN_STACK_ACTIVE, &st->status);
+ test_and_clear_bit(mISDN_STACK_ABORT, &st->status);
+ skb_queue_purge(&st->msgq);
+ st->thread = NULL;
+ if (st->notify != NULL) {
+ complete(st->notify);
+ st->notify = NULL;
+ }
+ return 0;
+}
+
+static int
+l1_receive(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+ if (!ch->st)
+ return -ENODEV;
+ __net_timestamp(skb);
+ _queue_message(ch->st, skb);
+ return 0;
+}
+
+void
+set_channel_address(struct mISDNchannel *ch, u_int sapi, u_int tei)
+{
+ ch->addr = sapi | (tei << 8);
+}
+
+void
+__add_layer2(struct mISDNchannel *ch, struct mISDNstack *st)
+{
+ list_add_tail(&ch->list, &st->layer2);
+}
+
+void
+add_layer2(struct mISDNchannel *ch, struct mISDNstack *st)
+{
+ mutex_lock(&st->lmutex);
+ __add_layer2(ch, st);
+ mutex_unlock(&st->lmutex);
+}
+
+static int
+st_own_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+ if (!ch->st || ch->st->layer1)
+ return -EINVAL;
+ return ch->st->layer1->ctrl(ch->st->layer1, cmd, arg);
+}
+
+int
+create_stack(struct mISDNdevice *dev)
+{
+ struct mISDNstack *newst;
+ int err;
+ DECLARE_COMPLETION_ONSTACK(done);
+
+ newst = kzalloc(sizeof(struct mISDNstack), GFP_KERNEL);
+ if (!newst) {
+ printk(KERN_ERR "kmalloc mISDN_stack failed\n");
+ return -ENOMEM;
+ }
+ newst->dev = dev;
+ INIT_LIST_HEAD(&newst->layer2);
+ INIT_HLIST_HEAD(&newst->l1sock.head);
+ rwlock_init(&newst->l1sock.lock);
+ init_waitqueue_head(&newst->workq);
+ skb_queue_head_init(&newst->msgq);
+ mutex_init(&newst->lmutex);
+ dev->D.st = newst;
+ err = create_teimanager(dev);
+ if (err) {
+ printk(KERN_ERR "kmalloc teimanager failed\n");
+ kfree(newst);
+ return err;
+ }
+ dev->teimgr->peer = &newst->own;
+ dev->teimgr->recv = mISDN_queue_message;
+ dev->teimgr->st = newst;
+ newst->layer1 = &dev->D;
+ dev->D.recv = l1_receive;
+ dev->D.peer = &newst->own;
+ newst->own.st = newst;
+ newst->own.ctrl = st_own_ctrl;
+ newst->own.send = mISDN_queue_message;
+ newst->own.recv = mISDN_queue_message;
+ if (*debug & DEBUG_CORE_FUNC)
+ printk(KERN_DEBUG "%s: st(%s)\n", __func__, newst->dev->name);
+ newst->notify = &done;
+ newst->thread = kthread_run(mISDNStackd, (void *)newst, "mISDN_%s",
+ newst->dev->name);
+ if (IS_ERR(newst->thread)) {
+ err = PTR_ERR(newst->thread);
+ printk(KERN_ERR
+ "mISDN:cannot create kernel thread for %s (%d)\n",
+ newst->dev->name, err);
+ delete_teimanager(dev->teimgr);
+ kfree(newst);
+ } else
+ wait_for_completion(&done);
+ return err;
+}
+
+int
+connect_layer1(struct mISDNdevice *dev, struct mISDNchannel *ch,
+ u_int protocol, struct sockaddr_mISDN *adr)
+{
+ struct mISDN_sock *msk = container_of(ch, struct mISDN_sock, ch);
+ struct channel_req rq;
+ int err;
+
+
+ if (*debug & DEBUG_CORE_FUNC)
+ printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n",
+ __func__, dev->name, protocol, adr->dev, adr->channel,
+ adr->sapi, adr->tei);
+ switch (protocol) {
+ case ISDN_P_NT_S0:
+ case ISDN_P_NT_E1:
+ case ISDN_P_TE_S0:
+ case ISDN_P_TE_E1:
+#ifdef PROTOCOL_CHECK
+ /* this should be enhanced */
+ if (!list_empty(&dev->D.st->layer2)
+ && dev->D.protocol != protocol)
+ return -EBUSY;
+ if (!hlist_empty(&dev->D.st->l1sock.head)
+ && dev->D.protocol != protocol)
+ return -EBUSY;
+#endif
+ ch->recv = mISDN_queue_message;
+ ch->peer = &dev->D.st->own;
+ ch->st = dev->D.st;
+ rq.protocol = protocol;
+ rq.adr.channel = 0;
+ err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq);
+ printk(KERN_DEBUG "%s: ret 1 %d\n", __func__, err);
+ if (err)
+ return err;
+ write_lock_bh(&dev->D.st->l1sock.lock);
+ sk_add_node(&msk->sk, &dev->D.st->l1sock.head);
+ write_unlock_bh(&dev->D.st->l1sock.lock);
+ break;
+ default:
+ return -ENOPROTOOPT;
+ }
+ return 0;
+}
+
+int
+connect_Bstack(struct mISDNdevice *dev, struct mISDNchannel *ch,
+ u_int protocol, struct sockaddr_mISDN *adr)
+{
+ struct channel_req rq, rq2;
+ int pmask, err;
+ struct Bprotocol *bp;
+
+ if (*debug & DEBUG_CORE_FUNC)
+ printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n",
+ __func__, dev->name, protocol,
+ adr->dev, adr->channel, adr->sapi,
+ adr->tei);
+ ch->st = dev->D.st;
+ pmask = 1 << (protocol & ISDN_P_B_MASK);
+ if (pmask & dev->Bprotocols) {
+ rq.protocol = protocol;
+ rq.adr = *adr;
+ err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq);
+ if (err)
+ return err;
+ ch->recv = rq.ch->send;
+ ch->peer = rq.ch;
+ rq.ch->recv = ch->send;
+ rq.ch->peer = ch;
+ rq.ch->st = dev->D.st;
+ } else {
+ bp = get_Bprotocol4mask(pmask);
+ if (!bp)
+ return -ENOPROTOOPT;
+ rq2.protocol = protocol;
+ rq2.adr = *adr;
+ rq2.ch = ch;
+ err = bp->create(&rq2);
+ if (err)
+ return err;
+ ch->recv = rq2.ch->send;
+ ch->peer = rq2.ch;
+ rq2.ch->st = dev->D.st;
+ rq.protocol = rq2.protocol;
+ rq.adr = *adr;
+ err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq);
+ if (err) {
+ rq2.ch->ctrl(rq2.ch, CLOSE_CHANNEL, NULL);
+ return err;
+ }
+ rq2.ch->recv = rq.ch->send;
+ rq2.ch->peer = rq.ch;
+ rq.ch->recv = rq2.ch->send;
+ rq.ch->peer = rq2.ch;
+ rq.ch->st = dev->D.st;
+ }
+ ch->protocol = protocol;
+ ch->nr = rq.ch->nr;
+ return 0;
+}
+
+int
+create_l2entity(struct mISDNdevice *dev, struct mISDNchannel *ch,
+ u_int protocol, struct sockaddr_mISDN *adr)
+{
+ struct channel_req rq;
+ int err;
+
+ if (*debug & DEBUG_CORE_FUNC)
+ printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n",
+ __func__, dev->name, protocol,
+ adr->dev, adr->channel, adr->sapi,
+ adr->tei);
+ rq.protocol = ISDN_P_TE_S0;
+ if (dev->Dprotocols & (1 << ISDN_P_TE_E1))
+ rq.protocol = ISDN_P_TE_E1;
+ switch (protocol) {
+ case ISDN_P_LAPD_NT:
+ rq.protocol = ISDN_P_NT_S0;
+ if (dev->Dprotocols & (1 << ISDN_P_NT_E1))
+ rq.protocol = ISDN_P_NT_E1;
+ case ISDN_P_LAPD_TE:
+#ifdef PROTOCOL_CHECK
+ /* this should be enhanced */
+ if (!list_empty(&dev->D.st->layer2)
+ && dev->D.protocol != protocol)
+ return -EBUSY;
+ if (!hlist_empty(&dev->D.st->l1sock.head)
+ && dev->D.protocol != protocol)
+ return -EBUSY;
+#endif
+ ch->recv = mISDN_queue_message;
+ ch->peer = &dev->D.st->own;
+ ch->st = dev->D.st;
+ rq.adr.channel = 0;
+ err = dev->D.ctrl(&dev->D, OPEN_CHANNEL, &rq);
+ printk(KERN_DEBUG "%s: ret 1 %d\n", __func__, err);
+ if (err)
+ break;
+ rq.protocol = protocol;
+ rq.adr = *adr;
+ rq.ch = ch;
+ err = dev->teimgr->ctrl(dev->teimgr, OPEN_CHANNEL, &rq);
+ printk(KERN_DEBUG "%s: ret 2 %d\n", __func__, err);
+ if (!err) {
+ if ((protocol == ISDN_P_LAPD_NT) && !rq.ch)
+ break;
+ add_layer2(rq.ch, dev->D.st);
+ rq.ch->recv = mISDN_queue_message;
+ rq.ch->peer = &dev->D.st->own;
+ rq.ch->ctrl(rq.ch, OPEN_CHANNEL, NULL); /* can't fail */
+ }
+ break;
+ default:
+ err = -EPROTONOSUPPORT;
+ }
+ return err;
+}
+
+void
+delete_channel(struct mISDNchannel *ch)
+{
+ struct mISDN_sock *msk = container_of(ch, struct mISDN_sock, ch);
+ struct mISDNchannel *pch;
+
+ if (!ch->st) {
+ printk(KERN_WARNING "%s: no stack\n", __func__);
+ return;
+ }
+ if (*debug & DEBUG_CORE_FUNC)
+ printk(KERN_DEBUG "%s: st(%s) protocol(%x)\n", __func__,
+ ch->st->dev->name, ch->protocol);
+ if (ch->protocol >= ISDN_P_B_START) {
+ if (ch->peer) {
+ ch->peer->ctrl(ch->peer, CLOSE_CHANNEL, NULL);
+ ch->peer = NULL;
+ }
+ return;
+ }
+ switch (ch->protocol) {
+ case ISDN_P_NT_S0:
+ case ISDN_P_TE_S0:
+ case ISDN_P_NT_E1:
+ case ISDN_P_TE_E1:
+ write_lock_bh(&ch->st->l1sock.lock);
+ sk_del_node_init(&msk->sk);
+ write_unlock_bh(&ch->st->l1sock.lock);
+ ch->st->dev->D.ctrl(&ch->st->dev->D, CLOSE_CHANNEL, NULL);
+ break;
+ case ISDN_P_LAPD_TE:
+ pch = get_channel4id(ch->st, ch->nr);
+ if (pch) {
+ mutex_lock(&ch->st->lmutex);
+ list_del(&pch->list);
+ mutex_unlock(&ch->st->lmutex);
+ pch->ctrl(pch, CLOSE_CHANNEL, NULL);
+ pch = ch->st->dev->teimgr;
+ pch->ctrl(pch, CLOSE_CHANNEL, NULL);
+ } else
+ printk(KERN_WARNING "%s: no l2 channel\n",
+ __func__);
+ break;
+ case ISDN_P_LAPD_NT:
+ pch = ch->st->dev->teimgr;
+ if (pch) {
+ pch->ctrl(pch, CLOSE_CHANNEL, NULL);
+ } else
+ printk(KERN_WARNING "%s: no l2 channel\n",
+ __func__);
+ break;
+ default:
+ break;
+ }
+ return;
+}
+
+void
+delete_stack(struct mISDNdevice *dev)
+{
+ struct mISDNstack *st = dev->D.st;
+ DECLARE_COMPLETION_ONSTACK(done);
+
+ if (*debug & DEBUG_CORE_FUNC)
+ printk(KERN_DEBUG "%s: st(%s)\n", __func__,
+ st->dev->name);
+ if (dev->teimgr)
+ delete_teimanager(dev->teimgr);
+ if (st->thread) {
+ if (st->notify) {
+ printk(KERN_WARNING "%s: notifier in use\n",
+ __func__);
+ complete(st->notify);
+ }
+ st->notify = &done;
+ test_and_set_bit(mISDN_STACK_ABORT, &st->status);
+ test_and_set_bit(mISDN_STACK_WAKEUP, &st->status);
+ wake_up_interruptible(&st->workq);
+ wait_for_completion(&done);
+ }
+ if (!list_empty(&st->layer2))
+ printk(KERN_WARNING "%s: layer2 list not empty\n",
+ __func__);
+ if (!hlist_empty(&st->l1sock.head))
+ printk(KERN_WARNING "%s: layer1 list not empty\n",
+ __func__);
+ kfree(st);
+}
+
+void
+mISDN_initstack(u_int *dp)
+{
+ debug = dp;
+}
diff --git a/drivers/isdn/mISDN/tei.c b/drivers/isdn/mISDN/tei.c
new file mode 100644
index 0000000..6fbae42
--- /dev/null
+++ b/drivers/isdn/mISDN/tei.c
@@ -0,0 +1,1340 @@
+/*
+ *
+ * Author Karsten Keil <kkeil@novell.com>
+ *
+ * Copyright 2008 by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+#include "layer2.h"
+#include <linux/random.h>
+#include "core.h"
+
+#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
+
+#define MGR_PH_ACTIVE 16
+#define MGR_PH_NOTREADY 17
+
+#define DATIMER_VAL 10000
+
+static u_int *debug;
+
+static struct Fsm deactfsm = {NULL, 0, 0, NULL, NULL};
+static struct Fsm teifsmu = {NULL, 0, 0, NULL, NULL};
+static struct Fsm teifsmn = {NULL, 0, 0, NULL, NULL};
+
+enum {
+ ST_L1_DEACT,
+ ST_L1_DEACT_PENDING,
+ ST_L1_ACTIV,
+};
+#define DEACT_STATE_COUNT (ST_L1_ACTIV+1)
+
+static char *strDeactState[] =
+{
+ "ST_L1_DEACT",
+ "ST_L1_DEACT_PENDING",
+ "ST_L1_ACTIV",
+};
+
+enum {
+ EV_ACTIVATE,
+ EV_ACTIVATE_IND,
+ EV_DEACTIVATE,
+ EV_DEACTIVATE_IND,
+ EV_UI,
+ EV_DATIMER,
+};
+
+#define DEACT_EVENT_COUNT (EV_DATIMER+1)
+
+static char *strDeactEvent[] =
+{
+ "EV_ACTIVATE",
+ "EV_ACTIVATE_IND",
+ "EV_DEACTIVATE",
+ "EV_DEACTIVATE_IND",
+ "EV_UI",
+ "EV_DATIMER",
+};
+
+static void
+da_debug(struct FsmInst *fi, char *fmt, ...)
+{
+ struct manager *mgr = fi->userdata;
+ va_list va;
+
+ if (!(*debug & DEBUG_L2_TEIFSM))
+ return;
+ va_start(va, fmt);
+ printk(KERN_DEBUG "mgr(%d): ", mgr->ch.st->dev->id);
+ vprintk(fmt, va);
+ printk("\n");
+ va_end(va);
+}
+
+static void
+da_activate(struct FsmInst *fi, int event, void *arg)
+{
+ struct manager *mgr = fi->userdata;
+
+ if (fi->state == ST_L1_DEACT_PENDING)
+ mISDN_FsmDelTimer(&mgr->datimer, 1);
+ mISDN_FsmChangeState(fi, ST_L1_ACTIV);
+}
+
+static void
+da_deactivate_ind(struct FsmInst *fi, int event, void *arg)
+{
+ mISDN_FsmChangeState(fi, ST_L1_DEACT);
+}
+
+static void
+da_deactivate(struct FsmInst *fi, int event, void *arg)
+{
+ struct manager *mgr = fi->userdata;
+ struct layer2 *l2;
+ u_long flags;
+
+ read_lock_irqsave(&mgr->lock, flags);
+ list_for_each_entry(l2, &mgr->layer2, list) {
+ if (l2->l2m.state > ST_L2_4) {
+ /* have still activ TEI */
+ read_unlock_irqrestore(&mgr->lock, flags);
+ return;
+ }
+ }
+ read_unlock_irqrestore(&mgr->lock, flags);
+ /* All TEI are inactiv */
+ mISDN_FsmAddTimer(&mgr->datimer, DATIMER_VAL, EV_DATIMER, NULL, 1);
+ mISDN_FsmChangeState(fi, ST_L1_DEACT_PENDING);
+}
+
+static void
+da_ui(struct FsmInst *fi, int event, void *arg)
+{
+ struct manager *mgr = fi->userdata;
+
+ /* restart da timer */
+ mISDN_FsmDelTimer(&mgr->datimer, 2);
+ mISDN_FsmAddTimer(&mgr->datimer, DATIMER_VAL, EV_DATIMER, NULL, 2);
+
+}
+
+static void
+da_timer(struct FsmInst *fi, int event, void *arg)
+{
+ struct manager *mgr = fi->userdata;
+ struct layer2 *l2;
+ u_long flags;
+
+ /* check again */
+ read_lock_irqsave(&mgr->lock, flags);
+ list_for_each_entry(l2, &mgr->layer2, list) {
+ if (l2->l2m.state > ST_L2_4) {
+ /* have still activ TEI */
+ read_unlock_irqrestore(&mgr->lock, flags);
+ mISDN_FsmChangeState(fi, ST_L1_ACTIV);
+ return;
+ }
+ }
+ read_unlock_irqrestore(&mgr->lock, flags);
+ /* All TEI are inactiv */
+ mISDN_FsmChangeState(fi, ST_L1_DEACT);
+ _queue_data(&mgr->ch, PH_DEACTIVATE_REQ, MISDN_ID_ANY, 0, NULL,
+ GFP_ATOMIC);
+}
+
+static struct FsmNode DeactFnList[] =
+{
+ {ST_L1_DEACT, EV_ACTIVATE_IND, da_activate},
+ {ST_L1_ACTIV, EV_DEACTIVATE_IND, da_deactivate_ind},
+ {ST_L1_ACTIV, EV_DEACTIVATE, da_deactivate},
+ {ST_L1_DEACT_PENDING, EV_ACTIVATE, da_activate},
+ {ST_L1_DEACT_PENDING, EV_UI, da_ui},
+ {ST_L1_DEACT_PENDING, EV_DATIMER, da_timer},
+};
+
+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_CHKRESP,
+ EV_REMOVE,
+ EV_VERIFY,
+ EV_TIMER,
+};
+
+#define TEI_EVENT_COUNT (EV_TIMER+1)
+
+static char *strTeiEvent[] =
+{
+ "EV_IDREQ",
+ "EV_ASSIGN",
+ "EV_ASSIGN_REQ",
+ "EV_DENIED",
+ "EV_CHKREQ",
+ "EV_CHKRESP",
+ "EV_REMOVE",
+ "EV_VERIFY",
+ "EV_TIMER",
+};
+
+static void
+tei_debug(struct FsmInst *fi, char *fmt, ...)
+{
+ struct teimgr *tm = fi->userdata;
+ va_list va;
+
+ if (!(*debug & DEBUG_L2_TEIFSM))
+ return;
+ va_start(va, fmt);
+ printk(KERN_DEBUG "tei(%d): ", tm->l2->tei);
+ vprintk(fmt, va);
+ printk("\n");
+ va_end(va);
+}
+
+
+
+static int
+get_free_id(struct manager *mgr)
+{
+ u64 ids = 0;
+ int i;
+ struct layer2 *l2;
+
+ list_for_each_entry(l2, &mgr->layer2, list) {
+ if (l2->ch.nr > 63) {
+ printk(KERN_WARNING
+ "%s: more as 63 layer2 for one device\n",
+ __func__);
+ return -EBUSY;
+ }
+ test_and_set_bit(l2->ch.nr, (u_long *)&ids);
+ }
+ for (i = 1; i < 64; i++)
+ if (!test_bit(i, (u_long *)&ids))
+ return i;
+ printk(KERN_WARNING "%s: more as 63 layer2 for one device\n",
+ __func__);
+ return -EBUSY;
+}
+
+static int
+get_free_tei(struct manager *mgr)
+{
+ u64 ids = 0;
+ int i;
+ struct layer2 *l2;
+
+ list_for_each_entry(l2, &mgr->layer2, list) {
+ if (l2->ch.nr == 0)
+ continue;
+ if ((l2->ch.addr & 0xff) != 0)
+ continue;
+ i = l2->ch.addr >> 8;
+ if (i < 64)
+ continue;
+ i -= 64;
+
+ test_and_set_bit(i, (u_long *)&ids);
+ }
+ for (i = 0; i < 64; i++)
+ if (!test_bit(i, (u_long *)&ids))
+ return i + 64;
+ printk(KERN_WARNING "%s: more as 63 dynamic tei for one device\n",
+ __func__);
+ return -1;
+}
+
+static void
+teiup_create(struct manager *mgr, u_int prim, int len, void *arg)
+{
+ struct sk_buff *skb;
+ struct mISDNhead *hh;
+ int err;
+
+ skb = mI_alloc_skb(len, GFP_ATOMIC);
+ if (!skb)
+ return;
+ hh = mISDN_HEAD_P(skb);
+ hh->prim = prim;
+ hh->id = (mgr->ch.nr << 16) | mgr->ch.addr;
+ if (len)
+ memcpy(skb_put(skb, len), arg, len);
+ err = mgr->up->send(mgr->up, skb);
+ if (err) {
+ printk(KERN_WARNING "%s: err=%d\n", __func__, err);
+ dev_kfree_skb(skb);
+ }
+}
+
+static u_int
+new_id(struct manager *mgr)
+{
+ u_int id;
+
+ id = mgr->nextid++;
+ if (id == 0x7fff)
+ mgr->nextid = 1;
+ id <<= 16;
+ id |= GROUP_TEI << 8;
+ id |= TEI_SAPI;
+ return id;
+}
+
+static void
+do_send(struct manager *mgr)
+{
+ if (!test_bit(MGR_PH_ACTIVE, &mgr->options))
+ return;
+
+ if (!test_and_set_bit(MGR_PH_NOTREADY, &mgr->options)) {
+ struct sk_buff *skb = skb_dequeue(&mgr->sendq);
+
+ if (!skb) {
+ test_and_clear_bit(MGR_PH_NOTREADY, &mgr->options);
+ return;
+ }
+ mgr->lastid = mISDN_HEAD_ID(skb);
+ mISDN_FsmEvent(&mgr->deact, EV_UI, NULL);
+ if (mgr->ch.recv(mgr->ch.peer, skb)) {
+ dev_kfree_skb(skb);
+ test_and_clear_bit(MGR_PH_NOTREADY, &mgr->options);
+ mgr->lastid = MISDN_ID_NONE;
+ }
+ }
+}
+
+static void
+do_ack(struct manager *mgr, u_int id)
+{
+ if (test_bit(MGR_PH_NOTREADY, &mgr->options)) {
+ if (id == mgr->lastid) {
+ if (test_bit(MGR_PH_ACTIVE, &mgr->options)) {
+ struct sk_buff *skb;
+
+ skb = skb_dequeue(&mgr->sendq);
+ if (skb) {
+ mgr->lastid = mISDN_HEAD_ID(skb);
+ if (!mgr->ch.recv(mgr->ch.peer, skb))
+ return;
+ dev_kfree_skb(skb);
+ }
+ }
+ mgr->lastid = MISDN_ID_NONE;
+ test_and_clear_bit(MGR_PH_NOTREADY, &mgr->options);
+ }
+ }
+}
+
+static void
+mgr_send_down(struct manager *mgr, struct sk_buff *skb)
+{
+ skb_queue_tail(&mgr->sendq, skb);
+ if (!test_bit(MGR_PH_ACTIVE, &mgr->options)) {
+ _queue_data(&mgr->ch, PH_ACTIVATE_REQ, MISDN_ID_ANY, 0,
+ NULL, GFP_KERNEL);
+ } else {
+ do_send(mgr);
+ }
+}
+
+static int
+dl_unit_data(struct manager *mgr, struct sk_buff *skb)
+{
+ if (!test_bit(MGR_OPT_NETWORK, &mgr->options)) /* only net send UI */
+ return -EINVAL;
+ if (!test_bit(MGR_PH_ACTIVE, &mgr->options))
+ _queue_data(&mgr->ch, PH_ACTIVATE_REQ, MISDN_ID_ANY, 0,
+ NULL, GFP_KERNEL);
+ skb_push(skb, 3);
+ skb->data[0] = 0x02; /* SAPI 0 C/R = 1 */
+ skb->data[1] = 0xff; /* TEI 127 */
+ skb->data[2] = UI; /* UI frame */
+ mISDN_HEAD_PRIM(skb) = PH_DATA_REQ;
+ mISDN_HEAD_ID(skb) = new_id(mgr);
+ skb_queue_tail(&mgr->sendq, skb);
+ do_send(mgr);
+ return 0;
+}
+
+unsigned int
+random_ri(void)
+{
+ u16 x;
+
+ get_random_bytes(&x, sizeof(x));
+ return x;
+}
+
+static struct layer2 *
+findtei(struct manager *mgr, int tei)
+{
+ struct layer2 *l2;
+ u_long flags;
+
+ read_lock_irqsave(&mgr->lock, flags);
+ list_for_each_entry(l2, &mgr->layer2, list) {
+ if ((l2->sapi == 0) && (l2->tei > 0) &&
+ (l2->tei != GROUP_TEI) && (l2->tei == tei))
+ goto done;
+ }
+ l2 = NULL;
+done:
+ read_unlock_irqrestore(&mgr->lock, flags);
+ return l2;
+}
+
+static void
+put_tei_msg(struct manager *mgr, 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(MGR_OPT_NETWORK, &mgr->options))
+ 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 = _alloc_mISDN_skb(PH_DATA_REQ, new_id(mgr),
+ 8, bp, GFP_ATOMIC);
+ if (!skb) {
+ printk(KERN_WARNING "%s: no skb for tei msg\n", __func__);
+ return;
+ }
+ mgr_send_down(mgr, skb);
+}
+
+static void
+tei_id_request(struct FsmInst *fi, int event, void *arg)
+{
+ struct teimgr *tm = fi->userdata;
+
+ if (tm->l2->tei != GROUP_TEI) {
+ tm->tei_m.printdebug(&tm->tei_m,
+ "assign request for allready assigned tei %d",
+ tm->l2->tei);
+ return;
+ }
+ tm->ri = random_ri();
+ if (*debug & DEBUG_L2_TEI)
+ tm->tei_m.printdebug(&tm->tei_m,
+ "assign request ri %d", tm->ri);
+ put_tei_msg(tm->mgr, ID_REQUEST, tm->ri, GROUP_TEI);
+ mISDN_FsmChangeState(fi, ST_TEI_IDREQ);
+ mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 1);
+ tm->nval = 3;
+}
+
+static void
+tei_id_assign(struct FsmInst *fi, int event, void *arg)
+{
+ struct teimgr *tm = fi->userdata;
+ struct layer2 *l2;
+ u_char *dp = arg;
+ int ri, tei;
+
+ ri = ((unsigned int) *dp++ << 8);
+ ri += *dp++;
+ dp++;
+ tei = *dp >> 1;
+ if (*debug & DEBUG_L2_TEI)
+ tm->tei_m.printdebug(fi, "identity assign ri %d tei %d",
+ ri, tei);
+ l2 = findtei(tm->mgr, tei);
+ if (l2) { /* same tei is in use */
+ if (ri != l2->tm->ri) {
+ tm->tei_m.printdebug(fi,
+ "possible duplicate assignment tei %d", tei);
+ tei_l2(l2, MDL_ERROR_RSP, 0);
+ }
+ } else if (ri == tm->ri) {
+ mISDN_FsmDelTimer(&tm->timer, 1);
+ mISDN_FsmChangeState(fi, ST_TEI_NOP);
+ tei_l2(tm->l2, MDL_ASSIGN_REQ, tei);
+ }
+}
+
+static void
+tei_id_test_dup(struct FsmInst *fi, int event, void *arg)
+{
+ struct teimgr *tm = fi->userdata;
+ struct layer2 *l2;
+ u_char *dp = arg;
+ int tei, ri;
+
+ ri = ((unsigned int) *dp++ << 8);
+ ri += *dp++;
+ dp++;
+ tei = *dp >> 1;
+ if (*debug & DEBUG_L2_TEI)
+ tm->tei_m.printdebug(fi, "foreign identity assign ri %d tei %d",
+ ri, tei);
+ l2 = findtei(tm->mgr, tei);
+ if (l2) { /* same tei is in use */
+ if (ri != l2->tm->ri) { /* and it wasn't our request */
+ tm->tei_m.printdebug(fi,
+ "possible duplicate assignment tei %d", tei);
+ mISDN_FsmEvent(&l2->tm->tei_m, EV_VERIFY, NULL);
+ }
+ }
+}
+
+static void
+tei_id_denied(struct FsmInst *fi, int event, void *arg)
+{
+ struct teimgr *tm = fi->userdata;
+ u_char *dp = arg;
+ int ri, tei;
+
+ ri = ((unsigned int) *dp++ << 8);
+ ri += *dp++;
+ dp++;
+ tei = *dp >> 1;
+ if (*debug & DEBUG_L2_TEI)
+ 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)
+{
+ struct teimgr *tm = fi->userdata;
+ u_char *dp = arg;
+ int tei;
+
+ tei = *(dp+3) >> 1;
+ if (*debug & DEBUG_L2_TEI)
+ tm->tei_m.printdebug(fi, "identity check req tei %d", tei);
+ if ((tm->l2->tei != GROUP_TEI) && ((tei == GROUP_TEI) ||
+ (tei == tm->l2->tei))) {
+ mISDN_FsmDelTimer(&tm->timer, 4);
+ mISDN_FsmChangeState(&tm->tei_m, ST_TEI_NOP);
+ put_tei_msg(tm->mgr, ID_CHK_RES, random_ri(), tm->l2->tei);
+ }
+}
+
+static void
+tei_id_remove(struct FsmInst *fi, int event, void *arg)
+{
+ struct teimgr *tm = fi->userdata;
+ u_char *dp = arg;
+ int tei;
+
+ tei = *(dp+3) >> 1;
+ if (*debug & DEBUG_L2_TEI)
+ tm->tei_m.printdebug(fi, "identity remove tei %d", tei);
+ if ((tm->l2->tei != GROUP_TEI) &&
+ ((tei == GROUP_TEI) || (tei == tm->l2->tei))) {
+ mISDN_FsmDelTimer(&tm->timer, 5);
+ mISDN_FsmChangeState(&tm->tei_m, ST_TEI_NOP);
+ tei_l2(tm->l2, MDL_REMOVE_REQ, 0);
+ }
+}
+
+static void
+tei_id_verify(struct FsmInst *fi, int event, void *arg)
+{
+ struct teimgr *tm = fi->userdata;
+
+ if (*debug & DEBUG_L2_TEI)
+ tm->tei_m.printdebug(fi, "id verify request for tei %d",
+ tm->l2->tei);
+ put_tei_msg(tm->mgr, ID_VERIFY, 0, tm->l2->tei);
+ mISDN_FsmChangeState(&tm->tei_m, ST_TEI_IDVERIFY);
+ mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 2);
+ tm->nval = 2;
+}
+
+static void
+tei_id_req_tout(struct FsmInst *fi, int event, void *arg)
+{
+ struct teimgr *tm = fi->userdata;
+
+ if (--tm->nval) {
+ tm->ri = random_ri();
+ if (*debug & DEBUG_L2_TEI)
+ tm->tei_m.printdebug(fi, "assign req(%d) ri %d",
+ 4 - tm->nval, tm->ri);
+ put_tei_msg(tm->mgr, ID_REQUEST, tm->ri, GROUP_TEI);
+ mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 3);
+ } else {
+ tm->tei_m.printdebug(fi, "assign req failed");
+ tei_l2(tm->l2, MDL_ERROR_RSP, 0);
+ mISDN_FsmChangeState(fi, ST_TEI_NOP);
+ }
+}
+
+static void
+tei_id_ver_tout(struct FsmInst *fi, int event, void *arg)
+{
+ struct teimgr *tm = fi->userdata;
+
+ if (--tm->nval) {
+ if (*debug & DEBUG_L2_TEI)
+ tm->tei_m.printdebug(fi,
+ "id verify req(%d) for tei %d",
+ 3 - tm->nval, tm->l2->tei);
+ put_tei_msg(tm->mgr, ID_VERIFY, 0, tm->l2->tei);
+ mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 4);
+ } else {
+ tm->tei_m.printdebug(fi, "verify req for tei %d failed",
+ tm->l2->tei);
+ tei_l2(tm->l2, MDL_REMOVE_REQ, 0);
+ mISDN_FsmChangeState(fi, ST_TEI_NOP);
+ }
+}
+
+static struct FsmNode TeiFnListUser[] =
+{
+ {ST_TEI_NOP, EV_IDREQ, tei_id_request},
+ {ST_TEI_NOP, EV_ASSIGN, tei_id_test_dup},
+ {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_TIMER, tei_id_req_tout},
+ {ST_TEI_IDREQ, EV_ASSIGN, tei_id_assign},
+ {ST_TEI_IDREQ, EV_DENIED, tei_id_denied},
+ {ST_TEI_IDVERIFY, EV_TIMER, tei_id_ver_tout},
+ {ST_TEI_IDVERIFY, EV_REMOVE, tei_id_remove},
+ {ST_TEI_IDVERIFY, EV_CHKREQ, tei_id_chk_req},
+};
+
+static void
+tei_l2remove(struct layer2 *l2)
+{
+ put_tei_msg(l2->tm->mgr, ID_REMOVE, 0, l2->tei);
+ tei_l2(l2, MDL_REMOVE_REQ, 0);
+ list_del(&l2->ch.list);
+ l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL);
+}
+
+static void
+tei_assign_req(struct FsmInst *fi, int event, void *arg)
+{
+ struct teimgr *tm = fi->userdata;
+ u_char *dp = arg;
+
+ if (tm->l2->tei == GROUP_TEI) {
+ tm->tei_m.printdebug(&tm->tei_m,
+ "net tei assign request without tei");
+ return;
+ }
+ tm->ri = ((unsigned int) *dp++ << 8);
+ tm->ri += *dp++;
+ if (*debug & DEBUG_L2_TEI)
+ tm->tei_m.printdebug(&tm->tei_m,
+ "net assign request ri %d teim %d", tm->ri, *dp);
+ put_tei_msg(tm->mgr, ID_ASSIGNED, tm->ri, tm->l2->tei);
+ mISDN_FsmChangeState(fi, ST_TEI_NOP);
+}
+
+static void
+tei_id_chk_req_net(struct FsmInst *fi, int event, void *arg)
+{
+ struct teimgr *tm = fi->userdata;
+
+ if (*debug & DEBUG_L2_TEI)
+ tm->tei_m.printdebug(fi, "id check request for tei %d",
+ tm->l2->tei);
+ tm->rcnt = 0;
+ put_tei_msg(tm->mgr, ID_CHK_REQ, 0, tm->l2->tei);
+ mISDN_FsmChangeState(&tm->tei_m, ST_TEI_IDVERIFY);
+ mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 2);
+ tm->nval = 2;
+}
+
+static void
+tei_id_chk_resp(struct FsmInst *fi, int event, void *arg)
+{
+ struct teimgr *tm = fi->userdata;
+ u_char *dp = arg;
+ int tei;
+
+ tei = dp[3] >> 1;
+ if (*debug & DEBUG_L2_TEI)
+ tm->tei_m.printdebug(fi, "identity check resp tei %d", tei);
+ if (tei == tm->l2->tei)
+ tm->rcnt++;
+}
+
+static void
+tei_id_verify_net(struct FsmInst *fi, int event, void *arg)
+{
+ struct teimgr *tm = fi->userdata;
+ u_char *dp = arg;
+ int tei;
+
+ tei = dp[3] >> 1;
+ if (*debug & DEBUG_L2_TEI)
+ tm->tei_m.printdebug(fi, "identity verify req tei %d/%d",
+ tei, tm->l2->tei);
+ if (tei == tm->l2->tei)
+ tei_id_chk_req_net(fi, event, arg);
+}
+
+static void
+tei_id_ver_tout_net(struct FsmInst *fi, int event, void *arg)
+{
+ struct teimgr *tm = fi->userdata;
+
+ if (tm->rcnt == 1) {
+ if (*debug & DEBUG_L2_TEI)
+ tm->tei_m.printdebug(fi,
+ "check req for tei %d sucessful\n", tm->l2->tei);
+ mISDN_FsmChangeState(fi, ST_TEI_NOP);
+ } else if (tm->rcnt > 1) {
+ /* duplicate assignment; remove */
+ tei_l2remove(tm->l2);
+ } else if (--tm->nval) {
+ if (*debug & DEBUG_L2_TEI)
+ tm->tei_m.printdebug(fi,
+ "id check req(%d) for tei %d",
+ 3 - tm->nval, tm->l2->tei);
+ put_tei_msg(tm->mgr, ID_CHK_REQ, 0, tm->l2->tei);
+ mISDN_FsmAddTimer(&tm->timer, tm->tval, EV_TIMER, NULL, 4);
+ } else {
+ tm->tei_m.printdebug(fi, "check req for tei %d failed",
+ tm->l2->tei);
+ mISDN_FsmChangeState(fi, ST_TEI_NOP);
+ tei_l2remove(tm->l2);
+ }
+}
+
+static struct FsmNode TeiFnListNet[] =
+{
+ {ST_TEI_NOP, EV_ASSIGN_REQ, tei_assign_req},
+ {ST_TEI_NOP, EV_VERIFY, tei_id_verify_net},
+ {ST_TEI_NOP, EV_CHKREQ, tei_id_chk_req_net},
+ {ST_TEI_IDVERIFY, EV_TIMER, tei_id_ver_tout_net},
+ {ST_TEI_IDVERIFY, EV_CHKRESP, tei_id_chk_resp},
+};
+
+static void
+tei_ph_data_ind(struct teimgr *tm, u_int mt, u_char *dp, int len)
+{
+ if (test_bit(FLG_FIXED_TEI, &tm->l2->flag))
+ return;
+ if (*debug & DEBUG_L2_TEI)
+ 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_VERIFY)
+ mISDN_FsmEvent(&tm->tei_m, EV_VERIFY, dp);
+ else if (mt == ID_CHK_RES)
+ mISDN_FsmEvent(&tm->tei_m, EV_CHKRESP, dp);
+}
+
+static struct layer2 *
+create_new_tei(struct manager *mgr, int tei)
+{
+ u_long opt = 0;
+ u_long flags;
+ int id;
+ struct layer2 *l2;
+
+ if (!mgr->up)
+ return NULL;
+ if (tei < 64)
+ test_and_set_bit(OPTION_L2_FIXEDTEI, &opt);
+ if (mgr->ch.st->dev->Dprotocols
+ & ((1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1)))
+ test_and_set_bit(OPTION_L2_PMX, &opt);
+ l2 = create_l2(mgr->up, ISDN_P_LAPD_NT, (u_int)opt, (u_long)tei);
+ if (!l2) {
+ printk(KERN_WARNING "%s:no memory for layer2\n", __func__);
+ return NULL;
+ }
+ l2->tm = kzalloc(sizeof(struct teimgr), GFP_KERNEL);
+ if (!l2->tm) {
+ kfree(l2);
+ printk(KERN_WARNING "%s:no memory for teimgr\n", __func__);
+ return NULL;
+ }
+ l2->tm->mgr = mgr;
+ l2->tm->l2 = l2;
+ l2->tm->tei_m.debug = *debug & DEBUG_L2_TEIFSM;
+ l2->tm->tei_m.userdata = l2->tm;
+ l2->tm->tei_m.printdebug = tei_debug;
+ l2->tm->tei_m.fsm = &teifsmn;
+ l2->tm->tei_m.state = ST_TEI_NOP;
+ l2->tm->tval = 2000; /* T202 2 sec */
+ mISDN_FsmInitTimer(&l2->tm->tei_m, &l2->tm->timer);
+ write_lock_irqsave(&mgr->lock, flags);
+ id = get_free_id(mgr);
+ list_add_tail(&l2->list, &mgr->layer2);
+ write_unlock_irqrestore(&mgr->lock, flags);
+ if (id < 0) {
+ l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL);
+ printk(KERN_WARNING "%s:no free id\n", __func__);
+ return NULL;
+ } else {
+ l2->ch.nr = id;
+ __add_layer2(&l2->ch, mgr->ch.st);
+ l2->ch.recv = mgr->ch.recv;
+ l2->ch.peer = mgr->ch.peer;
+ l2->ch.ctrl(&l2->ch, OPEN_CHANNEL, NULL);
+ }
+ return l2;
+}
+
+static void
+new_tei_req(struct manager *mgr, u_char *dp)
+{
+ int tei, ri;
+ struct layer2 *l2;
+
+ ri = dp[0] << 8;
+ ri += dp[1];
+ if (!mgr->up)
+ goto denied;
+ tei = get_free_tei(mgr);
+ if (tei < 0) {
+ printk(KERN_WARNING "%s:No free tei\n", __func__);
+ goto denied;
+ }
+ l2 = create_new_tei(mgr, tei);
+ if (!l2)
+ goto denied;
+ else
+ mISDN_FsmEvent(&l2->tm->tei_m, EV_ASSIGN_REQ, dp);
+ return;
+denied:
+ put_tei_msg(mgr, ID_DENIED, ri, GROUP_TEI);
+}
+
+static int
+ph_data_ind(struct manager *mgr, struct sk_buff *skb)
+{
+ int ret = -EINVAL;
+ struct layer2 *l2;
+ u_long flags;
+ u_char mt;
+
+ if (skb->len < 8) {
+ if (*debug & DEBUG_L2_TEI)
+ printk(KERN_DEBUG "%s: short mgr frame %d/8\n",
+ __func__, skb->len);
+ goto done;
+ }
+ if (*debug & DEBUG_L2_TEI)
+
+ if ((skb->data[0] >> 2) != TEI_SAPI) /* not for us */
+ goto done;
+ if (skb->data[0] & 1) /* EA0 formal error */
+ goto done;
+ if (!(skb->data[1] & 1)) /* EA1 formal error */
+ goto done;
+ if ((skb->data[1] >> 1) != GROUP_TEI) /* not for us */
+ goto done;
+ if ((skb->data[2] & 0xef) != UI) /* not UI */
+ goto done;
+ if (skb->data[3] != TEI_ENTITY_ID) /* not tei entity */
+ goto done;
+ mt = skb->data[6];
+ switch (mt) {
+ case ID_REQUEST:
+ case ID_CHK_RES:
+ case ID_VERIFY:
+ if (!test_bit(MGR_OPT_NETWORK, &mgr->options))
+ goto done;
+ break;
+ case ID_ASSIGNED:
+ case ID_DENIED:
+ case ID_CHK_REQ:
+ case ID_REMOVE:
+ if (test_bit(MGR_OPT_NETWORK, &mgr->options))
+ goto done;
+ break;
+ default:
+ goto done;
+ }
+ ret = 0;
+ if (mt == ID_REQUEST) {
+ new_tei_req(mgr, &skb->data[4]);
+ goto done;
+ }
+ read_lock_irqsave(&mgr->lock, flags);
+ list_for_each_entry(l2, &mgr->layer2, list) {
+ tei_ph_data_ind(l2->tm, mt, &skb->data[4], skb->len - 4);
+ }
+ read_unlock_irqrestore(&mgr->lock, flags);
+done:
+ return ret;
+}
+
+int
+l2_tei(struct layer2 *l2, u_int cmd, u_long arg)
+{
+ struct teimgr *tm = l2->tm;
+
+ if (test_bit(FLG_FIXED_TEI, &l2->flag))
+ return 0;
+ if (*debug & DEBUG_L2_TEI)
+ printk(KERN_DEBUG "%s: cmd(%x)\n", __func__, cmd);
+ switch (cmd) {
+ case MDL_ASSIGN_IND:
+ mISDN_FsmEvent(&tm->tei_m, EV_IDREQ, NULL);
+ break;
+ case MDL_ERROR_IND:
+ if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options))
+ mISDN_FsmEvent(&tm->tei_m, EV_CHKREQ, &l2->tei);
+ if (test_bit(MGR_OPT_USER, &tm->mgr->options))
+ mISDN_FsmEvent(&tm->tei_m, EV_VERIFY, NULL);
+ break;
+ case MDL_STATUS_UP_IND:
+ if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options))
+ mISDN_FsmEvent(&tm->mgr->deact, EV_ACTIVATE, NULL);
+ break;
+ case MDL_STATUS_DOWN_IND:
+ if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options))
+ mISDN_FsmEvent(&tm->mgr->deact, EV_DEACTIVATE, NULL);
+ break;
+ case MDL_STATUS_UI_IND:
+ if (test_bit(MGR_OPT_NETWORK, &tm->mgr->options))
+ mISDN_FsmEvent(&tm->mgr->deact, EV_UI, NULL);
+ break;
+ }
+ return 0;
+}
+
+void
+TEIrelease(struct layer2 *l2)
+{
+ struct teimgr *tm = l2->tm;
+ u_long flags;
+
+ mISDN_FsmDelTimer(&tm->timer, 1);
+ write_lock_irqsave(&tm->mgr->lock, flags);
+ list_del(&l2->list);
+ write_unlock_irqrestore(&tm->mgr->lock, flags);
+ l2->tm = NULL;
+ kfree(tm);
+}
+
+static int
+create_teimgr(struct manager *mgr, struct channel_req *crq)
+{
+ struct layer2 *l2;
+ u_long opt = 0;
+ u_long flags;
+ int id;
+
+ if (*debug & DEBUG_L2_TEI)
+ printk(KERN_DEBUG "%s: %s proto(%x) adr(%d %d %d %d)\n",
+ __func__, mgr->ch.st->dev->name, crq->protocol,
+ crq->adr.dev, crq->adr.channel, crq->adr.sapi,
+ crq->adr.tei);
+ if (crq->adr.sapi != 0) /* not supported yet */
+ return -EINVAL;
+ if (crq->adr.tei > GROUP_TEI)
+ return -EINVAL;
+ if (crq->adr.tei < 64)
+ test_and_set_bit(OPTION_L2_FIXEDTEI, &opt);
+ if (crq->adr.tei == 0)
+ test_and_set_bit(OPTION_L2_PTP, &opt);
+ if (test_bit(MGR_OPT_NETWORK, &mgr->options)) {
+ if (crq->protocol == ISDN_P_LAPD_TE)
+ return -EPROTONOSUPPORT;
+ if ((crq->adr.tei != 0) && (crq->adr.tei != 127))
+ return -EINVAL;
+ if (mgr->up) {
+ printk(KERN_WARNING
+ "%s: only one network manager is allowed\n",
+ __func__);
+ return -EBUSY;
+ }
+ } else if (test_bit(MGR_OPT_USER, &mgr->options)) {
+ if (crq->protocol == ISDN_P_LAPD_NT)
+ return -EPROTONOSUPPORT;
+ if ((crq->adr.tei >= 64) && (crq->adr.tei < GROUP_TEI))
+ return -EINVAL; /* dyn tei */
+ } else {
+ if (crq->protocol == ISDN_P_LAPD_NT)
+ test_and_set_bit(MGR_OPT_NETWORK, &mgr->options);
+ if (crq->protocol == ISDN_P_LAPD_TE)
+ test_and_set_bit(MGR_OPT_USER, &mgr->options);
+ }
+ if (mgr->ch.st->dev->Dprotocols
+ & ((1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1)))
+ test_and_set_bit(OPTION_L2_PMX, &opt);
+ if ((crq->protocol == ISDN_P_LAPD_NT) && (crq->adr.tei == 127)) {
+ mgr->up = crq->ch;
+ id = DL_INFO_L2_CONNECT;
+ teiup_create(mgr, DL_INFORMATION_IND, sizeof(id), &id);
+ crq->ch = NULL;
+ if (!list_empty(&mgr->layer2)) {
+ read_lock_irqsave(&mgr->lock, flags);
+ list_for_each_entry(l2, &mgr->layer2, list) {
+ l2->up = mgr->up;
+ l2->ch.ctrl(&l2->ch, OPEN_CHANNEL, NULL);
+ }
+ read_unlock_irqrestore(&mgr->lock, flags);
+ }
+ return 0;
+ }
+ l2 = create_l2(crq->ch, crq->protocol, (u_int)opt,
+ (u_long)crq->adr.tei);
+ if (!l2)
+ return -ENOMEM;
+ l2->tm = kzalloc(sizeof(struct teimgr), GFP_KERNEL);
+ if (!l2->tm) {
+ kfree(l2);
+ printk(KERN_ERR "kmalloc teimgr failed\n");
+ return -ENOMEM;
+ }
+ l2->tm->mgr = mgr;
+ l2->tm->l2 = l2;
+ l2->tm->tei_m.debug = *debug & DEBUG_L2_TEIFSM;
+ l2->tm->tei_m.userdata = l2->tm;
+ l2->tm->tei_m.printdebug = tei_debug;
+ if (crq->protocol == ISDN_P_LAPD_TE) {
+ l2->tm->tei_m.fsm = &teifsmu;
+ l2->tm->tei_m.state = ST_TEI_NOP;
+ l2->tm->tval = 1000; /* T201 1 sec */
+ } else {
+ l2->tm->tei_m.fsm = &teifsmn;
+ l2->tm->tei_m.state = ST_TEI_NOP;
+ l2->tm->tval = 2000; /* T202 2 sec */
+ }
+ mISDN_FsmInitTimer(&l2->tm->tei_m, &l2->tm->timer);
+ write_lock_irqsave(&mgr->lock, flags);
+ id = get_free_id(mgr);
+ list_add_tail(&l2->list, &mgr->layer2);
+ write_unlock_irqrestore(&mgr->lock, flags);
+ if (id < 0) {
+ l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL);
+ } else {
+ l2->ch.nr = id;
+ l2->up->nr = id;
+ crq->ch = &l2->ch;
+ id = 0;
+ }
+ return id;
+}
+
+static int
+mgr_send(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+ struct manager *mgr;
+ struct mISDNhead *hh = mISDN_HEAD_P(skb);
+ int ret = -EINVAL;
+
+ mgr = container_of(ch, struct manager, ch);
+ if (*debug & DEBUG_L2_RECV)
+ printk(KERN_DEBUG "%s: prim(%x) id(%x)\n",
+ __func__, hh->prim, hh->id);
+ switch (hh->prim) {
+ case PH_DATA_IND:
+ mISDN_FsmEvent(&mgr->deact, EV_UI, NULL);
+ ret = ph_data_ind(mgr, skb);
+ break;
+ case PH_DATA_CNF:
+ do_ack(mgr, hh->id);
+ ret = 0;
+ break;
+ case PH_ACTIVATE_IND:
+ test_and_set_bit(MGR_PH_ACTIVE, &mgr->options);
+ mISDN_FsmEvent(&mgr->deact, EV_ACTIVATE_IND, NULL);
+ do_send(mgr);
+ ret = 0;
+ break;
+ case PH_DEACTIVATE_IND:
+ test_and_clear_bit(MGR_PH_ACTIVE, &mgr->options);
+ mISDN_FsmEvent(&mgr->deact, EV_DEACTIVATE_IND, NULL);
+ ret = 0;
+ break;
+ case DL_UNITDATA_REQ:
+ return dl_unit_data(mgr, skb);
+ }
+ if (!ret)
+ dev_kfree_skb(skb);
+ return ret;
+}
+
+static int
+free_teimanager(struct manager *mgr)
+{
+ struct layer2 *l2, *nl2;
+
+ if (test_bit(MGR_OPT_NETWORK, &mgr->options)) {
+ /* not locked lock is taken in release tei */
+ mgr->up = NULL;
+ if (test_bit(OPTION_L2_CLEANUP, &mgr->options)) {
+ list_for_each_entry_safe(l2, nl2, &mgr->layer2, list) {
+ put_tei_msg(mgr, ID_REMOVE, 0, l2->tei);
+ mutex_lock(&mgr->ch.st->lmutex);
+ list_del(&l2->ch.list);
+ mutex_unlock(&mgr->ch.st->lmutex);
+ l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL);
+ }
+ test_and_clear_bit(MGR_OPT_NETWORK, &mgr->options);
+ } else {
+ list_for_each_entry_safe(l2, nl2, &mgr->layer2, list) {
+ l2->up = NULL;
+ }
+ }
+ }
+ if (test_bit(MGR_OPT_USER, &mgr->options)) {
+ if (list_empty(&mgr->layer2))
+ test_and_clear_bit(MGR_OPT_USER, &mgr->options);
+ }
+ mgr->ch.st->dev->D.ctrl(&mgr->ch.st->dev->D, CLOSE_CHANNEL, NULL);
+ return 0;
+}
+
+static int
+ctrl_teimanager(struct manager *mgr, void *arg)
+{
+ /* currently we only have one option */
+ int clean = *((int *)arg);
+
+ if (clean)
+ test_and_set_bit(OPTION_L2_CLEANUP, &mgr->options);
+ else
+ test_and_clear_bit(OPTION_L2_CLEANUP, &mgr->options);
+ return 0;
+}
+
+/* This function does create a L2 for fixed TEI in NT Mode */
+static int
+check_data(struct manager *mgr, struct sk_buff *skb)
+{
+ struct mISDNhead *hh = mISDN_HEAD_P(skb);
+ int ret, tei;
+ struct layer2 *l2;
+
+ if (*debug & DEBUG_L2_CTRL)
+ printk(KERN_DEBUG "%s: prim(%x) id(%x)\n",
+ __func__, hh->prim, hh->id);
+ if (test_bit(MGR_OPT_USER, &mgr->options))
+ return -ENOTCONN;
+ if (hh->prim != PH_DATA_IND)
+ return -ENOTCONN;
+ if (skb->len != 3)
+ return -ENOTCONN;
+ if (skb->data[0] != 0)
+ /* only SAPI 0 command */
+ return -ENOTCONN;
+ if (!(skb->data[1] & 1)) /* invalid EA1 */
+ return -EINVAL;
+ tei = skb->data[1] >> 0;
+ if (tei > 63) /* not a fixed tei */
+ return -ENOTCONN;
+ if ((skb->data[2] & ~0x10) != SABME)
+ return -ENOTCONN;
+ /* We got a SABME for a fixed TEI */
+ l2 = create_new_tei(mgr, tei);
+ if (!l2)
+ return -ENOMEM;
+ ret = l2->ch.send(&l2->ch, skb);
+ return ret;
+}
+
+void
+delete_teimanager(struct mISDNchannel *ch)
+{
+ struct manager *mgr;
+ struct layer2 *l2, *nl2;
+
+ mgr = container_of(ch, struct manager, ch);
+ /* not locked lock is taken in release tei */
+ list_for_each_entry_safe(l2, nl2, &mgr->layer2, list) {
+ mutex_lock(&mgr->ch.st->lmutex);
+ list_del(&l2->ch.list);
+ mutex_unlock(&mgr->ch.st->lmutex);
+ l2->ch.ctrl(&l2->ch, CLOSE_CHANNEL, NULL);
+ }
+ list_del(&mgr->ch.list);
+ list_del(&mgr->bcast.list);
+ skb_queue_purge(&mgr->sendq);
+ kfree(mgr);
+}
+
+static int
+mgr_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+ struct manager *mgr;
+ int ret = -EINVAL;
+
+ mgr = container_of(ch, struct manager, ch);
+ if (*debug & DEBUG_L2_CTRL)
+ printk(KERN_DEBUG "%s(%x, %p)\n", __func__, cmd, arg);
+ switch (cmd) {
+ case OPEN_CHANNEL:
+ ret = create_teimgr(mgr, arg);
+ break;
+ case CLOSE_CHANNEL:
+ ret = free_teimanager(mgr);
+ break;
+ case CONTROL_CHANNEL:
+ ret = ctrl_teimanager(mgr, arg);
+ break;
+ case CHECK_DATA:
+ ret = check_data(mgr, arg);
+ break;
+ }
+ return ret;
+}
+
+static int
+mgr_bcast(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+ struct manager *mgr = container_of(ch, struct manager, bcast);
+ struct mISDNhead *hh = mISDN_HEAD_P(skb);
+ struct sk_buff *cskb = NULL;
+ struct layer2 *l2;
+ u_long flags;
+ int ret;
+
+ read_lock_irqsave(&mgr->lock, flags);
+ list_for_each_entry(l2, &mgr->layer2, list) {
+ if ((hh->id & MISDN_ID_SAPI_MASK) ==
+ (l2->ch.addr & MISDN_ID_SAPI_MASK)) {
+ if (list_is_last(&l2->list, &mgr->layer2)) {
+ cskb = skb;
+ skb = NULL;
+ } else {
+ if (!cskb)
+ cskb = skb_copy(skb, GFP_KERNEL);
+ }
+ if (cskb) {
+ ret = l2->ch.send(&l2->ch, cskb);
+ if (ret) {
+ if (*debug & DEBUG_SEND_ERR)
+ printk(KERN_DEBUG
+ "%s ch%d prim(%x) addr(%x)"
+ " err %d\n",
+ __func__, l2->ch.nr,
+ hh->prim, l2->ch.addr, ret);
+ } else
+ cskb = NULL;
+ } else {
+ printk(KERN_WARNING "%s ch%d addr %x no mem\n",
+ __func__, ch->nr, ch->addr);
+ goto out;
+ }
+ }
+ }
+out:
+ read_unlock_irqrestore(&mgr->lock, flags);
+ if (cskb)
+ dev_kfree_skb(cskb);
+ if (skb)
+ dev_kfree_skb(skb);
+ return 0;
+}
+
+static int
+mgr_bcast_ctrl(struct mISDNchannel *ch, u_int cmd, void *arg)
+{
+
+ return -EINVAL;
+}
+
+int
+create_teimanager(struct mISDNdevice *dev)
+{
+ struct manager *mgr;
+
+ mgr = kzalloc(sizeof(struct manager), GFP_KERNEL);
+ if (!mgr)
+ return -ENOMEM;
+ INIT_LIST_HEAD(&mgr->layer2);
+ mgr->lock = __RW_LOCK_UNLOCKED(mgr->lock);
+ skb_queue_head_init(&mgr->sendq);
+ mgr->nextid = 1;
+ mgr->lastid = MISDN_ID_NONE;
+ mgr->ch.send = mgr_send;
+ mgr->ch.ctrl = mgr_ctrl;
+ mgr->ch.st = dev->D.st;
+ set_channel_address(&mgr->ch, TEI_SAPI, GROUP_TEI);
+ add_layer2(&mgr->ch, dev->D.st);
+ mgr->bcast.send = mgr_bcast;
+ mgr->bcast.ctrl = mgr_bcast_ctrl;
+ mgr->bcast.st = dev->D.st;
+ set_channel_address(&mgr->bcast, 0, GROUP_TEI);
+ add_layer2(&mgr->bcast, dev->D.st);
+ mgr->deact.debug = *debug & DEBUG_MANAGER;
+ mgr->deact.userdata = mgr;
+ mgr->deact.printdebug = da_debug;
+ mgr->deact.fsm = &deactfsm;
+ mgr->deact.state = ST_L1_DEACT;
+ mISDN_FsmInitTimer(&mgr->deact, &mgr->datimer);
+ dev->teimgr = &mgr->ch;
+ return 0;
+}
+
+int TEIInit(u_int *deb)
+{
+ debug = deb;
+ teifsmu.state_count = TEI_STATE_COUNT;
+ teifsmu.event_count = TEI_EVENT_COUNT;
+ teifsmu.strEvent = strTeiEvent;
+ teifsmu.strState = strTeiState;
+ mISDN_FsmNew(&teifsmu, TeiFnListUser, ARRAY_SIZE(TeiFnListUser));
+ teifsmn.state_count = TEI_STATE_COUNT;
+ teifsmn.event_count = TEI_EVENT_COUNT;
+ teifsmn.strEvent = strTeiEvent;
+ teifsmn.strState = strTeiState;
+ mISDN_FsmNew(&teifsmn, TeiFnListNet, ARRAY_SIZE(TeiFnListNet));
+ deactfsm.state_count = DEACT_STATE_COUNT;
+ deactfsm.event_count = DEACT_EVENT_COUNT;
+ deactfsm.strEvent = strDeactEvent;
+ deactfsm.strState = strDeactState;
+ mISDN_FsmNew(&deactfsm, DeactFnList, ARRAY_SIZE(DeactFnList));
+ return 0;
+}
+
+void TEIFree(void)
+{
+ mISDN_FsmFree(&teifsmu);
+ mISDN_FsmFree(&teifsmn);
+ mISDN_FsmFree(&deactfsm);
+}
diff --git a/drivers/isdn/mISDN/timerdev.c b/drivers/isdn/mISDN/timerdev.c
new file mode 100644
index 0000000..875fabe
--- /dev/null
+++ b/drivers/isdn/mISDN/timerdev.c
@@ -0,0 +1,291 @@
+/*
+ *
+ * general timer device for using in ISDN stacks
+ *
+ * Author Karsten Keil <kkeil@novell.com>
+ *
+ * Copyright 2008 by Karsten Keil <kkeil@novell.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/poll.h>
+#include <linux/vmalloc.h>
+#include <linux/timer.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/mISDNif.h>
+
+static int *debug;
+
+
+struct mISDNtimerdev {
+ int next_id;
+ struct list_head pending;
+ struct list_head expired;
+ wait_queue_head_t wait;
+ u_int work;
+ spinlock_t lock; /* protect lists */
+};
+
+struct mISDNtimer {
+ struct list_head list;
+ struct mISDNtimerdev *dev;
+ struct timer_list tl;
+ int id;
+};
+
+static int
+mISDN_open(struct inode *ino, struct file *filep)
+{
+ struct mISDNtimerdev *dev;
+
+ if (*debug & DEBUG_TIMER)
+ printk(KERN_DEBUG "%s(%p,%p)\n", __func__, ino, filep);
+ dev = kmalloc(sizeof(struct mISDNtimerdev) , GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+ dev->next_id = 1;
+ INIT_LIST_HEAD(&dev->pending);
+ INIT_LIST_HEAD(&dev->expired);
+ spin_lock_init(&dev->lock);
+ dev->work = 0;
+ init_waitqueue_head(&dev->wait);
+ filep->private_data = dev;
+ __module_get(THIS_MODULE);
+ return nonseekable_open(ino, filep);
+}
+
+static int
+mISDN_close(struct inode *ino, struct file *filep)
+{
+ struct mISDNtimerdev *dev = filep->private_data;
+ struct mISDNtimer *timer, *next;
+
+ if (*debug & DEBUG_TIMER)
+ printk(KERN_DEBUG "%s(%p,%p)\n", __func__, ino, filep);
+ list_for_each_entry_safe(timer, next, &dev->pending, list) {
+ del_timer(&timer->tl);
+ kfree(timer);
+ }
+ list_for_each_entry_safe(timer, next, &dev->expired, list) {
+ kfree(timer);
+ }
+ kfree(dev);
+ module_put(THIS_MODULE);
+ return 0;
+}
+
+static ssize_t
+mISDN_read(struct file *filep, char *buf, size_t count, loff_t *off)
+{
+ struct mISDNtimerdev *dev = filep->private_data;
+ struct mISDNtimer *timer;
+ u_long flags;
+ int ret = 0;
+
+ if (*debug & DEBUG_TIMER)
+ printk(KERN_DEBUG "%s(%p, %p, %d, %p)\n", __func__,
+ filep, buf, (int)count, off);
+ if (*off != filep->f_pos)
+ return -ESPIPE;
+
+ if (list_empty(&dev->expired) && (dev->work == 0)) {
+ if (filep->f_flags & O_NONBLOCK)
+ return -EAGAIN;
+ wait_event_interruptible(dev->wait, (dev->work ||
+ !list_empty(&dev->expired)));
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ }
+ if (count < sizeof(int))
+ return -ENOSPC;
+ if (dev->work)
+ dev->work = 0;
+ if (!list_empty(&dev->expired)) {
+ spin_lock_irqsave(&dev->lock, flags);
+ timer = (struct mISDNtimer *)dev->expired.next;
+ list_del(&timer->list);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ if (put_user(timer->id, (int *)buf))
+ ret = -EFAULT;
+ else
+ ret = sizeof(int);
+ kfree(timer);
+ }
+ return ret;
+}
+
+static unsigned int
+mISDN_poll(struct file *filep, poll_table *wait)
+{
+ struct mISDNtimerdev *dev = filep->private_data;
+ unsigned int mask = POLLERR;
+
+ if (*debug & DEBUG_TIMER)
+ printk(KERN_DEBUG "%s(%p, %p)\n", __func__, filep, wait);
+ if (dev) {
+ poll_wait(filep, &dev->wait, wait);
+ mask = 0;
+ if (dev->work || !list_empty(&dev->expired))
+ mask |= (POLLIN | POLLRDNORM);
+ if (*debug & DEBUG_TIMER)
+ printk(KERN_DEBUG "%s work(%d) empty(%d)\n", __func__,
+ dev->work, list_empty(&dev->expired));
+ }
+ return mask;
+}
+
+static void
+dev_expire_timer(unsigned long data)
+{
+ struct mISDNtimer *timer = (void *)data;
+ u_long flags;
+
+ spin_lock_irqsave(&timer->dev->lock, flags);
+ list_del(&timer->list);
+ list_add_tail(&timer->list, &timer->dev->expired);
+ spin_unlock_irqrestore(&timer->dev->lock, flags);
+ wake_up_interruptible(&timer->dev->wait);
+}
+
+static int
+misdn_add_timer(struct mISDNtimerdev *dev, int timeout)
+{
+ int id;
+ u_long flags;
+ struct mISDNtimer *timer;
+
+ if (!timeout) {
+ dev->work = 1;
+ wake_up_interruptible(&dev->wait);
+ id = 0;
+ } else {
+ timer = kzalloc(sizeof(struct mISDNtimer), GFP_KERNEL);
+ if (!timer)
+ return -ENOMEM;
+ spin_lock_irqsave(&dev->lock, flags);
+ timer->id = dev->next_id++;
+ if (dev->next_id < 0)
+ dev->next_id = 1;
+ list_add_tail(&timer->list, &dev->pending);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ timer->dev = dev;
+ timer->tl.data = (long)timer;
+ timer->tl.function = dev_expire_timer;
+ init_timer(&timer->tl);
+ timer->tl.expires = jiffies + ((HZ * (u_long)timeout) / 1000);
+ add_timer(&timer->tl);
+ id = timer->id;
+ }
+ return id;
+}
+
+static int
+misdn_del_timer(struct mISDNtimerdev *dev, int id)
+{
+ u_long flags;
+ struct mISDNtimer *timer;
+ int ret = 0;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ list_for_each_entry(timer, &dev->pending, list) {
+ if (timer->id == id) {
+ list_del_init(&timer->list);
+ /* RED-PEN AK: race -- timer can be still running on
+ * other CPU. Needs reference count I think
+ */
+ del_timer(&timer->tl);
+ ret = timer->id;
+ kfree(timer);
+ goto unlock;
+ }
+ }
+unlock:
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return ret;
+}
+
+static int
+mISDN_ioctl(struct inode *inode, struct file *filep, unsigned int cmd,
+ unsigned long arg)
+{
+ struct mISDNtimerdev *dev = filep->private_data;
+ int id, tout, ret = 0;
+
+
+ if (*debug & DEBUG_TIMER)
+ printk(KERN_DEBUG "%s(%p, %x, %lx)\n", __func__,
+ filep, cmd, arg);
+ switch (cmd) {
+ case IMADDTIMER:
+ if (get_user(tout, (int __user *)arg)) {
+ ret = -EFAULT;
+ break;
+ }
+ id = misdn_add_timer(dev, tout);
+ if (*debug & DEBUG_TIMER)
+ printk(KERN_DEBUG "%s add %d id %d\n", __func__,
+ tout, id);
+ if (id < 0) {
+ ret = id;
+ break;
+ }
+ if (put_user(id, (int __user *)arg))
+ ret = -EFAULT;
+ break;
+ case IMDELTIMER:
+ if (get_user(id, (int __user *)arg)) {
+ ret = -EFAULT;
+ break;
+ }
+ if (*debug & DEBUG_TIMER)
+ printk(KERN_DEBUG "%s del id %d\n", __func__, id);
+ id = misdn_del_timer(dev, id);
+ if (put_user(id, (int __user *)arg))
+ ret = -EFAULT;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static struct file_operations mISDN_fops = {
+ .read = mISDN_read,
+ .poll = mISDN_poll,
+ .ioctl = mISDN_ioctl,
+ .open = mISDN_open,
+ .release = mISDN_close,
+};
+
+static struct miscdevice mISDNtimer = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "mISDNtimer",
+ .fops = &mISDN_fops,
+};
+
+int
+mISDN_inittimer(int *deb)
+{
+ int err;
+
+ debug = deb;
+ err = misc_register(&mISDNtimer);
+ if (err)
+ printk(KERN_WARNING "mISDN: Could not register timer device\n");
+ return err;
+}
+
+void mISDN_timer_cleanup(void)
+{
+ misc_deregister(&mISDNtimer);
+}
diff --git a/drivers/isdn/pcbit/Kconfig b/drivers/isdn/pcbit/Kconfig
new file mode 100644
index 0000000..ffba6ec
--- /dev/null
+++ b/drivers/isdn/pcbit/Kconfig
@@ -0,0 +1,14 @@
+#
+# Config.in for PCBIT ISDN driver
+#
+config ISDN_DRV_PCBIT
+ tristate "PCBIT-D support"
+ depends on ISA && (BROKEN || X86)
+ help
+ This enables support for the PCBIT ISDN-card. This card is
+ manufactured in Portugal by Octal. For running this card,
+ additional firmware is necessary, which has to be downloaded into
+ the card using a utility which is distributed separately. See
+ <file:Documentation/isdn/README> and
+ <file:Documentation/isdn/README.pcbit> for more information.
+
diff --git a/drivers/isdn/pcbit/Makefile b/drivers/isdn/pcbit/Makefile
new file mode 100644
index 0000000..2d026c3
--- /dev/null
+++ b/drivers/isdn/pcbit/Makefile
@@ -0,0 +1,9 @@
+# Makefile for the pcbit ISDN device driver
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_ISDN_DRV_PCBIT) += pcbit.o
+
+# Multipart objects.
+
+pcbit-y := module.o edss1.o drv.o layer2.o capi.o callbacks.o
diff --git a/drivers/isdn/pcbit/callbacks.c b/drivers/isdn/pcbit/callbacks.c
new file mode 100644
index 0000000..43ecd0f
--- /dev/null
+++ b/drivers/isdn/pcbit/callbacks.c
@@ -0,0 +1,349 @@
+/*
+ * Callbacks for the FSM
+ *
+ * Copyright (C) 1996 Universidade de Lisboa
+ *
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU General Public License, incorporated herein by reference.
+ */
+
+/*
+ * Fix: 19981230 - Carlos Morgado <chbm@techie.com>
+ * Port of Nelson Escravana's <nelson.escravana@usa.net> fix to CalledPN
+ * NULL pointer dereference in cb_in_1 (originally fixed in 2.0)
+ */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/skbuff.h>
+
+#include <asm/io.h>
+
+#include <linux/isdnif.h>
+
+#include "pcbit.h"
+#include "layer2.h"
+#include "edss1.h"
+#include "callbacks.h"
+#include "capi.h"
+
+ushort last_ref_num = 1;
+
+/*
+ * send_conn_req
+ *
+ */
+
+void cb_out_1(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *cbdata)
+{
+ struct sk_buff *skb;
+ int len;
+ ushort refnum;
+
+
+#ifdef DEBUG
+ printk(KERN_DEBUG "Called Party Number: %s\n",
+ cbdata->data.setup.CalledPN);
+#endif
+ /*
+ * hdr - kmalloc in capi_conn_req
+ * - kfree when msg has been sent
+ */
+
+ if ((len = capi_conn_req(cbdata->data.setup.CalledPN, &skb,
+ chan->proto)) < 0)
+ {
+ printk("capi_conn_req failed\n");
+ return;
+ }
+
+
+ refnum = last_ref_num++ & 0x7fffU;
+
+ chan->callref = 0;
+ chan->layer2link = 0;
+ chan->snum = 0;
+ chan->s_refnum = refnum;
+
+ pcbit_l2_write(dev, MSG_CONN_REQ, refnum, skb, len);
+}
+
+/*
+ * rcv CONNECT
+ * will go into ACTIVE state
+ * send CONN_ACTIVE_RESP
+ * send Select protocol request
+ */
+
+void cb_out_2(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data)
+{
+ isdn_ctrl ictl;
+ struct sk_buff *skb;
+ int len;
+ ushort refnum;
+
+ if ((len=capi_conn_active_resp(chan, &skb)) < 0)
+ {
+ printk("capi_conn_active_req failed\n");
+ return;
+ }
+
+ refnum = last_ref_num++ & 0x7fffU;
+ chan->s_refnum = refnum;
+
+ pcbit_l2_write(dev, MSG_CONN_ACTV_RESP, refnum, skb, len);
+
+
+ ictl.command = ISDN_STAT_DCONN;
+ ictl.driver=dev->id;
+ ictl.arg=chan->id;
+ dev->dev_if->statcallb(&ictl);
+
+ /* ACTIVE D-channel */
+
+ /* Select protocol */
+
+ if ((len=capi_select_proto_req(chan, &skb, 1 /*outgoing*/)) < 0) {
+ printk("capi_select_proto_req failed\n");
+ return;
+ }
+
+ refnum = last_ref_num++ & 0x7fffU;
+ chan->s_refnum = refnum;
+
+ pcbit_l2_write(dev, MSG_SELP_REQ, refnum, skb, len);
+}
+
+
+/*
+ * Incoming call received
+ * inform user
+ */
+
+void cb_in_1(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *cbdata)
+{
+ isdn_ctrl ictl;
+ unsigned short refnum;
+ struct sk_buff *skb;
+ int len;
+
+
+ ictl.command = ISDN_STAT_ICALL;
+ ictl.driver=dev->id;
+ ictl.arg=chan->id;
+
+ /*
+ * ictl.num >= strlen() + strlen() + 5
+ */
+
+ if (cbdata->data.setup.CallingPN == NULL) {
+ printk(KERN_DEBUG "NULL CallingPN to phone; using 0\n");
+ strcpy(ictl.parm.setup.phone, "0");
+ }
+ else {
+ strcpy(ictl.parm.setup.phone, cbdata->data.setup.CallingPN);
+ }
+ if (cbdata->data.setup.CalledPN == NULL) {
+ printk(KERN_DEBUG "NULL CalledPN to eazmsn; using 0\n");
+ strcpy(ictl.parm.setup.eazmsn, "0");
+ }
+ else {
+ strcpy(ictl.parm.setup.eazmsn, cbdata->data.setup.CalledPN);
+ }
+ ictl.parm.setup.si1 = 7;
+ ictl.parm.setup.si2 = 0;
+ ictl.parm.setup.plan = 0;
+ ictl.parm.setup.screen = 0;
+
+#ifdef DEBUG
+ printk(KERN_DEBUG "statstr: %s\n", ictl.num);
+#endif
+
+ dev->dev_if->statcallb(&ictl);
+
+
+ if ((len=capi_conn_resp(chan, &skb)) < 0) {
+ printk(KERN_DEBUG "capi_conn_resp failed\n");
+ return;
+ }
+
+ refnum = last_ref_num++ & 0x7fffU;
+ chan->s_refnum = refnum;
+
+ pcbit_l2_write(dev, MSG_CONN_RESP, refnum, skb, len);
+}
+
+/*
+ * user has replied
+ * open the channel
+ * send CONNECT message CONNECT_ACTIVE_REQ in CAPI
+ */
+
+void cb_in_2(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data)
+{
+ unsigned short refnum;
+ struct sk_buff *skb;
+ int len;
+
+ if ((len = capi_conn_active_req(chan, &skb)) < 0) {
+ printk(KERN_DEBUG "capi_conn_active_req failed\n");
+ return;
+ }
+
+
+ refnum = last_ref_num++ & 0x7fffU;
+ chan->s_refnum = refnum;
+
+ printk(KERN_DEBUG "sending MSG_CONN_ACTV_REQ\n");
+ pcbit_l2_write(dev, MSG_CONN_ACTV_REQ, refnum, skb, len);
+}
+
+/*
+ * CONN_ACK arrived
+ * start b-proto selection
+ *
+ */
+
+void cb_in_3(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data)
+{
+ unsigned short refnum;
+ struct sk_buff *skb;
+ int len;
+
+ if ((len = capi_select_proto_req(chan, &skb, 0 /*incoming*/)) < 0)
+ {
+ printk("capi_select_proto_req failed\n");
+ return;
+ }
+
+ refnum = last_ref_num++ & 0x7fffU;
+ chan->s_refnum = refnum;
+
+ pcbit_l2_write(dev, MSG_SELP_REQ, refnum, skb, len);
+
+}
+
+
+/*
+ * Received disconnect ind on active state
+ * send disconnect resp
+ * send msg to user
+ */
+void cb_disc_1(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data)
+{
+ struct sk_buff *skb;
+ int len;
+ ushort refnum;
+ isdn_ctrl ictl;
+
+ if ((len = capi_disc_resp(chan, &skb)) < 0) {
+ printk("capi_disc_resp failed\n");
+ return;
+ }
+
+ refnum = last_ref_num++ & 0x7fffU;
+ chan->s_refnum = refnum;
+
+ pcbit_l2_write(dev, MSG_DISC_RESP, refnum, skb, len);
+
+ ictl.command = ISDN_STAT_BHUP;
+ ictl.driver=dev->id;
+ ictl.arg=chan->id;
+ dev->dev_if->statcallb(&ictl);
+}
+
+
+/*
+ * User HANGUP on active/call proceeding state
+ * send disc.req
+ */
+void cb_disc_2(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data)
+{
+ struct sk_buff *skb;
+ int len;
+ ushort refnum;
+
+ if ((len = capi_disc_req(chan->callref, &skb, CAUSE_NORMAL)) < 0)
+ {
+ printk("capi_disc_req failed\n");
+ return;
+ }
+
+ refnum = last_ref_num++ & 0x7fffU;
+ chan->s_refnum = refnum;
+
+ pcbit_l2_write(dev, MSG_DISC_REQ, refnum, skb, len);
+}
+
+/*
+ * Disc confirm received send BHUP
+ * Problem: when the HL driver sends the disc req itself
+ * LL receives BHUP
+ */
+void cb_disc_3(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data)
+{
+ isdn_ctrl ictl;
+
+ ictl.command = ISDN_STAT_BHUP;
+ ictl.driver=dev->id;
+ ictl.arg=chan->id;
+ dev->dev_if->statcallb(&ictl);
+}
+
+void cb_notdone(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data)
+{
+}
+
+/*
+ * send activate b-chan protocol
+ */
+void cb_selp_1(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data)
+{
+ struct sk_buff *skb;
+ int len;
+ ushort refnum;
+
+ if ((len = capi_activate_transp_req(chan, &skb)) < 0)
+ {
+ printk("capi_conn_activate_transp_req failed\n");
+ return;
+ }
+
+ refnum = last_ref_num++ & 0x7fffU;
+ chan->s_refnum = refnum;
+
+ pcbit_l2_write(dev, MSG_ACT_TRANSP_REQ, refnum, skb, len);
+}
+
+/*
+ * Inform User that the B-channel is available
+ */
+void cb_open(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data)
+{
+ isdn_ctrl ictl;
+
+ ictl.command = ISDN_STAT_BCONN;
+ ictl.driver=dev->id;
+ ictl.arg=chan->id;
+ dev->dev_if->statcallb(&ictl);
+}
+
+
+
diff --git a/drivers/isdn/pcbit/callbacks.h b/drivers/isdn/pcbit/callbacks.h
new file mode 100644
index 0000000..17aa0f5
--- /dev/null
+++ b/drivers/isdn/pcbit/callbacks.h
@@ -0,0 +1,46 @@
+/*
+ * Callbacks prototypes for FSM
+ *
+ * Copyright (C) 1996 Universidade de Lisboa
+ *
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU General Public License, incorporated herein by reference.
+ */
+
+#ifndef CALLBACKS_H
+#define CALLBACKS_H
+
+
+extern void cb_out_1(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data);
+
+extern void cb_out_2(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data);
+
+extern void cb_in_1(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data);
+extern void cb_in_2(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data);
+extern void cb_in_3(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data);
+
+extern void cb_disc_1(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data);
+extern void cb_disc_2(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data);
+extern void cb_disc_3(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data);
+
+extern void cb_notdone(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data);
+
+extern void cb_selp_1(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data);
+extern void cb_open(struct pcbit_dev * dev, struct pcbit_chan* chan,
+ struct callb_data *data);
+
+#endif
+
+
diff --git a/drivers/isdn/pcbit/capi.c b/drivers/isdn/pcbit/capi.c
new file mode 100644
index 0000000..ac5a91c
--- /dev/null
+++ b/drivers/isdn/pcbit/capi.c
@@ -0,0 +1,654 @@
+/*
+ * CAPI encoder/decoder for
+ * Portugal Telecom CAPI 2.0
+ *
+ * Copyright (C) 1996 Universidade de Lisboa
+ *
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU General Public License, incorporated herein by reference.
+ *
+ * Not compatible with the AVM Gmbh. CAPI 2.0
+ *
+ */
+
+/*
+ * Documentation:
+ * - "Common ISDN API - Perfil Português - Versão 2.1",
+ * Telecom Portugal, Fev 1992.
+ * - "Common ISDN API - Especificação de protocolos para
+ * acesso aos canais B", Inesc, Jan 1994.
+ */
+
+/*
+ * TODO: better decoding of Information Elements
+ * for debug purposes mainly
+ * encode our number in CallerPN and ConnectedPN
+ */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+
+#include <linux/skbuff.h>
+
+#include <asm/io.h>
+#include <asm/string.h>
+
+#include <linux/isdnif.h>
+
+#include "pcbit.h"
+#include "edss1.h"
+#include "capi.h"
+
+
+/*
+ * Encoding of CAPI messages
+ *
+ */
+
+int capi_conn_req(const char * calledPN, struct sk_buff **skb, int proto)
+{
+ ushort len;
+
+ /*
+ * length
+ * AppInfoMask - 2
+ * BC0 - 3
+ * BC1 - 1
+ * Chan - 2
+ * Keypad - 1
+ * CPN - 1
+ * CPSA - 1
+ * CalledPN - 2 + strlen
+ * CalledPSA - 1
+ * rest... - 4
+ * ----------------
+ * Total 18 + strlen
+ */
+
+ len = 18 + strlen(calledPN);
+
+ if (proto == ISDN_PROTO_L2_TRANS)
+ len++;
+
+ if ((*skb = dev_alloc_skb(len)) == NULL) {
+
+ printk(KERN_WARNING "capi_conn_req: alloc_skb failed\n");
+ return -1;
+ }
+
+ /* InfoElmMask */
+ *((ushort*) skb_put(*skb, 2)) = AppInfoMask;
+
+ if (proto == ISDN_PROTO_L2_TRANS)
+ {
+ /* Bearer Capability - Mandatory*/
+ *(skb_put(*skb, 1)) = 3; /* BC0.Length */
+ *(skb_put(*skb, 1)) = 0x80; /* Speech */
+ *(skb_put(*skb, 1)) = 0x10; /* Circuit Mode */
+ *(skb_put(*skb, 1)) = 0x23; /* A-law */
+ }
+ else
+ {
+ /* Bearer Capability - Mandatory*/
+ *(skb_put(*skb, 1)) = 2; /* BC0.Length */
+ *(skb_put(*skb, 1)) = 0x88; /* Digital Information */
+ *(skb_put(*skb, 1)) = 0x90; /* BC0.Octect4 */
+ }
+
+ /* Bearer Capability - Optional*/
+ *(skb_put(*skb, 1)) = 0; /* BC1.Length = 0 */
+
+ *(skb_put(*skb, 1)) = 1; /* ChannelID.Length = 1 */
+ *(skb_put(*skb, 1)) = 0x83; /* Basic Interface - Any Channel */
+
+ *(skb_put(*skb, 1)) = 0; /* Keypad.Length = 0 */
+
+
+ *(skb_put(*skb, 1)) = 0; /* CallingPN.Length = 0 */
+ *(skb_put(*skb, 1)) = 0; /* CallingPSA.Length = 0 */
+
+ /* Called Party Number */
+ *(skb_put(*skb, 1)) = strlen(calledPN) + 1;
+ *(skb_put(*skb, 1)) = 0x81;
+ memcpy(skb_put(*skb, strlen(calledPN)), calledPN, strlen(calledPN));
+
+ /* '#' */
+
+ *(skb_put(*skb, 1)) = 0; /* CalledPSA.Length = 0 */
+
+ /* LLC.Length = 0; */
+ /* HLC0.Length = 0; */
+ /* HLC1.Length = 0; */
+ /* UTUS.Length = 0; */
+ memset(skb_put(*skb, 4), 0, 4);
+
+ return len;
+}
+
+int capi_conn_resp(struct pcbit_chan* chan, struct sk_buff **skb)
+{
+
+ if ((*skb = dev_alloc_skb(5)) == NULL) {
+
+ printk(KERN_WARNING "capi_conn_resp: alloc_skb failed\n");
+ return -1;
+ }
+
+ *((ushort*) skb_put(*skb, 2) ) = chan->callref;
+ *(skb_put(*skb, 1)) = 0x01; /* ACCEPT_CALL */
+ *(skb_put(*skb, 1)) = 0;
+ *(skb_put(*skb, 1)) = 0;
+
+ return 5;
+}
+
+int capi_conn_active_req(struct pcbit_chan* chan, struct sk_buff **skb)
+{
+ /*
+ * 8 bytes
+ */
+
+ if ((*skb = dev_alloc_skb(8)) == NULL) {
+
+ printk(KERN_WARNING "capi_conn_active_req: alloc_skb failed\n");
+ return -1;
+ }
+
+ *((ushort*) skb_put(*skb, 2) ) = chan->callref;
+
+#ifdef DEBUG
+ printk(KERN_DEBUG "Call Reference: %04x\n", chan->callref);
+#endif
+
+ *(skb_put(*skb, 1)) = 0; /* BC.Length = 0; */
+ *(skb_put(*skb, 1)) = 0; /* ConnectedPN.Length = 0 */
+ *(skb_put(*skb, 1)) = 0; /* PSA.Length */
+ *(skb_put(*skb, 1)) = 0; /* LLC.Length = 0; */
+ *(skb_put(*skb, 1)) = 0; /* HLC.Length = 0; */
+ *(skb_put(*skb, 1)) = 0; /* UTUS.Length = 0; */
+
+ return 8;
+}
+
+int capi_conn_active_resp(struct pcbit_chan* chan, struct sk_buff **skb)
+{
+ /*
+ * 2 bytes
+ */
+
+ if ((*skb = dev_alloc_skb(2)) == NULL) {
+
+ printk(KERN_WARNING "capi_conn_active_resp: alloc_skb failed\n");
+ return -1;
+ }
+
+ *((ushort*) skb_put(*skb, 2) ) = chan->callref;
+
+ return 2;
+}
+
+
+int capi_select_proto_req(struct pcbit_chan *chan, struct sk_buff **skb,
+ int outgoing)
+{
+
+ /*
+ * 18 bytes
+ */
+
+ if ((*skb = dev_alloc_skb(18)) == NULL) {
+
+ printk(KERN_WARNING "capi_select_proto_req: alloc_skb failed\n");
+ return -1;
+ }
+
+ *((ushort*) skb_put(*skb, 2) ) = chan->callref;
+
+ /* Layer2 protocol */
+
+ switch (chan->proto) {
+ case ISDN_PROTO_L2_X75I:
+ *(skb_put(*skb, 1)) = 0x05; /* LAPB */
+ break;
+ case ISDN_PROTO_L2_HDLC:
+ *(skb_put(*skb, 1)) = 0x02;
+ break;
+ case ISDN_PROTO_L2_TRANS:
+ /*
+ * Voice (a-law)
+ */
+ *(skb_put(*skb, 1)) = 0x06;
+ break;
+ default:
+#ifdef DEBUG
+ printk(KERN_DEBUG "Transparent\n");
+#endif
+ *(skb_put(*skb, 1)) = 0x03;
+ break;
+ }
+
+ *(skb_put(*skb, 1)) = (outgoing ? 0x02 : 0x42); /* Don't ask */
+ *(skb_put(*skb, 1)) = 0x00;
+
+ *((ushort *) skb_put(*skb, 2)) = MRU;
+
+
+ *(skb_put(*skb, 1)) = 0x08; /* Modulo */
+ *(skb_put(*skb, 1)) = 0x07; /* Max Window */
+
+ *(skb_put(*skb, 1)) = 0x01; /* No Layer3 Protocol */
+
+ /*
+ * 2 - layer3 MTU [10]
+ * - Modulo [12]
+ * - Window
+ * - layer1 proto [14]
+ * - bitrate
+ * - sub-channel [16]
+ * - layer1dataformat [17]
+ */
+
+ memset(skb_put(*skb, 8), 0, 8);
+
+ return 18;
+}
+
+
+int capi_activate_transp_req(struct pcbit_chan *chan, struct sk_buff **skb)
+{
+
+ if ((*skb = dev_alloc_skb(7)) == NULL) {
+
+ printk(KERN_WARNING "capi_activate_transp_req: alloc_skb failed\n");
+ return -1;
+ }
+
+ *((ushort*) skb_put(*skb, 2) ) = chan->callref;
+
+
+ *(skb_put(*skb, 1)) = chan->layer2link; /* Layer2 id */
+ *(skb_put(*skb, 1)) = 0x00; /* Transmit by default */
+
+ *((ushort *) skb_put(*skb, 2)) = MRU;
+
+ *(skb_put(*skb, 1)) = 0x01; /* Enables reception*/
+
+ return 7;
+}
+
+int capi_tdata_req(struct pcbit_chan* chan, struct sk_buff *skb)
+{
+ ushort data_len;
+
+
+ /*
+ * callref - 2
+ * layer2link - 1
+ * wBlockLength - 2
+ * data - 4
+ * sernum - 1
+ */
+
+ data_len = skb->len;
+
+ if(skb_headroom(skb) < 10)
+ {
+ printk(KERN_CRIT "No headspace (%u) on headroom %p for capi header\n", skb_headroom(skb), skb);
+ }
+ else
+ {
+ skb_push(skb, 10);
+ }
+
+ *((u16 *) (skb->data)) = chan->callref;
+ skb->data[2] = chan->layer2link;
+ *((u16 *) (skb->data + 3)) = data_len;
+
+ chan->s_refnum = (chan->s_refnum + 1) % 8;
+ *((u32 *) (skb->data + 5)) = chan->s_refnum;
+
+ skb->data[9] = 0; /* HDLC frame number */
+
+ return 10;
+}
+
+int capi_tdata_resp(struct pcbit_chan *chan, struct sk_buff ** skb)
+
+{
+ if ((*skb = dev_alloc_skb(4)) == NULL) {
+
+ printk(KERN_WARNING "capi_tdata_resp: alloc_skb failed\n");
+ return -1;
+ }
+
+ *((ushort*) skb_put(*skb, 2) ) = chan->callref;
+
+ *(skb_put(*skb, 1)) = chan->layer2link;
+ *(skb_put(*skb, 1)) = chan->r_refnum;
+
+ return (*skb)->len;
+}
+
+int capi_disc_req(ushort callref, struct sk_buff **skb, u_char cause)
+{
+
+ if ((*skb = dev_alloc_skb(6)) == NULL) {
+
+ printk(KERN_WARNING "capi_disc_req: alloc_skb failed\n");
+ return -1;
+ }
+
+ *((ushort*) skb_put(*skb, 2) ) = callref;
+
+ *(skb_put(*skb, 1)) = 2; /* Cause.Length = 2; */
+ *(skb_put(*skb, 1)) = 0x80;
+ *(skb_put(*skb, 1)) = 0x80 | cause;
+
+ /*
+ * Change it: we should send 'Sic transit gloria Mundi' here ;-)
+ */
+
+ *(skb_put(*skb, 1)) = 0; /* UTUS.Length = 0; */
+
+ return 6;
+}
+
+int capi_disc_resp(struct pcbit_chan *chan, struct sk_buff **skb)
+{
+ if ((*skb = dev_alloc_skb(2)) == NULL) {
+
+ printk(KERN_WARNING "capi_disc_resp: alloc_skb failed\n");
+ return -1;
+ }
+
+ *((ushort*) skb_put(*skb, 2)) = chan->callref;
+
+ return 2;
+}
+
+
+/*
+ * Decoding of CAPI messages
+ *
+ */
+
+int capi_decode_conn_ind(struct pcbit_chan * chan,
+ struct sk_buff *skb,
+ struct callb_data *info)
+{
+ int CIlen, len;
+
+ /* Call Reference [CAPI] */
+ chan->callref = *((ushort*) skb->data);
+ skb_pull(skb, 2);
+
+#ifdef DEBUG
+ printk(KERN_DEBUG "Call Reference: %04x\n", chan->callref);
+#endif
+
+ /* Channel Identification */
+
+ /* Expect
+ Len = 1
+ Octect 3 = 0100 10CC - [ 7 Basic, 4 , 2-1 chan ]
+ */
+
+ CIlen = skb->data[0];
+#ifdef DEBUG
+ if (CIlen == 1) {
+
+ if ( ((skb->data[1]) & 0xFC) == 0x48 )
+ printk(KERN_DEBUG "decode_conn_ind: chan ok\n");
+ printk(KERN_DEBUG "phyChan = %d\n", skb->data[1] & 0x03);
+ }
+ else
+ printk(KERN_DEBUG "conn_ind: CIlen = %d\n", CIlen);
+#endif
+ skb_pull(skb, CIlen + 1);
+
+ /* Calling Party Number */
+ /* An "additional service" as far as Portugal Telecom is concerned */
+
+ len = skb->data[0];
+
+ if (len > 0) {
+ int count = 1;
+
+#ifdef DEBUG
+ printk(KERN_DEBUG "CPN: Octect 3 %02x\n", skb->data[1]);
+#endif
+ if ((skb->data[1] & 0x80) == 0)
+ count = 2;
+
+ if (!(info->data.setup.CallingPN = kmalloc(len - count + 1, GFP_ATOMIC)))
+ return -1;
+
+ skb_copy_from_linear_data_offset(skb, count + 1,
+ info->data.setup.CallingPN,
+ len - count);
+ info->data.setup.CallingPN[len - count] = 0;
+
+ }
+ else {
+ info->data.setup.CallingPN = NULL;
+ printk(KERN_DEBUG "NULL CallingPN\n");
+ }
+
+ skb_pull(skb, len + 1);
+
+ /* Calling Party Subaddress */
+ skb_pull(skb, skb->data[0] + 1);
+
+ /* Called Party Number */
+
+ len = skb->data[0];
+
+ if (len > 0) {
+ int count = 1;
+
+ if ((skb->data[1] & 0x80) == 0)
+ count = 2;
+
+ if (!(info->data.setup.CalledPN = kmalloc(len - count + 1, GFP_ATOMIC)))
+ return -1;
+
+ skb_copy_from_linear_data_offset(skb, count + 1,
+ info->data.setup.CalledPN,
+ len - count);
+ info->data.setup.CalledPN[len - count] = 0;
+
+ }
+ else {
+ info->data.setup.CalledPN = NULL;
+ printk(KERN_DEBUG "NULL CalledPN\n");
+ }
+
+ skb_pull(skb, len + 1);
+
+ /* Called Party Subaddress */
+ skb_pull(skb, skb->data[0] + 1);
+
+ /* LLC */
+ skb_pull(skb, skb->data[0] + 1);
+
+ /* HLC */
+ skb_pull(skb, skb->data[0] + 1);
+
+ /* U2U */
+ skb_pull(skb, skb->data[0] + 1);
+
+ return 0;
+}
+
+/*
+ * returns errcode
+ */
+
+int capi_decode_conn_conf(struct pcbit_chan * chan, struct sk_buff *skb,
+ int *complete)
+{
+ int errcode;
+
+ chan->callref = *((ushort *) skb->data); /* Update CallReference */
+ skb_pull(skb, 2);
+
+ errcode = *((ushort *) skb->data); /* read errcode */
+ skb_pull(skb, 2);
+
+ *complete = *(skb->data);
+ skb_pull(skb, 1);
+
+ /* FIX ME */
+ /* This is actually a firmware bug */
+ if (!*complete)
+ {
+ printk(KERN_DEBUG "complete=%02x\n", *complete);
+ *complete = 1;
+ }
+
+
+ /* Optional Bearer Capability */
+ skb_pull(skb, *(skb->data) + 1);
+
+ /* Channel Identification */
+ skb_pull(skb, *(skb->data) + 1);
+
+ /* High Layer Compatibility follows */
+ skb_pull(skb, *(skb->data) + 1);
+
+ return errcode;
+}
+
+int capi_decode_conn_actv_ind(struct pcbit_chan * chan, struct sk_buff *skb)
+{
+ ushort len;
+#ifdef DEBUG
+ char str[32];
+#endif
+
+ /* Yet Another Bearer Capability */
+ skb_pull(skb, *(skb->data) + 1);
+
+
+ /* Connected Party Number */
+ len=*(skb->data);
+
+#ifdef DEBUG
+ if (len > 1 && len < 31) {
+ skb_copy_from_linear_data_offset(skb, 2, str, len - 1);
+ str[len] = 0;
+ printk(KERN_DEBUG "Connected Party Number: %s\n", str);
+ }
+ else
+ printk(KERN_DEBUG "actv_ind CPN len = %d\n", len);
+#endif
+
+ skb_pull(skb, len + 1);
+
+ /* Connected Subaddress */
+ skb_pull(skb, *(skb->data) + 1);
+
+ /* Low Layer Capability */
+ skb_pull(skb, *(skb->data) + 1);
+
+ /* High Layer Capability */
+ skb_pull(skb, *(skb->data) + 1);
+
+ return 0;
+}
+
+int capi_decode_conn_actv_conf(struct pcbit_chan * chan, struct sk_buff *skb)
+{
+ ushort errcode;
+
+ errcode = *((ushort*) skb->data);
+ skb_pull(skb, 2);
+
+ /* Channel Identification
+ skb_pull(skb, skb->data[0] + 1);
+ */
+ return errcode;
+}
+
+
+int capi_decode_sel_proto_conf(struct pcbit_chan *chan, struct sk_buff *skb)
+{
+ ushort errcode;
+
+ chan->layer2link = *(skb->data);
+ skb_pull(skb, 1);
+
+ errcode = *((ushort*) skb->data);
+ skb_pull(skb, 2);
+
+ return errcode;
+}
+
+int capi_decode_actv_trans_conf(struct pcbit_chan *chan, struct sk_buff *skb)
+{
+ ushort errcode;
+
+ if (chan->layer2link != *(skb->data) )
+ printk("capi_decode_actv_trans_conf: layer2link doesn't match\n");
+
+ skb_pull(skb, 1);
+
+ errcode = *((ushort*) skb->data);
+ skb_pull(skb, 2);
+
+ return errcode;
+}
+
+int capi_decode_disc_ind(struct pcbit_chan *chan, struct sk_buff *skb)
+{
+ ushort len;
+#ifdef DEBUG
+ int i;
+#endif
+ /* Cause */
+
+ len = *(skb->data);
+ skb_pull(skb, 1);
+
+#ifdef DEBUG
+
+ for (i=0; i<len; i++)
+ printk(KERN_DEBUG "Cause Octect %d: %02x\n", i+3,
+ *(skb->data + i));
+#endif
+
+ skb_pull(skb, len);
+
+ return 0;
+}
+
+#ifdef DEBUG
+int capi_decode_debug_188(u_char *hdr, ushort hdrlen)
+{
+ char str[64];
+ int len;
+
+ len = hdr[0];
+
+ if (len < 64 && len == hdrlen - 1) {
+ memcpy(str, hdr + 1, hdrlen - 1);
+ str[hdrlen - 1] = 0;
+ printk("%s\n", str);
+ }
+ else
+ printk("debug message incorrect\n");
+
+ return 0;
+}
+#endif
+
+
+
+
+
diff --git a/drivers/isdn/pcbit/capi.h b/drivers/isdn/pcbit/capi.h
new file mode 100644
index 0000000..df8e73c
--- /dev/null
+++ b/drivers/isdn/pcbit/capi.h
@@ -0,0 +1,87 @@
+/*
+ * CAPI encode/decode prototypes and defines
+ *
+ * Copyright (C) 1996 Universidade de Lisboa
+ *
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU General Public License, incorporated herein by reference.
+ */
+
+#ifndef CAPI_H
+#define CAPI_H
+
+
+#define REQ_CAUSE 0x01
+#define REQ_DISPLAY 0x04
+#define REQ_USER_TO_USER 0x08
+
+#define AppInfoMask REQ_CAUSE|REQ_DISPLAY|REQ_USER_TO_USER
+
+/* Connection Setup */
+extern int capi_conn_req(const char * calledPN, struct sk_buff **buf,
+ int proto);
+extern int capi_decode_conn_conf(struct pcbit_chan * chan, struct sk_buff *skb,
+ int *complete);
+
+extern int capi_decode_conn_ind(struct pcbit_chan * chan, struct sk_buff *skb,
+ struct callb_data *info);
+extern int capi_conn_resp(struct pcbit_chan* chan, struct sk_buff **skb);
+
+extern int capi_conn_active_req(struct pcbit_chan* chan, struct sk_buff **skb);
+extern int capi_decode_conn_actv_conf(struct pcbit_chan * chan,
+ struct sk_buff *skb);
+
+extern int capi_decode_conn_actv_ind(struct pcbit_chan * chan,
+ struct sk_buff *skb);
+extern int capi_conn_active_resp(struct pcbit_chan* chan,
+ struct sk_buff **skb);
+
+/* Data */
+extern int capi_select_proto_req(struct pcbit_chan *chan, struct sk_buff **skb,
+ int outgoing);
+extern int capi_decode_sel_proto_conf(struct pcbit_chan *chan,
+ struct sk_buff *skb);
+
+extern int capi_activate_transp_req(struct pcbit_chan *chan,
+ struct sk_buff **skb);
+extern int capi_decode_actv_trans_conf(struct pcbit_chan *chan,
+ struct sk_buff *skb);
+
+extern int capi_tdata_req(struct pcbit_chan* chan, struct sk_buff *skb);
+extern int capi_tdata_resp(struct pcbit_chan *chan, struct sk_buff ** skb);
+
+/* Connection Termination */
+extern int capi_disc_req(ushort callref, struct sk_buff **skb, u_char cause);
+
+extern int capi_decode_disc_ind(struct pcbit_chan *chan, struct sk_buff *skb);
+extern int capi_disc_resp(struct pcbit_chan *chan, struct sk_buff **skb);
+
+#ifdef DEBUG
+extern int capi_decode_debug_188(u_char *hdr, ushort hdrlen);
+#endif
+
+static inline struct pcbit_chan *
+capi_channel(struct pcbit_dev *dev, struct sk_buff *skb)
+{
+ ushort callref;
+
+ callref = *((ushort*) skb->data);
+ skb_pull(skb, 2);
+
+ if (dev->b1->callref == callref)
+ return dev->b1;
+ else if (dev->b2->callref == callref)
+ return dev->b2;
+
+ return NULL;
+}
+
+#endif
+
+
+
+
+
+
diff --git a/drivers/isdn/pcbit/drv.c b/drivers/isdn/pcbit/drv.c
new file mode 100644
index 0000000..8c66bcb
--- /dev/null
+++ b/drivers/isdn/pcbit/drv.c
@@ -0,0 +1,1080 @@
+/*
+ * PCBIT-D interface with isdn4linux
+ *
+ * Copyright (C) 1996 Universidade de Lisboa
+ *
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU General Public License, incorporated herein by reference.
+ */
+
+/*
+ * Fixes:
+ *
+ * Nuno Grilo <l38486@alfa.ist.utl.pt>
+ * fixed msn_list NULL pointer dereference.
+ *
+ */
+
+#include <linux/module.h>
+
+
+#include <linux/kernel.h>
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/string.h>
+#include <linux/skbuff.h>
+
+#include <linux/isdnif.h>
+#include <asm/string.h>
+#include <asm/io.h>
+#include <linux/ioport.h>
+
+#include "pcbit.h"
+#include "edss1.h"
+#include "layer2.h"
+#include "capi.h"
+
+
+extern ushort last_ref_num;
+
+static int pcbit_ioctl(isdn_ctrl* ctl);
+
+static char* pcbit_devname[MAX_PCBIT_CARDS] = {
+ "pcbit0",
+ "pcbit1",
+ "pcbit2",
+ "pcbit3"
+};
+
+/*
+ * prototypes
+ */
+
+static int pcbit_command(isdn_ctrl* ctl);
+static int pcbit_stat(u_char __user * buf, int len, int, int);
+static int pcbit_xmit(int driver, int chan, int ack, struct sk_buff *skb);
+static int pcbit_writecmd(const u_char __user *, int, int, int);
+
+static int set_protocol_running(struct pcbit_dev * dev);
+
+static void pcbit_clear_msn(struct pcbit_dev *dev);
+static void pcbit_set_msn(struct pcbit_dev *dev, char *list);
+static int pcbit_check_msn(struct pcbit_dev *dev, char *msn);
+
+
+int pcbit_init_dev(int board, int mem_base, int irq)
+{
+ struct pcbit_dev *dev;
+ isdn_if *dev_if;
+
+ if ((dev=kzalloc(sizeof(struct pcbit_dev), GFP_KERNEL)) == NULL)
+ {
+ printk("pcbit_init: couldn't malloc pcbit_dev struct\n");
+ return -ENOMEM;
+ }
+
+ dev_pcbit[board] = dev;
+ init_waitqueue_head(&dev->set_running_wq);
+ spin_lock_init(&dev->lock);
+
+ if (mem_base >= 0xA0000 && mem_base <= 0xFFFFF ) {
+ dev->ph_mem = mem_base;
+ if (!request_mem_region(dev->ph_mem, 4096, "PCBIT mem")) {
+ printk(KERN_WARNING
+ "PCBIT: memory region %lx-%lx already in use\n",
+ dev->ph_mem, dev->ph_mem + 4096);
+ kfree(dev);
+ dev_pcbit[board] = NULL;
+ return -EACCES;
+ }
+ dev->sh_mem = ioremap(dev->ph_mem, 4096);
+ }
+ else
+ {
+ printk("memory address invalid");
+ kfree(dev);
+ dev_pcbit[board] = NULL;
+ return -EACCES;
+ }
+
+ dev->b1 = kzalloc(sizeof(struct pcbit_chan), GFP_KERNEL);
+ if (!dev->b1) {
+ printk("pcbit_init: couldn't malloc pcbit_chan struct\n");
+ iounmap(dev->sh_mem);
+ release_mem_region(dev->ph_mem, 4096);
+ kfree(dev);
+ return -ENOMEM;
+ }
+
+ dev->b2 = kzalloc(sizeof(struct pcbit_chan), GFP_KERNEL);
+ if (!dev->b2) {
+ printk("pcbit_init: couldn't malloc pcbit_chan struct\n");
+ kfree(dev->b1);
+ iounmap(dev->sh_mem);
+ release_mem_region(dev->ph_mem, 4096);
+ kfree(dev);
+ return -ENOMEM;
+ }
+
+ dev->b2->id = 1;
+
+ INIT_WORK(&dev->qdelivery, pcbit_deliver);
+
+ /*
+ * interrupts
+ */
+
+ if (request_irq(irq, &pcbit_irq_handler, 0, pcbit_devname[board], dev) != 0)
+ {
+ kfree(dev->b1);
+ kfree(dev->b2);
+ iounmap(dev->sh_mem);
+ release_mem_region(dev->ph_mem, 4096);
+ kfree(dev);
+ dev_pcbit[board] = NULL;
+ return -EIO;
+ }
+
+ dev->irq = irq;
+
+ /* next frame to be received */
+ dev->rcv_seq = 0;
+ dev->send_seq = 0;
+ dev->unack_seq = 0;
+
+ dev->hl_hdrlen = 16;
+
+ dev_if = kmalloc(sizeof(isdn_if), GFP_KERNEL);
+
+ if (!dev_if) {
+ free_irq(irq, dev);
+ kfree(dev->b1);
+ kfree(dev->b2);
+ iounmap(dev->sh_mem);
+ release_mem_region(dev->ph_mem, 4096);
+ kfree(dev);
+ dev_pcbit[board] = NULL;
+ return -EIO;
+ }
+
+ dev->dev_if = dev_if;
+
+ dev_if->owner = THIS_MODULE;
+
+ dev_if->channels = 2;
+
+ dev_if->features = (ISDN_FEATURE_P_EURO | ISDN_FEATURE_L3_TRANS |
+ ISDN_FEATURE_L2_HDLC | ISDN_FEATURE_L2_TRANS );
+
+ dev_if->writebuf_skb = pcbit_xmit;
+ dev_if->hl_hdrlen = 16;
+
+ dev_if->maxbufsize = MAXBUFSIZE;
+ dev_if->command = pcbit_command;
+
+ dev_if->writecmd = pcbit_writecmd;
+ dev_if->readstat = pcbit_stat;
+
+
+ strcpy(dev_if->id, pcbit_devname[board]);
+
+ if (!register_isdn(dev_if)) {
+ free_irq(irq, dev);
+ kfree(dev->b1);
+ kfree(dev->b2);
+ iounmap(dev->sh_mem);
+ release_mem_region(dev->ph_mem, 4096);
+ kfree(dev);
+ dev_pcbit[board] = NULL;
+ return -EIO;
+ }
+
+ dev->id = dev_if->channels;
+
+
+ dev->l2_state = L2_DOWN;
+ dev->free = 511;
+
+ /*
+ * set_protocol_running(dev);
+ */
+
+ return 0;
+}
+
+#ifdef MODULE
+void pcbit_terminate(int board)
+{
+ struct pcbit_dev * dev;
+
+ dev = dev_pcbit[board];
+
+ if (dev) {
+ /* unregister_isdn(dev->dev_if); */
+ free_irq(dev->irq, dev);
+ pcbit_clear_msn(dev);
+ kfree(dev->dev_if);
+ if (dev->b1->fsm_timer.function)
+ del_timer(&dev->b1->fsm_timer);
+ if (dev->b2->fsm_timer.function)
+ del_timer(&dev->b2->fsm_timer);
+ kfree(dev->b1);
+ kfree(dev->b2);
+ iounmap(dev->sh_mem);
+ release_mem_region(dev->ph_mem, 4096);
+ kfree(dev);
+ }
+}
+#endif
+
+static int pcbit_command(isdn_ctrl* ctl)
+{
+ struct pcbit_dev *dev;
+ struct pcbit_chan *chan;
+ struct callb_data info;
+
+ dev = finddev(ctl->driver);
+
+ if (!dev)
+ {
+ printk("pcbit_command: unknown device\n");
+ return -1;
+ }
+
+ chan = (ctl->arg & 0x0F) ? dev->b2 : dev->b1;
+
+
+ switch(ctl->command) {
+ case ISDN_CMD_IOCTL:
+ return pcbit_ioctl(ctl);
+ break;
+ case ISDN_CMD_DIAL:
+ info.type = EV_USR_SETUP_REQ;
+ info.data.setup.CalledPN = (char *) &ctl->parm.setup.phone;
+ pcbit_fsm_event(dev, chan, EV_USR_SETUP_REQ, &info);
+ break;
+ case ISDN_CMD_ACCEPTD:
+ pcbit_fsm_event(dev, chan, EV_USR_SETUP_RESP, NULL);
+ break;
+ case ISDN_CMD_ACCEPTB:
+ printk("ISDN_CMD_ACCEPTB - not really needed\n");
+ break;
+ case ISDN_CMD_HANGUP:
+ pcbit_fsm_event(dev, chan, EV_USR_RELEASE_REQ, NULL);
+ break;
+ case ISDN_CMD_SETL2:
+ chan->proto = (ctl->arg >> 8);
+ break;
+ case ISDN_CMD_CLREAZ:
+ pcbit_clear_msn(dev);
+ break;
+ case ISDN_CMD_SETEAZ:
+ pcbit_set_msn(dev, ctl->parm.num);
+ break;
+ case ISDN_CMD_SETL3:
+ if ((ctl->arg >> 8) != ISDN_PROTO_L3_TRANS)
+ printk(KERN_DEBUG "L3 protocol unknown\n");
+ break;
+ default:
+ printk(KERN_DEBUG "pcbit_command: unknown command\n");
+ break;
+ };
+
+ return 0;
+}
+
+/*
+ * Another Hack :-(
+ * on some conditions the board stops sending TDATA_CONFs
+ * let's see if we can turn around the problem
+ */
+
+#ifdef BLOCK_TIMER
+static void pcbit_block_timer(unsigned long data)
+{
+ struct pcbit_chan *chan;
+ struct pcbit_dev * dev;
+ isdn_ctrl ictl;
+
+ chan = (struct pcbit_chan *) data;
+
+ dev = chan2dev(chan);
+
+ if (dev == NULL) {
+ printk(KERN_DEBUG "pcbit: chan2dev failed\n");
+ return;
+ }
+
+ del_timer(&chan->block_timer);
+ chan->block_timer.function = NULL;
+
+#ifdef DEBUG
+ printk(KERN_DEBUG "pcbit_block_timer\n");
+#endif
+ chan->queued = 0;
+ ictl.driver = dev->id;
+ ictl.command = ISDN_STAT_BSENT;
+ ictl.arg = chan->id;
+ dev->dev_if->statcallb(&ictl);
+}
+#endif
+
+static int pcbit_xmit(int driver, int chnum, int ack, struct sk_buff *skb)
+{
+ ushort hdrlen;
+ int refnum, len;
+ struct pcbit_chan * chan;
+ struct pcbit_dev *dev;
+
+ dev = finddev(driver);
+ if (dev == NULL)
+ {
+ printk("finddev returned NULL");
+ return -1;
+ }
+
+ chan = chnum ? dev->b2 : dev->b1;
+
+
+ if (chan->fsm_state != ST_ACTIVE)
+ return -1;
+
+ if (chan->queued >= MAX_QUEUED )
+ {
+#ifdef DEBUG_QUEUE
+ printk(KERN_DEBUG
+ "pcbit: %d packets already in queue - write fails\n",
+ chan->queued);
+#endif
+ /*
+ * packet stays on the head of the device queue
+ * since dev_start_xmit will fail
+ * see net/core/dev.c
+ */
+#ifdef BLOCK_TIMER
+ if (chan->block_timer.function == NULL) {
+ init_timer(&chan->block_timer);
+ chan->block_timer.function = &pcbit_block_timer;
+ chan->block_timer.data = (long) chan;
+ chan->block_timer.expires = jiffies + 1 * HZ;
+ add_timer(&chan->block_timer);
+ }
+#endif
+ return 0;
+ }
+
+
+ chan->queued++;
+
+ len = skb->len;
+
+ hdrlen = capi_tdata_req(chan, skb);
+
+ refnum = last_ref_num++ & 0x7fffU;
+ chan->s_refnum = refnum;
+
+ pcbit_l2_write(dev, MSG_TDATA_REQ, refnum, skb, hdrlen);
+
+ return len;
+}
+
+static int pcbit_writecmd(const u_char __user *buf, int len, int driver, int channel)
+{
+ struct pcbit_dev * dev;
+ int i, j;
+ const u_char * loadbuf;
+ u_char * ptr = NULL;
+ u_char *cbuf;
+
+ int errstat;
+
+ dev = finddev(driver);
+
+ if (!dev)
+ {
+ printk("pcbit_writecmd: couldn't find device");
+ return -ENODEV;
+ }
+
+ switch(dev->l2_state) {
+ case L2_LWMODE:
+ /* check (size <= rdp_size); write buf into board */
+ if (len < 0 || len > BANK4 + 1 || len > 1024)
+ {
+ printk("pcbit_writecmd: invalid length %d\n", len);
+ return -EINVAL;
+ }
+
+ cbuf = kmalloc(len, GFP_KERNEL);
+ if (!cbuf)
+ return -ENOMEM;
+
+ if (copy_from_user(cbuf, buf, len)) {
+ kfree(cbuf);
+ return -EFAULT;
+ }
+ memcpy_toio(dev->sh_mem, cbuf, len);
+ kfree(cbuf);
+ return len;
+ case L2_FWMODE:
+ /* this is the hard part */
+ /* dumb board */
+ /* get it into kernel space */
+ if ((ptr = kmalloc(len, GFP_KERNEL))==NULL)
+ return -ENOMEM;
+ if (copy_from_user(ptr, buf, len)) {
+ kfree(ptr);
+ return -EFAULT;
+ }
+ loadbuf = ptr;
+
+ errstat = 0;
+
+ for (i=0; i < len; i++)
+ {
+ for(j=0; j < LOAD_RETRY; j++)
+ if (!(readb(dev->sh_mem + dev->loadptr)))
+ break;
+
+ if (j == LOAD_RETRY)
+ {
+ errstat = -ETIME;
+ printk("TIMEOUT i=%d\n", i);
+ break;
+ }
+ writeb(loadbuf[i], dev->sh_mem + dev->loadptr + 1);
+ writeb(0x01, dev->sh_mem + dev->loadptr);
+
+ dev->loadptr += 2;
+ if (dev->loadptr > LOAD_ZONE_END)
+ dev->loadptr = LOAD_ZONE_START;
+ }
+ kfree(ptr);
+
+ return errstat ? errstat : len;
+ default:
+ return -EBUSY;
+ }
+}
+
+/*
+ * demultiplexing of messages
+ *
+ */
+
+void pcbit_l3_receive(struct pcbit_dev * dev, ulong msg,
+ struct sk_buff * skb,
+ ushort hdr_len, ushort refnum)
+{
+ struct pcbit_chan *chan;
+ struct sk_buff *skb2;
+ unsigned short len;
+ struct callb_data cbdata;
+ int complete, err;
+ isdn_ctrl ictl;
+
+ switch(msg) {
+
+ case MSG_TDATA_IND:
+ if (!(chan = capi_channel(dev, skb))) {
+ printk(KERN_WARNING
+ "CAPI header: unknown channel id\n");
+ break;
+ }
+ chan->r_refnum = skb->data[7];
+ skb_pull(skb, 8);
+
+ dev->dev_if->rcvcallb_skb(dev->id, chan->id, skb);
+
+ if (capi_tdata_resp(chan, &skb2) > 0)
+ pcbit_l2_write(dev, MSG_TDATA_RESP, refnum,
+ skb2, skb2->len);
+ return;
+ break;
+ case MSG_TDATA_CONF:
+ if (!(chan = capi_channel(dev, skb))) {
+ printk(KERN_WARNING
+ "CAPI header: unknown channel id\n");
+ break;
+ }
+
+#ifdef DEBUG
+ if ( (*((ushort *) (skb->data + 2) )) != 0) {
+ printk(KERN_DEBUG "TDATA_CONF error\n");
+ }
+#endif
+#ifdef BLOCK_TIMER
+ if (chan->queued == MAX_QUEUED) {
+ del_timer(&chan->block_timer);
+ chan->block_timer.function = NULL;
+ }
+
+#endif
+ chan->queued--;
+
+ ictl.driver = dev->id;
+ ictl.command = ISDN_STAT_BSENT;
+ ictl.arg = chan->id;
+ dev->dev_if->statcallb(&ictl);
+ break;
+
+ case MSG_CONN_IND:
+ /*
+ * channel: 1st not used will do
+ * if both are used we're in trouble
+ */
+
+ if (!dev->b1->fsm_state)
+ chan = dev->b1;
+ else if (!dev->b2->fsm_state)
+ chan = dev->b2;
+ else {
+ printk(KERN_INFO
+ "Incoming connection: no channels available");
+
+ if ((len = capi_disc_req(*(ushort*)(skb->data), &skb2, CAUSE_NOCHAN)) > 0)
+ pcbit_l2_write(dev, MSG_DISC_REQ, refnum, skb2, len);
+ break;
+ }
+
+ cbdata.data.setup.CalledPN = NULL;
+ cbdata.data.setup.CallingPN = NULL;
+
+ capi_decode_conn_ind(chan, skb, &cbdata);
+ cbdata.type = EV_NET_SETUP;
+
+ pcbit_fsm_event(dev, chan, EV_NET_SETUP, NULL);
+
+ if (pcbit_check_msn(dev, cbdata.data.setup.CallingPN))
+ pcbit_fsm_event(dev, chan, EV_USR_PROCED_REQ, &cbdata);
+ else
+ pcbit_fsm_event(dev, chan, EV_USR_RELEASE_REQ, NULL);
+
+ kfree(cbdata.data.setup.CalledPN);
+ kfree(cbdata.data.setup.CallingPN);
+ break;
+
+ case MSG_CONN_CONF:
+ /*
+ * We should be able to find the channel by the message
+ * reference number. The current version of the firmware
+ * doesn't sent the ref number correctly.
+ */
+#ifdef DEBUG
+ printk(KERN_DEBUG "refnum=%04x b1=%04x b2=%04x\n", refnum,
+ dev->b1->s_refnum,
+ dev->b2->s_refnum);
+#endif
+ /* We just try to find a channel in the right state */
+
+ if (dev->b1->fsm_state == ST_CALL_INIT)
+ chan = dev->b1;
+ else {
+ if (dev->b2->s_refnum == ST_CALL_INIT)
+ chan = dev->b2;
+ else {
+ chan = NULL;
+ printk(KERN_WARNING "Connection Confirm - no channel in Call Init state\n");
+ break;
+ }
+ }
+ if (capi_decode_conn_conf(chan, skb, &complete)) {
+ printk(KERN_DEBUG "conn_conf indicates error\n");
+ pcbit_fsm_event(dev, chan, EV_ERROR, NULL);
+ }
+ else
+ if (complete)
+ pcbit_fsm_event(dev, chan, EV_NET_CALL_PROC, NULL);
+ else
+ pcbit_fsm_event(dev, chan, EV_NET_SETUP_ACK, NULL);
+ break;
+ case MSG_CONN_ACTV_IND:
+
+ if (!(chan = capi_channel(dev, skb))) {
+ printk(KERN_WARNING
+ "CAPI header: unknown channel id\n");
+ break;
+ }
+
+ if (capi_decode_conn_actv_ind(chan, skb)) {
+ printk("error in capi_decode_conn_actv_ind\n");
+ /* pcbit_fsm_event(dev, chan, EV_ERROR, NULL); */
+ break;
+ }
+ chan->r_refnum = refnum;
+ pcbit_fsm_event(dev, chan, EV_NET_CONN, NULL);
+ break;
+ case MSG_CONN_ACTV_CONF:
+
+ if (!(chan = capi_channel(dev, skb))) {
+ printk(KERN_WARNING
+ "CAPI header: unknown channel id\n");
+ break;
+ }
+
+ if (capi_decode_conn_actv_conf(chan, skb) == 0)
+ pcbit_fsm_event(dev, chan, EV_NET_CONN_ACK, NULL);
+
+ else
+ printk(KERN_DEBUG "decode_conn_actv_conf failed\n");
+ break;
+
+ case MSG_SELP_CONF:
+
+ if (!(chan = capi_channel(dev, skb))) {
+ printk(KERN_WARNING
+ "CAPI header: unknown channel id\n");
+ break;
+ }
+
+ if (!(err = capi_decode_sel_proto_conf(chan, skb)))
+ pcbit_fsm_event(dev, chan, EV_NET_SELP_RESP, NULL);
+ else {
+ /* Error */
+ printk("error %d - capi_decode_sel_proto_conf\n", err);
+ }
+ break;
+ case MSG_ACT_TRANSP_CONF:
+ if (!(chan = capi_channel(dev, skb))) {
+ printk(KERN_WARNING
+ "CAPI header: unknown channel id\n");
+ break;
+ }
+
+ if (!capi_decode_actv_trans_conf(chan, skb))
+ pcbit_fsm_event(dev, chan, EV_NET_ACTV_RESP, NULL);
+ break;
+
+ case MSG_DISC_IND:
+
+ if (!(chan = capi_channel(dev, skb))) {
+ printk(KERN_WARNING
+ "CAPI header: unknown channel id\n");
+ break;
+ }
+
+ if (!capi_decode_disc_ind(chan, skb))
+ pcbit_fsm_event(dev, chan, EV_NET_DISC, NULL);
+ else
+ printk(KERN_WARNING "capi_decode_disc_ind - error\n");
+ break;
+ case MSG_DISC_CONF:
+ if (!(chan = capi_channel(dev, skb))) {
+ printk(KERN_WARNING
+ "CAPI header: unknown channel id\n");
+ break;
+ }
+
+ if (!capi_decode_disc_ind(chan, skb))
+ pcbit_fsm_event(dev, chan, EV_NET_RELEASE, NULL);
+ else
+ printk(KERN_WARNING "capi_decode_disc_conf - error\n");
+ break;
+ case MSG_INFO_IND:
+#ifdef DEBUG
+ printk(KERN_DEBUG "received Info Indication - discarded\n");
+#endif
+ break;
+#ifdef DEBUG
+ case MSG_DEBUG_188:
+ capi_decode_debug_188(skb->data, skb->len);
+ break;
+
+ default:
+ printk(KERN_DEBUG "pcbit_l3_receive: unknown message %08lx\n",
+ msg);
+ break;
+#endif
+ }
+
+ kfree_skb(skb);
+
+}
+
+/*
+ * Single statbuf
+ * should be a statbuf per device
+ */
+
+static char statbuf[STATBUF_LEN];
+static int stat_st = 0;
+static int stat_end = 0;
+
+static int pcbit_stat(u_char __user *buf, int len, int driver, int channel)
+{
+ int stat_count;
+ stat_count = stat_end - stat_st;
+
+ if (stat_count < 0)
+ stat_count = STATBUF_LEN - stat_st + stat_end;
+
+ /* FIXME: should we sleep and wait for more cookies ? */
+ if (len > stat_count)
+ len = stat_count;
+
+ if (stat_st < stat_end)
+ {
+ if (copy_to_user(buf, statbuf + stat_st, len))
+ return -EFAULT;
+ stat_st += len;
+ }
+ else
+ {
+ if (len > STATBUF_LEN - stat_st)
+ {
+ if (copy_to_user(buf, statbuf + stat_st,
+ STATBUF_LEN - stat_st))
+ return -EFAULT;
+ if (copy_to_user(buf, statbuf,
+ len - (STATBUF_LEN - stat_st)))
+ return -EFAULT;
+
+ stat_st = len - (STATBUF_LEN - stat_st);
+ }
+ else
+ {
+ if (copy_to_user(buf, statbuf + stat_st, len))
+ return -EFAULT;
+
+ stat_st += len;
+
+ if (stat_st == STATBUF_LEN)
+ stat_st = 0;
+ }
+ }
+
+ if (stat_st == stat_end)
+ stat_st = stat_end = 0;
+
+ return len;
+}
+
+static void pcbit_logstat(struct pcbit_dev *dev, char *str)
+{
+ int i;
+ isdn_ctrl ictl;
+
+ for (i=stat_end; i<strlen(str); i++)
+ {
+ statbuf[i]=str[i];
+ stat_end = (stat_end + 1) % STATBUF_LEN;
+ if (stat_end == stat_st)
+ stat_st = (stat_st + 1) % STATBUF_LEN;
+ }
+
+ ictl.command=ISDN_STAT_STAVAIL;
+ ictl.driver=dev->id;
+ ictl.arg=strlen(str);
+ dev->dev_if->statcallb(&ictl);
+}
+
+void pcbit_state_change(struct pcbit_dev * dev, struct pcbit_chan * chan,
+ unsigned short i, unsigned short ev, unsigned short f)
+{
+ char buf[256];
+
+ sprintf(buf, "change on device: %d channel:%d\n%s -> %s -> %s\n",
+ dev->id, chan->id,
+ isdn_state_table[i], strisdnevent(ev), isdn_state_table[f]
+ );
+
+#ifdef DEBUG
+ printk("%s", buf);
+#endif
+
+ pcbit_logstat(dev, buf);
+}
+
+static void set_running_timeout(unsigned long ptr)
+{
+ struct pcbit_dev * dev;
+
+#ifdef DEBUG
+ printk(KERN_DEBUG "set_running_timeout\n");
+#endif
+ dev = (struct pcbit_dev *) ptr;
+
+ wake_up_interruptible(&dev->set_running_wq);
+}
+
+static int set_protocol_running(struct pcbit_dev * dev)
+{
+ isdn_ctrl ctl;
+
+ init_timer(&dev->set_running_timer);
+
+ dev->set_running_timer.function = &set_running_timeout;
+ dev->set_running_timer.data = (ulong) dev;
+ dev->set_running_timer.expires = jiffies + SET_RUN_TIMEOUT;
+
+ /* kick it */
+
+ dev->l2_state = L2_STARTING;
+
+ writeb((0x80U | ((dev->rcv_seq & 0x07) << 3) | (dev->send_seq & 0x07)),
+ dev->sh_mem + BANK4);
+
+ add_timer(&dev->set_running_timer);
+
+ interruptible_sleep_on(&dev->set_running_wq);
+
+ del_timer(&dev->set_running_timer);
+
+ if (dev->l2_state == L2_RUNNING)
+ {
+ printk(KERN_DEBUG "pcbit: running\n");
+
+ dev->unack_seq = dev->send_seq;
+
+ dev->writeptr = dev->sh_mem;
+ dev->readptr = dev->sh_mem + BANK2;
+
+ /* tell the good news to the upper layer */
+ ctl.driver = dev->id;
+ ctl.command = ISDN_STAT_RUN;
+
+ dev->dev_if->statcallb(&ctl);
+ }
+ else
+ {
+ printk(KERN_DEBUG "pcbit: initialization failed\n");
+ printk(KERN_DEBUG "pcbit: firmware not loaded\n");
+
+ dev->l2_state = L2_DOWN;
+
+#ifdef DEBUG
+ printk(KERN_DEBUG "Bank3 = %02x\n",
+ readb(dev->sh_mem + BANK3));
+#endif
+ writeb(0x40, dev->sh_mem + BANK4);
+
+ /* warn the upper layer */
+ ctl.driver = dev->id;
+ ctl.command = ISDN_STAT_STOP;
+
+ dev->dev_if->statcallb(&ctl);
+
+ return -EL2HLT; /* Level 2 halted */
+ }
+
+ return 0;
+}
+
+static int pcbit_ioctl(isdn_ctrl* ctl)
+{
+ struct pcbit_dev * dev;
+ struct pcbit_ioctl *cmd;
+
+ dev = finddev(ctl->driver);
+
+ if (!dev)
+ {
+ printk(KERN_DEBUG "pcbit_ioctl: unknown device\n");
+ return -ENODEV;
+ }
+
+ cmd = (struct pcbit_ioctl *) ctl->parm.num;
+
+ switch(ctl->arg) {
+ case PCBIT_IOCTL_GETSTAT:
+ cmd->info.l2_status = dev->l2_state;
+ break;
+
+ case PCBIT_IOCTL_STRLOAD:
+ if (dev->l2_state == L2_RUNNING)
+ return -EBUSY;
+
+ dev->unack_seq = dev->send_seq = dev->rcv_seq = 0;
+
+ dev->writeptr = dev->sh_mem;
+ dev->readptr = dev->sh_mem + BANK2;
+
+ dev->l2_state = L2_LOADING;
+ break;
+
+ case PCBIT_IOCTL_LWMODE:
+ if (dev->l2_state != L2_LOADING)
+ return -EINVAL;
+
+ dev->l2_state = L2_LWMODE;
+ break;
+
+ case PCBIT_IOCTL_FWMODE:
+ if (dev->l2_state == L2_RUNNING)
+ return -EBUSY;
+ dev->loadptr = LOAD_ZONE_START;
+ dev->l2_state = L2_FWMODE;
+
+ break;
+ case PCBIT_IOCTL_ENDLOAD:
+ if (dev->l2_state == L2_RUNNING)
+ return -EBUSY;
+ dev->l2_state = L2_DOWN;
+ break;
+
+ case PCBIT_IOCTL_SETBYTE:
+ if (dev->l2_state == L2_RUNNING)
+ return -EBUSY;
+
+ /* check addr */
+ if (cmd->info.rdp_byte.addr > BANK4)
+ return -EFAULT;
+
+ writeb(cmd->info.rdp_byte.value, dev->sh_mem + cmd->info.rdp_byte.addr);
+ break;
+ case PCBIT_IOCTL_GETBYTE:
+ if (dev->l2_state == L2_RUNNING)
+ return -EBUSY;
+
+ /* check addr */
+
+ if (cmd->info.rdp_byte.addr > BANK4)
+ {
+ printk("getbyte: invalid addr %04x\n", cmd->info.rdp_byte.addr);
+ return -EFAULT;
+ }
+
+ cmd->info.rdp_byte.value = readb(dev->sh_mem + cmd->info.rdp_byte.addr);
+ break;
+ case PCBIT_IOCTL_RUNNING:
+ if (dev->l2_state == L2_RUNNING)
+ return -EBUSY;
+ return set_protocol_running(dev);
+ break;
+ case PCBIT_IOCTL_WATCH188:
+ if (dev->l2_state != L2_LOADING)
+ return -EINVAL;
+ pcbit_l2_write(dev, MSG_WATCH188, 0x0001, NULL, 0);
+ break;
+ case PCBIT_IOCTL_PING188:
+ if (dev->l2_state != L2_LOADING)
+ return -EINVAL;
+ pcbit_l2_write(dev, MSG_PING188_REQ, 0x0001, NULL, 0);
+ break;
+ case PCBIT_IOCTL_APION:
+ if (dev->l2_state != L2_LOADING)
+ return -EINVAL;
+ pcbit_l2_write(dev, MSG_API_ON, 0x0001, NULL, 0);
+ break;
+ case PCBIT_IOCTL_STOP:
+ dev->l2_state = L2_DOWN;
+ writeb(0x40, dev->sh_mem + BANK4);
+ dev->rcv_seq = 0;
+ dev->send_seq = 0;
+ dev->unack_seq = 0;
+ break;
+ default:
+ printk("error: unknown ioctl\n");
+ break;
+ };
+ return 0;
+}
+
+/*
+ * MSN list handling
+ *
+ * if null reject all calls
+ * if first entry has null MSN accept all calls
+ */
+
+static void pcbit_clear_msn(struct pcbit_dev *dev)
+{
+ struct msn_entry *ptr, *back;
+
+ for (ptr=dev->msn_list; ptr; )
+ {
+ back = ptr->next;
+ kfree(ptr);
+ ptr = back;
+ }
+
+ dev->msn_list = NULL;
+}
+
+static void pcbit_set_msn(struct pcbit_dev *dev, char *list)
+{
+ struct msn_entry *ptr;
+ struct msn_entry *back = NULL;
+ char *cp, *sp;
+ int len;
+
+ if (strlen(list) == 0) {
+ ptr = kmalloc(sizeof(struct msn_entry), GFP_ATOMIC);
+ if (!ptr) {
+ printk(KERN_WARNING "kmalloc failed\n");
+ return;
+ }
+
+ ptr->msn = NULL;
+
+ ptr->next = dev->msn_list;
+ dev->msn_list = ptr;
+
+ return;
+ }
+
+ if (dev->msn_list)
+ for (back=dev->msn_list; back->next; back=back->next);
+
+ sp = list;
+
+ do {
+ cp=strchr(sp, ',');
+ if (cp)
+ len = cp - sp;
+ else
+ len = strlen(sp);
+
+ ptr = kmalloc(sizeof(struct msn_entry), GFP_ATOMIC);
+
+ if (!ptr) {
+ printk(KERN_WARNING "kmalloc failed\n");
+ return;
+ }
+ ptr->next = NULL;
+
+ ptr->msn = kmalloc(len, GFP_ATOMIC);
+ if (!ptr->msn) {
+ printk(KERN_WARNING "kmalloc failed\n");
+ kfree(ptr);
+ return;
+ }
+
+ memcpy(ptr->msn, sp, len - 1);
+ ptr->msn[len] = 0;
+
+#ifdef DEBUG
+ printk(KERN_DEBUG "msn: %s\n", ptr->msn);
+#endif
+ if (dev->msn_list == NULL)
+ dev->msn_list = ptr;
+ else
+ back->next = ptr;
+ back = ptr;
+ sp += len;
+ } while(cp);
+}
+
+/*
+ * check if we do signal or reject an incoming call
+ */
+static int pcbit_check_msn(struct pcbit_dev *dev, char *msn)
+{
+ struct msn_entry *ptr;
+
+ for (ptr=dev->msn_list; ptr; ptr=ptr->next) {
+
+ if (ptr->msn == NULL)
+ return 1;
+
+ if (strcmp(ptr->msn, msn) == 0)
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/drivers/isdn/pcbit/edss1.c b/drivers/isdn/pcbit/edss1.c
new file mode 100644
index 0000000..37e9626
--- /dev/null
+++ b/drivers/isdn/pcbit/edss1.c
@@ -0,0 +1,318 @@
+/*
+ * DSS.1 Finite State Machine
+ * base: ITU-T Rec Q.931
+ *
+ * Copyright (C) 1996 Universidade de Lisboa
+ *
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU General Public License, incorporated herein by reference.
+ */
+
+/*
+ * TODO: complete the FSM
+ * move state/event descriptions to a user space logger
+ */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/skbuff.h>
+
+#include <linux/timer.h>
+#include <asm/io.h>
+
+#include <linux/isdnif.h>
+
+#include "pcbit.h"
+#include "edss1.h"
+#include "layer2.h"
+#include "callbacks.h"
+
+
+char * isdn_state_table[] = {
+ "Closed",
+ "Call initiated",
+ "Overlap sending",
+ "Outgoing call proceeding",
+ "NOT DEFINED",
+ "Call delivered",
+ "Call present",
+ "Call received",
+ "Connect request",
+ "Incoming call proceeding",
+ "Active",
+ "Disconnect request",
+ "Disconnect indication",
+ "NOT DEFINED",
+ "NOT DEFINED",
+ "Suspend request",
+ "NOT DEFINED",
+ "Resume request",
+ "NOT DEFINED",
+ "Release Request",
+ "NOT DEFINED",
+ "NOT DEFINED",
+ "NOT DEFINED",
+ "NOT DEFINED",
+ "NOT DEFINED",
+ "Overlap receiving",
+ "Select protocol on B-Channel",
+ "Activate B-channel protocol"
+};
+
+#ifdef DEBUG_ERRS
+static
+struct CauseValue {
+ byte nr;
+ char *descr;
+} cvlist[]={
+ {0x01,"Unallocated (unassigned) number"},
+ {0x02,"No route to specified transit network"},
+ {0x03,"No route to destination"},
+ {0x04,"Send special information tone"},
+ {0x05,"Misdialled trunk prefix"},
+ {0x06,"Channel unacceptable"},
+ {0x07,"Channel awarded and being delivered in an established channel"},
+ {0x08,"Preemption"},
+ {0x09,"Preemption - circuit reserved for reuse"},
+ {0x10,"Normal call clearing"},
+ {0x11,"User busy"},
+ {0x12,"No user responding"},
+ {0x13,"No answer from user (user alerted)"},
+ {0x14,"Subscriber absent"},
+ {0x15,"Call rejected"},
+ {0x16,"Number changed"},
+ {0x1a,"non-selected user clearing"},
+ {0x1b,"Destination out of order"},
+ {0x1c,"Invalid number format (address incomplete)"},
+ {0x1d,"Facility rejected"},
+ {0x1e,"Response to Status enquiry"},
+ {0x1f,"Normal, unspecified"},
+ {0x22,"No circuit/channel available"},
+ {0x26,"Network out of order"},
+ {0x27,"Permanent frame mode connection out-of-service"},
+ {0x28,"Permanent frame mode connection operational"},
+ {0x29,"Temporary failure"},
+ {0x2a,"Switching equipment congestion"},
+ {0x2b,"Access information discarded"},
+ {0x2c,"Requested circuit/channel not available"},
+ {0x2e,"Precedence call blocked"},
+ {0x2f,"Resource unavailable, unspecified"},
+ {0x31,"Quality of service unavailable"},
+ {0x32,"Requested facility not subscribed"},
+ {0x35,"Outgoing calls barred within CUG"},
+ {0x37,"Incoming calls barred within CUG"},
+ {0x39,"Bearer capability not authorized"},
+ {0x3a,"Bearer capability not presently available"},
+ {0x3e,"Inconsistency in designated outgoing access information and subscriber class"},
+ {0x3f,"Service or option not available, unspecified"},
+ {0x41,"Bearer capability not implemented"},
+ {0x42,"Channel type not implemented"},
+ {0x43,"Requested facility not implemented"},
+ {0x44,"Only restricted digital information bearer capability is available"},
+ {0x4f,"Service or option not implemented"},
+ {0x51,"Invalid call reference value"},
+ {0x52,"Identified channel does not exist"},
+ {0x53,"A suspended call exists, but this call identity does not"},
+ {0x54,"Call identity in use"},
+ {0x55,"No call suspended"},
+ {0x56,"Call having the requested call identity has been cleared"},
+ {0x57,"User not member of CUG"},
+ {0x58,"Incompatible destination"},
+ {0x5a,"Non-existent CUG"},
+ {0x5b,"Invalid transit network selection"},
+ {0x5f,"Invalid message, unspecified"},
+ {0x60,"Mandatory information element is missing"},
+ {0x61,"Message type non-existent or not implemented"},
+ {0x62,"Message not compatible with call state or message type non-existent or not implemented"},
+ {0x63,"Information element/parameter non-existent or not implemented"},
+ {0x64,"Invalid information element contents"},
+ {0x65,"Message not compatible with call state"},
+ {0x66,"Recovery on timer expiry"},
+ {0x67,"Parameter non-existent or not implemented - passed on"},
+ {0x6e,"Message with unrecognized parameter discarded"},
+ {0x6f,"Protocol error, unspecified"},
+ {0x7f,"Interworking, unspecified"}
+};
+
+#endif
+
+static struct isdn_event_desc {
+ unsigned short ev;
+ char * desc;
+} isdn_event_table [] = {
+ {EV_USR_SETUP_REQ, "CC->L3: Setup Request"},
+ {EV_USR_SETUP_RESP, "CC->L3: Setup Response"},
+ {EV_USR_PROCED_REQ, "CC->L3: Proceeding Request"},
+ {EV_USR_RELEASE_REQ, "CC->L3: Release Request"},
+
+ {EV_NET_SETUP, "NET->TE: setup "},
+ {EV_NET_CALL_PROC, "NET->TE: call proceeding"},
+ {EV_NET_SETUP_ACK, "NET->TE: setup acknowledge (more info needed)"},
+ {EV_NET_CONN, "NET->TE: connect"},
+ {EV_NET_CONN_ACK, "NET->TE: connect acknowledge"},
+ {EV_NET_DISC, "NET->TE: disconnect indication"},
+ {EV_NET_RELEASE, "NET->TE: release"},
+ {EV_NET_RELEASE_COMP, "NET->TE: release complete"},
+ {EV_NET_SELP_RESP, "Board: Select B-channel protocol ack"},
+ {EV_NET_ACTV_RESP, "Board: Activate B-channel protocol ack"},
+ {EV_TIMER, "Timeout"},
+ {0, "NULL"}
+};
+
+char * strisdnevent(ushort ev)
+{
+ struct isdn_event_desc * entry;
+
+ for (entry = isdn_event_table; entry->ev; entry++)
+ if (entry->ev == ev)
+ break;
+
+ return entry->desc;
+}
+
+/*
+ * Euro ISDN finite state machine
+ */
+
+static struct fsm_timer_entry fsm_timers[] = {
+ {ST_CALL_PROC, 10},
+ {ST_DISC_REQ, 2},
+ {ST_ACTIVE_SELP, 5},
+ {ST_ACTIVE_ACTV, 5},
+ {ST_INCM_PROC, 10},
+ {ST_CONN_REQ, 2},
+ {0xff, 0}
+};
+
+static struct fsm_entry fsm_table[] = {
+/* Connect Phase */
+ /* Outgoing */
+ {ST_NULL, ST_CALL_INIT, EV_USR_SETUP_REQ, cb_out_1},
+
+ {ST_CALL_INIT, ST_OVER_SEND, EV_NET_SETUP_ACK, cb_notdone},
+ {ST_CALL_INIT, ST_CALL_PROC, EV_NET_CALL_PROC, NULL},
+ {ST_CALL_INIT, ST_NULL, EV_NET_DISC, cb_out_2},
+
+ {ST_CALL_PROC, ST_ACTIVE_SELP, EV_NET_CONN, cb_out_2},
+ {ST_CALL_PROC, ST_NULL, EV_NET_DISC, cb_disc_1},
+ {ST_CALL_PROC, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2},
+
+ /* Incoming */
+ {ST_NULL, ST_CALL_PRES, EV_NET_SETUP, NULL},
+
+ {ST_CALL_PRES, ST_INCM_PROC, EV_USR_PROCED_REQ, cb_in_1},
+ {ST_CALL_PRES, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2},
+
+ {ST_INCM_PROC, ST_CONN_REQ, EV_USR_SETUP_RESP, cb_in_2},
+ {ST_INCM_PROC, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2},
+
+ {ST_CONN_REQ, ST_ACTIVE_SELP, EV_NET_CONN_ACK, cb_in_3},
+
+ /* Active */
+ {ST_ACTIVE, ST_NULL, EV_NET_DISC, cb_disc_1},
+ {ST_ACTIVE, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2},
+ {ST_ACTIVE, ST_NULL, EV_NET_RELEASE, cb_disc_3},
+
+ /* Disconnect */
+
+ {ST_DISC_REQ, ST_NULL, EV_NET_DISC, cb_disc_1},
+ {ST_DISC_REQ, ST_NULL, EV_NET_RELEASE, cb_disc_3},
+
+ /* protocol selection */
+ {ST_ACTIVE_SELP, ST_ACTIVE_ACTV, EV_NET_SELP_RESP, cb_selp_1},
+ {ST_ACTIVE_SELP, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2},
+
+ {ST_ACTIVE_ACTV, ST_ACTIVE, EV_NET_ACTV_RESP, cb_open},
+ {ST_ACTIVE_ACTV, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2},
+
+ /* Timers */
+ {ST_CALL_PROC, ST_DISC_REQ, EV_TIMER, cb_disc_2},
+ {ST_DISC_REQ, ST_NULL, EV_TIMER, cb_disc_3},
+ {ST_ACTIVE_SELP, ST_DISC_REQ, EV_TIMER, cb_disc_2},
+ {ST_ACTIVE_ACTV, ST_DISC_REQ, EV_TIMER, cb_disc_2},
+ {ST_INCM_PROC, ST_DISC_REQ, EV_TIMER, cb_disc_2},
+ {ST_CONN_REQ, ST_CONN_REQ, EV_TIMER, cb_in_2},
+
+ {0xff, 0, 0, NULL}
+};
+
+
+static void pcbit_fsm_timer(unsigned long data)
+{
+ struct pcbit_dev *dev;
+ struct pcbit_chan *chan;
+
+ chan = (struct pcbit_chan *) data;
+
+ del_timer(&chan->fsm_timer);
+ chan->fsm_timer.function = NULL;
+
+ dev = chan2dev(chan);
+
+ if (dev == NULL) {
+ printk(KERN_WARNING "pcbit: timer for unknown device\n");
+ return;
+ }
+
+ pcbit_fsm_event(dev, chan, EV_TIMER, NULL);
+}
+
+
+void pcbit_fsm_event(struct pcbit_dev *dev, struct pcbit_chan *chan,
+ unsigned short event, struct callb_data *data)
+{
+ struct fsm_entry * action;
+ struct fsm_timer_entry *tentry;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ for (action = fsm_table; action->init != 0xff; action++)
+ if (action->init == chan->fsm_state && action->event == event)
+ break;
+
+ if (action->init == 0xff) {
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+ printk(KERN_DEBUG "fsm error: event %x on state %x\n",
+ event, chan->fsm_state);
+ return;
+ }
+
+ if (chan->fsm_timer.function) {
+ del_timer(&chan->fsm_timer);
+ chan->fsm_timer.function = NULL;
+ }
+
+ chan->fsm_state = action->final;
+
+ pcbit_state_change(dev, chan, action->init, event, action->final);
+
+ for (tentry = fsm_timers; tentry->init != 0xff; tentry++)
+ if (tentry->init == chan->fsm_state)
+ break;
+
+ if (tentry->init != 0xff) {
+ init_timer(&chan->fsm_timer);
+ chan->fsm_timer.function = &pcbit_fsm_timer;
+ chan->fsm_timer.data = (ulong) chan;
+ chan->fsm_timer.expires = jiffies + tentry->timeout * HZ;
+ add_timer(&chan->fsm_timer);
+ }
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ if (action->callb)
+ action->callb(dev, chan, data);
+
+}
+
+
+
+
diff --git a/drivers/isdn/pcbit/edss1.h b/drivers/isdn/pcbit/edss1.h
new file mode 100644
index 0000000..0b64f97
--- /dev/null
+++ b/drivers/isdn/pcbit/edss1.h
@@ -0,0 +1,102 @@
+/*
+ * DSS.1 module definitions
+ *
+ * Copyright (C) 1996 Universidade de Lisboa
+ *
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU General Public License, incorporated herein by reference.
+ */
+
+#ifndef EDSS1_H
+#define EDSS1_H
+
+/* ISDN states */
+
+#define ST_NULL 0
+#define ST_CALL_INIT 1 /* Call initiated */
+#define ST_OVER_SEND 2 /* Overlap sending - Requests More Info 4 call */
+#define ST_CALL_PROC 3 /* Call Proceeding */
+#define ST_CALL_DELV 4
+#define ST_CALL_PRES 6 /* Call Present - Received CONN.IND */
+#define ST_CALL_RECV 7 /* Alerting sent */
+#define ST_CONN_REQ 8 /* Answered - waiting 4 CONN.CONF */
+#define ST_INCM_PROC 9
+#define ST_ACTIVE 10
+#define ST_DISC_REQ 11
+#define ST_DISC_IND 12
+#define ST_SUSP_REQ 15
+#define ST_RESM_REQ 17
+#define ST_RELS_REQ 19
+#define ST_OVER_RECV 25
+
+#define ST_ACTIVE_SELP 26 /* Select protocol on B-Channel */
+#define ST_ACTIVE_ACTV 27 /* Activate B-channel protocol */
+
+#define MAX_STATE ST_ACTIVE_ACTV
+
+#define EV_NULL 0
+#define EV_USR_SETUP_REQ 1
+#define EV_USR_SETUP_RESP 2
+#define EV_USR_PROCED_REQ 3
+#define EV_USR_RELEASE_REQ 4
+#define EV_USR_REJECT_REQ 4
+
+#define EV_NET_SETUP 16
+#define EV_NET_CALL_PROC 17
+#define EV_NET_SETUP_ACK 18
+#define EV_NET_CONN 19
+#define EV_NET_CONN_ACK 20
+
+#define EV_NET_SELP_RESP 21
+#define EV_NET_ACTV_RESP 22
+
+#define EV_NET_DISC 23
+#define EV_NET_RELEASE 24
+#define EV_NET_RELEASE_COMP 25
+
+#define EV_TIMER 26
+#define EV_ERROR 32
+
+/*
+ * Cause values
+ * only the ones we use
+ */
+
+#define CAUSE_NORMAL 0x10U
+#define CAUSE_NOCHAN 0x22U
+
+struct callb_data {
+ unsigned short type;
+ union {
+ struct ConnInfo {
+ char *CalledPN;
+ char *CallingPN;
+ } setup;
+ unsigned short cause;
+ } data;
+};
+
+struct fsm_entry {
+ unsigned short init;
+ unsigned short final;
+ unsigned short event;
+ void (*callb)(struct pcbit_dev *, struct pcbit_chan *, struct callb_data*);
+};
+
+struct fsm_timer_entry {
+ unsigned short init;
+ unsigned long timeout; /* in seconds */
+};
+
+extern char * isdn_state_table[];
+
+void pcbit_fsm_event(struct pcbit_dev *, struct pcbit_chan *,
+ unsigned short event, struct callb_data *);
+char * strisdnevent(ushort ev);
+
+#endif
+
+
+
diff --git a/drivers/isdn/pcbit/layer2.c b/drivers/isdn/pcbit/layer2.c
new file mode 100644
index 0000000..5ba2a87
--- /dev/null
+++ b/drivers/isdn/pcbit/layer2.c
@@ -0,0 +1,715 @@
+/*
+ * PCBIT-D low-layer interface
+ *
+ * Copyright (C) 1996 Universidade de Lisboa
+ *
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU General Public License, incorporated herein by reference.
+ */
+
+/*
+ * 19991203 - Fernando Carvalho - takion@superbofh.org
+ * Hacked to compile with egcs and run with current version of isdn modules
+*/
+
+/*
+ * Based on documentation provided by Inesc:
+ * - "Interface com bus do PC para o PCBIT e PCBIT-D", Inesc, Jan 93
+ */
+
+/*
+ * TODO: better handling of errors
+ * re-write/remove debug printks
+ */
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/mm.h>
+#include <linux/skbuff.h>
+
+#include <linux/isdnif.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+
+
+#include "pcbit.h"
+#include "layer2.h"
+#include "edss1.h"
+
+#undef DEBUG_FRAG
+
+
+/*
+ * Prototypes
+ */
+
+static void pcbit_transmit(struct pcbit_dev *dev);
+
+static void pcbit_recv_ack(struct pcbit_dev *dev, unsigned char ack);
+
+static void pcbit_l2_error(struct pcbit_dev *dev);
+static void pcbit_l2_active_conf(struct pcbit_dev *dev, u_char info);
+static void pcbit_l2_err_recover(unsigned long data);
+
+static void pcbit_firmware_bug(struct pcbit_dev *dev);
+
+static __inline__ void
+pcbit_sched_delivery(struct pcbit_dev *dev)
+{
+ schedule_work(&dev->qdelivery);
+}
+
+
+/*
+ * Called from layer3
+ */
+
+int
+pcbit_l2_write(struct pcbit_dev *dev, ulong msg, ushort refnum,
+ struct sk_buff *skb, unsigned short hdr_len)
+{
+ struct frame_buf *frame,
+ *ptr;
+ unsigned long flags;
+
+ if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING) {
+ dev_kfree_skb(skb);
+ return -1;
+ }
+ if ((frame = kmalloc(sizeof(struct frame_buf),
+ GFP_ATOMIC)) == NULL) {
+ printk(KERN_WARNING "pcbit_2_write: kmalloc failed\n");
+ dev_kfree_skb(skb);
+ return -1;
+ }
+ frame->msg = msg;
+ frame->refnum = refnum;
+ frame->copied = 0;
+ frame->hdr_len = hdr_len;
+
+ if (skb)
+ frame->dt_len = skb->len - hdr_len;
+ else
+ frame->dt_len = 0;
+
+ frame->skb = skb;
+
+ frame->next = NULL;
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ if (dev->write_queue == NULL) {
+ dev->write_queue = frame;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ pcbit_transmit(dev);
+ } else {
+ for (ptr = dev->write_queue; ptr->next; ptr = ptr->next);
+ ptr->next = frame;
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+ }
+ return 0;
+}
+
+static __inline__ void
+pcbit_tx_update(struct pcbit_dev *dev, ushort len)
+{
+ u_char info;
+
+ dev->send_seq = (dev->send_seq + 1) % 8;
+
+ dev->fsize[dev->send_seq] = len;
+ info = 0;
+ info |= dev->rcv_seq << 3;
+ info |= dev->send_seq;
+
+ writeb(info, dev->sh_mem + BANK4);
+
+}
+
+/*
+ * called by interrupt service routine or by write_2
+ */
+
+static void
+pcbit_transmit(struct pcbit_dev *dev)
+{
+ struct frame_buf *frame = NULL;
+ unsigned char unacked;
+ int flen; /* fragment frame length including all headers */
+ int free;
+ int count,
+ cp_len;
+ unsigned long flags;
+ unsigned short tt;
+
+ if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING)
+ return;
+
+ unacked = (dev->send_seq + (8 - dev->unack_seq)) & 0x07;
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ if (dev->free > 16 && dev->write_queue && unacked < 7) {
+
+ if (!dev->w_busy)
+ dev->w_busy = 1;
+ else {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return;
+ }
+
+
+ frame = dev->write_queue;
+ free = dev->free;
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ if (frame->copied == 0) {
+
+ /* Type 0 frame */
+
+ ulong msg;
+
+ if (frame->skb)
+ flen = FRAME_HDR_LEN + PREHDR_LEN + frame->skb->len;
+ else
+ flen = FRAME_HDR_LEN + PREHDR_LEN;
+
+ if (flen > free)
+ flen = free;
+
+ msg = frame->msg;
+
+ /*
+ * Board level 2 header
+ */
+
+ pcbit_writew(dev, flen - FRAME_HDR_LEN);
+
+ pcbit_writeb(dev, GET_MSG_CPU(msg));
+
+ pcbit_writeb(dev, GET_MSG_PROC(msg));
+
+ /* TH */
+ pcbit_writew(dev, frame->hdr_len + PREHDR_LEN);
+
+ /* TD */
+ pcbit_writew(dev, frame->dt_len);
+
+
+ /*
+ * Board level 3 fixed-header
+ */
+
+ /* LEN = TH */
+ pcbit_writew(dev, frame->hdr_len + PREHDR_LEN);
+
+ /* XX */
+ pcbit_writew(dev, 0);
+
+ /* C + S */
+ pcbit_writeb(dev, GET_MSG_CMD(msg));
+ pcbit_writeb(dev, GET_MSG_SCMD(msg));
+
+ /* NUM */
+ pcbit_writew(dev, frame->refnum);
+
+ count = FRAME_HDR_LEN + PREHDR_LEN;
+ } else {
+ /* Type 1 frame */
+
+ flen = 2 + (frame->skb->len - frame->copied);
+
+ if (flen > free)
+ flen = free;
+
+ /* TT */
+ tt = ((ushort) (flen - 2)) | 0x8000U; /* Type 1 */
+ pcbit_writew(dev, tt);
+
+ count = 2;
+ }
+
+ if (frame->skb) {
+ cp_len = frame->skb->len - frame->copied;
+ if (cp_len > flen - count)
+ cp_len = flen - count;
+
+ memcpy_topcbit(dev, frame->skb->data + frame->copied,
+ cp_len);
+ frame->copied += cp_len;
+ }
+ /* bookkeeping */
+ dev->free -= flen;
+ pcbit_tx_update(dev, flen);
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ if (frame->skb == NULL || frame->copied == frame->skb->len) {
+
+ dev->write_queue = frame->next;
+
+ if (frame->skb != NULL) {
+ /* free frame */
+ dev_kfree_skb(frame->skb);
+ }
+ kfree(frame);
+ }
+ dev->w_busy = 0;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ } else {
+ spin_unlock_irqrestore(&dev->lock, flags);
+#ifdef DEBUG
+ printk(KERN_DEBUG "unacked %d free %d write_queue %s\n",
+ unacked, dev->free, dev->write_queue ? "not empty" :
+ "empty");
+#endif
+ }
+}
+
+
+/*
+ * deliver a queued frame to the upper layer
+ */
+
+void
+pcbit_deliver(struct work_struct *work)
+{
+ struct frame_buf *frame;
+ unsigned long flags, msg;
+ struct pcbit_dev *dev =
+ container_of(work, struct pcbit_dev, qdelivery);
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ while ((frame = dev->read_queue)) {
+ dev->read_queue = frame->next;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ msg = 0;
+ SET_MSG_CPU(msg, 0);
+ SET_MSG_PROC(msg, 0);
+ SET_MSG_CMD(msg, frame->skb->data[2]);
+ SET_MSG_SCMD(msg, frame->skb->data[3]);
+
+ frame->refnum = *((ushort *) frame->skb->data + 4);
+ frame->msg = *((ulong *) & msg);
+
+ skb_pull(frame->skb, 6);
+
+ pcbit_l3_receive(dev, frame->msg, frame->skb, frame->hdr_len,
+ frame->refnum);
+
+ kfree(frame);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ }
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+/*
+ * Reads BANK 2 & Reassembles
+ */
+
+static void
+pcbit_receive(struct pcbit_dev *dev)
+{
+ unsigned short tt;
+ u_char cpu,
+ proc;
+ struct frame_buf *frame = NULL;
+ unsigned long flags;
+ u_char type1;
+
+ if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING)
+ return;
+
+ tt = pcbit_readw(dev);
+
+ if ((tt & 0x7fffU) > 511) {
+ printk(KERN_INFO "pcbit: invalid frame length -> TT=%04x\n",
+ tt);
+ pcbit_l2_error(dev);
+ return;
+ }
+ if (!(tt & 0x8000U)) { /* Type 0 */
+ type1 = 0;
+
+ if (dev->read_frame) {
+ printk(KERN_DEBUG "pcbit_receive: Type 0 frame and read_frame != NULL\n");
+ /* discard previous queued frame */
+ if (dev->read_frame->skb)
+ kfree_skb(dev->read_frame->skb);
+ kfree(dev->read_frame);
+ dev->read_frame = NULL;
+ }
+ frame = kzalloc(sizeof(struct frame_buf), GFP_ATOMIC);
+
+ if (frame == NULL) {
+ printk(KERN_WARNING "kmalloc failed\n");
+ return;
+ }
+
+ cpu = pcbit_readb(dev);
+ proc = pcbit_readb(dev);
+
+
+ if (cpu != 0x06 && cpu != 0x02) {
+ printk(KERN_DEBUG "pcbit: invalid cpu value\n");
+ kfree(frame);
+ pcbit_l2_error(dev);
+ return;
+ }
+ /*
+ * we discard cpu & proc on receiving
+ * but we read it to update the pointer
+ */
+
+ frame->hdr_len = pcbit_readw(dev);
+ frame->dt_len = pcbit_readw(dev);
+
+ /*
+ * 0 sized packet
+ * I don't know if they are an error or not...
+ * But they are very frequent
+ * Not documented
+ */
+
+ if (frame->hdr_len == 0) {
+ kfree(frame);
+#ifdef DEBUG
+ printk(KERN_DEBUG "0 sized frame\n");
+#endif
+ pcbit_firmware_bug(dev);
+ return;
+ }
+ /* sanity check the length values */
+ if (frame->hdr_len > 1024 || frame->dt_len > 2048) {
+#ifdef DEBUG
+ printk(KERN_DEBUG "length problem: ");
+ printk(KERN_DEBUG "TH=%04x TD=%04x\n",
+ frame->hdr_len,
+ frame->dt_len);
+#endif
+ pcbit_l2_error(dev);
+ kfree(frame);
+ return;
+ }
+ /* minimum frame read */
+
+ frame->skb = dev_alloc_skb(frame->hdr_len + frame->dt_len +
+ ((frame->hdr_len + 15) & ~15));
+
+ if (!frame->skb) {
+ printk(KERN_DEBUG "pcbit_receive: out of memory\n");
+ kfree(frame);
+ return;
+ }
+ /* 16 byte alignment for IP */
+ if (frame->dt_len)
+ skb_reserve(frame->skb, (frame->hdr_len + 15) & ~15);
+
+ } else {
+ /* Type 1 */
+ type1 = 1;
+ tt &= 0x7fffU;
+
+ if (!(frame = dev->read_frame)) {
+ printk("Type 1 frame and no frame queued\n");
+ /* usually after an error: toss frame */
+ dev->readptr += tt;
+ if (dev->readptr > dev->sh_mem + BANK2 + BANKLEN)
+ dev->readptr -= BANKLEN;
+ return;
+
+ }
+ }
+
+ memcpy_frompcbit(dev, skb_put(frame->skb, tt), tt);
+
+ frame->copied += tt;
+ spin_lock_irqsave(&dev->lock, flags);
+ if (frame->copied == frame->hdr_len + frame->dt_len) {
+
+ if (type1) {
+ dev->read_frame = NULL;
+ }
+ if (dev->read_queue) {
+ struct frame_buf *ptr;
+ for (ptr = dev->read_queue; ptr->next; ptr = ptr->next);
+ ptr->next = frame;
+ } else
+ dev->read_queue = frame;
+
+ } else {
+ dev->read_frame = frame;
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+/*
+ * The board sends 0 sized frames
+ * They are TDATA_CONFs that get messed up somehow
+ * gotta send a fake acknowledgment to the upper layer somehow
+ */
+
+static __inline__ void
+pcbit_fake_conf(struct pcbit_dev *dev, struct pcbit_chan *chan)
+{
+ isdn_ctrl ictl;
+
+ if (chan->queued) {
+ chan->queued--;
+
+ ictl.driver = dev->id;
+ ictl.command = ISDN_STAT_BSENT;
+ ictl.arg = chan->id;
+ dev->dev_if->statcallb(&ictl);
+ }
+}
+
+static void
+pcbit_firmware_bug(struct pcbit_dev *dev)
+{
+ struct pcbit_chan *chan;
+
+ chan = dev->b1;
+
+ if (chan->fsm_state == ST_ACTIVE) {
+ pcbit_fake_conf(dev, chan);
+ }
+ chan = dev->b2;
+
+ if (chan->fsm_state == ST_ACTIVE) {
+ pcbit_fake_conf(dev, chan);
+ }
+}
+
+irqreturn_t
+pcbit_irq_handler(int interrupt, void *devptr)
+{
+ struct pcbit_dev *dev;
+ u_char info,
+ ack_seq,
+ read_seq;
+
+ dev = (struct pcbit_dev *) devptr;
+
+ if (!dev) {
+ printk(KERN_WARNING "pcbit_irq_handler: wrong device\n");
+ return IRQ_NONE;
+ }
+ if (dev->interrupt) {
+ printk(KERN_DEBUG "pcbit: reentering interrupt hander\n");
+ return IRQ_HANDLED;
+ }
+ dev->interrupt = 1;
+
+ info = readb(dev->sh_mem + BANK3);
+
+ if (dev->l2_state == L2_STARTING || dev->l2_state == L2_ERROR) {
+ pcbit_l2_active_conf(dev, info);
+ dev->interrupt = 0;
+ return IRQ_HANDLED;
+ }
+ if (info & 0x40U) { /* E bit set */
+#ifdef DEBUG
+ printk(KERN_DEBUG "pcbit_irq_handler: E bit on\n");
+#endif
+ pcbit_l2_error(dev);
+ dev->interrupt = 0;
+ return IRQ_HANDLED;
+ }
+ if (dev->l2_state != L2_RUNNING && dev->l2_state != L2_LOADING) {
+ dev->interrupt = 0;
+ return IRQ_HANDLED;
+ }
+ ack_seq = (info >> 3) & 0x07U;
+ read_seq = (info & 0x07U);
+
+ dev->interrupt = 0;
+
+ if (read_seq != dev->rcv_seq) {
+ while (read_seq != dev->rcv_seq) {
+ pcbit_receive(dev);
+ dev->rcv_seq = (dev->rcv_seq + 1) % 8;
+ }
+ pcbit_sched_delivery(dev);
+ }
+ if (ack_seq != dev->unack_seq) {
+ pcbit_recv_ack(dev, ack_seq);
+ }
+ info = dev->rcv_seq << 3;
+ info |= dev->send_seq;
+
+ writeb(info, dev->sh_mem + BANK4);
+ return IRQ_HANDLED;
+}
+
+
+static void
+pcbit_l2_active_conf(struct pcbit_dev *dev, u_char info)
+{
+ u_char state;
+
+ state = dev->l2_state;
+
+#ifdef DEBUG
+ printk(KERN_DEBUG "layer2_active_confirm\n");
+#endif
+
+
+ if (info & 0x80U) {
+ dev->rcv_seq = info & 0x07U;
+ dev->l2_state = L2_RUNNING;
+ } else
+ dev->l2_state = L2_DOWN;
+
+ if (state == L2_STARTING)
+ wake_up_interruptible(&dev->set_running_wq);
+
+ if (state == L2_ERROR && dev->l2_state == L2_RUNNING) {
+ pcbit_transmit(dev);
+ }
+}
+
+static void
+pcbit_l2_err_recover(unsigned long data)
+{
+
+ struct pcbit_dev *dev;
+ struct frame_buf *frame;
+
+ dev = (struct pcbit_dev *) data;
+
+ del_timer(&dev->error_recover_timer);
+ if (dev->w_busy || dev->r_busy) {
+ init_timer(&dev->error_recover_timer);
+ dev->error_recover_timer.expires = jiffies + ERRTIME;
+ add_timer(&dev->error_recover_timer);
+ return;
+ }
+ dev->w_busy = dev->r_busy = 1;
+
+ if (dev->read_frame) {
+ if (dev->read_frame->skb)
+ kfree_skb(dev->read_frame->skb);
+ kfree(dev->read_frame);
+ dev->read_frame = NULL;
+ }
+ if (dev->write_queue) {
+ frame = dev->write_queue;
+#ifdef FREE_ON_ERROR
+ dev->write_queue = dev->write_queue->next;
+
+ if (frame->skb) {
+ dev_kfree_skb(frame->skb);
+ }
+ kfree(frame);
+#else
+ frame->copied = 0;
+#endif
+ }
+ dev->rcv_seq = dev->send_seq = dev->unack_seq = 0;
+ dev->free = 511;
+ dev->l2_state = L2_ERROR;
+
+ /* this is an hack... */
+ pcbit_firmware_bug(dev);
+
+ dev->writeptr = dev->sh_mem;
+ dev->readptr = dev->sh_mem + BANK2;
+
+ writeb((0x80U | ((dev->rcv_seq & 0x07) << 3) | (dev->send_seq & 0x07)),
+ dev->sh_mem + BANK4);
+ dev->w_busy = dev->r_busy = 0;
+
+}
+
+static void
+pcbit_l2_error(struct pcbit_dev *dev)
+{
+ if (dev->l2_state == L2_RUNNING) {
+
+ printk(KERN_INFO "pcbit: layer 2 error\n");
+
+#ifdef DEBUG
+ log_state(dev);
+#endif
+
+ dev->l2_state = L2_DOWN;
+
+ init_timer(&dev->error_recover_timer);
+ dev->error_recover_timer.function = &pcbit_l2_err_recover;
+ dev->error_recover_timer.data = (ulong) dev;
+ dev->error_recover_timer.expires = jiffies + ERRTIME;
+ add_timer(&dev->error_recover_timer);
+ }
+}
+
+/*
+ * Description:
+ * if board acks frames
+ * update dev->free
+ * call pcbit_transmit to write possible queued frames
+ */
+
+static void
+pcbit_recv_ack(struct pcbit_dev *dev, unsigned char ack)
+{
+ int i,
+ count;
+ int unacked;
+
+ unacked = (dev->send_seq + (8 - dev->unack_seq)) & 0x07;
+
+ /* dev->unack_seq < ack <= dev->send_seq; */
+
+ if (unacked) {
+
+ if (dev->send_seq > dev->unack_seq) {
+ if (ack <= dev->unack_seq || ack > dev->send_seq) {
+ printk(KERN_DEBUG
+ "layer 2 ack unacceptable - dev %d",
+ dev->id);
+
+ pcbit_l2_error(dev);
+ } else if (ack > dev->send_seq && ack <= dev->unack_seq) {
+ printk(KERN_DEBUG
+ "layer 2 ack unacceptable - dev %d",
+ dev->id);
+ pcbit_l2_error(dev);
+ }
+ }
+ /* ack is acceptable */
+
+
+ i = dev->unack_seq;
+
+ do {
+ dev->unack_seq = i = (i + 1) % 8;
+ dev->free += dev->fsize[i];
+ } while (i != ack);
+
+ count = 0;
+ while (count < 7 && dev->write_queue) {
+ u8 lsend_seq = dev->send_seq;
+
+ pcbit_transmit(dev);
+
+ if (dev->send_seq == lsend_seq)
+ break;
+ count++;
+ }
+ } else
+ printk(KERN_DEBUG "recv_ack: unacked = 0\n");
+}
diff --git a/drivers/isdn/pcbit/layer2.h b/drivers/isdn/pcbit/layer2.h
new file mode 100644
index 0000000..2ac295e
--- /dev/null
+++ b/drivers/isdn/pcbit/layer2.h
@@ -0,0 +1,288 @@
+/*
+ * PCBIT-D low-layer interface definitions
+ *
+ * Copyright (C) 1996 Universidade de Lisboa
+ *
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU General Public License, incorporated herein by reference.
+ */
+
+/*
+ * 19991203 - Fernando Carvalho - takion@superbofh.org
+ * Hacked to compile with egcs and run with current version of isdn modules
+*/
+
+#ifndef LAYER2_H
+#define LAYER2_H
+
+#include <linux/interrupt.h>
+
+#include <asm/byteorder.h>
+
+#define BANK1 0x0000U /* PC -> Board */
+#define BANK2 0x01ffU /* Board -> PC */
+#define BANK3 0x03feU /* Att Board */
+#define BANK4 0x03ffU /* Att PC */
+
+#define BANKLEN 0x01FFU
+
+#define LOAD_ZONE_START 0x03f8U
+#define LOAD_ZONE_END 0x03fdU
+
+#define LOAD_RETRY 18000000
+
+
+
+/* TAM - XX - C - S - NUM */
+#define PREHDR_LEN 8
+/* TT - M - I - TH - TD */
+#define FRAME_HDR_LEN 8
+
+#define MSG_CONN_REQ 0x08000100
+#define MSG_CONN_CONF 0x00000101
+#define MSG_CONN_IND 0x00000102
+#define MSG_CONN_RESP 0x08000103
+
+#define MSG_CONN_ACTV_REQ 0x08000300
+#define MSG_CONN_ACTV_CONF 0x00000301
+#define MSG_CONN_ACTV_IND 0x00000302
+#define MSG_CONN_ACTV_RESP 0x08000303
+
+#define MSG_DISC_REQ 0x08000400
+#define MSG_DISC_CONF 0x00000401
+#define MSG_DISC_IND 0x00000402
+#define MSG_DISC_RESP 0x08000403
+
+#define MSG_TDATA_REQ 0x0908E200
+#define MSG_TDATA_CONF 0x0000E201
+#define MSG_TDATA_IND 0x0000E202
+#define MSG_TDATA_RESP 0x0908E203
+
+#define MSG_SELP_REQ 0x09004000
+#define MSG_SELP_CONF 0x00004001
+
+#define MSG_ACT_TRANSP_REQ 0x0908E000
+#define MSG_ACT_TRANSP_CONF 0x0000E001
+
+#define MSG_STPROT_REQ 0x09004100
+#define MSG_STPROT_CONF 0x00004101
+
+#define MSG_PING188_REQ 0x09030500
+#define MSG_PING188_CONF 0x000005bc
+
+#define MSG_WATCH188 0x09030400
+
+#define MSG_API_ON 0x08020102
+#define MSG_POOL_PCBIT 0x08020400
+#define MSG_POOL_PCBIT_CONF 0x00000401
+
+#define MSG_INFO_IND 0x00002602
+#define MSG_INFO_RESP 0x08002603
+
+#define MSG_DEBUG_188 0x0000ff00
+
+/*
+
+ long 4 3 2 1
+ Intel 1 2 3 4
+*/
+
+#ifdef __LITTLE_ENDIAN
+#define SET_MSG_SCMD(msg, ch) (msg = (msg & 0xffffff00) | (((ch) & 0xff)))
+#define SET_MSG_CMD(msg, ch) (msg = (msg & 0xffff00ff) | (((ch) & 0xff) << 8))
+#define SET_MSG_PROC(msg, ch) (msg = (msg & 0xff00ffff) | (((ch) & 0xff) << 16))
+#define SET_MSG_CPU(msg, ch) (msg = (msg & 0x00ffffff) | (((ch) & 0xff) << 24))
+
+#define GET_MSG_SCMD(msg) ((msg) & 0xFF)
+#define GET_MSG_CMD(msg) ((msg) >> 8 & 0xFF)
+#define GET_MSG_PROC(msg) ((msg) >> 16 & 0xFF)
+#define GET_MSG_CPU(msg) ((msg) >> 24)
+
+#else
+#error "Non-Intel CPU"
+#endif
+
+#define MAX_QUEUED 7
+
+#define SCHED_READ 0x01
+#define SCHED_WRITE 0x02
+
+#define SET_RUN_TIMEOUT 2*HZ /* 2 seconds */
+
+struct frame_buf {
+ ulong msg;
+ unsigned int refnum;
+ unsigned int dt_len;
+ unsigned int hdr_len;
+ struct sk_buff *skb;
+ unsigned int copied;
+ struct frame_buf * next;
+};
+
+extern int pcbit_l2_write(struct pcbit_dev * dev, ulong msg, ushort refnum,
+ struct sk_buff *skb, unsigned short hdr_len);
+
+extern irqreturn_t pcbit_irq_handler(int interrupt, void *);
+
+extern struct pcbit_dev * dev_pcbit[MAX_PCBIT_CARDS];
+
+#ifdef DEBUG
+static __inline__ void log_state(struct pcbit_dev *dev) {
+ printk(KERN_DEBUG "writeptr = %ld\n",
+ (ulong) (dev->writeptr - dev->sh_mem));
+ printk(KERN_DEBUG "readptr = %ld\n",
+ (ulong) (dev->readptr - (dev->sh_mem + BANK2)));
+ printk(KERN_DEBUG "{rcv_seq=%01x, send_seq=%01x, unack_seq=%01x}\n",
+ dev->rcv_seq, dev->send_seq, dev->unack_seq);
+}
+#endif
+
+static __inline__ struct pcbit_dev * chan2dev(struct pcbit_chan * chan)
+{
+ struct pcbit_dev * dev;
+ int i;
+
+
+ for (i=0; i<MAX_PCBIT_CARDS; i++)
+ if ((dev=dev_pcbit[i]))
+ if (dev->b1 == chan || dev->b2 == chan)
+ return dev;
+ return NULL;
+
+}
+
+static __inline__ struct pcbit_dev * finddev(int id)
+{
+ struct pcbit_dev * dev;
+ int i;
+
+ for (i=0; i<MAX_PCBIT_CARDS; i++)
+ if ((dev=dev_pcbit[i]))
+ if (dev->id == id)
+ return dev;
+ return NULL;
+}
+
+
+/*
+ * Support routines for reading and writing in the board
+ */
+
+static __inline__ void pcbit_writeb(struct pcbit_dev *dev, unsigned char dt)
+{
+ writeb(dt, dev->writeptr++);
+ if (dev->writeptr == dev->sh_mem + BANKLEN)
+ dev->writeptr = dev->sh_mem;
+}
+
+static __inline__ void pcbit_writew(struct pcbit_dev *dev, unsigned short dt)
+{
+ int dist;
+
+ dist = BANKLEN - (dev->writeptr - dev->sh_mem);
+ switch (dist) {
+ case 2:
+ writew(dt, dev->writeptr);
+ dev->writeptr = dev->sh_mem;
+ break;
+ case 1:
+ writeb((u_char) (dt & 0x00ffU), dev->writeptr);
+ dev->writeptr = dev->sh_mem;
+ writeb((u_char) (dt >> 8), dev->writeptr++);
+ break;
+ default:
+ writew(dt, dev->writeptr);
+ dev->writeptr += 2;
+ break;
+ };
+}
+
+static __inline__ void memcpy_topcbit(struct pcbit_dev * dev, u_char * data,
+ int len)
+{
+ int diff;
+
+ diff = len - (BANKLEN - (dev->writeptr - dev->sh_mem) );
+
+ if (diff > 0)
+ {
+ memcpy_toio(dev->writeptr, data, len - diff);
+ memcpy_toio(dev->sh_mem, data + (len - diff), diff);
+ dev->writeptr = dev->sh_mem + diff;
+ }
+ else
+ {
+ memcpy_toio(dev->writeptr, data, len);
+
+ dev->writeptr += len;
+ if (diff == 0)
+ dev->writeptr = dev->sh_mem;
+ }
+}
+
+static __inline__ unsigned char pcbit_readb(struct pcbit_dev *dev)
+{
+ unsigned char val;
+
+ val = readb(dev->readptr++);
+ if (dev->readptr == dev->sh_mem + BANK2 + BANKLEN)
+ dev->readptr = dev->sh_mem + BANK2;
+
+ return val;
+}
+
+static __inline__ unsigned short pcbit_readw(struct pcbit_dev *dev)
+{
+ int dist;
+ unsigned short val;
+
+ dist = BANKLEN - ( dev->readptr - (dev->sh_mem + BANK2 ) );
+ switch (dist) {
+ case 2:
+ val = readw(dev->readptr);
+ dev->readptr = dev->sh_mem + BANK2;
+ break;
+ case 1:
+ val = readb(dev->readptr);
+ dev->readptr = dev->sh_mem + BANK2;
+ val = (readb(dev->readptr++) << 8) | val;
+ break;
+ default:
+ val = readw(dev->readptr);
+ dev->readptr += 2;
+ break;
+ };
+ return val;
+}
+
+static __inline__ void memcpy_frompcbit(struct pcbit_dev * dev, u_char * data, int len)
+{
+ int diff;
+
+ diff = len - (BANKLEN - (dev->readptr - (dev->sh_mem + BANK2) ) );
+ if (diff > 0)
+ {
+ memcpy_fromio(data, dev->readptr, len - diff);
+ memcpy_fromio(data + (len - diff), dev->sh_mem + BANK2 , diff);
+ dev->readptr = dev->sh_mem + BANK2 + diff;
+ }
+ else
+ {
+ memcpy_fromio(data, dev->readptr, len);
+ dev->readptr += len;
+ if (diff == 0)
+ dev->readptr = dev->sh_mem + BANK2;
+ }
+}
+
+
+#endif
+
+
+
+
+
+
+
diff --git a/drivers/isdn/pcbit/module.c b/drivers/isdn/pcbit/module.c
new file mode 100644
index 0000000..04ea241
--- /dev/null
+++ b/drivers/isdn/pcbit/module.c
@@ -0,0 +1,126 @@
+/*
+ * PCBIT-D module support
+ *
+ * Copyright (C) 1996 Universidade de Lisboa
+ *
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU General Public License, incorporated herein by reference.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/skbuff.h>
+
+#include <linux/isdnif.h>
+#include "pcbit.h"
+
+MODULE_DESCRIPTION("ISDN4Linux: Driver for PCBIT-T card");
+MODULE_AUTHOR("Pedro Roque Marques");
+MODULE_LICENSE("GPL");
+
+static int mem[MAX_PCBIT_CARDS];
+static int irq[MAX_PCBIT_CARDS];
+
+module_param_array(mem, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+
+static int num_boards;
+struct pcbit_dev * dev_pcbit[MAX_PCBIT_CARDS];
+
+static int __init pcbit_init(void)
+{
+ int board;
+
+ num_boards = 0;
+
+ printk(KERN_NOTICE
+ "PCBIT-D device driver v 0.5-fjpc0 19991204 - "
+ "Copyright (C) 1996 Universidade de Lisboa\n");
+
+ if (mem[0] || irq[0])
+ {
+ for (board=0; board < MAX_PCBIT_CARDS && mem[board] && irq[board]; board++)
+ {
+ if (!mem[board])
+ mem[board] = 0xD0000;
+ if (!irq[board])
+ irq[board] = 5;
+
+ if (pcbit_init_dev(board, mem[board], irq[board]) == 0)
+ num_boards++;
+
+ else
+ {
+ printk(KERN_WARNING
+ "pcbit_init failed for dev %d",
+ board + 1);
+ return -EIO;
+ }
+ }
+ }
+
+ /* Hardcoded default settings detection */
+
+ if (!num_boards)
+ {
+ printk(KERN_INFO
+ "Trying to detect board using default settings\n");
+ if (pcbit_init_dev(0, 0xD0000, 5) == 0)
+ num_boards++;
+ else
+ return -EIO;
+ }
+ return 0;
+}
+
+static void __exit pcbit_exit(void)
+{
+#ifdef MODULE
+ int board;
+
+ for (board = 0; board < num_boards; board++)
+ pcbit_terminate(board);
+ printk(KERN_NOTICE
+ "PCBIT-D module unloaded\n");
+#endif
+}
+
+#ifndef MODULE
+#define MAX_PARA (MAX_PCBIT_CARDS * 2)
+static int __init pcbit_setup(char *line)
+{
+ int i, j, argc;
+ char *str;
+ int ints[MAX_PARA+1];
+
+ str = get_options(line, MAX_PARA, ints);
+ argc = ints[0];
+ i = 0;
+ j = 1;
+
+ while (argc && (i<MAX_PCBIT_CARDS)) {
+
+ if (argc) {
+ mem[i] = ints[j];
+ j++; argc--;
+ }
+
+ if (argc) {
+ irq[i] = ints[j];
+ j++; argc--;
+ }
+
+ i++;
+ }
+ return(1);
+}
+__setup("pcbit=", pcbit_setup);
+#endif
+
+module_init(pcbit_init);
+module_exit(pcbit_exit);
+
diff --git a/drivers/isdn/pcbit/pcbit.h b/drivers/isdn/pcbit/pcbit.h
new file mode 100644
index 0000000..d76fffc
--- /dev/null
+++ b/drivers/isdn/pcbit/pcbit.h
@@ -0,0 +1,177 @@
+/*
+ * PCBIT-D device driver definitions
+ *
+ * Copyright (C) 1996 Universidade de Lisboa
+ *
+ * Written by Pedro Roque Marques (roque@di.fc.ul.pt)
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU General Public License, incorporated herein by reference.
+ */
+
+#ifndef PCBIT_H
+#define PCBIT_H
+
+#include <linux/workqueue.h>
+
+#define MAX_PCBIT_CARDS 4
+
+
+#define BLOCK_TIMER
+
+#ifdef __KERNEL__
+
+struct pcbit_chan {
+ unsigned short id;
+ unsigned short callref; /* Call Reference */
+ unsigned char proto; /* layer2protocol */
+ unsigned char queued; /* unacked data messages */
+ unsigned char layer2link; /* used in TData */
+ unsigned char snum; /* used in TData */
+ unsigned short s_refnum;
+ unsigned short r_refnum;
+ unsigned short fsm_state;
+ struct timer_list fsm_timer;
+#ifdef BLOCK_TIMER
+ struct timer_list block_timer;
+#endif
+};
+
+struct msn_entry {
+ char *msn;
+ struct msn_entry * next;
+};
+
+struct pcbit_dev {
+ /* board */
+
+ volatile unsigned char __iomem *sh_mem; /* RDP address */
+ unsigned long ph_mem;
+ unsigned int irq;
+ unsigned int id;
+ unsigned int interrupt; /* set during interrupt
+ processing */
+ spinlock_t lock;
+ /* isdn4linux */
+
+ struct msn_entry * msn_list; /* ISDN address list */
+
+ isdn_if * dev_if;
+
+ ushort ll_hdrlen;
+ ushort hl_hdrlen;
+
+ /* link layer */
+ unsigned char l2_state;
+
+ struct frame_buf *read_queue;
+ struct frame_buf *read_frame;
+ struct frame_buf *write_queue;
+
+ /* Protocol start */
+ wait_queue_head_t set_running_wq;
+ struct timer_list set_running_timer;
+
+ struct timer_list error_recover_timer;
+
+ struct work_struct qdelivery;
+
+ u_char w_busy;
+ u_char r_busy;
+
+ volatile unsigned char __iomem *readptr;
+ volatile unsigned char __iomem *writeptr;
+
+ ushort loadptr;
+
+ unsigned short fsize[8]; /* sent layer2 frames size */
+
+ unsigned char send_seq;
+ unsigned char rcv_seq;
+ unsigned char unack_seq;
+
+ unsigned short free;
+
+ /* channels */
+
+ struct pcbit_chan *b1;
+ struct pcbit_chan *b2;
+};
+
+#define STATS_TIMER (10*HZ)
+#define ERRTIME (HZ/10)
+
+/* MRU */
+#define MAXBUFSIZE 1534
+#define MRU MAXBUFSIZE
+
+#define STATBUF_LEN 2048
+/*
+ *
+ */
+
+#endif /* __KERNEL__ */
+
+/* isdn_ctrl only allows a long sized argument */
+
+struct pcbit_ioctl {
+ union {
+ struct byte_op {
+ ushort addr;
+ ushort value;
+ } rdp_byte;
+ unsigned long l2_status;
+ } info;
+};
+
+
+
+#define PCBIT_IOCTL_GETSTAT 0x01 /* layer2 status */
+#define PCBIT_IOCTL_LWMODE 0x02 /* linear write mode */
+#define PCBIT_IOCTL_STRLOAD 0x03 /* start load mode */
+#define PCBIT_IOCTL_ENDLOAD 0x04 /* end load mode */
+#define PCBIT_IOCTL_SETBYTE 0x05 /* set byte */
+#define PCBIT_IOCTL_GETBYTE 0x06 /* get byte */
+#define PCBIT_IOCTL_RUNNING 0x07 /* set protocol running */
+#define PCBIT_IOCTL_WATCH188 0x08 /* set watch 188 */
+#define PCBIT_IOCTL_PING188 0x09 /* ping 188 */
+#define PCBIT_IOCTL_FWMODE 0x0A /* firmware write mode */
+#define PCBIT_IOCTL_STOP 0x0B /* stop protocol */
+#define PCBIT_IOCTL_APION 0x0C /* issue API_ON */
+
+#ifndef __KERNEL__
+
+#define PCBIT_GETSTAT (PCBIT_IOCTL_GETSTAT + IIOCDRVCTL)
+#define PCBIT_LWMODE (PCBIT_IOCTL_LWMODE + IIOCDRVCTL)
+#define PCBIT_STRLOAD (PCBIT_IOCTL_STRLOAD + IIOCDRVCTL)
+#define PCBIT_ENDLOAD (PCBIT_IOCTL_ENDLOAD + IIOCDRVCTL)
+#define PCBIT_SETBYTE (PCBIT_IOCTL_SETBYTE + IIOCDRVCTL)
+#define PCBIT_GETBYTE (PCBIT_IOCTL_GETBYTE + IIOCDRVCTL)
+#define PCBIT_RUNNING (PCBIT_IOCTL_RUNNING + IIOCDRVCTL)
+#define PCBIT_WATCH188 (PCBIT_IOCTL_WATCH188 + IIOCDRVCTL)
+#define PCBIT_PING188 (PCBIT_IOCTL_PING188 + IIOCDRVCTL)
+#define PCBIT_FWMODE (PCBIT_IOCTL_FWMODE + IIOCDRVCTL)
+#define PCBIT_STOP (PCBIT_IOCTL_STOP + IIOCDRVCTL)
+#define PCBIT_APION (PCBIT_IOCTL_APION + IIOCDRVCTL)
+
+#define MAXSUPERLINE 3000
+
+#endif
+
+#define L2_DOWN 0
+#define L2_LOADING 1
+#define L2_LWMODE 2
+#define L2_FWMODE 3
+#define L2_STARTING 4
+#define L2_RUNNING 5
+#define L2_ERROR 6
+
+void pcbit_deliver(struct work_struct *work);
+int pcbit_init_dev(int board, int mem_base, int irq);
+void pcbit_terminate(int board);
+void pcbit_l3_receive(struct pcbit_dev * dev, ulong msg, struct sk_buff * skb,
+ ushort hdr_len, ushort refnum);
+void pcbit_state_change(struct pcbit_dev * dev, struct pcbit_chan * chan,
+ unsigned short i, unsigned short ev, unsigned short f);
+
+#endif
diff --git a/drivers/isdn/sc/Kconfig b/drivers/isdn/sc/Kconfig
new file mode 100644
index 0000000..e6510ca
--- /dev/null
+++ b/drivers/isdn/sc/Kconfig
@@ -0,0 +1,12 @@
+#
+# Config.in for Spellcaster ISDN driver
+#
+config ISDN_DRV_SC
+ tristate "Spellcaster support"
+ depends on ISA
+ help
+ This enables support for the Spellcaster BRI ISDN boards. This
+ driver currently builds only in a modularized version.
+ To build it, choose M here: the module will be called sc.
+ See <file:Documentation/isdn/README.sc> for more information.
+
diff --git a/drivers/isdn/sc/Makefile b/drivers/isdn/sc/Makefile
new file mode 100644
index 0000000..0f2b7d6
--- /dev/null
+++ b/drivers/isdn/sc/Makefile
@@ -0,0 +1,10 @@
+# Makefile for the sc ISDN device driver
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_ISDN_DRV_SC) += sc.o
+
+# Multipart objects.
+
+sc-y := shmem.o init.o packet.o command.o event.o \
+ ioctl.o interrupt.o message.o timer.o
diff --git a/drivers/isdn/sc/card.h b/drivers/isdn/sc/card.h
new file mode 100644
index 0000000..0120bcf
--- /dev/null
+++ b/drivers/isdn/sc/card.h
@@ -0,0 +1,131 @@
+/* $Id: card.h,v 1.1.10.1 2001/09/23 22:24:59 kai Exp $
+ *
+ * Driver parameters for SpellCaster ISA ISDN adapters
+ *
+ * Copyright (C) 1996 SpellCaster Telecommunications Inc.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For more information, please contact gpl-info@spellcast.com or write:
+ *
+ * SpellCaster Telecommunications Inc.
+ * 5621 Finch Avenue East, Unit #3
+ * Scarborough, Ontario Canada
+ * M1B 2T9
+ * +1 (416) 297-8565
+ * +1 (416) 297-6433 Facsimile
+ */
+
+#ifndef CARD_H
+#define CARD_H
+
+/*
+ * We need these if they're not already included
+ */
+#include <linux/timer.h>
+#include <linux/time.h>
+#include <linux/isdnif.h>
+#include <linux/irqreturn.h>
+#include "message.h"
+#include "scioc.h"
+
+/*
+ * Amount of time to wait for a reset to complete
+ */
+#define CHECKRESET_TIME msecs_to_jiffies(4000)
+
+/*
+ * Amount of time between line status checks
+ */
+#define CHECKSTAT_TIME msecs_to_jiffies(8000)
+
+/*
+ * The maximum amount of time to wait for a message response
+ * to arrive. Use exclusively by send_and_receive
+ */
+#define SAR_TIMEOUT msecs_to_jiffies(10000)
+
+/*
+ * Macro to determine is a card id is valid
+ */
+#define IS_VALID_CARD(x) ((x >= 0) && (x <= cinst))
+
+/*
+ * Per channel status and configuration
+ */
+typedef struct {
+ int l2_proto;
+ int l3_proto;
+ char dn[50];
+ unsigned long first_sendbuf; /* Offset of first send buffer */
+ unsigned int num_sendbufs; /* Number of send buffers */
+ unsigned int free_sendbufs; /* Number of free sendbufs */
+ unsigned int next_sendbuf; /* Next sequential buffer */
+ char eazlist[50]; /* Set with SETEAZ */
+ char sillist[50]; /* Set with SETSIL */
+ int eazclear; /* Don't accept calls if TRUE */
+} bchan;
+
+/*
+ * Everything you want to know about the adapter ...
+ */
+typedef struct {
+ int model;
+ int driverId; /* LL Id */
+ char devicename[20]; /* The device name */
+ isdn_if *card; /* ISDN4Linux structure */
+ bchan *channel; /* status of the B channels */
+ char nChannels; /* Number of channels */
+ unsigned int interrupt; /* Interrupt number */
+ int iobase; /* I/O Base address */
+ int ioport[MAX_IO_REGS]; /* Index to I/O ports */
+ int shmem_pgport; /* port for the exp mem page reg. */
+ int shmem_magic; /* adapter magic number */
+ unsigned int rambase; /* Shared RAM base address */
+ unsigned int ramsize; /* Size of shared memory */
+ RspMessage async_msg; /* Async response message */
+ int want_async_messages; /* Snoop the Q ? */
+ unsigned char seq_no; /* Next send seq. number */
+ struct timer_list reset_timer; /* Check reset timer */
+ struct timer_list stat_timer; /* Check startproc timer */
+ unsigned char nphystat; /* Latest PhyStat info */
+ unsigned char phystat; /* Last PhyStat info */
+ HWConfig_pl hwconfig; /* Hardware config info */
+ char load_ver[11]; /* CommManage Version string */
+ char proc_ver[11]; /* CommEngine Version */
+ int StartOnReset; /* Indicates startproc after reset */
+ int EngineUp; /* Indicates CommEngine Up */
+ int trace_mode; /* Indicate if tracing is on */
+ spinlock_t lock; /* local lock */
+} board;
+
+
+extern board *sc_adapter[];
+extern int cinst;
+
+void memcpy_toshmem(int card, void *dest, const void *src, size_t n);
+void memcpy_fromshmem(int card, void *dest, const void *src, size_t n);
+int get_card_from_id(int driver);
+int indicate_status(int card, int event, ulong Channel, char *Data);
+irqreturn_t interrupt_handler(int interrupt, void *cardptr);
+int sndpkt(int devId, int channel, int ack, struct sk_buff *data);
+void rcvpkt(int card, RspMessage *rcvmsg);
+int command(isdn_ctrl *cmd);
+int reset(int card);
+int startproc(int card);
+int send_and_receive(int card, unsigned int procid, unsigned char type,
+ unsigned char class, unsigned char code,
+ unsigned char link, unsigned char data_len,
+ unsigned char *data, RspMessage *mesgdata, int timeout);
+void flushreadfifo (int card);
+int sendmessage(int card, unsigned int procid, unsigned int type,
+ unsigned int class, unsigned int code, unsigned int link,
+ unsigned int data_len, unsigned int *data);
+int receivemessage(int card, RspMessage *rspmsg);
+int sc_ioctl(int card, scs_ioctl *data);
+int setup_buffers(int card, int c);
+void sc_check_reset(unsigned long data);
+void check_phystat(unsigned long data);
+
+#endif /* CARD_H */
diff --git a/drivers/isdn/sc/command.c b/drivers/isdn/sc/command.c
new file mode 100644
index 0000000..0e4969c
--- /dev/null
+++ b/drivers/isdn/sc/command.c
@@ -0,0 +1,363 @@
+/* $Id: command.c,v 1.4.10.1 2001/09/23 22:24:59 kai Exp $
+ *
+ * Copyright (C) 1996 SpellCaster Telecommunications Inc.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For more information, please contact gpl-info@spellcast.com or write:
+ *
+ * SpellCaster Telecommunications Inc.
+ * 5621 Finch Avenue East, Unit #3
+ * Scarborough, Ontario Canada
+ * M1B 2T9
+ * +1 (416) 297-8565
+ * +1 (416) 297-6433 Facsimile
+ */
+
+#include <linux/module.h>
+#include "includes.h" /* This must be first */
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+#include "scioc.h"
+
+static int dial(int card, unsigned long channel, setup_parm setup);
+static int hangup(int card, unsigned long channel);
+static int answer(int card, unsigned long channel);
+static int clreaz(int card, unsigned long channel);
+static int seteaz(int card, unsigned long channel, char *);
+static int setl2(int card, unsigned long arg);
+static int setl3(int card, unsigned long arg);
+static int acceptb(int card, unsigned long channel);
+
+#ifdef DEBUG
+/*
+ * Translate command codes to strings
+ */
+static char *commands[] = { "ISDN_CMD_IOCTL",
+ "ISDN_CMD_DIAL",
+ "ISDN_CMD_ACCEPTB",
+ "ISDN_CMD_ACCEPTB",
+ "ISDN_CMD_HANGUP",
+ "ISDN_CMD_CLREAZ",
+ "ISDN_CMD_SETEAZ",
+ NULL,
+ NULL,
+ NULL,
+ "ISDN_CMD_SETL2",
+ NULL,
+ "ISDN_CMD_SETL3",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL, };
+
+/*
+ * Translates ISDN4Linux protocol codes to strings for debug messages
+ */
+static char *l3protos[] = { "ISDN_PROTO_L3_TRANS" };
+static char *l2protos[] = { "ISDN_PROTO_L2_X75I",
+ "ISDN_PROTO_L2_X75UI",
+ "ISDN_PROTO_L2_X75BUI",
+ "ISDN_PROTO_L2_HDLC",
+ "ISDN_PROTO_L2_TRANS" };
+#endif
+
+int get_card_from_id(int driver)
+{
+ int i;
+
+ for(i = 0 ; i < cinst ; i++) {
+ if(sc_adapter[i]->driverId == driver)
+ return i;
+ }
+ return -ENODEV;
+}
+
+/*
+ * command
+ */
+
+int command(isdn_ctrl *cmd)
+{
+ int card;
+
+ card = get_card_from_id(cmd->driver);
+ if(!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return -ENODEV;
+ }
+
+ /*
+ * Dispatch the command
+ */
+ switch(cmd->command) {
+ case ISDN_CMD_IOCTL:
+ {
+ unsigned long cmdptr;
+ scs_ioctl ioc;
+
+ memcpy(&cmdptr, cmd->parm.num, sizeof(unsigned long));
+ if (copy_from_user(&ioc, (scs_ioctl __user *)cmdptr,
+ sizeof(scs_ioctl))) {
+ pr_debug("%s: Failed to verify user space 0x%lx\n",
+ sc_adapter[card]->devicename, cmdptr);
+ return -EFAULT;
+ }
+ return sc_ioctl(card, &ioc);
+ }
+ case ISDN_CMD_DIAL:
+ return dial(card, cmd->arg, cmd->parm.setup);
+ case ISDN_CMD_HANGUP:
+ return hangup(card, cmd->arg);
+ case ISDN_CMD_ACCEPTD:
+ return answer(card, cmd->arg);
+ case ISDN_CMD_ACCEPTB:
+ return acceptb(card, cmd->arg);
+ case ISDN_CMD_CLREAZ:
+ return clreaz(card, cmd->arg);
+ case ISDN_CMD_SETEAZ:
+ return seteaz(card, cmd->arg, cmd->parm.num);
+ case ISDN_CMD_SETL2:
+ return setl2(card, cmd->arg);
+ case ISDN_CMD_SETL3:
+ return setl3(card, cmd->arg);
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * start the onboard firmware
+ */
+int startproc(int card)
+{
+ int status;
+
+ if(!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return -ENODEV;
+ }
+
+ /*
+ * send start msg
+ */
+ status = sendmessage(card, CMPID,cmReqType2,
+ cmReqClass0,
+ cmReqStartProc,
+ 0,0,NULL);
+ pr_debug("%s: Sent startProc\n", sc_adapter[card]->devicename);
+
+ return status;
+}
+
+
+/*
+ * Dials the number passed in
+ */
+static int dial(int card, unsigned long channel, setup_parm setup)
+{
+ int status;
+ char Phone[48];
+
+ if(!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return -ENODEV;
+ }
+
+ /*extract ISDN number to dial from eaz/msn string*/
+ strcpy(Phone,setup.phone);
+
+ /*send the connection message*/
+ status = sendmessage(card, CEPID,ceReqTypePhy,
+ ceReqClass1,
+ ceReqPhyConnect,
+ (unsigned char) channel+1,
+ strlen(Phone),
+ (unsigned int *) Phone);
+
+ pr_debug("%s: Dialing %s on channel %lu\n",
+ sc_adapter[card]->devicename, Phone, channel+1);
+
+ return status;
+}
+
+/*
+ * Answer an incoming call
+ */
+static int answer(int card, unsigned long channel)
+{
+ if(!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return -ENODEV;
+ }
+
+ if(setup_buffers(card, channel+1)) {
+ hangup(card, channel+1);
+ return -ENOBUFS;
+ }
+
+ indicate_status(card, ISDN_STAT_BCONN,channel,NULL);
+ pr_debug("%s: Answered incoming call on channel %lu\n",
+ sc_adapter[card]->devicename, channel+1);
+ return 0;
+}
+
+/*
+ * Hangup up the call on specified channel
+ */
+static int hangup(int card, unsigned long channel)
+{
+ int status;
+
+ if(!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return -ENODEV;
+ }
+
+ status = sendmessage(card, CEPID, ceReqTypePhy,
+ ceReqClass1,
+ ceReqPhyDisconnect,
+ (unsigned char) channel+1,
+ 0,
+ NULL);
+ pr_debug("%s: Sent HANGUP message to channel %lu\n",
+ sc_adapter[card]->devicename, channel+1);
+ return status;
+}
+
+/*
+ * Set the layer 2 protocol (X.25, HDLC, Raw)
+ */
+static int setl2(int card, unsigned long arg)
+{
+ int status =0;
+ int protocol,channel;
+
+ if(!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return -ENODEV;
+ }
+ protocol = arg >> 8;
+ channel = arg & 0xff;
+ sc_adapter[card]->channel[channel].l2_proto = protocol;
+
+ /*
+ * check that the adapter is also set to the correct protocol
+ */
+ pr_debug("%s: Sending GetFrameFormat for channel %d\n",
+ sc_adapter[card]->devicename, channel+1);
+ status = sendmessage(card, CEPID, ceReqTypeCall,
+ ceReqClass0,
+ ceReqCallGetFrameFormat,
+ (unsigned char)channel+1,
+ 1,
+ (unsigned int *) protocol);
+ if(status)
+ return status;
+ return 0;
+}
+
+/*
+ * Set the layer 3 protocol
+ */
+static int setl3(int card, unsigned long channel)
+{
+ int protocol = channel >> 8;
+
+ if(!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return -ENODEV;
+ }
+
+ sc_adapter[card]->channel[channel].l3_proto = protocol;
+ return 0;
+}
+
+static int acceptb(int card, unsigned long channel)
+{
+ if(!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return -ENODEV;
+ }
+
+ if(setup_buffers(card, channel+1))
+ {
+ hangup(card, channel+1);
+ return -ENOBUFS;
+ }
+
+ pr_debug("%s: B-Channel connection accepted on channel %lu\n",
+ sc_adapter[card]->devicename, channel+1);
+ indicate_status(card, ISDN_STAT_BCONN, channel, NULL);
+ return 0;
+}
+
+static int clreaz(int card, unsigned long arg)
+{
+ if(!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return -ENODEV;
+ }
+
+ strcpy(sc_adapter[card]->channel[arg].eazlist, "");
+ sc_adapter[card]->channel[arg].eazclear = 1;
+ pr_debug("%s: EAZ List cleared for channel %lu\n",
+ sc_adapter[card]->devicename, arg+1);
+ return 0;
+}
+
+static int seteaz(int card, unsigned long arg, char *num)
+{
+ if(!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return -ENODEV;
+ }
+
+ strcpy(sc_adapter[card]->channel[arg].eazlist, num);
+ sc_adapter[card]->channel[arg].eazclear = 0;
+ pr_debug("%s: EAZ list for channel %lu set to: %s\n",
+ sc_adapter[card]->devicename, arg+1,
+ sc_adapter[card]->channel[arg].eazlist);
+ return 0;
+}
+
+int reset(int card)
+{
+ unsigned long flags;
+
+ if(!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return -ENODEV;
+ }
+
+ indicate_status(card, ISDN_STAT_STOP, 0, NULL);
+
+ if(sc_adapter[card]->EngineUp) {
+ del_timer(&sc_adapter[card]->stat_timer);
+ }
+
+ sc_adapter[card]->EngineUp = 0;
+
+ spin_lock_irqsave(&sc_adapter[card]->lock, flags);
+ init_timer(&sc_adapter[card]->reset_timer);
+ sc_adapter[card]->reset_timer.function = sc_check_reset;
+ sc_adapter[card]->reset_timer.data = card;
+ sc_adapter[card]->reset_timer.expires = jiffies + CHECKRESET_TIME;
+ add_timer(&sc_adapter[card]->reset_timer);
+ spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
+
+ outb(0x1,sc_adapter[card]->ioport[SFT_RESET]);
+
+ pr_debug("%s: Adapter Reset\n", sc_adapter[card]->devicename);
+ return 0;
+}
+
+void flushreadfifo (int card)
+{
+ while(inb(sc_adapter[card]->ioport[FIFO_STATUS]) & RF_HAS_DATA)
+ inb(sc_adapter[card]->ioport[FIFO_READ]);
+}
diff --git a/drivers/isdn/sc/event.c b/drivers/isdn/sc/event.c
new file mode 100644
index 0000000..498f403
--- /dev/null
+++ b/drivers/isdn/sc/event.c
@@ -0,0 +1,68 @@
+/* $Id: event.c,v 1.4.8.1 2001/09/23 22:24:59 kai Exp $
+ *
+ * Copyright (C) 1996 SpellCaster Telecommunications Inc.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For more information, please contact gpl-info@spellcast.com or write:
+ *
+ * SpellCaster Telecommunications Inc.
+ * 5621 Finch Avenue East, Unit #3
+ * Scarborough, Ontario Canada
+ * M1B 2T9
+ * +1 (416) 297-8565
+ * +1 (416) 297-6433 Facsimile
+ */
+
+#include "includes.h"
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+
+#ifdef DEBUG
+static char *events[] = { "ISDN_STAT_STAVAIL",
+ "ISDN_STAT_ICALL",
+ "ISDN_STAT_RUN",
+ "ISDN_STAT_STOP",
+ "ISDN_STAT_DCONN",
+ "ISDN_STAT_BCONN",
+ "ISDN_STAT_DHUP",
+ "ISDN_STAT_BHUP",
+ "ISDN_STAT_CINF",
+ "ISDN_STAT_LOAD",
+ "ISDN_STAT_UNLOAD",
+ "ISDN_STAT_BSENT",
+ "ISDN_STAT_NODCH",
+ "ISDN_STAT_ADDCH",
+ "ISDN_STAT_CAUSE" };
+#endif
+
+int indicate_status(int card, int event,ulong Channel,char *Data)
+{
+ isdn_ctrl cmd;
+
+#ifdef DEBUG
+ pr_debug("%s: Indicating event %s on Channel %d\n",
+ sc_adapter[card]->devicename, events[event-256], Channel);
+#endif
+ if (Data != NULL){
+ pr_debug("%s: Event data: %s\n", sc_adapter[card]->devicename,
+ Data);
+ switch (event) {
+ case ISDN_STAT_BSENT:
+ memcpy(&cmd.parm.length, Data, sizeof(cmd.parm.length));
+ break;
+ case ISDN_STAT_ICALL:
+ memcpy(&cmd.parm.setup, Data, sizeof(cmd.parm.setup));
+ break;
+ default:
+ strcpy(cmd.parm.num, Data);
+ }
+ }
+
+ cmd.command = event;
+ cmd.driver = sc_adapter[card]->driverId;
+ cmd.arg = Channel;
+ return sc_adapter[card]->card->statcallb(&cmd);
+}
diff --git a/drivers/isdn/sc/hardware.h b/drivers/isdn/sc/hardware.h
new file mode 100644
index 0000000..9e6d530
--- /dev/null
+++ b/drivers/isdn/sc/hardware.h
@@ -0,0 +1,110 @@
+/*
+ * Hardware specific macros, defines and structures
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef HARDWARE_H
+#define HARDWARE_H
+
+#include <asm/param.h> /* For HZ */
+
+/*
+ * General hardware parameters common to all ISA adapters
+ */
+
+#define MAX_CARDS 4 /* The maximum number of cards to
+ control or probe for. */
+
+#define SIGNATURE 0x87654321 /* Board reset signature */
+#define SIG_OFFSET 0x1004 /* Where to find signature in shared RAM */
+#define TRACE_OFFSET 0x1008 /* Trace enable word offset in shared RAM */
+#define BUFFER_OFFSET 0x1800 /* Beginning of buffers */
+
+/* I/O Port parameters */
+#define IOBASE_MIN 0x180 /* Lowest I/O port address */
+#define IOBASE_MAX 0x3C0 /* Highest I/O port address */
+#define IOBASE_OFFSET 0x20 /* Inter-board I/O port gap used during
+ probing */
+#define FIFORD_OFFSET 0x0
+#define FIFOWR_OFFSET 0x400
+#define FIFOSTAT_OFFSET 0x1000
+#define RESET_OFFSET 0x2800
+#define PG0_OFFSET 0x3000 /* Offset from I/O Base for Page 0 register */
+#define PG1_OFFSET 0x3400 /* Offset from I/O Base for Page 1 register */
+#define PG2_OFFSET 0x3800 /* Offset from I/O Base for Page 2 register */
+#define PG3_OFFSET 0x3C00 /* Offset from I/O Base for Page 3 register */
+
+#define FIFO_READ 0 /* FIFO Read register */
+#define FIFO_WRITE 1 /* FIFO Write rgister */
+#define LO_ADDR_PTR 2 /* Extended RAM Low Addr Pointer */
+#define HI_ADDR_PTR 3 /* Extended RAM High Addr Pointer */
+#define NOT_USED_1 4
+#define FIFO_STATUS 5 /* FIFO Status Register */
+#define NOT_USED_2 6
+#define MEM_OFFSET 7
+#define SFT_RESET 10 /* Reset Register */
+#define EXP_BASE 11 /* Shared RAM Base address */
+#define EXP_PAGE0 12 /* Shared RAM Page0 register */
+#define EXP_PAGE1 13 /* Shared RAM Page1 register */
+#define EXP_PAGE2 14 /* Shared RAM Page2 register */
+#define EXP_PAGE3 15 /* Shared RAM Page3 register */
+#define IRQ_SELECT 16 /* IRQ selection register */
+#define MAX_IO_REGS 17 /* Total number of I/O ports */
+
+/* FIFO register values */
+#define RF_HAS_DATA 0x01 /* fifo has data */
+#define RF_QUART_FULL 0x02 /* fifo quarter full */
+#define RF_HALF_FULL 0x04 /* fifo half full */
+#define RF_NOT_FULL 0x08 /* fifo not full */
+#define WF_HAS_DATA 0x10 /* fifo has data */
+#define WF_QUART_FULL 0x20 /* fifo quarter full */
+#define WF_HALF_FULL 0x40 /* fifo half full */
+#define WF_NOT_FULL 0x80 /* fifo not full */
+
+/* Shared RAM parameters */
+#define SRAM_MIN 0xC0000 /* Lowest host shared RAM address */
+#define SRAM_MAX 0xEFFFF /* Highest host shared RAM address */
+#define SRAM_PAGESIZE 0x4000 /* Size of one RAM page (16K) */
+
+/* Shared RAM buffer parameters */
+#define BUFFER_SIZE 0x800 /* The size of a buffer in bytes */
+#define BUFFER_BASE BUFFER_OFFSET /* Offset from start of shared RAM
+ where buffer start */
+#define BUFFERS_MAX 16 /* Maximum number of send/receive
+ buffers per channel */
+#define HDLC_PROTO 0x01 /* Frame Format for Layer 2 */
+
+#define BRI_BOARD 0
+#define POTS_BOARD 1
+#define PRI_BOARD 2
+
+/*
+ * Specific hardware parameters for the DataCommute/BRI
+ */
+#define BRI_CHANNELS 2 /* Number of B channels */
+#define BRI_BASEPG_VAL 0x98
+#define BRI_MAGIC 0x60000 /* Magic Number */
+#define BRI_MEMSIZE 0x10000 /* Ammount of RAM (64K) */
+#define BRI_PARTNO "72-029"
+#define BRI_FEATURES ISDN_FEATURE_L2_HDLC | ISDN_FEATURE_L3_TRANS;
+/*
+ * Specific hardware parameters for the DataCommute/PRI
+ */
+#define PRI_CHANNELS 23 /* Number of B channels */
+#define PRI_BASEPG_VAL 0x88
+#define PRI_MAGIC 0x20000 /* Magic Number */
+#define PRI_MEMSIZE 0x100000 /* Amount of RAM (1M) */
+#define PRI_PARTNO "72-030"
+#define PRI_FEATURES ISDN_FEATURE_L2_HDLC | ISDN_FEATURE_L3_TRANS;
+
+/*
+ * Some handy macros
+ */
+
+/* Determine if a channel number is valid for the adapter */
+#define IS_VALID_CHANNEL(y,x) ((x>0) && (x <= sc_adapter[y]->channels))
+
+#endif
diff --git a/drivers/isdn/sc/includes.h b/drivers/isdn/sc/includes.h
new file mode 100644
index 0000000..4766e5b
--- /dev/null
+++ b/drivers/isdn/sc/includes.h
@@ -0,0 +1,16 @@
+/*
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/errno.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/isdnif.h>
diff --git a/drivers/isdn/sc/init.c b/drivers/isdn/sc/init.c
new file mode 100644
index 0000000..dd0acd0
--- /dev/null
+++ b/drivers/isdn/sc/init.c
@@ -0,0 +1,550 @@
+/*
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include "includes.h"
+#include "hardware.h"
+#include "card.h"
+
+MODULE_DESCRIPTION("ISDN4Linux: Driver for Spellcaster card");
+MODULE_AUTHOR("Spellcaster Telecommunications Inc.");
+MODULE_LICENSE("GPL");
+
+board *sc_adapter[MAX_CARDS];
+int cinst;
+
+static char devname[] = "scX";
+static const char version[] = "2.0b1";
+
+static const char *boardname[] = { "DataCommute/BRI", "DataCommute/PRI", "TeleCommute/BRI" };
+
+/* insmod set parameters */
+static unsigned int io[] = {0,0,0,0};
+static unsigned char irq[] = {0,0,0,0};
+static unsigned long ram[] = {0,0,0,0};
+static int do_reset = 0;
+
+module_param_array(io, int, NULL, 0);
+module_param_array(irq, int, NULL, 0);
+module_param_array(ram, int, NULL, 0);
+module_param(do_reset, bool, 0);
+
+static int identify_board(unsigned long, unsigned int);
+
+static int __init sc_init(void)
+{
+ int b = -1;
+ int i, j;
+ int status = -ENODEV;
+
+ unsigned long memsize = 0;
+ unsigned long features = 0;
+ isdn_if *interface;
+ unsigned char channels;
+ unsigned char pgport;
+ unsigned long magic;
+ int model;
+ int last_base = IOBASE_MIN;
+ int probe_exhasted = 0;
+
+#ifdef MODULE
+ pr_info("SpellCaster ISA ISDN Adapter Driver rev. %s Loaded\n", version);
+#else
+ pr_info("SpellCaster ISA ISDN Adapter Driver rev. %s\n", version);
+#endif
+ pr_info("Copyright (C) 1996 SpellCaster Telecommunications Inc.\n");
+
+ while(b++ < MAX_CARDS - 1) {
+ pr_debug("Probing for adapter #%d\n", b);
+ /*
+ * Initialize reusable variables
+ */
+ model = -1;
+ magic = 0;
+ channels = 0;
+ pgport = 0;
+
+ /*
+ * See if we should probe for IO base
+ */
+ pr_debug("I/O Base for board %d is 0x%x, %s probe\n", b, io[b],
+ io[b] == 0 ? "will" : "won't");
+ if(io[b]) {
+ /*
+ * No, I/O Base has been provided
+ */
+ for (i = 0 ; i < MAX_IO_REGS - 1 ; i++) {
+ if(!request_region(io[b] + i * 0x400, 1, "sc test")) {
+ pr_debug("request_region for 0x%x failed\n", io[b] + i * 0x400);
+ io[b] = 0;
+ break;
+ } else
+ release_region(io[b] + i * 0x400, 1);
+ }
+
+ /*
+ * Confirm the I/O Address with a test
+ */
+ if(io[b] == 0) {
+ pr_debug("I/O Address invalid.\n");
+ continue;
+ }
+
+ outb(0x18, io[b] + 0x400 * EXP_PAGE0);
+ if(inb(io[b] + 0x400 * EXP_PAGE0) != 0x18) {
+ pr_debug("I/O Base 0x%x fails test\n",
+ io[b] + 0x400 * EXP_PAGE0);
+ continue;
+ }
+ }
+ else {
+ /*
+ * Yes, probe for I/O Base
+ */
+ if(probe_exhasted) {
+ pr_debug("All probe addresses exhasted, skipping\n");
+ continue;
+ }
+ pr_debug("Probing for I/O...\n");
+ for (i = last_base ; i <= IOBASE_MAX ; i += IOBASE_OFFSET) {
+ int found_io = 1;
+ if (i == IOBASE_MAX) {
+ probe_exhasted = 1; /* No more addresses to probe */
+ pr_debug("End of Probes\n");
+ }
+ last_base = i + IOBASE_OFFSET;
+ pr_debug(" checking 0x%x...", i);
+ for ( j = 0 ; j < MAX_IO_REGS - 1 ; j++) {
+ if(!request_region(i + j * 0x400, 1, "sc test")) {
+ pr_debug("Failed\n");
+ found_io = 0;
+ break;
+ } else
+ release_region(i + j * 0x400, 1);
+ }
+
+ if(found_io) {
+ io[b] = i;
+ outb(0x18, io[b] + 0x400 * EXP_PAGE0);
+ if(inb(io[b] + 0x400 * EXP_PAGE0) != 0x18) {
+ pr_debug("Failed by test\n");
+ continue;
+ }
+ pr_debug("Passed\n");
+ break;
+ }
+ }
+ if(probe_exhasted) {
+ continue;
+ }
+ }
+
+ /*
+ * See if we should probe for shared RAM
+ */
+ if(do_reset) {
+ pr_debug("Doing a SAFE probe reset\n");
+ outb(0xFF, io[b] + RESET_OFFSET);
+ msleep_interruptible(10000);
+ }
+ pr_debug("RAM Base for board %d is 0x%lx, %s probe\n", b,
+ ram[b], ram[b] == 0 ? "will" : "won't");
+
+ if(ram[b]) {
+ /*
+ * No, the RAM base has been provided
+ * Just look for a signature and ID the
+ * board model
+ */
+ if(request_region(ram[b], SRAM_PAGESIZE, "sc test")) {
+ pr_debug("request_region for RAM base 0x%lx succeeded\n", ram[b]);
+ model = identify_board(ram[b], io[b]);
+ release_region(ram[b], SRAM_PAGESIZE);
+ }
+ }
+ else {
+ /*
+ * Yes, probe for free RAM and look for
+ * a signature and id the board model
+ */
+ for (i = SRAM_MIN ; i < SRAM_MAX ; i += SRAM_PAGESIZE) {
+ pr_debug("Checking RAM address 0x%x...\n", i);
+ if(request_region(i, SRAM_PAGESIZE, "sc test")) {
+ pr_debug(" request_region succeeded\n");
+ model = identify_board(i, io[b]);
+ release_region(i, SRAM_PAGESIZE);
+ if (model >= 0) {
+ pr_debug(" Identified a %s\n",
+ boardname[model]);
+ ram[b] = i;
+ break;
+ }
+ pr_debug(" Unidentifed or inaccessible\n");
+ continue;
+ }
+ pr_debug(" request failed\n");
+ }
+ }
+ /*
+ * See if we found free RAM and the board model
+ */
+ if(!ram[b] || model < 0) {
+ /*
+ * Nope, there was no place in RAM for the
+ * board, or it couldn't be identified
+ */
+ pr_debug("Failed to find an adapter at 0x%lx\n", ram[b]);
+ continue;
+ }
+
+ /*
+ * Set the board's magic number, memory size and page register
+ */
+ switch(model) {
+ case PRI_BOARD:
+ channels = 23;
+ magic = 0x20000;
+ memsize = 0x100000;
+ features = PRI_FEATURES;
+ break;
+
+ case BRI_BOARD:
+ case POTS_BOARD:
+ channels = 2;
+ magic = 0x60000;
+ memsize = 0x10000;
+ features = BRI_FEATURES;
+ break;
+ }
+ switch(ram[b] >> 12 & 0x0F) {
+ case 0x0:
+ pr_debug("RAM Page register set to EXP_PAGE0\n");
+ pgport = EXP_PAGE0;
+ break;
+
+ case 0x4:
+ pr_debug("RAM Page register set to EXP_PAGE1\n");
+ pgport = EXP_PAGE1;
+ break;
+
+ case 0x8:
+ pr_debug("RAM Page register set to EXP_PAGE2\n");
+ pgport = EXP_PAGE2;
+ break;
+
+ case 0xC:
+ pr_debug("RAM Page register set to EXP_PAGE3\n");
+ pgport = EXP_PAGE3;
+ break;
+
+ default:
+ pr_debug("RAM base address doesn't fall on 16K boundary\n");
+ continue;
+ }
+
+ pr_debug("current IRQ: %d b: %d\n",irq[b],b);
+
+ /*
+ * Make sure we got an IRQ
+ */
+ if(!irq[b]) {
+ /*
+ * No interrupt could be used
+ */
+ pr_debug("Failed to acquire an IRQ line\n");
+ continue;
+ }
+
+ /*
+ * Horray! We found a board, Make sure we can register
+ * it with ISDN4Linux
+ */
+ interface = kzalloc(sizeof(isdn_if), GFP_KERNEL);
+ if (interface == NULL) {
+ /*
+ * Oops, can't malloc isdn_if
+ */
+ continue;
+ }
+
+ interface->owner = THIS_MODULE;
+ interface->hl_hdrlen = 0;
+ interface->channels = channels;
+ interface->maxbufsize = BUFFER_SIZE;
+ interface->features = features;
+ interface->writebuf_skb = sndpkt;
+ interface->writecmd = NULL;
+ interface->command = command;
+ strcpy(interface->id, devname);
+ interface->id[2] = '0' + cinst;
+
+ /*
+ * Allocate the board structure
+ */
+ sc_adapter[cinst] = kzalloc(sizeof(board), GFP_KERNEL);
+ if (sc_adapter[cinst] == NULL) {
+ /*
+ * Oops, can't alloc memory for the board
+ */
+ kfree(interface);
+ continue;
+ }
+ spin_lock_init(&sc_adapter[cinst]->lock);
+
+ if(!register_isdn(interface)) {
+ /*
+ * Oops, couldn't register for some reason
+ */
+ kfree(interface);
+ kfree(sc_adapter[cinst]);
+ continue;
+ }
+
+ sc_adapter[cinst]->card = interface;
+ sc_adapter[cinst]->driverId = interface->channels;
+ strcpy(sc_adapter[cinst]->devicename, interface->id);
+ sc_adapter[cinst]->nChannels = channels;
+ sc_adapter[cinst]->ramsize = memsize;
+ sc_adapter[cinst]->shmem_magic = magic;
+ sc_adapter[cinst]->shmem_pgport = pgport;
+ sc_adapter[cinst]->StartOnReset = 1;
+
+ /*
+ * Allocate channels status structures
+ */
+ sc_adapter[cinst]->channel = kzalloc(sizeof(bchan) * channels, GFP_KERNEL);
+ if (sc_adapter[cinst]->channel == NULL) {
+ /*
+ * Oops, can't alloc memory for the channels
+ */
+ indicate_status(cinst, ISDN_STAT_UNLOAD, 0, NULL); /* Fix me */
+ kfree(interface);
+ kfree(sc_adapter[cinst]);
+ continue;
+ }
+
+ /*
+ * Lock down the hardware resources
+ */
+ sc_adapter[cinst]->interrupt = irq[b];
+ if (request_irq(sc_adapter[cinst]->interrupt, interrupt_handler,
+ IRQF_DISABLED, interface->id,
+ (void *)(unsigned long) cinst))
+ {
+ kfree(sc_adapter[cinst]->channel);
+ indicate_status(cinst, ISDN_STAT_UNLOAD, 0, NULL); /* Fix me */
+ kfree(interface);
+ kfree(sc_adapter[cinst]);
+ continue;
+
+ }
+ sc_adapter[cinst]->iobase = io[b];
+ for(i = 0 ; i < MAX_IO_REGS - 1 ; i++) {
+ sc_adapter[cinst]->ioport[i] = io[b] + i * 0x400;
+ request_region(sc_adapter[cinst]->ioport[i], 1,
+ interface->id);
+ pr_debug("Requesting I/O Port %#x\n",
+ sc_adapter[cinst]->ioport[i]);
+ }
+ sc_adapter[cinst]->ioport[IRQ_SELECT] = io[b] + 0x2;
+ request_region(sc_adapter[cinst]->ioport[IRQ_SELECT], 1,
+ interface->id);
+ pr_debug("Requesting I/O Port %#x\n",
+ sc_adapter[cinst]->ioport[IRQ_SELECT]);
+ sc_adapter[cinst]->rambase = ram[b];
+ request_region(sc_adapter[cinst]->rambase, SRAM_PAGESIZE,
+ interface->id);
+
+ pr_info(" %s (%d) - %s %d channels IRQ %d, I/O Base 0x%x, RAM Base 0x%lx\n",
+ sc_adapter[cinst]->devicename,
+ sc_adapter[cinst]->driverId,
+ boardname[model], channels, irq[b], io[b], ram[b]);
+
+ /*
+ * reset the adapter to put things in motion
+ */
+ reset(cinst);
+
+ cinst++;
+ status = 0;
+ }
+ if (status)
+ pr_info("Failed to find any adapters, driver unloaded\n");
+ return status;
+}
+
+static void __exit sc_exit(void)
+{
+ int i, j;
+
+ for(i = 0 ; i < cinst ; i++) {
+ pr_debug("Cleaning up after adapter %d\n", i);
+ /*
+ * kill the timers
+ */
+ del_timer(&(sc_adapter[i]->reset_timer));
+ del_timer(&(sc_adapter[i]->stat_timer));
+
+ /*
+ * Tell I4L we're toast
+ */
+ indicate_status(i, ISDN_STAT_STOP, 0, NULL);
+ indicate_status(i, ISDN_STAT_UNLOAD, 0, NULL);
+
+ /*
+ * Release shared RAM
+ */
+ release_region(sc_adapter[i]->rambase, SRAM_PAGESIZE);
+
+ /*
+ * Release the IRQ
+ */
+ free_irq(sc_adapter[i]->interrupt, NULL);
+
+ /*
+ * Reset for a clean start
+ */
+ outb(0xFF, sc_adapter[i]->ioport[SFT_RESET]);
+
+ /*
+ * Release the I/O Port regions
+ */
+ for(j = 0 ; j < MAX_IO_REGS - 1; j++) {
+ release_region(sc_adapter[i]->ioport[j], 1);
+ pr_debug("Releasing I/O Port %#x\n",
+ sc_adapter[i]->ioport[j]);
+ }
+ release_region(sc_adapter[i]->ioport[IRQ_SELECT], 1);
+ pr_debug("Releasing I/O Port %#x\n",
+ sc_adapter[i]->ioport[IRQ_SELECT]);
+
+ /*
+ * Release any memory we alloced
+ */
+ kfree(sc_adapter[i]->channel);
+ kfree(sc_adapter[i]->card);
+ kfree(sc_adapter[i]);
+ }
+ pr_info("SpellCaster ISA ISDN Adapter Driver Unloaded.\n");
+}
+
+static int identify_board(unsigned long rambase, unsigned int iobase)
+{
+ unsigned int pgport;
+ unsigned long sig;
+ DualPortMemory *dpm;
+ RspMessage rcvmsg;
+ ReqMessage sndmsg;
+ HWConfig_pl hwci;
+ int x;
+
+ pr_debug("Attempting to identify adapter @ 0x%lx io 0x%x\n",
+ rambase, iobase);
+
+ /*
+ * Enable the base pointer
+ */
+ outb(rambase >> 12, iobase + 0x2c00);
+
+ switch(rambase >> 12 & 0x0F) {
+ case 0x0:
+ pgport = iobase + PG0_OFFSET;
+ pr_debug("Page Register offset is 0x%x\n", PG0_OFFSET);
+ break;
+
+ case 0x4:
+ pgport = iobase + PG1_OFFSET;
+ pr_debug("Page Register offset is 0x%x\n", PG1_OFFSET);
+ break;
+
+ case 0x8:
+ pgport = iobase + PG2_OFFSET;
+ pr_debug("Page Register offset is 0x%x\n", PG2_OFFSET);
+ break;
+
+ case 0xC:
+ pgport = iobase + PG3_OFFSET;
+ pr_debug("Page Register offset is 0x%x\n", PG3_OFFSET);
+ break;
+ default:
+ pr_debug("Invalid rambase 0x%lx\n", rambase);
+ return -1;
+ }
+
+ /*
+ * Try to identify a PRI card
+ */
+ outb(PRI_BASEPG_VAL, pgport);
+ msleep_interruptible(1000);
+ sig = readl(rambase + SIG_OFFSET);
+ pr_debug("Looking for a signature, got 0x%lx\n", sig);
+ if(sig == SIGNATURE)
+ return PRI_BOARD;
+
+ /*
+ * Try to identify a PRI card
+ */
+ outb(BRI_BASEPG_VAL, pgport);
+ msleep_interruptible(1000);
+ sig = readl(rambase + SIG_OFFSET);
+ pr_debug("Looking for a signature, got 0x%lx\n", sig);
+ if(sig == SIGNATURE)
+ return BRI_BOARD;
+
+ return -1;
+
+ /*
+ * Try to spot a card
+ */
+ sig = readl(rambase + SIG_OFFSET);
+ pr_debug("Looking for a signature, got 0x%lx\n", sig);
+ if(sig != SIGNATURE)
+ return -1;
+
+ dpm = (DualPortMemory *) rambase;
+
+ memset(&sndmsg, 0, MSG_LEN);
+ sndmsg.msg_byte_cnt = 3;
+ sndmsg.type = cmReqType1;
+ sndmsg.class = cmReqClass0;
+ sndmsg.code = cmReqHWConfig;
+ memcpy_toio(&(dpm->req_queue[dpm->req_head++]), &sndmsg, MSG_LEN);
+ outb(0, iobase + 0x400);
+ pr_debug("Sent HWConfig message\n");
+ /*
+ * Wait for the response
+ */
+ x = 0;
+ while((inb(iobase + FIFOSTAT_OFFSET) & RF_HAS_DATA) && x < 100) {
+ schedule_timeout_interruptible(1);
+ x++;
+ }
+ if(x == 100) {
+ pr_debug("Timeout waiting for response\n");
+ return -1;
+ }
+
+ memcpy_fromio(&rcvmsg, &(dpm->rsp_queue[dpm->rsp_tail]), MSG_LEN);
+ pr_debug("Got HWConfig response, status = 0x%x\n", rcvmsg.rsp_status);
+ memcpy(&hwci, &(rcvmsg.msg_data.HWCresponse), sizeof(HWConfig_pl));
+ pr_debug("Hardware Config: Interface: %s, RAM Size: %ld, Serial: %s\n"
+ " Part: %s, Rev: %s\n",
+ hwci.st_u_sense ? "S/T" : "U", hwci.ram_size,
+ hwci.serial_no, hwci.part_no, hwci.rev_no);
+
+ if(!strncmp(PRI_PARTNO, hwci.part_no, 6))
+ return PRI_BOARD;
+ if(!strncmp(BRI_PARTNO, hwci.part_no, 6))
+ return BRI_BOARD;
+
+ return -1;
+}
+
+module_init(sc_init);
+module_exit(sc_exit);
diff --git a/drivers/isdn/sc/interrupt.c b/drivers/isdn/sc/interrupt.c
new file mode 100644
index 0000000..485be8b
--- /dev/null
+++ b/drivers/isdn/sc/interrupt.c
@@ -0,0 +1,237 @@
+/* $Id: interrupt.c,v 1.4.8.3 2001/09/23 22:24:59 kai Exp $
+ *
+ * Copyright (C) 1996 SpellCaster Telecommunications Inc.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For more information, please contact gpl-info@spellcast.com or write:
+ *
+ * SpellCaster Telecommunications Inc.
+ * 5621 Finch Avenue East, Unit #3
+ * Scarborough, Ontario Canada
+ * M1B 2T9
+ * +1 (416) 297-8565
+ * +1 (416) 297-6433 Facsimile
+ */
+
+#include "includes.h"
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+#include <linux/interrupt.h>
+
+/*
+ *
+ */
+irqreturn_t interrupt_handler(int dummy, void *card_inst)
+{
+
+ RspMessage rcvmsg;
+ int channel;
+ int card = (int)(unsigned long) card_inst;
+
+ if(!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return IRQ_NONE;
+ }
+
+ pr_debug("%s: Entered Interrupt handler\n",
+ sc_adapter[card]->devicename);
+
+ /*
+ * Pull all of the waiting messages off the response queue
+ */
+ while (!receivemessage(card, &rcvmsg)) {
+ /*
+ * Push the message to the adapter structure for
+ * send_and_receive to snoop
+ */
+ if(sc_adapter[card]->want_async_messages)
+ memcpy(&(sc_adapter[card]->async_msg),
+ &rcvmsg, sizeof(RspMessage));
+
+ channel = (unsigned int) rcvmsg.phy_link_no;
+
+ /*
+ * Trap Invalid request messages
+ */
+ if(IS_CM_MESSAGE(rcvmsg, 0, 0, Invalid)) {
+ pr_debug("%s: Invalid request Message, rsp_status = %d\n",
+ sc_adapter[card]->devicename,
+ rcvmsg.rsp_status);
+ break;
+ }
+
+ /*
+ * Check for a linkRead message
+ */
+ if (IS_CE_MESSAGE(rcvmsg, Lnk, 1, Read))
+ {
+ pr_debug("%s: Received packet 0x%x bytes long at 0x%lx\n",
+ sc_adapter[card]->devicename,
+ rcvmsg.msg_data.response.msg_len,
+ rcvmsg.msg_data.response.buff_offset);
+ rcvpkt(card, &rcvmsg);
+ continue;
+
+ }
+
+ /*
+ * Handle a write acknoledgement
+ */
+ if(IS_CE_MESSAGE(rcvmsg, Lnk, 1, Write)) {
+ pr_debug("%s: Packet Send ACK on channel %d\n",
+ sc_adapter[card]->devicename,
+ rcvmsg.phy_link_no);
+ sc_adapter[card]->channel[rcvmsg.phy_link_no-1].free_sendbufs++;
+ continue;
+ }
+
+ /*
+ * Handle a connection message
+ */
+ if (IS_CE_MESSAGE(rcvmsg, Phy, 1, Connect))
+ {
+ unsigned int callid;
+ setup_parm setup;
+ pr_debug("%s: Connect message: line %d: status %d: cause 0x%x\n",
+ sc_adapter[card]->devicename,
+ rcvmsg.phy_link_no,
+ rcvmsg.rsp_status,
+ rcvmsg.msg_data.byte_array[2]);
+
+ memcpy(&callid,rcvmsg.msg_data.byte_array,sizeof(int));
+ if(callid>=0x8000 && callid<=0xFFFF)
+ {
+ pr_debug("%s: Got Dial-Out Rsp\n",
+ sc_adapter[card]->devicename);
+ indicate_status(card, ISDN_STAT_DCONN,
+ (unsigned long)rcvmsg.phy_link_no-1,NULL);
+
+ }
+ else if(callid>=0x0000 && callid<=0x7FFF)
+ {
+ pr_debug("%s: Got Incoming Call\n",
+ sc_adapter[card]->devicename);
+ strcpy(setup.phone,&(rcvmsg.msg_data.byte_array[4]));
+ strcpy(setup.eazmsn,
+ sc_adapter[card]->channel[rcvmsg.phy_link_no-1].dn);
+ setup.si1 = 7;
+ setup.si2 = 0;
+ setup.plan = 0;
+ setup.screen = 0;
+
+ indicate_status(card, ISDN_STAT_ICALL,(unsigned long)rcvmsg.phy_link_no-1,(char *)&setup);
+ indicate_status(card, ISDN_STAT_DCONN,(unsigned long)rcvmsg.phy_link_no-1,NULL);
+ }
+ continue;
+ }
+
+ /*
+ * Handle a disconnection message
+ */
+ if (IS_CE_MESSAGE(rcvmsg, Phy, 1, Disconnect))
+ {
+ pr_debug("%s: disconnect message: line %d: status %d: cause 0x%x\n",
+ sc_adapter[card]->devicename,
+ rcvmsg.phy_link_no,
+ rcvmsg.rsp_status,
+ rcvmsg.msg_data.byte_array[2]);
+
+ indicate_status(card, ISDN_STAT_BHUP,(unsigned long)rcvmsg.phy_link_no-1,NULL);
+ indicate_status(card, ISDN_STAT_DHUP,(unsigned long)rcvmsg.phy_link_no-1,NULL);
+ continue;
+
+ }
+
+ /*
+ * Handle a startProc engine up message
+ */
+ if (IS_CM_MESSAGE(rcvmsg, 5, 0, MiscEngineUp)) {
+ pr_debug("%s: Received EngineUp message\n",
+ sc_adapter[card]->devicename);
+ sc_adapter[card]->EngineUp = 1;
+ sendmessage(card, CEPID,ceReqTypeCall,ceReqClass0,ceReqCallGetMyNumber,1,0,NULL);
+ sendmessage(card, CEPID,ceReqTypeCall,ceReqClass0,ceReqCallGetMyNumber,2,0,NULL);
+ init_timer(&sc_adapter[card]->stat_timer);
+ sc_adapter[card]->stat_timer.function = check_phystat;
+ sc_adapter[card]->stat_timer.data = card;
+ sc_adapter[card]->stat_timer.expires = jiffies + CHECKSTAT_TIME;
+ add_timer(&sc_adapter[card]->stat_timer);
+ continue;
+ }
+
+ /*
+ * Start proc response
+ */
+ if (IS_CM_MESSAGE(rcvmsg, 2, 0, StartProc)) {
+ pr_debug("%s: StartProc Response Status %d\n",
+ sc_adapter[card]->devicename,
+ rcvmsg.rsp_status);
+ continue;
+ }
+
+ /*
+ * Handle a GetMyNumber Rsp
+ */
+ if (IS_CE_MESSAGE(rcvmsg,Call,0,GetMyNumber)){
+ strcpy(sc_adapter[card]->channel[rcvmsg.phy_link_no-1].dn,rcvmsg.msg_data.byte_array);
+ continue;
+ }
+
+ /*
+ * PhyStatus response
+ */
+ if(IS_CE_MESSAGE(rcvmsg, Phy, 2, Status)) {
+ unsigned int b1stat, b2stat;
+
+ /*
+ * Covert the message data to the adapter->phystat code
+ */
+ b1stat = (unsigned int) rcvmsg.msg_data.byte_array[0];
+ b2stat = (unsigned int) rcvmsg.msg_data.byte_array[1];
+
+ sc_adapter[card]->nphystat = (b2stat >> 8) | b1stat; /* endian?? */
+ pr_debug("%s: PhyStat is 0x%2x\n",
+ sc_adapter[card]->devicename,
+ sc_adapter[card]->nphystat);
+ continue;
+ }
+
+
+ /*
+ * Handle a GetFramFormat
+ */
+ if(IS_CE_MESSAGE(rcvmsg, Call, 0, GetFrameFormat)) {
+ if(rcvmsg.msg_data.byte_array[0] != HDLC_PROTO) {
+ unsigned int proto = HDLC_PROTO;
+ /*
+ * Set board format to HDLC if it wasn't already
+ */
+ pr_debug("%s: current frame format: 0x%x, will change to HDLC\n",
+ sc_adapter[card]->devicename,
+ rcvmsg.msg_data.byte_array[0]);
+ sendmessage(card, CEPID, ceReqTypeCall,
+ ceReqClass0,
+ ceReqCallSetFrameFormat,
+ (unsigned char) channel +1,
+ 1,&proto);
+ }
+ continue;
+ }
+
+ /*
+ * Hmm...
+ */
+ pr_debug("%s: Received unhandled message (%d,%d,%d) link %d\n",
+ sc_adapter[card]->devicename,
+ rcvmsg.type, rcvmsg.class, rcvmsg.code,
+ rcvmsg.phy_link_no);
+
+ } /* while */
+
+ pr_debug("%s: Exiting Interrupt Handler\n",
+ sc_adapter[card]->devicename);
+ return IRQ_HANDLED;
+}
diff --git a/drivers/isdn/sc/ioctl.c b/drivers/isdn/sc/ioctl.c
new file mode 100644
index 0000000..1081091
--- /dev/null
+++ b/drivers/isdn/sc/ioctl.c
@@ -0,0 +1,593 @@
+/*
+ * Copyright (C) 1996 SpellCaster Telecommunications Inc.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#include "includes.h"
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+#include "scioc.h"
+
+static int GetStatus(int card, boardInfo *);
+
+/*
+ * Process private IOCTL messages (typically from scctrl)
+ */
+int sc_ioctl(int card, scs_ioctl *data)
+{
+ int status;
+ RspMessage *rcvmsg;
+ char *spid;
+ char *dn;
+ char switchtype;
+ char speed;
+
+ rcvmsg = kmalloc(sizeof(RspMessage), GFP_KERNEL);
+ if (!rcvmsg)
+ return -ENOMEM;
+
+ switch(data->command) {
+ case SCIOCRESET: /* Perform a hard reset of the adapter */
+ {
+ pr_debug("%s: SCIOCRESET: ioctl received\n",
+ sc_adapter[card]->devicename);
+ sc_adapter[card]->StartOnReset = 0;
+ kfree(rcvmsg);
+ return reset(card);
+ }
+
+ case SCIOCLOAD:
+ {
+ char *srec;
+
+ srec = kmalloc(SCIOC_SRECSIZE, GFP_KERNEL);
+ if (!srec) {
+ kfree(rcvmsg);
+ return -ENOMEM;
+ }
+ pr_debug("%s: SCIOLOAD: ioctl received\n",
+ sc_adapter[card]->devicename);
+ if(sc_adapter[card]->EngineUp) {
+ pr_debug("%s: SCIOCLOAD: command failed, LoadProc while engine running.\n",
+ sc_adapter[card]->devicename);
+ kfree(rcvmsg);
+ kfree(srec);
+ return -1;
+ }
+
+ /*
+ * Get the SRec from user space
+ */
+ if (copy_from_user(srec, data->dataptr, SCIOC_SRECSIZE)) {
+ kfree(rcvmsg);
+ kfree(srec);
+ return -EFAULT;
+ }
+
+ status = send_and_receive(card, CMPID, cmReqType2, cmReqClass0, cmReqLoadProc,
+ 0, SCIOC_SRECSIZE, srec, rcvmsg, SAR_TIMEOUT);
+ kfree(rcvmsg);
+ kfree(srec);
+
+ if(status) {
+ pr_debug("%s: SCIOCLOAD: command failed, status = %d\n",
+ sc_adapter[card]->devicename, status);
+ return -1;
+ }
+ else {
+ pr_debug("%s: SCIOCLOAD: command successful\n",
+ sc_adapter[card]->devicename);
+ return 0;
+ }
+ }
+
+ case SCIOCSTART:
+ {
+ kfree(rcvmsg);
+ pr_debug("%s: SCIOSTART: ioctl received\n",
+ sc_adapter[card]->devicename);
+ if(sc_adapter[card]->EngineUp) {
+ pr_debug("%s: SCIOCSTART: command failed, engine already running.\n",
+ sc_adapter[card]->devicename);
+ return -1;
+ }
+
+ sc_adapter[card]->StartOnReset = 1;
+ startproc(card);
+ return 0;
+ }
+
+ case SCIOCSETSWITCH:
+ {
+ pr_debug("%s: SCIOSETSWITCH: ioctl received\n",
+ sc_adapter[card]->devicename);
+
+ /*
+ * Get the switch type from user space
+ */
+ if (copy_from_user(&switchtype, data->dataptr, sizeof(char))) {
+ kfree(rcvmsg);
+ return -EFAULT;
+ }
+
+ pr_debug("%s: SCIOCSETSWITCH: setting switch type to %d\n",
+ sc_adapter[card]->devicename,
+ switchtype);
+ status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallSetSwitchType,
+ 0, sizeof(char),&switchtype, rcvmsg, SAR_TIMEOUT);
+ if(!status && !(rcvmsg->rsp_status)) {
+ pr_debug("%s: SCIOCSETSWITCH: command successful\n",
+ sc_adapter[card]->devicename);
+ kfree(rcvmsg);
+ return 0;
+ }
+ else {
+ pr_debug("%s: SCIOCSETSWITCH: command failed (status = %d)\n",
+ sc_adapter[card]->devicename, status);
+ kfree(rcvmsg);
+ return status;
+ }
+ }
+
+ case SCIOCGETSWITCH:
+ {
+ pr_debug("%s: SCIOGETSWITCH: ioctl received\n",
+ sc_adapter[card]->devicename);
+
+ /*
+ * Get the switch type from the board
+ */
+ status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0,
+ ceReqCallGetSwitchType, 0, 0, NULL, rcvmsg, SAR_TIMEOUT);
+ if (!status && !(rcvmsg->rsp_status)) {
+ pr_debug("%s: SCIOCGETSWITCH: command successful\n",
+ sc_adapter[card]->devicename);
+ }
+ else {
+ pr_debug("%s: SCIOCGETSWITCH: command failed (status = %d)\n",
+ sc_adapter[card]->devicename, status);
+ kfree(rcvmsg);
+ return status;
+ }
+
+ switchtype = rcvmsg->msg_data.byte_array[0];
+
+ /*
+ * Package the switch type and send to user space
+ */
+ if (copy_to_user(data->dataptr, &switchtype,
+ sizeof(char))) {
+ kfree(rcvmsg);
+ return -EFAULT;
+ }
+
+ kfree(rcvmsg);
+ return 0;
+ }
+
+ case SCIOCGETSPID:
+ {
+ pr_debug("%s: SCIOGETSPID: ioctl received\n",
+ sc_adapter[card]->devicename);
+
+ spid = kmalloc(SCIOC_SPIDSIZE, GFP_KERNEL);
+ if (!spid) {
+ kfree(rcvmsg);
+ return -ENOMEM;
+ }
+ /*
+ * Get the spid from the board
+ */
+ status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallGetSPID,
+ data->channel, 0, NULL, rcvmsg, SAR_TIMEOUT);
+ if (!status) {
+ pr_debug("%s: SCIOCGETSPID: command successful\n",
+ sc_adapter[card]->devicename);
+ } else {
+ pr_debug("%s: SCIOCGETSPID: command failed (status = %d)\n",
+ sc_adapter[card]->devicename, status);
+ kfree(spid);
+ kfree(rcvmsg);
+ return status;
+ }
+ strcpy(spid, rcvmsg->msg_data.byte_array);
+
+ /*
+ * Package the switch type and send to user space
+ */
+ if (copy_to_user(data->dataptr, spid, SCIOC_SPIDSIZE)) {
+ kfree(spid);
+ kfree(rcvmsg);
+ return -EFAULT;
+ }
+
+ kfree(spid);
+ kfree(rcvmsg);
+ return 0;
+ }
+
+ case SCIOCSETSPID:
+ {
+ pr_debug("%s: DCBIOSETSPID: ioctl received\n",
+ sc_adapter[card]->devicename);
+
+ spid = kmalloc(SCIOC_SPIDSIZE, GFP_KERNEL);
+ if(!spid) {
+ kfree(rcvmsg);
+ return -ENOMEM;
+ }
+
+ /*
+ * Get the spid from user space
+ */
+ if (copy_from_user(spid, data->dataptr, SCIOC_SPIDSIZE)) {
+ kfree(rcvmsg);
+ kfree(spid);
+ return -EFAULT;
+ }
+
+ pr_debug("%s: SCIOCSETSPID: setting channel %d spid to %s\n",
+ sc_adapter[card]->devicename, data->channel, spid);
+ status = send_and_receive(card, CEPID, ceReqTypeCall,
+ ceReqClass0, ceReqCallSetSPID, data->channel,
+ strlen(spid), spid, rcvmsg, SAR_TIMEOUT);
+ if(!status && !(rcvmsg->rsp_status)) {
+ pr_debug("%s: SCIOCSETSPID: command successful\n",
+ sc_adapter[card]->devicename);
+ kfree(rcvmsg);
+ kfree(spid);
+ return 0;
+ }
+ else {
+ pr_debug("%s: SCIOCSETSPID: command failed (status = %d)\n",
+ sc_adapter[card]->devicename, status);
+ kfree(rcvmsg);
+ kfree(spid);
+ return status;
+ }
+ }
+
+ case SCIOCGETDN:
+ {
+ pr_debug("%s: SCIOGETDN: ioctl received\n",
+ sc_adapter[card]->devicename);
+
+ /*
+ * Get the dn from the board
+ */
+ status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0, ceReqCallGetMyNumber,
+ data->channel, 0, NULL, rcvmsg, SAR_TIMEOUT);
+ if (!status) {
+ pr_debug("%s: SCIOCGETDN: command successful\n",
+ sc_adapter[card]->devicename);
+ }
+ else {
+ pr_debug("%s: SCIOCGETDN: command failed (status = %d)\n",
+ sc_adapter[card]->devicename, status);
+ kfree(rcvmsg);
+ return status;
+ }
+
+ dn = kmalloc(SCIOC_DNSIZE, GFP_KERNEL);
+ if (!dn) {
+ kfree(rcvmsg);
+ return -ENOMEM;
+ }
+ strcpy(dn, rcvmsg->msg_data.byte_array);
+ kfree(rcvmsg);
+
+ /*
+ * Package the dn and send to user space
+ */
+ if (copy_to_user(data->dataptr, dn, SCIOC_DNSIZE)) {
+ kfree(dn);
+ return -EFAULT;
+ }
+ kfree(dn);
+ return 0;
+ }
+
+ case SCIOCSETDN:
+ {
+ pr_debug("%s: SCIOSETDN: ioctl received\n",
+ sc_adapter[card]->devicename);
+
+ dn = kmalloc(SCIOC_DNSIZE, GFP_KERNEL);
+ if (!dn) {
+ kfree(rcvmsg);
+ return -ENOMEM;
+ }
+ /*
+ * Get the spid from user space
+ */
+ if (copy_from_user(dn, data->dataptr, SCIOC_DNSIZE)) {
+ kfree(rcvmsg);
+ kfree(dn);
+ return -EFAULT;
+ }
+
+ pr_debug("%s: SCIOCSETDN: setting channel %d dn to %s\n",
+ sc_adapter[card]->devicename, data->channel, dn);
+ status = send_and_receive(card, CEPID, ceReqTypeCall,
+ ceReqClass0, ceReqCallSetMyNumber, data->channel,
+ strlen(dn),dn,rcvmsg, SAR_TIMEOUT);
+ if(!status && !(rcvmsg->rsp_status)) {
+ pr_debug("%s: SCIOCSETDN: command successful\n",
+ sc_adapter[card]->devicename);
+ kfree(rcvmsg);
+ kfree(dn);
+ return 0;
+ }
+ else {
+ pr_debug("%s: SCIOCSETDN: command failed (status = %d)\n",
+ sc_adapter[card]->devicename, status);
+ kfree(rcvmsg);
+ kfree(dn);
+ return status;
+ }
+ }
+
+ case SCIOCTRACE:
+
+ pr_debug("%s: SCIOTRACE: ioctl received\n",
+ sc_adapter[card]->devicename);
+/* sc_adapter[card]->trace = !sc_adapter[card]->trace;
+ pr_debug("%s: SCIOCTRACE: tracing turned %s\n",
+ sc_adapter[card]->devicename,
+ sc_adapter[card]->trace ? "ON" : "OFF"); */
+ break;
+
+ case SCIOCSTAT:
+ {
+ boardInfo *bi;
+
+ pr_debug("%s: SCIOSTAT: ioctl received\n",
+ sc_adapter[card]->devicename);
+
+ bi = kmalloc (sizeof(boardInfo), GFP_KERNEL);
+ if (!bi) {
+ kfree(rcvmsg);
+ return -ENOMEM;
+ }
+
+ kfree(rcvmsg);
+ GetStatus(card, bi);
+
+ if (copy_to_user(data->dataptr, bi, sizeof(boardInfo))) {
+ kfree(bi);
+ return -EFAULT;
+ }
+
+ kfree(bi);
+ return 0;
+ }
+
+ case SCIOCGETSPEED:
+ {
+ pr_debug("%s: SCIOGETSPEED: ioctl received\n",
+ sc_adapter[card]->devicename);
+
+ /*
+ * Get the speed from the board
+ */
+ status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0,
+ ceReqCallGetCallType, data->channel, 0, NULL, rcvmsg, SAR_TIMEOUT);
+ if (!status && !(rcvmsg->rsp_status)) {
+ pr_debug("%s: SCIOCGETSPEED: command successful\n",
+ sc_adapter[card]->devicename);
+ }
+ else {
+ pr_debug("%s: SCIOCGETSPEED: command failed (status = %d)\n",
+ sc_adapter[card]->devicename, status);
+ kfree(rcvmsg);
+ return status;
+ }
+
+ speed = rcvmsg->msg_data.byte_array[0];
+
+ kfree(rcvmsg);
+
+ /*
+ * Package the switch type and send to user space
+ */
+
+ if (copy_to_user(data->dataptr, &speed, sizeof(char)))
+ return -EFAULT;
+
+ return 0;
+ }
+
+ case SCIOCSETSPEED:
+ pr_debug("%s: SCIOCSETSPEED: ioctl received\n",
+ sc_adapter[card]->devicename);
+ break;
+
+ case SCIOCLOOPTST:
+ pr_debug("%s: SCIOCLOOPTST: ioctl received\n",
+ sc_adapter[card]->devicename);
+ break;
+
+ default:
+ kfree(rcvmsg);
+ return -1;
+ }
+
+ kfree(rcvmsg);
+ return 0;
+}
+
+static int GetStatus(int card, boardInfo *bi)
+{
+ RspMessage rcvmsg;
+ int i, status;
+
+ /*
+ * Fill in some of the basic info about the board
+ */
+ bi->modelid = sc_adapter[card]->model;
+ strcpy(bi->serial_no, sc_adapter[card]->hwconfig.serial_no);
+ strcpy(bi->part_no, sc_adapter[card]->hwconfig.part_no);
+ bi->iobase = sc_adapter[card]->iobase;
+ bi->rambase = sc_adapter[card]->rambase;
+ bi->irq = sc_adapter[card]->interrupt;
+ bi->ramsize = sc_adapter[card]->hwconfig.ram_size;
+ bi->interface = sc_adapter[card]->hwconfig.st_u_sense;
+ strcpy(bi->load_ver, sc_adapter[card]->load_ver);
+ strcpy(bi->proc_ver, sc_adapter[card]->proc_ver);
+
+ /*
+ * Get the current PhyStats and LnkStats
+ */
+ status = send_and_receive(card, CEPID, ceReqTypePhy, ceReqClass2,
+ ceReqPhyStatus, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+ if(!status) {
+ if(sc_adapter[card]->model < PRI_BOARD) {
+ bi->l1_status = rcvmsg.msg_data.byte_array[2];
+ for(i = 0 ; i < BRI_CHANNELS ; i++)
+ bi->status.bristats[i].phy_stat =
+ rcvmsg.msg_data.byte_array[i];
+ }
+ else {
+ bi->l1_status = rcvmsg.msg_data.byte_array[0];
+ bi->l2_status = rcvmsg.msg_data.byte_array[1];
+ for(i = 0 ; i < PRI_CHANNELS ; i++)
+ bi->status.pristats[i].phy_stat =
+ rcvmsg.msg_data.byte_array[i+2];
+ }
+ }
+
+ /*
+ * Get the call types for each channel
+ */
+ for (i = 0 ; i < sc_adapter[card]->nChannels ; i++) {
+ status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0,
+ ceReqCallGetCallType, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+ if(!status) {
+ if (sc_adapter[card]->model == PRI_BOARD) {
+ bi->status.pristats[i].call_type =
+ rcvmsg.msg_data.byte_array[0];
+ }
+ else {
+ bi->status.bristats[i].call_type =
+ rcvmsg.msg_data.byte_array[0];
+ }
+ }
+ }
+
+ /*
+ * If PRI, get the call states and service states for each channel
+ */
+ if (sc_adapter[card]->model == PRI_BOARD) {
+ /*
+ * Get the call states
+ */
+ status = send_and_receive(card, CEPID, ceReqTypeStat, ceReqClass2,
+ ceReqPhyChCallState, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+ if(!status) {
+ for( i = 0 ; i < PRI_CHANNELS ; i++ )
+ bi->status.pristats[i].call_state =
+ rcvmsg.msg_data.byte_array[i];
+ }
+
+ /*
+ * Get the service states
+ */
+ status = send_and_receive(card, CEPID, ceReqTypeStat, ceReqClass2,
+ ceReqPhyChServState, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+ if(!status) {
+ for( i = 0 ; i < PRI_CHANNELS ; i++ )
+ bi->status.pristats[i].serv_state =
+ rcvmsg.msg_data.byte_array[i];
+ }
+
+ /*
+ * Get the link stats for the channels
+ */
+ for (i = 1 ; i <= PRI_CHANNELS ; i++) {
+ status = send_and_receive(card, CEPID, ceReqTypeLnk, ceReqClass0,
+ ceReqLnkGetStats, i, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+ if (!status) {
+ bi->status.pristats[i-1].link_stats.tx_good =
+ (unsigned long)rcvmsg.msg_data.byte_array[0];
+ bi->status.pristats[i-1].link_stats.tx_bad =
+ (unsigned long)rcvmsg.msg_data.byte_array[4];
+ bi->status.pristats[i-1].link_stats.rx_good =
+ (unsigned long)rcvmsg.msg_data.byte_array[8];
+ bi->status.pristats[i-1].link_stats.rx_bad =
+ (unsigned long)rcvmsg.msg_data.byte_array[12];
+ }
+ }
+
+ /*
+ * Link stats for the D channel
+ */
+ status = send_and_receive(card, CEPID, ceReqTypeLnk, ceReqClass0,
+ ceReqLnkGetStats, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+ if (!status) {
+ bi->dch_stats.tx_good = (unsigned long)rcvmsg.msg_data.byte_array[0];
+ bi->dch_stats.tx_bad = (unsigned long)rcvmsg.msg_data.byte_array[4];
+ bi->dch_stats.rx_good = (unsigned long)rcvmsg.msg_data.byte_array[8];
+ bi->dch_stats.rx_bad = (unsigned long)rcvmsg.msg_data.byte_array[12];
+ }
+
+ return 0;
+ }
+
+ /*
+ * If BRI or POTS, Get SPID, DN and call types for each channel
+ */
+
+ /*
+ * Get the link stats for the channels
+ */
+ status = send_and_receive(card, CEPID, ceReqTypeLnk, ceReqClass0,
+ ceReqLnkGetStats, 0, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+ if (!status) {
+ bi->dch_stats.tx_good = (unsigned long)rcvmsg.msg_data.byte_array[0];
+ bi->dch_stats.tx_bad = (unsigned long)rcvmsg.msg_data.byte_array[4];
+ bi->dch_stats.rx_good = (unsigned long)rcvmsg.msg_data.byte_array[8];
+ bi->dch_stats.rx_bad = (unsigned long)rcvmsg.msg_data.byte_array[12];
+ bi->status.bristats[0].link_stats.tx_good =
+ (unsigned long)rcvmsg.msg_data.byte_array[16];
+ bi->status.bristats[0].link_stats.tx_bad =
+ (unsigned long)rcvmsg.msg_data.byte_array[20];
+ bi->status.bristats[0].link_stats.rx_good =
+ (unsigned long)rcvmsg.msg_data.byte_array[24];
+ bi->status.bristats[0].link_stats.rx_bad =
+ (unsigned long)rcvmsg.msg_data.byte_array[28];
+ bi->status.bristats[1].link_stats.tx_good =
+ (unsigned long)rcvmsg.msg_data.byte_array[32];
+ bi->status.bristats[1].link_stats.tx_bad =
+ (unsigned long)rcvmsg.msg_data.byte_array[36];
+ bi->status.bristats[1].link_stats.rx_good =
+ (unsigned long)rcvmsg.msg_data.byte_array[40];
+ bi->status.bristats[1].link_stats.rx_bad =
+ (unsigned long)rcvmsg.msg_data.byte_array[44];
+ }
+
+ /*
+ * Get the SPIDs
+ */
+ for (i = 0 ; i < BRI_CHANNELS ; i++) {
+ status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0,
+ ceReqCallGetSPID, i+1, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+ if (!status)
+ strcpy(bi->status.bristats[i].spid, rcvmsg.msg_data.byte_array);
+ }
+
+ /*
+ * Get the DNs
+ */
+ for (i = 0 ; i < BRI_CHANNELS ; i++) {
+ status = send_and_receive(card, CEPID, ceReqTypeCall, ceReqClass0,
+ ceReqCallGetMyNumber, i+1, 0, NULL, &rcvmsg, SAR_TIMEOUT);
+ if (!status)
+ strcpy(bi->status.bristats[i].dn, rcvmsg.msg_data.byte_array);
+ }
+
+ return 0;
+}
diff --git a/drivers/isdn/sc/message.c b/drivers/isdn/sc/message.c
new file mode 100644
index 0000000..0b4c4f1
--- /dev/null
+++ b/drivers/isdn/sc/message.c
@@ -0,0 +1,230 @@
+/* $Id: message.c,v 1.5.8.2 2001/09/23 22:24:59 kai Exp $
+ *
+ * functions for sending and receiving control messages
+ *
+ * Copyright (C) 1996 SpellCaster Telecommunications Inc.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For more information, please contact gpl-info@spellcast.com or write:
+ *
+ * SpellCaster Telecommunications Inc.
+ * 5621 Finch Avenue East, Unit #3
+ * Scarborough, Ontario Canada
+ * M1B 2T9
+ * +1 (416) 297-8565
+ * +1 (416) 297-6433 Facsimile
+ */
+#include <linux/sched.h>
+#include "includes.h"
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+
+/*
+ * receive a message from the board
+ */
+int receivemessage(int card, RspMessage *rspmsg)
+{
+ DualPortMemory *dpm;
+ unsigned long flags;
+
+ if (!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: Entered receivemessage\n",
+ sc_adapter[card]->devicename);
+
+ /*
+ * See if there are messages waiting
+ */
+ if (inb(sc_adapter[card]->ioport[FIFO_STATUS]) & RF_HAS_DATA) {
+ /*
+ * Map in the DPM to the base page and copy the message
+ */
+ spin_lock_irqsave(&sc_adapter[card]->lock, flags);
+ outb((sc_adapter[card]->shmem_magic >> 14) | 0x80,
+ sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport]);
+ dpm = (DualPortMemory *) sc_adapter[card]->rambase;
+ memcpy_fromio(rspmsg, &(dpm->rsp_queue[dpm->rsp_tail]),
+ MSG_LEN);
+ dpm->rsp_tail = (dpm->rsp_tail+1) % MAX_MESSAGES;
+ inb(sc_adapter[card]->ioport[FIFO_READ]);
+ spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
+ /*
+ * Tell the board that the message is received
+ */
+ pr_debug("%s: Received Message seq:%d pid:%d time:%d cmd:%d "
+ "cnt:%d (type,class,code):(%d,%d,%d) "
+ "link:%d stat:0x%x\n",
+ sc_adapter[card]->devicename,
+ rspmsg->sequence_no,
+ rspmsg->process_id,
+ rspmsg->time_stamp,
+ rspmsg->cmd_sequence_no,
+ rspmsg->msg_byte_cnt,
+ rspmsg->type,
+ rspmsg->class,
+ rspmsg->code,
+ rspmsg->phy_link_no,
+ rspmsg->rsp_status);
+
+ return 0;
+ }
+ return -ENOMSG;
+}
+
+/*
+ * send a message to the board
+ */
+int sendmessage(int card,
+ unsigned int procid,
+ unsigned int type,
+ unsigned int class,
+ unsigned int code,
+ unsigned int link,
+ unsigned int data_len,
+ unsigned int *data)
+{
+ DualPortMemory *dpm;
+ ReqMessage sndmsg;
+ unsigned long flags;
+
+ if (!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return -EINVAL;
+ }
+
+ /*
+ * Make sure we only send CEPID messages when the engine is up
+ * and CMPID messages when it is down
+ */
+ if(sc_adapter[card]->EngineUp && procid == CMPID) {
+ pr_debug("%s: Attempt to send CM message with engine up\n",
+ sc_adapter[card]->devicename);
+ return -ESRCH;
+ }
+
+ if(!sc_adapter[card]->EngineUp && procid == CEPID) {
+ pr_debug("%s: Attempt to send CE message with engine down\n",
+ sc_adapter[card]->devicename);
+ return -ESRCH;
+ }
+
+ memset(&sndmsg, 0, MSG_LEN);
+ sndmsg.msg_byte_cnt = 4;
+ sndmsg.type = type;
+ sndmsg.class = class;
+ sndmsg.code = code;
+ sndmsg.phy_link_no = link;
+
+ if (data_len > 0) {
+ if (data_len > MSG_DATA_LEN)
+ data_len = MSG_DATA_LEN;
+ memcpy(&(sndmsg.msg_data), data, data_len);
+ sndmsg.msg_byte_cnt = data_len + 8;
+ }
+
+ sndmsg.process_id = procid;
+ sndmsg.sequence_no = sc_adapter[card]->seq_no++ % 256;
+
+ /*
+ * wait for an empty slot in the queue
+ */
+ while (!(inb(sc_adapter[card]->ioport[FIFO_STATUS]) & WF_NOT_FULL))
+ udelay(1);
+
+ /*
+ * Disable interrupts and map in shared memory
+ */
+ spin_lock_irqsave(&sc_adapter[card]->lock, flags);
+ outb((sc_adapter[card]->shmem_magic >> 14) | 0x80,
+ sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport]);
+ dpm = (DualPortMemory *) sc_adapter[card]->rambase; /* Fix me */
+ memcpy_toio(&(dpm->req_queue[dpm->req_head]),&sndmsg,MSG_LEN);
+ dpm->req_head = (dpm->req_head+1) % MAX_MESSAGES;
+ outb(sndmsg.sequence_no, sc_adapter[card]->ioport[FIFO_WRITE]);
+ spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
+
+ pr_debug("%s: Sent Message seq:%d pid:%d time:%d "
+ "cnt:%d (type,class,code):(%d,%d,%d) "
+ "link:%d\n ",
+ sc_adapter[card]->devicename,
+ sndmsg.sequence_no,
+ sndmsg.process_id,
+ sndmsg.time_stamp,
+ sndmsg.msg_byte_cnt,
+ sndmsg.type,
+ sndmsg.class,
+ sndmsg.code,
+ sndmsg.phy_link_no);
+
+ return 0;
+}
+
+int send_and_receive(int card,
+ unsigned int procid,
+ unsigned char type,
+ unsigned char class,
+ unsigned char code,
+ unsigned char link,
+ unsigned char data_len,
+ unsigned char *data,
+ RspMessage *mesgdata,
+ int timeout)
+{
+ int retval;
+ int tries;
+
+ if (!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return -EINVAL;
+ }
+
+ sc_adapter[card]->want_async_messages = 1;
+ retval = sendmessage(card, procid, type, class, code, link,
+ data_len, (unsigned int *) data);
+
+ if (retval) {
+ pr_debug("%s: SendMessage failed in SAR\n",
+ sc_adapter[card]->devicename);
+ sc_adapter[card]->want_async_messages = 0;
+ return -EIO;
+ }
+
+ tries = 0;
+ /* wait for the response */
+ while (tries < timeout) {
+ schedule_timeout_interruptible(1);
+
+ pr_debug("SAR waiting..\n");
+
+ /*
+ * See if we got our message back
+ */
+ if ((sc_adapter[card]->async_msg.type == type) &&
+ (sc_adapter[card]->async_msg.class == class) &&
+ (sc_adapter[card]->async_msg.code == code) &&
+ (sc_adapter[card]->async_msg.phy_link_no == link)) {
+
+ /*
+ * Got it!
+ */
+ pr_debug("%s: Got ASYNC message\n",
+ sc_adapter[card]->devicename);
+ memcpy(mesgdata, &(sc_adapter[card]->async_msg),
+ sizeof(RspMessage));
+ sc_adapter[card]->want_async_messages = 0;
+ return 0;
+ }
+
+ tries++;
+ }
+
+ pr_debug("%s: SAR message timeout\n", sc_adapter[card]->devicename);
+ sc_adapter[card]->want_async_messages = 0;
+ return -ETIME;
+}
diff --git a/drivers/isdn/sc/message.h b/drivers/isdn/sc/message.h
new file mode 100644
index 0000000..8eb15e7
--- /dev/null
+++ b/drivers/isdn/sc/message.h
@@ -0,0 +1,245 @@
+/* $Id: message.h,v 1.1.10.1 2001/09/23 22:24:59 kai Exp $
+ *
+ * Copyright (C) 1996 SpellCaster Telecommunications Inc.
+ *
+ * structures, macros and defines useful for sending
+ * messages to the adapter
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For more information, please contact gpl-info@spellcast.com or write:
+ *
+ * SpellCaster Telecommunications Inc.
+ * 5621 Finch Avenue East, Unit #3
+ * Scarborough, Ontario Canada
+ * M1B 2T9
+ * +1 (416) 297-8565
+ * +1 (416) 297-6433 Facsimile
+ */
+
+/*
+ * Board message macros, defines and structures
+ */
+
+#ifndef MESSAGE_H
+#define MESSAGE_H
+
+#define MAX_MESSAGES 32 /* Maximum messages that can be
+ queued */
+#define MSG_DATA_LEN 48 /* Maximum size of message payload */
+#define MSG_LEN 64 /* Size of a message */
+#define CMPID 0 /* Loader message process ID */
+#define CEPID 64 /* Firmware message process ID */
+
+/*
+ * Macro to determine if a message is a loader message
+ */
+#define IS_CM_MESSAGE(mesg, tx, cx, dx) \
+ ((mesg.type == cmRspType##tx) \
+ &&(mesg.class == cmRspClass##cx) \
+ &&(mesg.code == cmRsp##dx))
+
+/*
+ * Macro to determine if a message is a firmware message
+ */
+#define IS_CE_MESSAGE(mesg, tx, cx, dx) \
+ ((mesg.type == ceRspType##tx) \
+ &&(mesg.class == ceRspClass##cx) \
+ &&(mesg.code == ceRsp##tx##dx))
+
+/*
+ * Loader Request and Response Messages
+ */
+
+/* message types */
+#define cmReqType1 1
+#define cmReqType2 2
+#define cmRspType0 0
+#define cmRspType1 1
+#define cmRspType2 2
+#define cmRspType5 5
+
+/* message classes */
+#define cmReqClass0 0
+#define cmRspClass0 0
+
+/* message codes */
+#define cmReqHWConfig 1 /* 1,0,1 */
+#define cmReqMsgLpbk 2 /* 1,0,2 */
+#define cmReqVersion 3 /* 1,0,3 */
+#define cmReqLoadProc 1 /* 2,0,1 */
+#define cmReqStartProc 2 /* 2,0,2 */
+#define cmReqReadMem 6 /* 2,0,6 */
+#define cmRspHWConfig cmReqHWConfig
+#define cmRspMsgLpbk cmReqMsgLpbk
+#define cmRspVersion cmReqVersion
+#define cmRspLoadProc cmReqLoadProc
+#define cmRspStartProc cmReqStartProc
+#define cmRspReadMem cmReqReadMem
+#define cmRspMiscEngineUp 1 /* 5,0,1 */
+#define cmRspInvalid 0 /* 0,0,0 */
+
+
+/*
+ * Firmware Request and Response Messages
+ */
+
+/* message types */
+#define ceReqTypePhy 1
+#define ceReqTypeLnk 2
+#define ceReqTypeCall 3
+#define ceReqTypeStat 1
+#define ceRspTypeErr 0
+#define ceRspTypePhy ceReqTypePhy
+#define ceRspTypeLnk ceReqTypeLnk
+#define ceRspTypeCall ceReqTypeCall
+#define ceRspTypeStat ceReqTypeStat
+
+/* message classes */
+#define ceReqClass0 0
+#define ceReqClass1 1
+#define ceReqClass2 2
+#define ceReqClass3 3
+#define ceRspClass0 ceReqClass0
+#define ceRspClass1 ceReqClass1
+#define ceRspClass2 ceReqClass2
+#define ceRspClass3 ceReqClass3
+
+/* message codes (B) = BRI only, (P) = PRI only, (V) = POTS only */
+#define ceReqPhyProcInfo 1 /* 1,0,1 */
+#define ceReqPhyConnect 1 /* 1,1,1 */
+#define ceReqPhyDisconnect 2 /* 1,1,2 */
+#define ceReqPhySetParams 3 /* 1,1,3 (P) */
+#define ceReqPhyGetParams 4 /* 1,1,4 (P) */
+#define ceReqPhyStatus 1 /* 1,2,1 */
+#define ceReqPhyAcfaStatus 2 /* 1,2,2 (P) */
+#define ceReqPhyChCallState 3 /* 1,2,3 (P) */
+#define ceReqPhyChServState 4 /* 1,2,4 (P) */
+#define ceReqPhyRLoopBack 1 /* 1,3,1 */
+#define ceRspPhyProcInfo ceReqPhyProcInfo
+#define ceRspPhyConnect ceReqPhyConnect
+#define ceRspPhyDisconnect ceReqPhyDisconnect
+#define ceRspPhySetParams ceReqPhySetParams
+#define ceRspPhyGetParams ceReqPhyGetParams
+#define ceRspPhyStatus ceReqPhyStatus
+#define ceRspPhyAcfaStatus ceReqPhyAcfaStatus
+#define ceRspPhyChCallState ceReqPhyChCallState
+#define ceRspPhyChServState ceReqPhyChServState
+#define ceRspPhyRLoopBack ceReqphyRLoopBack
+#define ceReqLnkSetParam 1 /* 2,0,1 */
+#define ceReqLnkGetParam 2 /* 2,0,2 */
+#define ceReqLnkGetStats 3 /* 2,0,3 */
+#define ceReqLnkWrite 1 /* 2,1,1 */
+#define ceReqLnkRead 2 /* 2,1,2 */
+#define ceReqLnkFlush 3 /* 2,1,3 */
+#define ceReqLnkWrBufTrc 4 /* 2,1,4 */
+#define ceReqLnkRdBufTrc 5 /* 2,1,5 */
+#define ceRspLnkSetParam ceReqLnkSetParam
+#define ceRspLnkGetParam ceReqLnkGetParam
+#define ceRspLnkGetStats ceReqLnkGetStats
+#define ceRspLnkWrite ceReqLnkWrite
+#define ceRspLnkRead ceReqLnkRead
+#define ceRspLnkFlush ceReqLnkFlush
+#define ceRspLnkWrBufTrc ceReqLnkWrBufTrc
+#define ceRspLnkRdBufTrc ceReqLnkRdBufTrc
+#define ceReqCallSetSwitchType 1 /* 3,0,1 */
+#define ceReqCallGetSwitchType 2 /* 3,0,2 */
+#define ceReqCallSetFrameFormat 3 /* 3,0,3 */
+#define ceReqCallGetFrameFormat 4 /* 3,0,4 */
+#define ceReqCallSetCallType 5 /* 3,0,5 */
+#define ceReqCallGetCallType 6 /* 3,0,6 */
+#define ceReqCallSetSPID 7 /* 3,0,7 (!P) */
+#define ceReqCallGetSPID 8 /* 3,0,8 (!P) */
+#define ceReqCallSetMyNumber 9 /* 3,0,9 (!P) */
+#define ceReqCallGetMyNumber 10 /* 3,0,10 (!P) */
+#define ceRspCallSetSwitchType ceReqCallSetSwitchType
+#define ceRspCallGetSwitchType ceReqCallSetSwitchType
+#define ceRspCallSetFrameFormat ceReqCallSetFrameFormat
+#define ceRspCallGetFrameFormat ceReqCallGetFrameFormat
+#define ceRspCallSetCallType ceReqCallSetCallType
+#define ceRspCallGetCallType ceReqCallGetCallType
+#define ceRspCallSetSPID ceReqCallSetSPID
+#define ceRspCallGetSPID ceReqCallGetSPID
+#define ceRspCallSetMyNumber ceReqCallSetMyNumber
+#define ceRspCallGetMyNumber ceReqCallGetMyNumber
+#define ceRspStatAcfaStatus 2
+#define ceRspStat
+#define ceRspErrError 0 /* 0,0,0 */
+
+/*
+ * Call Types
+ */
+#define CALLTYPE_64K 0
+#define CALLTYPE_56K 1
+#define CALLTYPE_SPEECH 2
+#define CALLTYPE_31KHZ 3
+
+/*
+ * Link Level data contains a pointer to and the length of
+ * a buffer in shared RAM. Used by LnkRead and LnkWrite message
+ * types. Part of RspMsgStruct and ReqMsgStruct.
+ */
+typedef struct {
+ unsigned long buff_offset;
+ unsigned short msg_len;
+} LLData;
+
+
+/*
+ * Message payload template for an HWConfig message
+ */
+typedef struct {
+ char st_u_sense;
+ char powr_sense;
+ char sply_sense;
+ unsigned char asic_id;
+ long ram_size;
+ char serial_no[13];
+ char part_no[13];
+ char rev_no[2];
+} HWConfig_pl;
+
+/*
+ * A Message
+ */
+struct message {
+ unsigned char sequence_no;
+ unsigned char process_id;
+ unsigned char time_stamp;
+ unsigned char cmd_sequence_no; /* Rsp messages only */
+ unsigned char reserved1[3];
+ unsigned char msg_byte_cnt;
+ unsigned char type;
+ unsigned char class;
+ unsigned char code;
+ unsigned char phy_link_no;
+ unsigned char rsp_status; /* Rsp messages only */
+ unsigned char reseved2[3];
+ union {
+ unsigned char byte_array[MSG_DATA_LEN];
+ LLData response;
+ HWConfig_pl HWCresponse;
+ } msg_data;
+};
+
+typedef struct message ReqMessage; /* Request message */
+typedef struct message RspMessage; /* Response message */
+
+/*
+ * The first 5010 bytes of shared memory contain the message queues,
+ * indexes and other data. This structure is its template
+ */
+typedef struct {
+ volatile ReqMessage req_queue[MAX_MESSAGES];
+ volatile RspMessage rsp_queue[MAX_MESSAGES];
+ volatile unsigned char req_head;
+ volatile unsigned char req_tail;
+ volatile unsigned char rsp_head;
+ volatile unsigned char rsp_tail;
+ volatile unsigned long signature;
+ volatile unsigned long trace_enable;
+ volatile unsigned char reserved[4];
+} DualPortMemory;
+
+#endif
diff --git a/drivers/isdn/sc/packet.c b/drivers/isdn/sc/packet.c
new file mode 100644
index 0000000..5ff6ae8
--- /dev/null
+++ b/drivers/isdn/sc/packet.c
@@ -0,0 +1,205 @@
+/* $Id: packet.c,v 1.5.8.1 2001/09/23 22:24:59 kai Exp $
+ *
+ * Copyright (C) 1996 SpellCaster Telecommunications Inc.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For more information, please contact gpl-info@spellcast.com or write:
+ *
+ * SpellCaster Telecommunications Inc.
+ * 5621 Finch Avenue East, Unit #3
+ * Scarborough, Ontario Canada
+ * M1B 2T9
+ * +1 (416) 297-8565
+ * +1 (416) 297-6433 Facsimile
+ */
+
+#include "includes.h"
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+
+int sndpkt(int devId, int channel, int ack, struct sk_buff *data)
+{
+ LLData ReqLnkWrite;
+ int status;
+ int card;
+ unsigned long len;
+
+ card = get_card_from_id(devId);
+
+ if(!IS_VALID_CARD(card)) {
+ pr_debug("invalid param: %d is not a valid card id\n", card);
+ return -ENODEV;
+ }
+
+ pr_debug("%s: sndpkt: frst = 0x%lx nxt = %d f = %d n = %d\n",
+ sc_adapter[card]->devicename,
+ sc_adapter[card]->channel[channel].first_sendbuf,
+ sc_adapter[card]->channel[channel].next_sendbuf,
+ sc_adapter[card]->channel[channel].free_sendbufs,
+ sc_adapter[card]->channel[channel].num_sendbufs);
+
+ if(!sc_adapter[card]->channel[channel].free_sendbufs) {
+ pr_debug("%s: out of TX buffers\n",
+ sc_adapter[card]->devicename);
+ return -EINVAL;
+ }
+
+ if(data->len > BUFFER_SIZE) {
+ pr_debug("%s: data overflows buffer size (data > buffer)\n",
+ sc_adapter[card]->devicename);
+ return -EINVAL;
+ }
+
+ ReqLnkWrite.buff_offset = sc_adapter[card]->channel[channel].next_sendbuf *
+ BUFFER_SIZE + sc_adapter[card]->channel[channel].first_sendbuf;
+ ReqLnkWrite.msg_len = data->len; /* sk_buff size */
+ pr_debug("%s: writing %d bytes to buffer offset 0x%lx\n",
+ sc_adapter[card]->devicename,
+ ReqLnkWrite.msg_len, ReqLnkWrite.buff_offset);
+ memcpy_toshmem(card, (char *)ReqLnkWrite.buff_offset, data->data, ReqLnkWrite.msg_len);
+
+ /*
+ * sendmessage
+ */
+ pr_debug("%s: sndpkt size=%d, buf_offset=0x%lx buf_indx=%d\n",
+ sc_adapter[card]->devicename,
+ ReqLnkWrite.msg_len, ReqLnkWrite.buff_offset,
+ sc_adapter[card]->channel[channel].next_sendbuf);
+
+ status = sendmessage(card, CEPID, ceReqTypeLnk, ceReqClass1, ceReqLnkWrite,
+ channel+1, sizeof(LLData), (unsigned int*)&ReqLnkWrite);
+ len = data->len;
+ if(status) {
+ pr_debug("%s: failed to send packet, status = %d\n",
+ sc_adapter[card]->devicename, status);
+ return -1;
+ }
+ else {
+ sc_adapter[card]->channel[channel].free_sendbufs--;
+ sc_adapter[card]->channel[channel].next_sendbuf =
+ ++sc_adapter[card]->channel[channel].next_sendbuf ==
+ sc_adapter[card]->channel[channel].num_sendbufs ? 0 :
+ sc_adapter[card]->channel[channel].next_sendbuf;
+ pr_debug("%s: packet sent successfully\n", sc_adapter[card]->devicename);
+ dev_kfree_skb(data);
+ indicate_status(card,ISDN_STAT_BSENT,channel, (char *)&len);
+ }
+ return len;
+}
+
+void rcvpkt(int card, RspMessage *rcvmsg)
+{
+ LLData newll;
+ struct sk_buff *skb;
+
+ if(!IS_VALID_CARD(card)) {
+ pr_debug("invalid param: %d is not a valid card id\n", card);
+ return;
+ }
+
+ switch(rcvmsg->rsp_status){
+ case 0x01:
+ case 0x02:
+ case 0x70:
+ pr_debug("%s: error status code: 0x%x\n",
+ sc_adapter[card]->devicename, rcvmsg->rsp_status);
+ return;
+ case 0x00:
+ if (!(skb = dev_alloc_skb(rcvmsg->msg_data.response.msg_len))) {
+ printk(KERN_WARNING "%s: rcvpkt out of memory, dropping packet\n",
+ sc_adapter[card]->devicename);
+ return;
+ }
+ skb_put(skb, rcvmsg->msg_data.response.msg_len);
+ pr_debug("%s: getting data from offset: 0x%lx\n",
+ sc_adapter[card]->devicename,
+ rcvmsg->msg_data.response.buff_offset);
+ memcpy_fromshmem(card,
+ skb_put(skb, rcvmsg->msg_data.response.msg_len),
+ (char *)rcvmsg->msg_data.response.buff_offset,
+ rcvmsg->msg_data.response.msg_len);
+ sc_adapter[card]->card->rcvcallb_skb(sc_adapter[card]->driverId,
+ rcvmsg->phy_link_no-1, skb);
+
+ case 0x03:
+ /*
+ * Recycle the buffer
+ */
+ pr_debug("%s: buffer size : %d\n",
+ sc_adapter[card]->devicename, BUFFER_SIZE);
+/* memset_shmem(card, rcvmsg->msg_data.response.buff_offset, 0, BUFFER_SIZE); */
+ newll.buff_offset = rcvmsg->msg_data.response.buff_offset;
+ newll.msg_len = BUFFER_SIZE;
+ pr_debug("%s: recycled buffer at offset 0x%lx size %d\n",
+ sc_adapter[card]->devicename,
+ newll.buff_offset, newll.msg_len);
+ sendmessage(card, CEPID, ceReqTypeLnk, ceReqClass1, ceReqLnkRead,
+ rcvmsg->phy_link_no, sizeof(LLData), (unsigned int *)&newll);
+ }
+
+}
+
+int setup_buffers(int card, int c)
+{
+ unsigned int nBuffers, i, cBase;
+ unsigned int buffer_size;
+ LLData RcvBuffOffset;
+
+ if(!IS_VALID_CARD(card)) {
+ pr_debug("invalid param: %d is not a valid card id\n", card);
+ return -ENODEV;
+ }
+
+ /*
+ * Calculate the buffer offsets (send/recv/send/recv)
+ */
+ pr_debug("%s: setting up channel buffer space in shared RAM\n",
+ sc_adapter[card]->devicename);
+ buffer_size = BUFFER_SIZE;
+ nBuffers = ((sc_adapter[card]->ramsize - BUFFER_BASE) / buffer_size) / 2;
+ nBuffers = nBuffers > BUFFERS_MAX ? BUFFERS_MAX : nBuffers;
+ pr_debug("%s: calculating buffer space: %d buffers, %d big\n",
+ sc_adapter[card]->devicename,
+ nBuffers, buffer_size);
+ if(nBuffers < 2) {
+ pr_debug("%s: not enough buffer space\n",
+ sc_adapter[card]->devicename);
+ return -1;
+ }
+ cBase = (nBuffers * buffer_size) * (c - 1);
+ pr_debug("%s: channel buffer offset from shared RAM: 0x%x\n",
+ sc_adapter[card]->devicename, cBase);
+ sc_adapter[card]->channel[c-1].first_sendbuf = BUFFER_BASE + cBase;
+ sc_adapter[card]->channel[c-1].num_sendbufs = nBuffers / 2;
+ sc_adapter[card]->channel[c-1].free_sendbufs = nBuffers / 2;
+ sc_adapter[card]->channel[c-1].next_sendbuf = 0;
+ pr_debug("%s: send buffer setup complete: first=0x%lx n=%d f=%d, nxt=%d\n",
+ sc_adapter[card]->devicename,
+ sc_adapter[card]->channel[c-1].first_sendbuf,
+ sc_adapter[card]->channel[c-1].num_sendbufs,
+ sc_adapter[card]->channel[c-1].free_sendbufs,
+ sc_adapter[card]->channel[c-1].next_sendbuf);
+
+ /*
+ * Prep the receive buffers
+ */
+ pr_debug("%s: adding %d RecvBuffers:\n",
+ sc_adapter[card]->devicename, nBuffers /2);
+ for (i = 0 ; i < nBuffers / 2; i++) {
+ RcvBuffOffset.buff_offset =
+ ((sc_adapter[card]->channel[c-1].first_sendbuf +
+ (nBuffers / 2) * buffer_size) + (buffer_size * i));
+ RcvBuffOffset.msg_len = buffer_size;
+ pr_debug("%s: adding RcvBuffer #%d offset=0x%lx sz=%d bufsz:%d\n",
+ sc_adapter[card]->devicename,
+ i + 1, RcvBuffOffset.buff_offset,
+ RcvBuffOffset.msg_len,buffer_size);
+ sendmessage(card, CEPID, ceReqTypeLnk, ceReqClass1, ceReqLnkRead,
+ c, sizeof(LLData), (unsigned int *)&RcvBuffOffset);
+ }
+ return 0;
+}
+
diff --git a/drivers/isdn/sc/scioc.h b/drivers/isdn/sc/scioc.h
new file mode 100644
index 0000000..dfb107a
--- /dev/null
+++ b/drivers/isdn/sc/scioc.h
@@ -0,0 +1,111 @@
+#ifndef __ISDN_SC_SCIOC_H__
+#define __ISDN_SC_SCIOC_H__
+
+/*
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+/*
+ * IOCTL Command Codes
+ */
+#define SCIOCLOAD 0x01 /* Load a firmware record */
+#define SCIOCRESET 0x02 /* Perform hard reset */
+#define SCIOCDEBUG 0x03 /* Set debug level */
+#define SCIOCREV 0x04 /* Get driver revision(s) */
+#define SCIOCSTART 0x05 /* Start the firmware */
+#define SCIOCGETSWITCH 0x06 /* Get switch type */
+#define SCIOCSETSWITCH 0x07 /* Set switch type */
+#define SCIOCGETSPID 0x08 /* Get channel SPID */
+#define SCIOCSETSPID 0x09 /* Set channel SPID */
+#define SCIOCGETDN 0x0A /* Get channel DN */
+#define SCIOCSETDN 0x0B /* Set channel DN */
+#define SCIOCTRACE 0x0C /* Toggle trace mode */
+#define SCIOCSTAT 0x0D /* Get line status */
+#define SCIOCGETSPEED 0x0E /* Set channel speed */
+#define SCIOCSETSPEED 0x0F /* Set channel speed */
+#define SCIOCLOOPTST 0x10 /* Perform loopback test */
+
+typedef struct {
+ int device;
+ int channel;
+ unsigned long command;
+ void __user *dataptr;
+} scs_ioctl;
+
+/* Size of strings */
+#define SCIOC_SPIDSIZE 49
+#define SCIOC_DNSIZE SCIOC_SPIDSIZE
+#define SCIOC_REVSIZE SCIOC_SPIDSIZE
+#define SCIOC_SRECSIZE 49
+
+typedef struct {
+ unsigned long tx_good;
+ unsigned long tx_bad;
+ unsigned long rx_good;
+ unsigned long rx_bad;
+} ChLinkStats;
+
+typedef struct {
+ char spid[49];
+ char dn[49];
+ char call_type;
+ char phy_stat;
+ ChLinkStats link_stats;
+} BRIStat;
+
+typedef BRIStat POTStat;
+
+typedef struct {
+ char call_type;
+ char call_state;
+ char serv_state;
+ char phy_stat;
+ ChLinkStats link_stats;
+} PRIStat;
+
+typedef char PRIInfo;
+typedef char BRIInfo;
+typedef char POTInfo;
+
+
+typedef struct {
+ char acfa_nos;
+ char acfa_ais;
+ char acfa_los;
+ char acfa_rra;
+ char acfa_slpp;
+ char acfa_slpn;
+ char acfa_fsrf;
+} ACFAStat;
+
+typedef struct {
+ unsigned char modelid;
+ char serial_no[13];
+ char part_no[13];
+ char load_ver[11];
+ char proc_ver[11];
+ int iobase;
+ long rambase;
+ char irq;
+ long ramsize;
+ char interface;
+ char switch_type;
+ char l1_status;
+ char l2_status;
+ ChLinkStats dch_stats;
+ ACFAStat AcfaStats;
+ union {
+ PRIStat pristats[23];
+ BRIStat bristats[2];
+ POTStat potsstats[2];
+ } status;
+ union {
+ PRIInfo priinfo;
+ BRIInfo briinfo;
+ POTInfo potsinfo;
+ } info;
+} boardInfo;
+
+#endif /* __ISDN_SC_SCIOC_H__ */
+
diff --git a/drivers/isdn/sc/shmem.c b/drivers/isdn/sc/shmem.c
new file mode 100644
index 0000000..712220c
--- /dev/null
+++ b/drivers/isdn/sc/shmem.c
@@ -0,0 +1,138 @@
+/* $Id: shmem.c,v 1.2.10.1 2001/09/23 22:24:59 kai Exp $
+ *
+ * Copyright (C) 1996 SpellCaster Telecommunications Inc.
+ *
+ * Card functions implementing ISDN4Linux functionality
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For more information, please contact gpl-info@spellcast.com or write:
+ *
+ * SpellCaster Telecommunications Inc.
+ * 5621 Finch Avenue East, Unit #3
+ * Scarborough, Ontario Canada
+ * M1B 2T9
+ * +1 (416) 297-8565
+ * +1 (416) 297-6433 Facsimile
+ */
+
+#include "includes.h" /* This must be first */
+#include "hardware.h"
+#include "card.h"
+
+/*
+ *
+ */
+void memcpy_toshmem(int card, void *dest, const void *src, size_t n)
+{
+ unsigned long flags;
+ unsigned char ch;
+ unsigned long dest_rem = ((unsigned long) dest) % 0x4000;
+
+ if (!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return;
+ }
+
+ if (n > SRAM_PAGESIZE)
+ return;
+
+ /*
+ * determine the page to load from the address
+ */
+ ch = (unsigned long) dest / SRAM_PAGESIZE;
+ pr_debug("%s: loaded page %d\n", sc_adapter[card]->devicename,ch);
+ /*
+ * Block interrupts and load the page
+ */
+ spin_lock_irqsave(&sc_adapter[card]->lock, flags);
+
+ outb(((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80,
+ sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport]);
+ memcpy_toio((void __iomem *)(sc_adapter[card]->rambase + dest_rem), src, n);
+ spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
+ pr_debug("%s: set page to %#x\n",sc_adapter[card]->devicename,
+ ((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE)>>14)|0x80);
+ pr_debug("%s: copying %d bytes from %#lx to %#lx\n",
+ sc_adapter[card]->devicename, n,
+ (unsigned long) src,
+ sc_adapter[card]->rambase + ((unsigned long) dest %0x4000));
+}
+
+/*
+ * Reverse of above
+ */
+void memcpy_fromshmem(int card, void *dest, const void *src, size_t n)
+{
+ unsigned long flags;
+ unsigned char ch;
+
+ if(!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return;
+ }
+
+ if(n > SRAM_PAGESIZE) {
+ return;
+ }
+
+ /*
+ * determine the page to load from the address
+ */
+ ch = (unsigned long) src / SRAM_PAGESIZE;
+ pr_debug("%s: loaded page %d\n", sc_adapter[card]->devicename,ch);
+
+
+ /*
+ * Block interrupts and load the page
+ */
+ spin_lock_irqsave(&sc_adapter[card]->lock, flags);
+
+ outb(((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80,
+ sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport]);
+ memcpy_fromio(dest,(void *)(sc_adapter[card]->rambase +
+ ((unsigned long) src % 0x4000)), n);
+ spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
+ pr_debug("%s: set page to %#x\n",sc_adapter[card]->devicename,
+ ((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE)>>14)|0x80);
+/* pr_debug("%s: copying %d bytes from %#x to %#x\n",
+ sc_adapter[card]->devicename, n,
+ sc_adapter[card]->rambase + ((unsigned long) src %0x4000), (unsigned long) dest); */
+}
+
+#if 0
+void memset_shmem(int card, void *dest, int c, size_t n)
+{
+ unsigned long flags;
+ unsigned char ch;
+
+ if(!IS_VALID_CARD(card)) {
+ pr_debug("Invalid param: %d is not a valid card id\n", card);
+ return;
+ }
+
+ if(n > SRAM_PAGESIZE) {
+ return;
+ }
+
+ /*
+ * determine the page to load from the address
+ */
+ ch = (unsigned long) dest / SRAM_PAGESIZE;
+ pr_debug("%s: loaded page %d\n",sc_adapter[card]->devicename,ch);
+
+ /*
+ * Block interrupts and load the page
+ */
+ spin_lock_irqsave(&sc_adapter[card]->lock, flags);
+
+ outb(((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE) >> 14) | 0x80,
+ sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport]);
+ memset_io(sc_adapter[card]->rambase +
+ ((unsigned long) dest % 0x4000), c, n);
+ pr_debug("%s: set page to %#x\n",sc_adapter[card]->devicename,
+ ((sc_adapter[card]->shmem_magic + ch * SRAM_PAGESIZE)>>14)|0x80);
+ spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
+}
+#endif /* 0 */
diff --git a/drivers/isdn/sc/timer.c b/drivers/isdn/sc/timer.c
new file mode 100644
index 0000000..91fbe0d
--- /dev/null
+++ b/drivers/isdn/sc/timer.c
@@ -0,0 +1,123 @@
+/* $Id: timer.c,v 1.3.6.1 2001/09/23 22:24:59 kai Exp $
+ *
+ * Copyright (C) 1996 SpellCaster Telecommunications Inc.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * For more information, please contact gpl-info@spellcast.com or write:
+ *
+ * SpellCaster Telecommunications Inc.
+ * 5621 Finch Avenue East, Unit #3
+ * Scarborough, Ontario Canada
+ * M1B 2T9
+ * +1 (416) 297-8565
+ * +1 (416) 297-6433 Facsimile
+ */
+
+#include "includes.h"
+#include "hardware.h"
+#include "message.h"
+#include "card.h"
+
+
+/*
+ * Write the proper values into the I/O ports following a reset
+ */
+static void setup_ports(int card)
+{
+
+ outb((sc_adapter[card]->rambase >> 12), sc_adapter[card]->ioport[EXP_BASE]);
+
+ /* And the IRQ */
+ outb((sc_adapter[card]->interrupt | 0x80),
+ sc_adapter[card]->ioport[IRQ_SELECT]);
+}
+
+/*
+ * Timed function to check the status of a previous reset
+ * Must be very fast as this function runs in the context of
+ * an interrupt handler.
+ *
+ * Setup the ioports for the board that were cleared by the reset.
+ * Then, check to see if the signate has been set. Next, set the
+ * signature to a known value and issue a startproc if needed.
+ */
+void sc_check_reset(unsigned long data)
+{
+ unsigned long flags;
+ unsigned long sig;
+ int card = (unsigned int) data;
+
+ pr_debug("%s: check_timer timer called\n",
+ sc_adapter[card]->devicename);
+
+ /* Setup the io ports */
+ setup_ports(card);
+
+ spin_lock_irqsave(&sc_adapter[card]->lock, flags);
+ outb(sc_adapter[card]->ioport[sc_adapter[card]->shmem_pgport],
+ (sc_adapter[card]->shmem_magic>>14) | 0x80);
+ sig = (unsigned long) *((unsigned long *)(sc_adapter[card]->rambase + SIG_OFFSET));
+
+ /* check the signature */
+ if(sig == SIGNATURE) {
+ flushreadfifo(card);
+ spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
+ /* See if we need to do a startproc */
+ if (sc_adapter[card]->StartOnReset)
+ startproc(card);
+ } else {
+ pr_debug("%s: No signature yet, waiting another %lu jiffies.\n",
+ sc_adapter[card]->devicename, CHECKRESET_TIME);
+ mod_timer(&sc_adapter[card]->reset_timer, jiffies+CHECKRESET_TIME);
+ spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
+ }
+}
+
+/*
+ * Timed function to check the status of a previous reset
+ * Must be very fast as this function runs in the context of
+ * an interrupt handler.
+ *
+ * Send check sc_adapter->phystat to see if the channels are up
+ * If they are, tell ISDN4Linux that the board is up. If not,
+ * tell IADN4Linux that it is up. Always reset the timer to
+ * fire again (endless loop).
+ */
+void check_phystat(unsigned long data)
+{
+ unsigned long flags;
+ int card = (unsigned int) data;
+
+ pr_debug("%s: Checking status...\n", sc_adapter[card]->devicename);
+ /*
+ * check the results of the last PhyStat and change only if
+ * has changed drastically
+ */
+ if (sc_adapter[card]->nphystat && !sc_adapter[card]->phystat) { /* All is well */
+ pr_debug("PhyStat transition to RUN\n");
+ pr_info("%s: Switch contacted, transmitter enabled\n",
+ sc_adapter[card]->devicename);
+ indicate_status(card, ISDN_STAT_RUN, 0, NULL);
+ }
+ else if (!sc_adapter[card]->nphystat && sc_adapter[card]->phystat) { /* All is not well */
+ pr_debug("PhyStat transition to STOP\n");
+ pr_info("%s: Switch connection lost, transmitter disabled\n",
+ sc_adapter[card]->devicename);
+
+ indicate_status(card, ISDN_STAT_STOP, 0, NULL);
+ }
+
+ sc_adapter[card]->phystat = sc_adapter[card]->nphystat;
+
+ /* Reinitialize the timer */
+ spin_lock_irqsave(&sc_adapter[card]->lock, flags);
+ mod_timer(&sc_adapter[card]->stat_timer, jiffies+CHECKSTAT_TIME);
+ spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
+
+ /* Send a new cePhyStatus message */
+ sendmessage(card, CEPID,ceReqTypePhy,ceReqClass2,
+ ceReqPhyStatus,0,0,NULL);
+}
+
OpenPOWER on IntegriCloud