diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/isdn/i4l |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/isdn/i4l')
-rw-r--r-- | drivers/isdn/i4l/Kconfig | 141 | ||||
-rw-r--r-- | drivers/isdn/i4l/Makefile | 18 | ||||
-rw-r--r-- | drivers/isdn/i4l/isdn_audio.c | 720 | ||||
-rw-r--r-- | drivers/isdn/i4l/isdn_audio.h | 45 | ||||
-rw-r--r-- | drivers/isdn/i4l/isdn_bsdcomp.c | 937 | ||||
-rw-r--r-- | drivers/isdn/i4l/isdn_common.c | 2253 | ||||
-rw-r--r-- | drivers/isdn/i4l/isdn_common.h | 47 | ||||
-rw-r--r-- | drivers/isdn/i4l/isdn_concap.c | 108 | ||||
-rw-r--r-- | drivers/isdn/i4l/isdn_concap.h | 14 | ||||
-rw-r--r-- | drivers/isdn/i4l/isdn_net.c | 3222 | ||||
-rw-r--r-- | drivers/isdn/i4l/isdn_net.h | 190 | ||||
-rw-r--r-- | drivers/isdn/i4l/isdn_ppp.c | 3020 | ||||
-rw-r--r-- | drivers/isdn/i4l/isdn_ppp.h | 43 | ||||
-rw-r--r-- | drivers/isdn/i4l/isdn_tty.c | 3911 | ||||
-rw-r--r-- | drivers/isdn/i4l/isdn_tty.h | 122 | ||||
-rw-r--r-- | drivers/isdn/i4l/isdn_ttyfax.c | 1122 | ||||
-rw-r--r-- | drivers/isdn/i4l/isdn_ttyfax.h | 18 | ||||
-rw-r--r-- | drivers/isdn/i4l/isdn_v110.c | 617 | ||||
-rw-r--r-- | drivers/isdn/i4l/isdn_v110.h | 29 | ||||
-rw-r--r-- | drivers/isdn/i4l/isdn_x25iface.c | 328 | ||||
-rw-r--r-- | drivers/isdn/i4l/isdn_x25iface.h | 39 |
21 files changed, 16944 insertions, 0 deletions
diff --git a/drivers/isdn/i4l/Kconfig b/drivers/isdn/i4l/Kconfig new file mode 100644 index 000000000000..1789b607f090 --- /dev/null +++ b/drivers/isdn/i4l/Kconfig | |||
@@ -0,0 +1,141 @@ | |||
1 | # | ||
2 | # Old ISDN4Linux config | ||
3 | # | ||
4 | |||
5 | config ISDN_PPP | ||
6 | bool "Support synchronous PPP" | ||
7 | depends on INET | ||
8 | help | ||
9 | Over digital connections such as ISDN, there is no need to | ||
10 | synchronize sender and recipient's clocks with start and stop bits | ||
11 | as is done over analog telephone lines. Instead, one can use | ||
12 | "synchronous PPP". Saying Y here will include this protocol. This | ||
13 | protocol is used by Cisco and Sun for example. So you want to say Y | ||
14 | here if the other end of your ISDN connection supports it. You will | ||
15 | need a special version of pppd (called ipppd) for using this | ||
16 | feature. See <file:Documentation/isdn/README.syncppp> and | ||
17 | <file:Documentation/isdn/syncPPP.FAQ> for more information. | ||
18 | |||
19 | config ISDN_PPP_VJ | ||
20 | bool "Use VJ-compression with synchronous PPP" | ||
21 | depends on ISDN_PPP | ||
22 | help | ||
23 | This enables Van Jacobson header compression for synchronous PPP. | ||
24 | Say Y if the other end of the connection supports it. | ||
25 | |||
26 | config ISDN_MPP | ||
27 | bool "Support generic MP (RFC 1717)" | ||
28 | depends on ISDN_PPP | ||
29 | help | ||
30 | With synchronous PPP enabled, it is possible to increase throughput | ||
31 | by bundling several ISDN-connections, using this protocol. See | ||
32 | <file:Documentation/isdn/README.syncppp> for more information. | ||
33 | |||
34 | config IPPP_FILTER | ||
35 | bool "Filtering for synchronous PPP" | ||
36 | depends on ISDN_PPP | ||
37 | help | ||
38 | Say Y here if you want to be able to filter the packets passing over | ||
39 | IPPP interfaces. This allows you to control which packets count as | ||
40 | activity (i.e. which packets will reset the idle timer or bring up | ||
41 | a demand-dialled link) and which packets are to be dropped entirely. | ||
42 | You need to say Y here if you wish to use the pass-filter and | ||
43 | active-filter options to ipppd. | ||
44 | |||
45 | config ISDN_PPP_BSDCOMP | ||
46 | tristate "Support BSD compression" | ||
47 | depends on ISDN_PPP | ||
48 | help | ||
49 | Support for the BSD-Compress compression method for PPP, which uses | ||
50 | the LZW compression method to compress each PPP packet before it is | ||
51 | sent over the wire. The machine at the other end of the PPP link | ||
52 | (usually your ISP) has to support the BSD-Compress compression | ||
53 | method as well for this to be useful. Even if they don't support it, | ||
54 | it is safe to say Y here. | ||
55 | |||
56 | config ISDN_AUDIO | ||
57 | bool "Support audio via ISDN" | ||
58 | help | ||
59 | If you say Y here, the modem-emulator will support a subset of the | ||
60 | EIA Class 8 Voice commands. Using a getty with voice-support | ||
61 | (mgetty+sendfax by <gert@greenie.muc.de> with an extension, available | ||
62 | with the ISDN utility package for example), you will be able to use | ||
63 | your Linux box as an ISDN-answering machine. Of course, this must be | ||
64 | supported by the lowlevel driver also. Currently, the HiSax driver | ||
65 | is the only voice-supporting driver. See | ||
66 | <file:Documentation/isdn/README.audio> for more information. | ||
67 | |||
68 | config ISDN_TTY_FAX | ||
69 | bool "Support AT-Fax Class 1 and 2 commands" | ||
70 | depends on ISDN_AUDIO | ||
71 | help | ||
72 | If you say Y here, the modem-emulator will support a subset of the | ||
73 | Fax Class 1 and 2 commands. Using a getty with fax-support | ||
74 | (mgetty+sendfax, hylafax), you will be able to use your Linux box as | ||
75 | an ISDN-fax-machine. This must be supported by the lowlevel driver | ||
76 | also. See <file:Documentation/isdn/README.fax> for more information. | ||
77 | |||
78 | config ISDN_X25 | ||
79 | bool "X.25 PLP on top of ISDN" | ||
80 | depends on X25 | ||
81 | help | ||
82 | This feature provides the X.25 protocol over ISDN connections. | ||
83 | See <file:Documentation/isdn/README.x25> for more information | ||
84 | if you are thinking about using this. | ||
85 | |||
86 | |||
87 | menu "ISDN feature submodules" | ||
88 | depends on ISDN | ||
89 | |||
90 | config ISDN_DRV_LOOP | ||
91 | tristate "isdnloop support" | ||
92 | depends on BROKEN_ON_SMP | ||
93 | help | ||
94 | This driver provides a virtual ISDN card. Its primary purpose is | ||
95 | testing of linklevel features or configuration without getting | ||
96 | charged by your service-provider for lots of phone calls. | ||
97 | You need will need the loopctrl utility from the latest isdn4k-utils | ||
98 | package to set up this driver. | ||
99 | |||
100 | config ISDN_DIVERSION | ||
101 | tristate "Support isdn diversion services" | ||
102 | depends on ISDN && ISDN_I4L | ||
103 | help | ||
104 | This option allows you to use some supplementary diversion | ||
105 | services in conjunction with the HiSax driver on an EURO/DSS1 | ||
106 | line. | ||
107 | |||
108 | Supported options are CD (call deflection), CFU (Call forward | ||
109 | unconditional), CFB (Call forward when busy) and CFNR (call forward | ||
110 | not reachable). Additionally the actual CFU, CFB and CFNR state may | ||
111 | be interrogated. | ||
112 | |||
113 | The use of CFU, CFB, CFNR and interrogation may be limited to some | ||
114 | countries. The keypad protocol is still not implemented. CD should | ||
115 | work in all countries if the service has been subscribed to. | ||
116 | |||
117 | Please read the file <file:Documentation/isdn/README.diversion>. | ||
118 | |||
119 | endmenu | ||
120 | |||
121 | comment "ISDN4Linux hardware drivers" | ||
122 | depends on NET && ISDN && ISDN_I4L | ||
123 | |||
124 | source "drivers/isdn/hisax/Kconfig" | ||
125 | |||
126 | |||
127 | menu "Active cards" | ||
128 | depends on NET && ISDN && ISDN_I4L!=n | ||
129 | |||
130 | source "drivers/isdn/icn/Kconfig" | ||
131 | |||
132 | source "drivers/isdn/pcbit/Kconfig" | ||
133 | |||
134 | source "drivers/isdn/sc/Kconfig" | ||
135 | |||
136 | source "drivers/isdn/act2000/Kconfig" | ||
137 | |||
138 | source "drivers/isdn/hysdn/Kconfig" | ||
139 | |||
140 | endmenu | ||
141 | |||
diff --git a/drivers/isdn/i4l/Makefile b/drivers/isdn/i4l/Makefile new file mode 100644 index 000000000000..49a06c0005dd --- /dev/null +++ b/drivers/isdn/i4l/Makefile | |||
@@ -0,0 +1,18 @@ | |||
1 | # Makefile for the kernel ISDN subsystem and device drivers. | ||
2 | |||
3 | # Each configuration option enables a list of files. | ||
4 | |||
5 | obj-$(CONFIG_ISDN_I4L) += isdn.o | ||
6 | obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o | ||
7 | |||
8 | # Multipart objects. | ||
9 | |||
10 | isdn-y := isdn_net.o isdn_tty.o isdn_v110.o isdn_common.o | ||
11 | |||
12 | # Optional parts of multipart objects. | ||
13 | |||
14 | isdn-$(CONFIG_ISDN_PPP) += isdn_ppp.o | ||
15 | isdn-$(CONFIG_ISDN_X25) += isdn_concap.o isdn_x25iface.o | ||
16 | isdn-$(CONFIG_ISDN_AUDIO) += isdn_audio.o | ||
17 | isdn-$(CONFIG_ISDN_TTY_FAX) += isdn_ttyfax.o | ||
18 | |||
diff --git a/drivers/isdn/i4l/isdn_audio.c b/drivers/isdn/i4l/isdn_audio.c new file mode 100644 index 000000000000..5350836a4f98 --- /dev/null +++ b/drivers/isdn/i4l/isdn_audio.c | |||
@@ -0,0 +1,720 @@ | |||
1 | /* $Id: isdn_audio.c,v 1.1.2.2 2004/01/12 22:37:18 keil Exp $ | ||
2 | * | ||
3 | * Linux ISDN subsystem, audio conversion and compression (linklevel). | ||
4 | * | ||
5 | * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de) | ||
6 | * DTMF code (c) 1996 by Christian Mock (cm@kukuruz.ping.at) | ||
7 | * Silence detection (c) 1998 by Armin Schindler (mac@gismo.telekom.de) | ||
8 | * | ||
9 | * This software may be used and distributed according to the terms | ||
10 | * of the GNU General Public License, incorporated herein by reference. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <linux/isdn.h> | ||
15 | #include "isdn_audio.h" | ||
16 | #include "isdn_common.h" | ||
17 | |||
18 | char *isdn_audio_revision = "$Revision: 1.1.2.2 $"; | ||
19 | |||
20 | /* | ||
21 | * Misc. lookup-tables. | ||
22 | */ | ||
23 | |||
24 | /* ulaw -> signed 16-bit */ | ||
25 | static short isdn_audio_ulaw_to_s16[] = | ||
26 | { | ||
27 | 0x8284, 0x8684, 0x8a84, 0x8e84, 0x9284, 0x9684, 0x9a84, 0x9e84, | ||
28 | 0xa284, 0xa684, 0xaa84, 0xae84, 0xb284, 0xb684, 0xba84, 0xbe84, | ||
29 | 0xc184, 0xc384, 0xc584, 0xc784, 0xc984, 0xcb84, 0xcd84, 0xcf84, | ||
30 | 0xd184, 0xd384, 0xd584, 0xd784, 0xd984, 0xdb84, 0xdd84, 0xdf84, | ||
31 | 0xe104, 0xe204, 0xe304, 0xe404, 0xe504, 0xe604, 0xe704, 0xe804, | ||
32 | 0xe904, 0xea04, 0xeb04, 0xec04, 0xed04, 0xee04, 0xef04, 0xf004, | ||
33 | 0xf0c4, 0xf144, 0xf1c4, 0xf244, 0xf2c4, 0xf344, 0xf3c4, 0xf444, | ||
34 | 0xf4c4, 0xf544, 0xf5c4, 0xf644, 0xf6c4, 0xf744, 0xf7c4, 0xf844, | ||
35 | 0xf8a4, 0xf8e4, 0xf924, 0xf964, 0xf9a4, 0xf9e4, 0xfa24, 0xfa64, | ||
36 | 0xfaa4, 0xfae4, 0xfb24, 0xfb64, 0xfba4, 0xfbe4, 0xfc24, 0xfc64, | ||
37 | 0xfc94, 0xfcb4, 0xfcd4, 0xfcf4, 0xfd14, 0xfd34, 0xfd54, 0xfd74, | ||
38 | 0xfd94, 0xfdb4, 0xfdd4, 0xfdf4, 0xfe14, 0xfe34, 0xfe54, 0xfe74, | ||
39 | 0xfe8c, 0xfe9c, 0xfeac, 0xfebc, 0xfecc, 0xfedc, 0xfeec, 0xfefc, | ||
40 | 0xff0c, 0xff1c, 0xff2c, 0xff3c, 0xff4c, 0xff5c, 0xff6c, 0xff7c, | ||
41 | 0xff88, 0xff90, 0xff98, 0xffa0, 0xffa8, 0xffb0, 0xffb8, 0xffc0, | ||
42 | 0xffc8, 0xffd0, 0xffd8, 0xffe0, 0xffe8, 0xfff0, 0xfff8, 0x0000, | ||
43 | 0x7d7c, 0x797c, 0x757c, 0x717c, 0x6d7c, 0x697c, 0x657c, 0x617c, | ||
44 | 0x5d7c, 0x597c, 0x557c, 0x517c, 0x4d7c, 0x497c, 0x457c, 0x417c, | ||
45 | 0x3e7c, 0x3c7c, 0x3a7c, 0x387c, 0x367c, 0x347c, 0x327c, 0x307c, | ||
46 | 0x2e7c, 0x2c7c, 0x2a7c, 0x287c, 0x267c, 0x247c, 0x227c, 0x207c, | ||
47 | 0x1efc, 0x1dfc, 0x1cfc, 0x1bfc, 0x1afc, 0x19fc, 0x18fc, 0x17fc, | ||
48 | 0x16fc, 0x15fc, 0x14fc, 0x13fc, 0x12fc, 0x11fc, 0x10fc, 0x0ffc, | ||
49 | 0x0f3c, 0x0ebc, 0x0e3c, 0x0dbc, 0x0d3c, 0x0cbc, 0x0c3c, 0x0bbc, | ||
50 | 0x0b3c, 0x0abc, 0x0a3c, 0x09bc, 0x093c, 0x08bc, 0x083c, 0x07bc, | ||
51 | 0x075c, 0x071c, 0x06dc, 0x069c, 0x065c, 0x061c, 0x05dc, 0x059c, | ||
52 | 0x055c, 0x051c, 0x04dc, 0x049c, 0x045c, 0x041c, 0x03dc, 0x039c, | ||
53 | 0x036c, 0x034c, 0x032c, 0x030c, 0x02ec, 0x02cc, 0x02ac, 0x028c, | ||
54 | 0x026c, 0x024c, 0x022c, 0x020c, 0x01ec, 0x01cc, 0x01ac, 0x018c, | ||
55 | 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, | ||
56 | 0x00f4, 0x00e4, 0x00d4, 0x00c4, 0x00b4, 0x00a4, 0x0094, 0x0084, | ||
57 | 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, | ||
58 | 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000 | ||
59 | }; | ||
60 | |||
61 | /* alaw -> signed 16-bit */ | ||
62 | static short isdn_audio_alaw_to_s16[] = | ||
63 | { | ||
64 | 0x13fc, 0xec04, 0x0144, 0xfebc, 0x517c, 0xae84, 0x051c, 0xfae4, | ||
65 | 0x0a3c, 0xf5c4, 0x0048, 0xffb8, 0x287c, 0xd784, 0x028c, 0xfd74, | ||
66 | 0x1bfc, 0xe404, 0x01cc, 0xfe34, 0x717c, 0x8e84, 0x071c, 0xf8e4, | ||
67 | 0x0e3c, 0xf1c4, 0x00c4, 0xff3c, 0x387c, 0xc784, 0x039c, 0xfc64, | ||
68 | 0x0ffc, 0xf004, 0x0104, 0xfefc, 0x417c, 0xbe84, 0x041c, 0xfbe4, | ||
69 | 0x083c, 0xf7c4, 0x0008, 0xfff8, 0x207c, 0xdf84, 0x020c, 0xfdf4, | ||
70 | 0x17fc, 0xe804, 0x018c, 0xfe74, 0x617c, 0x9e84, 0x061c, 0xf9e4, | ||
71 | 0x0c3c, 0xf3c4, 0x0084, 0xff7c, 0x307c, 0xcf84, 0x030c, 0xfcf4, | ||
72 | 0x15fc, 0xea04, 0x0164, 0xfe9c, 0x597c, 0xa684, 0x059c, 0xfa64, | ||
73 | 0x0b3c, 0xf4c4, 0x0068, 0xff98, 0x2c7c, 0xd384, 0x02cc, 0xfd34, | ||
74 | 0x1dfc, 0xe204, 0x01ec, 0xfe14, 0x797c, 0x8684, 0x07bc, 0xf844, | ||
75 | 0x0f3c, 0xf0c4, 0x00e4, 0xff1c, 0x3c7c, 0xc384, 0x03dc, 0xfc24, | ||
76 | 0x11fc, 0xee04, 0x0124, 0xfedc, 0x497c, 0xb684, 0x049c, 0xfb64, | ||
77 | 0x093c, 0xf6c4, 0x0028, 0xffd8, 0x247c, 0xdb84, 0x024c, 0xfdb4, | ||
78 | 0x19fc, 0xe604, 0x01ac, 0xfe54, 0x697c, 0x9684, 0x069c, 0xf964, | ||
79 | 0x0d3c, 0xf2c4, 0x00a4, 0xff5c, 0x347c, 0xcb84, 0x034c, 0xfcb4, | ||
80 | 0x12fc, 0xed04, 0x0134, 0xfecc, 0x4d7c, 0xb284, 0x04dc, 0xfb24, | ||
81 | 0x09bc, 0xf644, 0x0038, 0xffc8, 0x267c, 0xd984, 0x026c, 0xfd94, | ||
82 | 0x1afc, 0xe504, 0x01ac, 0xfe54, 0x6d7c, 0x9284, 0x06dc, 0xf924, | ||
83 | 0x0dbc, 0xf244, 0x00b4, 0xff4c, 0x367c, 0xc984, 0x036c, 0xfc94, | ||
84 | 0x0f3c, 0xf0c4, 0x00f4, 0xff0c, 0x3e7c, 0xc184, 0x03dc, 0xfc24, | ||
85 | 0x07bc, 0xf844, 0x0008, 0xfff8, 0x1efc, 0xe104, 0x01ec, 0xfe14, | ||
86 | 0x16fc, 0xe904, 0x0174, 0xfe8c, 0x5d7c, 0xa284, 0x05dc, 0xfa24, | ||
87 | 0x0bbc, 0xf444, 0x0078, 0xff88, 0x2e7c, 0xd184, 0x02ec, 0xfd14, | ||
88 | 0x14fc, 0xeb04, 0x0154, 0xfeac, 0x557c, 0xaa84, 0x055c, 0xfaa4, | ||
89 | 0x0abc, 0xf544, 0x0058, 0xffa8, 0x2a7c, 0xd584, 0x02ac, 0xfd54, | ||
90 | 0x1cfc, 0xe304, 0x01cc, 0xfe34, 0x757c, 0x8a84, 0x075c, 0xf8a4, | ||
91 | 0x0ebc, 0xf144, 0x00d4, 0xff2c, 0x3a7c, 0xc584, 0x039c, 0xfc64, | ||
92 | 0x10fc, 0xef04, 0x0114, 0xfeec, 0x457c, 0xba84, 0x045c, 0xfba4, | ||
93 | 0x08bc, 0xf744, 0x0018, 0xffe8, 0x227c, 0xdd84, 0x022c, 0xfdd4, | ||
94 | 0x18fc, 0xe704, 0x018c, 0xfe74, 0x657c, 0x9a84, 0x065c, 0xf9a4, | ||
95 | 0x0cbc, 0xf344, 0x0094, 0xff6c, 0x327c, 0xcd84, 0x032c, 0xfcd4 | ||
96 | }; | ||
97 | |||
98 | /* alaw -> ulaw */ | ||
99 | static char isdn_audio_alaw_to_ulaw[] = | ||
100 | { | ||
101 | 0xab, 0x2b, 0xe3, 0x63, 0x8b, 0x0b, 0xc9, 0x49, | ||
102 | 0xba, 0x3a, 0xf6, 0x76, 0x9b, 0x1b, 0xd7, 0x57, | ||
103 | 0xa3, 0x23, 0xdd, 0x5d, 0x83, 0x03, 0xc1, 0x41, | ||
104 | 0xb2, 0x32, 0xeb, 0x6b, 0x93, 0x13, 0xcf, 0x4f, | ||
105 | 0xaf, 0x2f, 0xe7, 0x67, 0x8f, 0x0f, 0xcd, 0x4d, | ||
106 | 0xbe, 0x3e, 0xfe, 0x7e, 0x9f, 0x1f, 0xdb, 0x5b, | ||
107 | 0xa7, 0x27, 0xdf, 0x5f, 0x87, 0x07, 0xc5, 0x45, | ||
108 | 0xb6, 0x36, 0xef, 0x6f, 0x97, 0x17, 0xd3, 0x53, | ||
109 | 0xa9, 0x29, 0xe1, 0x61, 0x89, 0x09, 0xc7, 0x47, | ||
110 | 0xb8, 0x38, 0xf2, 0x72, 0x99, 0x19, 0xd5, 0x55, | ||
111 | 0xa1, 0x21, 0xdc, 0x5c, 0x81, 0x01, 0xbf, 0x3f, | ||
112 | 0xb0, 0x30, 0xe9, 0x69, 0x91, 0x11, 0xce, 0x4e, | ||
113 | 0xad, 0x2d, 0xe5, 0x65, 0x8d, 0x0d, 0xcb, 0x4b, | ||
114 | 0xbc, 0x3c, 0xfa, 0x7a, 0x9d, 0x1d, 0xd9, 0x59, | ||
115 | 0xa5, 0x25, 0xde, 0x5e, 0x85, 0x05, 0xc3, 0x43, | ||
116 | 0xb4, 0x34, 0xed, 0x6d, 0x95, 0x15, 0xd1, 0x51, | ||
117 | 0xac, 0x2c, 0xe4, 0x64, 0x8c, 0x0c, 0xca, 0x4a, | ||
118 | 0xbb, 0x3b, 0xf8, 0x78, 0x9c, 0x1c, 0xd8, 0x58, | ||
119 | 0xa4, 0x24, 0xde, 0x5e, 0x84, 0x04, 0xc2, 0x42, | ||
120 | 0xb3, 0x33, 0xec, 0x6c, 0x94, 0x14, 0xd0, 0x50, | ||
121 | 0xb0, 0x30, 0xe8, 0x68, 0x90, 0x10, 0xce, 0x4e, | ||
122 | 0xbf, 0x3f, 0xfe, 0x7e, 0xa0, 0x20, 0xdc, 0x5c, | ||
123 | 0xa8, 0x28, 0xe0, 0x60, 0x88, 0x08, 0xc6, 0x46, | ||
124 | 0xb7, 0x37, 0xf0, 0x70, 0x98, 0x18, 0xd4, 0x54, | ||
125 | 0xaa, 0x2a, 0xe2, 0x62, 0x8a, 0x0a, 0xc8, 0x48, | ||
126 | 0xb9, 0x39, 0xf4, 0x74, 0x9a, 0x1a, 0xd6, 0x56, | ||
127 | 0xa2, 0x22, 0xdd, 0x5d, 0x82, 0x02, 0xc0, 0x40, | ||
128 | 0xb1, 0x31, 0xea, 0x6a, 0x92, 0x12, 0xcf, 0x4f, | ||
129 | 0xae, 0x2e, 0xe6, 0x66, 0x8e, 0x0e, 0xcc, 0x4c, | ||
130 | 0xbd, 0x3d, 0xfc, 0x7c, 0x9e, 0x1e, 0xda, 0x5a, | ||
131 | 0xa6, 0x26, 0xdf, 0x5f, 0x86, 0x06, 0xc4, 0x44, | ||
132 | 0xb5, 0x35, 0xee, 0x6e, 0x96, 0x16, 0xd2, 0x52 | ||
133 | }; | ||
134 | |||
135 | /* ulaw -> alaw */ | ||
136 | static char isdn_audio_ulaw_to_alaw[] = | ||
137 | { | ||
138 | 0xab, 0x55, 0xd5, 0x15, 0x95, 0x75, 0xf5, 0x35, | ||
139 | 0xb5, 0x45, 0xc5, 0x05, 0x85, 0x65, 0xe5, 0x25, | ||
140 | 0xa5, 0x5d, 0xdd, 0x1d, 0x9d, 0x7d, 0xfd, 0x3d, | ||
141 | 0xbd, 0x4d, 0xcd, 0x0d, 0x8d, 0x6d, 0xed, 0x2d, | ||
142 | 0xad, 0x51, 0xd1, 0x11, 0x91, 0x71, 0xf1, 0x31, | ||
143 | 0xb1, 0x41, 0xc1, 0x01, 0x81, 0x61, 0xe1, 0x21, | ||
144 | 0x59, 0xd9, 0x19, 0x99, 0x79, 0xf9, 0x39, 0xb9, | ||
145 | 0x49, 0xc9, 0x09, 0x89, 0x69, 0xe9, 0x29, 0xa9, | ||
146 | 0xd7, 0x17, 0x97, 0x77, 0xf7, 0x37, 0xb7, 0x47, | ||
147 | 0xc7, 0x07, 0x87, 0x67, 0xe7, 0x27, 0xa7, 0xdf, | ||
148 | 0x9f, 0x7f, 0xff, 0x3f, 0xbf, 0x4f, 0xcf, 0x0f, | ||
149 | 0x8f, 0x6f, 0xef, 0x2f, 0x53, 0x13, 0x73, 0x33, | ||
150 | 0xb3, 0x43, 0xc3, 0x03, 0x83, 0x63, 0xe3, 0x23, | ||
151 | 0xa3, 0x5b, 0xdb, 0x1b, 0x9b, 0x7b, 0xfb, 0x3b, | ||
152 | 0xbb, 0xbb, 0x4b, 0x4b, 0xcb, 0xcb, 0x0b, 0x0b, | ||
153 | 0x8b, 0x8b, 0x6b, 0x6b, 0xeb, 0xeb, 0x2b, 0x2b, | ||
154 | 0xab, 0x54, 0xd4, 0x14, 0x94, 0x74, 0xf4, 0x34, | ||
155 | 0xb4, 0x44, 0xc4, 0x04, 0x84, 0x64, 0xe4, 0x24, | ||
156 | 0xa4, 0x5c, 0xdc, 0x1c, 0x9c, 0x7c, 0xfc, 0x3c, | ||
157 | 0xbc, 0x4c, 0xcc, 0x0c, 0x8c, 0x6c, 0xec, 0x2c, | ||
158 | 0xac, 0x50, 0xd0, 0x10, 0x90, 0x70, 0xf0, 0x30, | ||
159 | 0xb0, 0x40, 0xc0, 0x00, 0x80, 0x60, 0xe0, 0x20, | ||
160 | 0x58, 0xd8, 0x18, 0x98, 0x78, 0xf8, 0x38, 0xb8, | ||
161 | 0x48, 0xc8, 0x08, 0x88, 0x68, 0xe8, 0x28, 0xa8, | ||
162 | 0xd6, 0x16, 0x96, 0x76, 0xf6, 0x36, 0xb6, 0x46, | ||
163 | 0xc6, 0x06, 0x86, 0x66, 0xe6, 0x26, 0xa6, 0xde, | ||
164 | 0x9e, 0x7e, 0xfe, 0x3e, 0xbe, 0x4e, 0xce, 0x0e, | ||
165 | 0x8e, 0x6e, 0xee, 0x2e, 0x52, 0x12, 0x72, 0x32, | ||
166 | 0xb2, 0x42, 0xc2, 0x02, 0x82, 0x62, 0xe2, 0x22, | ||
167 | 0xa2, 0x5a, 0xda, 0x1a, 0x9a, 0x7a, 0xfa, 0x3a, | ||
168 | 0xba, 0xba, 0x4a, 0x4a, 0xca, 0xca, 0x0a, 0x0a, | ||
169 | 0x8a, 0x8a, 0x6a, 0x6a, 0xea, 0xea, 0x2a, 0x2a | ||
170 | }; | ||
171 | |||
172 | #define NCOEFF 8 /* number of frequencies to be analyzed */ | ||
173 | #define DTMF_TRESH 4000 /* above this is dtmf */ | ||
174 | #define SILENCE_TRESH 200 /* below this is silence */ | ||
175 | #define AMP_BITS 9 /* bits per sample, reduced to avoid overflow */ | ||
176 | #define LOGRP 0 | ||
177 | #define HIGRP 1 | ||
178 | |||
179 | /* For DTMF recognition: | ||
180 | * 2 * cos(2 * PI * k / N) precalculated for all k | ||
181 | */ | ||
182 | static int cos2pik[NCOEFF] = | ||
183 | { | ||
184 | 55813, 53604, 51193, 48591, 38114, 33057, 25889, 18332 | ||
185 | }; | ||
186 | |||
187 | static char dtmf_matrix[4][4] = | ||
188 | { | ||
189 | {'1', '2', '3', 'A'}, | ||
190 | {'4', '5', '6', 'B'}, | ||
191 | {'7', '8', '9', 'C'}, | ||
192 | {'*', '0', '#', 'D'} | ||
193 | }; | ||
194 | |||
195 | static inline void | ||
196 | isdn_audio_tlookup(const u_char *table, u_char *buff, unsigned long n) | ||
197 | { | ||
198 | #ifdef __i386__ | ||
199 | unsigned long d0, d1, d2, d3; | ||
200 | __asm__ __volatile__( | ||
201 | "cld\n" | ||
202 | "1:\tlodsb\n\t" | ||
203 | "xlatb\n\t" | ||
204 | "stosb\n\t" | ||
205 | "loop 1b\n\t" | ||
206 | : "=&b"(d0), "=&c"(d1), "=&D"(d2), "=&S"(d3) | ||
207 | : "0"((long) table), "1"(n), "2"((long) buff), "3"((long) buff) | ||
208 | : "memory", "ax"); | ||
209 | #else | ||
210 | while (n--) | ||
211 | *buff = table[*(unsigned char *)buff], buff++; | ||
212 | #endif | ||
213 | } | ||
214 | |||
215 | void | ||
216 | isdn_audio_ulaw2alaw(unsigned char *buff, unsigned long len) | ||
217 | { | ||
218 | isdn_audio_tlookup(isdn_audio_ulaw_to_alaw, buff, len); | ||
219 | } | ||
220 | |||
221 | void | ||
222 | isdn_audio_alaw2ulaw(unsigned char *buff, unsigned long len) | ||
223 | { | ||
224 | isdn_audio_tlookup(isdn_audio_alaw_to_ulaw, buff, len); | ||
225 | } | ||
226 | |||
227 | /* | ||
228 | * linear <-> adpcm conversion stuff | ||
229 | * Most parts from the mgetty-package. | ||
230 | * (C) by Gert Doering and Klaus Weidner | ||
231 | * Used by permission of Gert Doering | ||
232 | */ | ||
233 | |||
234 | |||
235 | #define ZEROTRAP /* turn on the trap as per the MIL-STD */ | ||
236 | #undef ZEROTRAP | ||
237 | #define BIAS 0x84 /* define the add-in bias for 16 bit samples */ | ||
238 | #define CLIP 32635 | ||
239 | |||
240 | static unsigned char | ||
241 | isdn_audio_linear2ulaw(int sample) | ||
242 | { | ||
243 | static int exp_lut[256] = | ||
244 | { | ||
245 | 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, | ||
246 | 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, | ||
247 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, | ||
248 | 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, | ||
249 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, | ||
250 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, | ||
251 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, | ||
252 | 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, | ||
253 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, | ||
254 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, | ||
255 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, | ||
256 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, | ||
257 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, | ||
258 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, | ||
259 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, | ||
260 | 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 | ||
261 | }; | ||
262 | int sign, | ||
263 | exponent, | ||
264 | mantissa; | ||
265 | unsigned char ulawbyte; | ||
266 | |||
267 | /* Get the sample into sign-magnitude. */ | ||
268 | sign = (sample >> 8) & 0x80; /* set aside the sign */ | ||
269 | if (sign != 0) | ||
270 | sample = -sample; /* get magnitude */ | ||
271 | if (sample > CLIP) | ||
272 | sample = CLIP; /* clip the magnitude */ | ||
273 | |||
274 | /* Convert from 16 bit linear to ulaw. */ | ||
275 | sample = sample + BIAS; | ||
276 | exponent = exp_lut[(sample >> 7) & 0xFF]; | ||
277 | mantissa = (sample >> (exponent + 3)) & 0x0F; | ||
278 | ulawbyte = ~(sign | (exponent << 4) | mantissa); | ||
279 | #ifdef ZEROTRAP | ||
280 | /* optional CCITT trap */ | ||
281 | if (ulawbyte == 0) | ||
282 | ulawbyte = 0x02; | ||
283 | #endif | ||
284 | return (ulawbyte); | ||
285 | } | ||
286 | |||
287 | |||
288 | static int Mx[3][8] = | ||
289 | { | ||
290 | {0x3800, 0x5600, 0, 0, 0, 0, 0, 0}, | ||
291 | {0x399a, 0x3a9f, 0x4d14, 0x6607, 0, 0, 0, 0}, | ||
292 | {0x3556, 0x3556, 0x399A, 0x3A9F, 0x4200, 0x4D14, 0x6607, 0x6607}, | ||
293 | }; | ||
294 | |||
295 | static int bitmask[9] = | ||
296 | { | ||
297 | 0, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff | ||
298 | }; | ||
299 | |||
300 | static int | ||
301 | isdn_audio_get_bits(adpcm_state * s, unsigned char **in, int *len) | ||
302 | { | ||
303 | while (s->nleft < s->nbits) { | ||
304 | int d = *((*in)++); | ||
305 | (*len)--; | ||
306 | s->word = (s->word << 8) | d; | ||
307 | s->nleft += 8; | ||
308 | } | ||
309 | s->nleft -= s->nbits; | ||
310 | return (s->word >> s->nleft) & bitmask[s->nbits]; | ||
311 | } | ||
312 | |||
313 | static void | ||
314 | isdn_audio_put_bits(int data, int nbits, adpcm_state * s, | ||
315 | unsigned char **out, int *len) | ||
316 | { | ||
317 | s->word = (s->word << nbits) | (data & bitmask[nbits]); | ||
318 | s->nleft += nbits; | ||
319 | while (s->nleft >= 8) { | ||
320 | int d = (s->word >> (s->nleft - 8)); | ||
321 | *(out[0]++) = d & 255; | ||
322 | (*len)++; | ||
323 | s->nleft -= 8; | ||
324 | } | ||
325 | } | ||
326 | |||
327 | adpcm_state * | ||
328 | isdn_audio_adpcm_init(adpcm_state * s, int nbits) | ||
329 | { | ||
330 | if (!s) | ||
331 | s = (adpcm_state *) kmalloc(sizeof(adpcm_state), GFP_ATOMIC); | ||
332 | if (s) { | ||
333 | s->a = 0; | ||
334 | s->d = 5; | ||
335 | s->word = 0; | ||
336 | s->nleft = 0; | ||
337 | s->nbits = nbits; | ||
338 | } | ||
339 | return s; | ||
340 | } | ||
341 | |||
342 | dtmf_state * | ||
343 | isdn_audio_dtmf_init(dtmf_state * s) | ||
344 | { | ||
345 | if (!s) | ||
346 | s = (dtmf_state *) kmalloc(sizeof(dtmf_state), GFP_ATOMIC); | ||
347 | if (s) { | ||
348 | s->idx = 0; | ||
349 | s->last = ' '; | ||
350 | } | ||
351 | return s; | ||
352 | } | ||
353 | |||
354 | /* | ||
355 | * Decompression of adpcm data to a/u-law | ||
356 | * | ||
357 | */ | ||
358 | |||
359 | int | ||
360 | isdn_audio_adpcm2xlaw(adpcm_state * s, int fmt, unsigned char *in, | ||
361 | unsigned char *out, int len) | ||
362 | { | ||
363 | int a = s->a; | ||
364 | int d = s->d; | ||
365 | int nbits = s->nbits; | ||
366 | int olen = 0; | ||
367 | |||
368 | while (len) { | ||
369 | int e = isdn_audio_get_bits(s, &in, &len); | ||
370 | int sign; | ||
371 | |||
372 | if (nbits == 4 && e == 0) | ||
373 | d = 4; | ||
374 | sign = (e >> (nbits - 1)) ? -1 : 1; | ||
375 | e &= bitmask[nbits - 1]; | ||
376 | a += sign * ((e << 1) + 1) * d >> 1; | ||
377 | if (d & 1) | ||
378 | a++; | ||
379 | if (fmt) | ||
380 | *out++ = isdn_audio_ulaw_to_alaw[ | ||
381 | isdn_audio_linear2ulaw(a << 2)]; | ||
382 | else | ||
383 | *out++ = isdn_audio_linear2ulaw(a << 2); | ||
384 | olen++; | ||
385 | d = (d * Mx[nbits - 2][e] + 0x2000) >> 14; | ||
386 | if (d < 5) | ||
387 | d = 5; | ||
388 | } | ||
389 | s->a = a; | ||
390 | s->d = d; | ||
391 | return olen; | ||
392 | } | ||
393 | |||
394 | int | ||
395 | isdn_audio_2adpcm_flush(adpcm_state * s, unsigned char *out) | ||
396 | { | ||
397 | int olen = 0; | ||
398 | |||
399 | if (s->nleft) | ||
400 | isdn_audio_put_bits(0, 8 - s->nleft, s, &out, &olen); | ||
401 | return olen; | ||
402 | } | ||
403 | |||
404 | int | ||
405 | isdn_audio_xlaw2adpcm(adpcm_state * s, int fmt, unsigned char *in, | ||
406 | unsigned char *out, int len) | ||
407 | { | ||
408 | int a = s->a; | ||
409 | int d = s->d; | ||
410 | int nbits = s->nbits; | ||
411 | int olen = 0; | ||
412 | |||
413 | while (len--) { | ||
414 | int e = 0, | ||
415 | nmax = 1 << (nbits - 1); | ||
416 | int sign, | ||
417 | delta; | ||
418 | |||
419 | if (fmt) | ||
420 | delta = (isdn_audio_alaw_to_s16[*in++] >> 2) - a; | ||
421 | else | ||
422 | delta = (isdn_audio_ulaw_to_s16[*in++] >> 2) - a; | ||
423 | if (delta < 0) { | ||
424 | e = nmax; | ||
425 | delta = -delta; | ||
426 | } | ||
427 | while (--nmax && delta > d) { | ||
428 | delta -= d; | ||
429 | e++; | ||
430 | } | ||
431 | if (nbits == 4 && ((e & 0x0f) == 0)) | ||
432 | e = 8; | ||
433 | isdn_audio_put_bits(e, nbits, s, &out, &olen); | ||
434 | sign = (e >> (nbits - 1)) ? -1 : 1; | ||
435 | e &= bitmask[nbits - 1]; | ||
436 | |||
437 | a += sign * ((e << 1) + 1) * d >> 1; | ||
438 | if (d & 1) | ||
439 | a++; | ||
440 | d = (d * Mx[nbits - 2][e] + 0x2000) >> 14; | ||
441 | if (d < 5) | ||
442 | d = 5; | ||
443 | } | ||
444 | s->a = a; | ||
445 | s->d = d; | ||
446 | return olen; | ||
447 | } | ||
448 | |||
449 | /* | ||
450 | * Goertzel algorithm. | ||
451 | * See http://ptolemy.eecs.berkeley.edu/~pino/Ptolemy/papers/96/dtmf_ict/ | ||
452 | * for more info. | ||
453 | * Result is stored into an sk_buff and queued up for later | ||
454 | * evaluation. | ||
455 | */ | ||
456 | static void | ||
457 | isdn_audio_goertzel(int *sample, modem_info * info) | ||
458 | { | ||
459 | int sk, | ||
460 | sk1, | ||
461 | sk2; | ||
462 | int k, | ||
463 | n; | ||
464 | struct sk_buff *skb; | ||
465 | int *result; | ||
466 | |||
467 | skb = dev_alloc_skb(sizeof(int) * NCOEFF); | ||
468 | if (!skb) { | ||
469 | printk(KERN_WARNING | ||
470 | "isdn_audio: Could not alloc DTMF result for ttyI%d\n", | ||
471 | info->line); | ||
472 | return; | ||
473 | } | ||
474 | result = (int *) skb_put(skb, sizeof(int) * NCOEFF); | ||
475 | for (k = 0; k < NCOEFF; k++) { | ||
476 | sk = sk1 = sk2 = 0; | ||
477 | for (n = 0; n < DTMF_NPOINTS; n++) { | ||
478 | sk = sample[n] + ((cos2pik[k] * sk1) >> 15) - sk2; | ||
479 | sk2 = sk1; | ||
480 | sk1 = sk; | ||
481 | } | ||
482 | /* Avoid overflows */ | ||
483 | sk >>= 1; | ||
484 | sk2 >>= 1; | ||
485 | /* compute |X(k)|**2 */ | ||
486 | /* report overflows. This should not happen. */ | ||
487 | /* Comment this out if desired */ | ||
488 | if (sk < -32768 || sk > 32767) | ||
489 | printk(KERN_DEBUG | ||
490 | "isdn_audio: dtmf goertzel overflow, sk=%d\n", sk); | ||
491 | if (sk2 < -32768 || sk2 > 32767) | ||
492 | printk(KERN_DEBUG | ||
493 | "isdn_audio: dtmf goertzel overflow, sk2=%d\n", sk2); | ||
494 | result[k] = | ||
495 | ((sk * sk) >> AMP_BITS) - | ||
496 | ((((cos2pik[k] * sk) >> 15) * sk2) >> AMP_BITS) + | ||
497 | ((sk2 * sk2) >> AMP_BITS); | ||
498 | } | ||
499 | skb_queue_tail(&info->dtmf_queue, skb); | ||
500 | isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1); | ||
501 | } | ||
502 | |||
503 | void | ||
504 | isdn_audio_eval_dtmf(modem_info * info) | ||
505 | { | ||
506 | struct sk_buff *skb; | ||
507 | int *result; | ||
508 | dtmf_state *s; | ||
509 | int silence; | ||
510 | int i; | ||
511 | int di; | ||
512 | int ch; | ||
513 | int grp[2]; | ||
514 | char what; | ||
515 | char *p; | ||
516 | int thresh; | ||
517 | |||
518 | while ((skb = skb_dequeue(&info->dtmf_queue))) { | ||
519 | result = (int *) skb->data; | ||
520 | s = info->dtmf_state; | ||
521 | grp[LOGRP] = grp[HIGRP] = -1; | ||
522 | silence = 0; | ||
523 | thresh = 0; | ||
524 | for (i = 0; i < NCOEFF; i++) { | ||
525 | if (result[i] > DTMF_TRESH) { | ||
526 | if (result[i] > thresh) | ||
527 | thresh = result[i]; | ||
528 | } | ||
529 | else if (result[i] < SILENCE_TRESH) | ||
530 | silence++; | ||
531 | } | ||
532 | if (silence == NCOEFF) | ||
533 | what = ' '; | ||
534 | else { | ||
535 | if (thresh > 0) { | ||
536 | thresh = thresh >> 4; /* touchtones must match within 12 dB */ | ||
537 | for (i = 0; i < NCOEFF; i++) { | ||
538 | if (result[i] < thresh) | ||
539 | continue; /* ignore */ | ||
540 | /* good level found. This is allowed only one time per group */ | ||
541 | if (i < NCOEFF / 2) { | ||
542 | /* lowgroup*/ | ||
543 | if (grp[LOGRP] >= 0) { | ||
544 | // Bad. Another tone found. */ | ||
545 | grp[LOGRP] = -1; | ||
546 | break; | ||
547 | } | ||
548 | else | ||
549 | grp[LOGRP] = i; | ||
550 | } | ||
551 | else { /* higroup */ | ||
552 | if (grp[HIGRP] >= 0) { // Bad. Another tone found. */ | ||
553 | grp[HIGRP] = -1; | ||
554 | break; | ||
555 | } | ||
556 | else | ||
557 | grp[HIGRP] = i - NCOEFF/2; | ||
558 | } | ||
559 | } | ||
560 | if ((grp[LOGRP] >= 0) && (grp[HIGRP] >= 0)) { | ||
561 | what = dtmf_matrix[grp[LOGRP]][grp[HIGRP]]; | ||
562 | if (s->last != ' ' && s->last != '.') | ||
563 | s->last = what; /* min. 1 non-DTMF between DTMF */ | ||
564 | } else | ||
565 | what = '.'; | ||
566 | } | ||
567 | else | ||
568 | what = '.'; | ||
569 | } | ||
570 | if ((what != s->last) && (what != ' ') && (what != '.')) { | ||
571 | printk(KERN_DEBUG "dtmf: tt='%c'\n", what); | ||
572 | p = skb->data; | ||
573 | *p++ = 0x10; | ||
574 | *p = what; | ||
575 | skb_trim(skb, 2); | ||
576 | ISDN_AUDIO_SKB_DLECOUNT(skb) = 0; | ||
577 | ISDN_AUDIO_SKB_LOCK(skb) = 0; | ||
578 | di = info->isdn_driver; | ||
579 | ch = info->isdn_channel; | ||
580 | __skb_queue_tail(&dev->drv[di]->rpqueue[ch], skb); | ||
581 | dev->drv[di]->rcvcount[ch] += 2; | ||
582 | /* Schedule dequeuing */ | ||
583 | if ((dev->modempoll) && (info->rcvsched)) | ||
584 | isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1); | ||
585 | wake_up_interruptible(&dev->drv[di]->rcv_waitq[ch]); | ||
586 | } else | ||
587 | kfree_skb(skb); | ||
588 | s->last = what; | ||
589 | } | ||
590 | } | ||
591 | |||
592 | /* | ||
593 | * Decode DTMF tones, queue result in separate sk_buf for | ||
594 | * later examination. | ||
595 | * Parameters: | ||
596 | * s = pointer to state-struct. | ||
597 | * buf = input audio data | ||
598 | * len = size of audio data. | ||
599 | * fmt = audio data format (0 = ulaw, 1 = alaw) | ||
600 | */ | ||
601 | void | ||
602 | isdn_audio_calc_dtmf(modem_info * info, unsigned char *buf, int len, int fmt) | ||
603 | { | ||
604 | dtmf_state *s = info->dtmf_state; | ||
605 | int i; | ||
606 | int c; | ||
607 | |||
608 | while (len) { | ||
609 | c = DTMF_NPOINTS - s->idx; | ||
610 | if (c > len) | ||
611 | c = len; | ||
612 | if (c <= 0) | ||
613 | break; | ||
614 | for (i = 0; i < c; i++) { | ||
615 | if (fmt) | ||
616 | s->buf[s->idx++] = | ||
617 | isdn_audio_alaw_to_s16[*buf++] >> (15 - AMP_BITS); | ||
618 | else | ||
619 | s->buf[s->idx++] = | ||
620 | isdn_audio_ulaw_to_s16[*buf++] >> (15 - AMP_BITS); | ||
621 | } | ||
622 | if (s->idx == DTMF_NPOINTS) { | ||
623 | isdn_audio_goertzel(s->buf, info); | ||
624 | s->idx = 0; | ||
625 | } | ||
626 | len -= c; | ||
627 | } | ||
628 | } | ||
629 | |||
630 | silence_state * | ||
631 | isdn_audio_silence_init(silence_state * s) | ||
632 | { | ||
633 | if (!s) | ||
634 | s = (silence_state *) kmalloc(sizeof(silence_state), GFP_ATOMIC); | ||
635 | if (s) { | ||
636 | s->idx = 0; | ||
637 | s->state = 0; | ||
638 | } | ||
639 | return s; | ||
640 | } | ||
641 | |||
642 | void | ||
643 | isdn_audio_calc_silence(modem_info * info, unsigned char *buf, int len, int fmt) | ||
644 | { | ||
645 | silence_state *s = info->silence_state; | ||
646 | int i; | ||
647 | signed char c; | ||
648 | |||
649 | if (!info->emu.vpar[1]) return; | ||
650 | |||
651 | for (i = 0; i < len; i++) { | ||
652 | if (fmt) | ||
653 | c = isdn_audio_alaw_to_ulaw[*buf++]; | ||
654 | else | ||
655 | c = *buf++; | ||
656 | |||
657 | if (c > 0) c -= 128; | ||
658 | c = abs(c); | ||
659 | |||
660 | if (c > (info->emu.vpar[1] * 4)) { | ||
661 | s->idx = 0; | ||
662 | s->state = 1; | ||
663 | } else { | ||
664 | if (s->idx < 210000) s->idx++; | ||
665 | } | ||
666 | } | ||
667 | } | ||
668 | |||
669 | void | ||
670 | isdn_audio_put_dle_code(modem_info * info, u_char code) | ||
671 | { | ||
672 | struct sk_buff *skb; | ||
673 | int di; | ||
674 | int ch; | ||
675 | char *p; | ||
676 | |||
677 | skb = dev_alloc_skb(2); | ||
678 | if (!skb) { | ||
679 | printk(KERN_WARNING | ||
680 | "isdn_audio: Could not alloc skb for ttyI%d\n", | ||
681 | info->line); | ||
682 | return; | ||
683 | } | ||
684 | p = (char *) skb_put(skb, 2); | ||
685 | p[0] = 0x10; | ||
686 | p[1] = code; | ||
687 | ISDN_AUDIO_SKB_DLECOUNT(skb) = 0; | ||
688 | ISDN_AUDIO_SKB_LOCK(skb) = 0; | ||
689 | di = info->isdn_driver; | ||
690 | ch = info->isdn_channel; | ||
691 | __skb_queue_tail(&dev->drv[di]->rpqueue[ch], skb); | ||
692 | dev->drv[di]->rcvcount[ch] += 2; | ||
693 | /* Schedule dequeuing */ | ||
694 | if ((dev->modempoll) && (info->rcvsched)) | ||
695 | isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1); | ||
696 | wake_up_interruptible(&dev->drv[di]->rcv_waitq[ch]); | ||
697 | } | ||
698 | |||
699 | void | ||
700 | isdn_audio_eval_silence(modem_info * info) | ||
701 | { | ||
702 | silence_state *s = info->silence_state; | ||
703 | char what; | ||
704 | |||
705 | what = ' '; | ||
706 | |||
707 | if (s->idx > (info->emu.vpar[2] * 800)) { | ||
708 | s->idx = 0; | ||
709 | if (!s->state) { /* silence from beginning of rec */ | ||
710 | what = 's'; | ||
711 | } else { | ||
712 | what = 'q'; | ||
713 | } | ||
714 | } | ||
715 | if ((what == 's') || (what == 'q')) { | ||
716 | printk(KERN_DEBUG "ttyI%d: %s\n", info->line, | ||
717 | (what=='s') ? "silence":"quiet"); | ||
718 | isdn_audio_put_dle_code(info, what); | ||
719 | } | ||
720 | } | ||
diff --git a/drivers/isdn/i4l/isdn_audio.h b/drivers/isdn/i4l/isdn_audio.h new file mode 100644 index 000000000000..5a977b21dcfa --- /dev/null +++ b/drivers/isdn/i4l/isdn_audio.h | |||
@@ -0,0 +1,45 @@ | |||
1 | /* $Id: isdn_audio.h,v 1.1.2.2 2004/01/12 22:37:18 keil Exp $ | ||
2 | * | ||
3 | * Linux ISDN subsystem, audio conversion and compression (linklevel). | ||
4 | * | ||
5 | * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de) | ||
6 | * | ||
7 | * This software may be used and distributed according to the terms | ||
8 | * of the GNU General Public License, incorporated herein by reference. | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #define DTMF_NPOINTS 205 /* Number of samples for DTMF recognition */ | ||
13 | typedef struct adpcm_state { | ||
14 | int a; | ||
15 | int d; | ||
16 | int word; | ||
17 | int nleft; | ||
18 | int nbits; | ||
19 | } adpcm_state; | ||
20 | |||
21 | typedef struct dtmf_state { | ||
22 | char last; | ||
23 | char llast; | ||
24 | int idx; | ||
25 | int buf[DTMF_NPOINTS]; | ||
26 | } dtmf_state; | ||
27 | |||
28 | typedef struct silence_state { | ||
29 | int state; | ||
30 | unsigned int idx; | ||
31 | } silence_state; | ||
32 | |||
33 | extern void isdn_audio_ulaw2alaw(unsigned char *, unsigned long); | ||
34 | extern void isdn_audio_alaw2ulaw(unsigned char *, unsigned long); | ||
35 | extern adpcm_state *isdn_audio_adpcm_init(adpcm_state *, int); | ||
36 | extern int isdn_audio_adpcm2xlaw(adpcm_state *, int, unsigned char *, unsigned char *, int); | ||
37 | extern int isdn_audio_xlaw2adpcm(adpcm_state *, int, unsigned char *, unsigned char *, int); | ||
38 | extern int isdn_audio_2adpcm_flush(adpcm_state * s, unsigned char *out); | ||
39 | extern void isdn_audio_calc_dtmf(modem_info *, unsigned char *, int, int); | ||
40 | extern void isdn_audio_eval_dtmf(modem_info *); | ||
41 | dtmf_state *isdn_audio_dtmf_init(dtmf_state *); | ||
42 | extern void isdn_audio_calc_silence(modem_info *, unsigned char *, int, int); | ||
43 | extern void isdn_audio_eval_silence(modem_info *); | ||
44 | silence_state *isdn_audio_silence_init(silence_state *); | ||
45 | 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 000000000000..baf4bcad9bf9 --- /dev/null +++ b/drivers/isdn/i4l/isdn_bsdcomp.c | |||
@@ -0,0 +1,937 @@ | |||
1 | /* | ||
2 | * BSD compression module | ||
3 | * | ||
4 | * Patched version for ISDN syncPPP written 1997/1998 by Michael Hipp | ||
5 | * The whole module is now SKB based. | ||
6 | * | ||
7 | */ | ||
8 | |||
9 | /* | ||
10 | * Update: The Berkeley copyright was changed, and the change | ||
11 | * is retroactive to all "true" BSD software (ie everything | ||
12 | * from UCB as opposed to other peoples code that just carried | ||
13 | * the same license). The new copyright doesn't clash with the | ||
14 | * GPL, so the module-only restriction has been removed.. | ||
15 | */ | ||
16 | |||
17 | /* | ||
18 | * Original copyright notice: | ||
19 | * | ||
20 | * Copyright (c) 1985, 1986 The Regents of the University of California. | ||
21 | * All rights reserved. | ||
22 | * | ||
23 | * This code is derived from software contributed to Berkeley by | ||
24 | * James A. Woods, derived from original work by Spencer Thomas | ||
25 | * and Joseph Orost. | ||
26 | * | ||
27 | * Redistribution and use in source and binary forms, with or without | ||
28 | * modification, are permitted provided that the following conditions | ||
29 | * are met: | ||
30 | * 1. Redistributions of source code must retain the above copyright | ||
31 | * notice, this list of conditions and the following disclaimer. | ||
32 | * 2. Redistributions in binary form must reproduce the above copyright | ||
33 | * notice, this list of conditions and the following disclaimer in the | ||
34 | * documentation and/or other materials provided with the distribution. | ||
35 | * 3. All advertising materials mentioning features or use of this software | ||
36 | * must display the following acknowledgement: | ||
37 | * This product includes software developed by the University of | ||
38 | * California, Berkeley and its contributors. | ||
39 | * 4. Neither the name of the University nor the names of its contributors | ||
40 | * may be used to endorse or promote products derived from this software | ||
41 | * without specific prior written permission. | ||
42 | * | ||
43 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | ||
44 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
45 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
46 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | ||
47 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
48 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
49 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
50 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
51 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
52 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
53 | * SUCH DAMAGE. | ||
54 | */ | ||
55 | |||
56 | #include <linux/module.h> | ||
57 | #include <linux/init.h> | ||
58 | #include <linux/kernel.h> | ||
59 | #include <linux/sched.h> | ||
60 | #include <linux/types.h> | ||
61 | #include <linux/fcntl.h> | ||
62 | #include <linux/interrupt.h> | ||
63 | #include <linux/ptrace.h> | ||
64 | #include <linux/ioport.h> | ||
65 | #include <linux/in.h> | ||
66 | #include <linux/slab.h> | ||
67 | #include <linux/tty.h> | ||
68 | #include <linux/errno.h> | ||
69 | #include <linux/string.h> /* used in new tty drivers */ | ||
70 | #include <linux/signal.h> /* used in new tty drivers */ | ||
71 | #include <linux/bitops.h> | ||
72 | |||
73 | #include <asm/system.h> | ||
74 | #include <asm/byteorder.h> | ||
75 | #include <asm/types.h> | ||
76 | |||
77 | #include <linux/if.h> | ||
78 | |||
79 | #include <linux/if_ether.h> | ||
80 | #include <linux/netdevice.h> | ||
81 | #include <linux/skbuff.h> | ||
82 | #include <linux/inet.h> | ||
83 | #include <linux/ioctl.h> | ||
84 | #include <linux/vmalloc.h> | ||
85 | |||
86 | #include <linux/ppp_defs.h> | ||
87 | |||
88 | #include <linux/isdn.h> | ||
89 | #include <linux/isdn_ppp.h> | ||
90 | #include <linux/ip.h> | ||
91 | #include <linux/tcp.h> | ||
92 | #include <linux/if_arp.h> | ||
93 | #include <linux/ppp-comp.h> | ||
94 | |||
95 | #include "isdn_ppp.h" | ||
96 | |||
97 | MODULE_DESCRIPTION("ISDN4Linux: BSD Compression for PPP over ISDN"); | ||
98 | MODULE_LICENSE("Dual BSD/GPL"); | ||
99 | |||
100 | #define BSD_VERSION(x) ((x) >> 5) | ||
101 | #define BSD_NBITS(x) ((x) & 0x1F) | ||
102 | |||
103 | #define BSD_CURRENT_VERSION 1 | ||
104 | |||
105 | #define DEBUG 1 | ||
106 | |||
107 | /* | ||
108 | * A dictionary for doing BSD compress. | ||
109 | */ | ||
110 | |||
111 | struct bsd_dict { | ||
112 | u32 fcode; | ||
113 | u16 codem1; /* output of hash table -1 */ | ||
114 | u16 cptr; /* map code to hash table entry */ | ||
115 | }; | ||
116 | |||
117 | struct bsd_db { | ||
118 | int totlen; /* length of this structure */ | ||
119 | unsigned int hsize; /* size of the hash table */ | ||
120 | unsigned char hshift; /* used in hash function */ | ||
121 | unsigned char n_bits; /* current bits/code */ | ||
122 | unsigned char maxbits; /* maximum bits/code */ | ||
123 | unsigned char debug; /* non-zero if debug desired */ | ||
124 | unsigned char unit; /* ppp unit number */ | ||
125 | u16 seqno; /* sequence # of next packet */ | ||
126 | unsigned int mru; /* size of receive (decompress) bufr */ | ||
127 | unsigned int maxmaxcode; /* largest valid code */ | ||
128 | unsigned int max_ent; /* largest code in use */ | ||
129 | unsigned int in_count; /* uncompressed bytes, aged */ | ||
130 | unsigned int bytes_out; /* compressed bytes, aged */ | ||
131 | unsigned int ratio; /* recent compression ratio */ | ||
132 | unsigned int checkpoint; /* when to next check the ratio */ | ||
133 | unsigned int clear_count; /* times dictionary cleared */ | ||
134 | unsigned int incomp_count; /* incompressible packets */ | ||
135 | unsigned int incomp_bytes; /* incompressible bytes */ | ||
136 | unsigned int uncomp_count; /* uncompressed packets */ | ||
137 | unsigned int uncomp_bytes; /* uncompressed bytes */ | ||
138 | unsigned int comp_count; /* compressed packets */ | ||
139 | unsigned int comp_bytes; /* compressed bytes */ | ||
140 | unsigned short *lens; /* array of lengths of codes */ | ||
141 | struct bsd_dict *dict; /* dictionary */ | ||
142 | int xmit; | ||
143 | }; | ||
144 | |||
145 | #define BSD_OVHD 2 /* BSD compress overhead/packet */ | ||
146 | #define MIN_BSD_BITS 9 | ||
147 | #define BSD_INIT_BITS MIN_BSD_BITS | ||
148 | #define MAX_BSD_BITS 15 | ||
149 | |||
150 | /* | ||
151 | * the next two codes should not be changed lightly, as they must not | ||
152 | * lie within the contiguous general code space. | ||
153 | */ | ||
154 | #define CLEAR 256 /* table clear output code */ | ||
155 | #define FIRST 257 /* first free entry */ | ||
156 | #define LAST 255 | ||
157 | |||
158 | #define MAXCODE(b) ((1 << (b)) - 1) | ||
159 | #define BADCODEM1 MAXCODE(MAX_BSD_BITS); | ||
160 | |||
161 | #define BSD_HASH(prefix,suffix,hshift) ((((unsigned long)(suffix))<<(hshift)) \ | ||
162 | ^ (unsigned long)(prefix)) | ||
163 | #define BSD_KEY(prefix,suffix) ((((unsigned long)(suffix)) << 16) \ | ||
164 | + (unsigned long)(prefix)) | ||
165 | |||
166 | #define CHECK_GAP 10000 /* Ratio check interval */ | ||
167 | |||
168 | #define RATIO_SCALE_LOG 8 | ||
169 | #define RATIO_SCALE (1<<RATIO_SCALE_LOG) | ||
170 | #define RATIO_MAX (0x7fffffff>>RATIO_SCALE_LOG) | ||
171 | |||
172 | /* | ||
173 | * clear the dictionary | ||
174 | */ | ||
175 | |||
176 | static void bsd_clear(struct bsd_db *db) | ||
177 | { | ||
178 | db->clear_count++; | ||
179 | db->max_ent = FIRST-1; | ||
180 | db->n_bits = BSD_INIT_BITS; | ||
181 | db->bytes_out = 0; | ||
182 | db->in_count = 0; | ||
183 | db->incomp_count = 0; | ||
184 | db->ratio = 0; | ||
185 | db->checkpoint = CHECK_GAP; | ||
186 | } | ||
187 | |||
188 | /* | ||
189 | * If the dictionary is full, then see if it is time to reset it. | ||
190 | * | ||
191 | * Compute the compression ratio using fixed-point arithmetic | ||
192 | * with 8 fractional bits. | ||
193 | * | ||
194 | * Since we have an infinite stream instead of a single file, | ||
195 | * watch only the local compression ratio. | ||
196 | * | ||
197 | * Since both peers must reset the dictionary at the same time even in | ||
198 | * the absence of CLEAR codes (while packets are incompressible), they | ||
199 | * must compute the same ratio. | ||
200 | */ | ||
201 | static int bsd_check (struct bsd_db *db) /* 1=output CLEAR */ | ||
202 | { | ||
203 | unsigned int new_ratio; | ||
204 | |||
205 | if (db->in_count >= db->checkpoint) | ||
206 | { | ||
207 | /* age the ratio by limiting the size of the counts */ | ||
208 | if (db->in_count >= RATIO_MAX || db->bytes_out >= RATIO_MAX) | ||
209 | { | ||
210 | db->in_count -= (db->in_count >> 2); | ||
211 | db->bytes_out -= (db->bytes_out >> 2); | ||
212 | } | ||
213 | |||
214 | db->checkpoint = db->in_count + CHECK_GAP; | ||
215 | |||
216 | if (db->max_ent >= db->maxmaxcode) | ||
217 | { | ||
218 | /* Reset the dictionary only if the ratio is worse, | ||
219 | * or if it looks as if it has been poisoned | ||
220 | * by incompressible data. | ||
221 | * | ||
222 | * This does not overflow, because | ||
223 | * db->in_count <= RATIO_MAX. | ||
224 | */ | ||
225 | |||
226 | new_ratio = db->in_count << RATIO_SCALE_LOG; | ||
227 | if (db->bytes_out != 0) | ||
228 | { | ||
229 | new_ratio /= db->bytes_out; | ||
230 | } | ||
231 | |||
232 | if (new_ratio < db->ratio || new_ratio < 1 * RATIO_SCALE) | ||
233 | { | ||
234 | bsd_clear (db); | ||
235 | return 1; | ||
236 | } | ||
237 | db->ratio = new_ratio; | ||
238 | } | ||
239 | } | ||
240 | return 0; | ||
241 | } | ||
242 | |||
243 | /* | ||
244 | * Return statistics. | ||
245 | */ | ||
246 | |||
247 | static void bsd_stats (void *state, struct compstat *stats) | ||
248 | { | ||
249 | struct bsd_db *db = (struct bsd_db *) state; | ||
250 | |||
251 | stats->unc_bytes = db->uncomp_bytes; | ||
252 | stats->unc_packets = db->uncomp_count; | ||
253 | stats->comp_bytes = db->comp_bytes; | ||
254 | stats->comp_packets = db->comp_count; | ||
255 | stats->inc_bytes = db->incomp_bytes; | ||
256 | stats->inc_packets = db->incomp_count; | ||
257 | stats->in_count = db->in_count; | ||
258 | stats->bytes_out = db->bytes_out; | ||
259 | } | ||
260 | |||
261 | /* | ||
262 | * Reset state, as on a CCP ResetReq. | ||
263 | */ | ||
264 | static void bsd_reset (void *state,unsigned char code, unsigned char id, | ||
265 | unsigned char *data, unsigned len, | ||
266 | struct isdn_ppp_resetparams *rsparm) | ||
267 | { | ||
268 | struct bsd_db *db = (struct bsd_db *) state; | ||
269 | |||
270 | bsd_clear(db); | ||
271 | db->seqno = 0; | ||
272 | db->clear_count = 0; | ||
273 | } | ||
274 | |||
275 | /* | ||
276 | * Release the compression structure | ||
277 | */ | ||
278 | static void bsd_free (void *state) | ||
279 | { | ||
280 | struct bsd_db *db = (struct bsd_db *) state; | ||
281 | |||
282 | if (db) { | ||
283 | /* | ||
284 | * Release the dictionary | ||
285 | */ | ||
286 | if (db->dict) { | ||
287 | vfree (db->dict); | ||
288 | db->dict = NULL; | ||
289 | } | ||
290 | |||
291 | /* | ||
292 | * Release the string buffer | ||
293 | */ | ||
294 | if (db->lens) { | ||
295 | vfree (db->lens); | ||
296 | db->lens = NULL; | ||
297 | } | ||
298 | |||
299 | /* | ||
300 | * Finally release the structure itself. | ||
301 | */ | ||
302 | kfree (db); | ||
303 | } | ||
304 | } | ||
305 | |||
306 | |||
307 | /* | ||
308 | * Allocate space for a (de) compressor. | ||
309 | */ | ||
310 | static void *bsd_alloc (struct isdn_ppp_comp_data *data) | ||
311 | { | ||
312 | int bits; | ||
313 | unsigned int hsize, hshift, maxmaxcode; | ||
314 | struct bsd_db *db; | ||
315 | int decomp; | ||
316 | |||
317 | static unsigned int htab[][2] = { | ||
318 | { 5003 , 4 } , { 5003 , 4 } , { 5003 , 4 } , { 5003 , 4 } , | ||
319 | { 9001 , 5 } , { 18013 , 6 } , { 35023 , 7 } , { 69001 , 8 } | ||
320 | }; | ||
321 | |||
322 | if (data->optlen != 1 || data->num != CI_BSD_COMPRESS | ||
323 | || BSD_VERSION(data->options[0]) != BSD_CURRENT_VERSION) | ||
324 | return NULL; | ||
325 | |||
326 | bits = BSD_NBITS(data->options[0]); | ||
327 | |||
328 | if(bits < 9 || bits > 15) | ||
329 | return NULL; | ||
330 | |||
331 | hsize = htab[bits-9][0]; | ||
332 | hshift = htab[bits-9][1]; | ||
333 | |||
334 | /* | ||
335 | * Allocate the main control structure for this instance. | ||
336 | */ | ||
337 | maxmaxcode = MAXCODE(bits); | ||
338 | db = (struct bsd_db *) kmalloc (sizeof (struct bsd_db),GFP_KERNEL); | ||
339 | if (!db) | ||
340 | return NULL; | ||
341 | |||
342 | memset (db, 0, sizeof(struct bsd_db)); | ||
343 | |||
344 | db->xmit = data->flags & IPPP_COMP_FLAG_XMIT; | ||
345 | decomp = db->xmit ? 0 : 1; | ||
346 | |||
347 | /* | ||
348 | * Allocate space for the dictionary. This may be more than one page in | ||
349 | * length. | ||
350 | */ | ||
351 | db->dict = (struct bsd_dict *) vmalloc (hsize * sizeof (struct bsd_dict)); | ||
352 | if (!db->dict) { | ||
353 | bsd_free (db); | ||
354 | return NULL; | ||
355 | } | ||
356 | |||
357 | /* | ||
358 | * If this is the compression buffer then there is no length data. | ||
359 | * For decompression, the length information is needed as well. | ||
360 | */ | ||
361 | if (!decomp) | ||
362 | db->lens = NULL; | ||
363 | else { | ||
364 | db->lens = (unsigned short *) vmalloc ((maxmaxcode + 1) * | ||
365 | sizeof (db->lens[0])); | ||
366 | if (!db->lens) { | ||
367 | bsd_free (db); | ||
368 | return (NULL); | ||
369 | } | ||
370 | } | ||
371 | |||
372 | /* | ||
373 | * Initialize the data information for the compression code | ||
374 | */ | ||
375 | db->totlen = sizeof (struct bsd_db) + (sizeof (struct bsd_dict) * hsize); | ||
376 | db->hsize = hsize; | ||
377 | db->hshift = hshift; | ||
378 | db->maxmaxcode = maxmaxcode; | ||
379 | db->maxbits = bits; | ||
380 | |||
381 | return (void *) db; | ||
382 | } | ||
383 | |||
384 | /* | ||
385 | * Initialize the database. | ||
386 | */ | ||
387 | static int bsd_init (void *state, struct isdn_ppp_comp_data *data, int unit, int debug) | ||
388 | { | ||
389 | struct bsd_db *db = state; | ||
390 | int indx; | ||
391 | int decomp; | ||
392 | |||
393 | if(!state || !data) { | ||
394 | printk(KERN_ERR "isdn_bsd_init: [%d] ERR, state %lx data %lx\n",unit,(long)state,(long)data); | ||
395 | return 0; | ||
396 | } | ||
397 | |||
398 | decomp = db->xmit ? 0 : 1; | ||
399 | |||
400 | if (data->optlen != 1 || data->num != CI_BSD_COMPRESS | ||
401 | || (BSD_VERSION(data->options[0]) != BSD_CURRENT_VERSION) | ||
402 | || (BSD_NBITS(data->options[0]) != db->maxbits) | ||
403 | || (decomp && db->lens == NULL)) { | ||
404 | printk(KERN_ERR "isdn_bsd: %d %d %d %d %lx\n",data->optlen,data->num,data->options[0],decomp,(unsigned long)db->lens); | ||
405 | return 0; | ||
406 | } | ||
407 | |||
408 | if (decomp) | ||
409 | for(indx=LAST;indx>=0;indx--) | ||
410 | db->lens[indx] = 1; | ||
411 | |||
412 | indx = db->hsize; | ||
413 | while (indx-- != 0) { | ||
414 | db->dict[indx].codem1 = BADCODEM1; | ||
415 | db->dict[indx].cptr = 0; | ||
416 | } | ||
417 | |||
418 | db->unit = unit; | ||
419 | db->mru = 0; | ||
420 | |||
421 | db->debug = 1; | ||
422 | |||
423 | bsd_reset(db,0,0,NULL,0,NULL); | ||
424 | |||
425 | return 1; | ||
426 | } | ||
427 | |||
428 | /* | ||
429 | * Obtain pointers to the various structures in the compression tables | ||
430 | */ | ||
431 | |||
432 | #define dict_ptrx(p,idx) &(p->dict[idx]) | ||
433 | #define lens_ptrx(p,idx) &(p->lens[idx]) | ||
434 | |||
435 | #ifdef DEBUG | ||
436 | static unsigned short *lens_ptr(struct bsd_db *db, int idx) | ||
437 | { | ||
438 | if ((unsigned int) idx > (unsigned int) db->maxmaxcode) { | ||
439 | printk (KERN_DEBUG "<9>ppp: lens_ptr(%d) > max\n", idx); | ||
440 | idx = 0; | ||
441 | } | ||
442 | return lens_ptrx (db, idx); | ||
443 | } | ||
444 | |||
445 | static struct bsd_dict *dict_ptr(struct bsd_db *db, int idx) | ||
446 | { | ||
447 | if ((unsigned int) idx >= (unsigned int) db->hsize) { | ||
448 | printk (KERN_DEBUG "<9>ppp: dict_ptr(%d) > max\n", idx); | ||
449 | idx = 0; | ||
450 | } | ||
451 | return dict_ptrx (db, idx); | ||
452 | } | ||
453 | |||
454 | #else | ||
455 | #define lens_ptr(db,idx) lens_ptrx(db,idx) | ||
456 | #define dict_ptr(db,idx) dict_ptrx(db,idx) | ||
457 | #endif | ||
458 | |||
459 | /* | ||
460 | * compress a packet | ||
461 | */ | ||
462 | static int bsd_compress (void *state, struct sk_buff *skb_in, struct sk_buff *skb_out,int proto) | ||
463 | { | ||
464 | struct bsd_db *db; | ||
465 | int hshift; | ||
466 | unsigned int max_ent; | ||
467 | unsigned int n_bits; | ||
468 | unsigned int bitno; | ||
469 | unsigned long accm; | ||
470 | int ent; | ||
471 | unsigned long fcode; | ||
472 | struct bsd_dict *dictp; | ||
473 | unsigned char c; | ||
474 | int hval,disp,ilen,mxcode; | ||
475 | unsigned char *rptr = skb_in->data; | ||
476 | int isize = skb_in->len; | ||
477 | |||
478 | #define OUTPUT(ent) \ | ||
479 | { \ | ||
480 | bitno -= n_bits; \ | ||
481 | accm |= ((ent) << bitno); \ | ||
482 | do { \ | ||
483 | if(skb_out && skb_tailroom(skb_out) > 0) \ | ||
484 | *(skb_put(skb_out,1)) = (unsigned char) (accm>>24); \ | ||
485 | accm <<= 8; \ | ||
486 | bitno += 8; \ | ||
487 | } while (bitno <= 24); \ | ||
488 | } | ||
489 | |||
490 | /* | ||
491 | * If the protocol is not in the range we're interested in, | ||
492 | * just return without compressing the packet. If it is, | ||
493 | * the protocol becomes the first byte to compress. | ||
494 | */ | ||
495 | printk(KERN_DEBUG "bsd_compress called with %x\n",proto); | ||
496 | |||
497 | ent = proto; | ||
498 | if (proto < 0x21 || proto > 0xf9 || !(proto & 0x1) ) | ||
499 | return 0; | ||
500 | |||
501 | db = (struct bsd_db *) state; | ||
502 | hshift = db->hshift; | ||
503 | max_ent = db->max_ent; | ||
504 | n_bits = db->n_bits; | ||
505 | bitno = 32; | ||
506 | accm = 0; | ||
507 | mxcode = MAXCODE (n_bits); | ||
508 | |||
509 | /* This is the PPP header information */ | ||
510 | if(skb_out && skb_tailroom(skb_out) >= 2) { | ||
511 | char *v = skb_put(skb_out,2); | ||
512 | /* we only push our own data on the header, | ||
513 | AC,PC and protos is pushed by caller */ | ||
514 | v[0] = db->seqno >> 8; | ||
515 | v[1] = db->seqno; | ||
516 | } | ||
517 | |||
518 | ilen = ++isize; /* This is off by one, but that is what is in draft! */ | ||
519 | |||
520 | while (--ilen > 0) { | ||
521 | c = *rptr++; | ||
522 | fcode = BSD_KEY (ent, c); | ||
523 | hval = BSD_HASH (ent, c, hshift); | ||
524 | dictp = dict_ptr (db, hval); | ||
525 | |||
526 | /* Validate and then check the entry. */ | ||
527 | if (dictp->codem1 >= max_ent) | ||
528 | goto nomatch; | ||
529 | |||
530 | if (dictp->fcode == fcode) { | ||
531 | ent = dictp->codem1 + 1; | ||
532 | continue; /* found (prefix,suffix) */ | ||
533 | } | ||
534 | |||
535 | /* continue probing until a match or invalid entry */ | ||
536 | disp = (hval == 0) ? 1 : hval; | ||
537 | |||
538 | do { | ||
539 | hval += disp; | ||
540 | if (hval >= db->hsize) | ||
541 | hval -= db->hsize; | ||
542 | dictp = dict_ptr (db, hval); | ||
543 | if (dictp->codem1 >= max_ent) | ||
544 | goto nomatch; | ||
545 | } while (dictp->fcode != fcode); | ||
546 | |||
547 | ent = dictp->codem1 + 1; /* finally found (prefix,suffix) */ | ||
548 | continue; | ||
549 | |||
550 | nomatch: | ||
551 | OUTPUT(ent); /* output the prefix */ | ||
552 | |||
553 | /* code -> hashtable */ | ||
554 | if (max_ent < db->maxmaxcode) { | ||
555 | struct bsd_dict *dictp2; | ||
556 | struct bsd_dict *dictp3; | ||
557 | int indx; | ||
558 | |||
559 | /* expand code size if needed */ | ||
560 | if (max_ent >= mxcode) { | ||
561 | db->n_bits = ++n_bits; | ||
562 | mxcode = MAXCODE (n_bits); | ||
563 | } | ||
564 | |||
565 | /* | ||
566 | * Invalidate old hash table entry using | ||
567 | * this code, and then take it over. | ||
568 | */ | ||
569 | dictp2 = dict_ptr (db, max_ent + 1); | ||
570 | indx = dictp2->cptr; | ||
571 | dictp3 = dict_ptr (db, indx); | ||
572 | |||
573 | if (dictp3->codem1 == max_ent) | ||
574 | dictp3->codem1 = BADCODEM1; | ||
575 | |||
576 | dictp2->cptr = hval; | ||
577 | dictp->codem1 = max_ent; | ||
578 | dictp->fcode = fcode; | ||
579 | db->max_ent = ++max_ent; | ||
580 | |||
581 | if (db->lens) { | ||
582 | unsigned short *len1 = lens_ptr (db, max_ent); | ||
583 | unsigned short *len2 = lens_ptr (db, ent); | ||
584 | *len1 = *len2 + 1; | ||
585 | } | ||
586 | } | ||
587 | ent = c; | ||
588 | } | ||
589 | |||
590 | OUTPUT(ent); /* output the last code */ | ||
591 | |||
592 | if(skb_out) | ||
593 | db->bytes_out += skb_out->len; /* Do not count bytes from here */ | ||
594 | db->uncomp_bytes += isize; | ||
595 | db->in_count += isize; | ||
596 | ++db->uncomp_count; | ||
597 | ++db->seqno; | ||
598 | |||
599 | if (bitno < 32) | ||
600 | ++db->bytes_out; /* must be set before calling bsd_check */ | ||
601 | |||
602 | /* | ||
603 | * Generate the clear command if needed | ||
604 | */ | ||
605 | |||
606 | if (bsd_check(db)) | ||
607 | OUTPUT (CLEAR); | ||
608 | |||
609 | /* | ||
610 | * Pad dribble bits of last code with ones. | ||
611 | * Do not emit a completely useless byte of ones. | ||
612 | */ | ||
613 | if (bitno < 32 && skb_out && skb_tailroom(skb_out) > 0) | ||
614 | *(skb_put(skb_out,1)) = (unsigned char) ((accm | (0xff << (bitno-8))) >> 24); | ||
615 | |||
616 | /* | ||
617 | * Increase code size if we would have without the packet | ||
618 | * boundary because the decompressor will do so. | ||
619 | */ | ||
620 | if (max_ent >= mxcode && max_ent < db->maxmaxcode) | ||
621 | db->n_bits++; | ||
622 | |||
623 | /* If output length is too large then this is an incompressible frame. */ | ||
624 | if (!skb_out || (skb_out && skb_out->len >= skb_in->len) ) { | ||
625 | ++db->incomp_count; | ||
626 | db->incomp_bytes += isize; | ||
627 | return 0; | ||
628 | } | ||
629 | |||
630 | /* Count the number of compressed frames */ | ||
631 | ++db->comp_count; | ||
632 | db->comp_bytes += skb_out->len; | ||
633 | return skb_out->len; | ||
634 | |||
635 | #undef OUTPUT | ||
636 | } | ||
637 | |||
638 | /* | ||
639 | * Update the "BSD Compress" dictionary on the receiver for | ||
640 | * incompressible data by pretending to compress the incoming data. | ||
641 | */ | ||
642 | static void bsd_incomp (void *state, struct sk_buff *skb_in,int proto) | ||
643 | { | ||
644 | bsd_compress (state, skb_in, NULL, proto); | ||
645 | } | ||
646 | |||
647 | /* | ||
648 | * Decompress "BSD Compress". | ||
649 | */ | ||
650 | static int bsd_decompress (void *state, struct sk_buff *skb_in, struct sk_buff *skb_out, | ||
651 | struct isdn_ppp_resetparams *rsparm) | ||
652 | { | ||
653 | struct bsd_db *db; | ||
654 | unsigned int max_ent; | ||
655 | unsigned long accm; | ||
656 | unsigned int bitno; /* 1st valid bit in accm */ | ||
657 | unsigned int n_bits; | ||
658 | unsigned int tgtbitno; /* bitno when we have a code */ | ||
659 | struct bsd_dict *dictp; | ||
660 | int seq; | ||
661 | unsigned int incode; | ||
662 | unsigned int oldcode; | ||
663 | unsigned int finchar; | ||
664 | unsigned char *p,*ibuf; | ||
665 | int ilen; | ||
666 | int codelen; | ||
667 | int extra; | ||
668 | |||
669 | db = (struct bsd_db *) state; | ||
670 | max_ent = db->max_ent; | ||
671 | accm = 0; | ||
672 | bitno = 32; /* 1st valid bit in accm */ | ||
673 | n_bits = db->n_bits; | ||
674 | tgtbitno = 32 - n_bits; /* bitno when we have a code */ | ||
675 | |||
676 | printk(KERN_DEBUG "bsd_decompress called\n"); | ||
677 | |||
678 | if(!skb_in || !skb_out) { | ||
679 | printk(KERN_ERR "bsd_decompress called with NULL parameter\n"); | ||
680 | return DECOMP_ERROR; | ||
681 | } | ||
682 | |||
683 | /* | ||
684 | * Get the sequence number. | ||
685 | */ | ||
686 | if( (p = skb_pull(skb_in,2)) == NULL) { | ||
687 | return DECOMP_ERROR; | ||
688 | } | ||
689 | p-=2; | ||
690 | seq = (p[0] << 8) + p[1]; | ||
691 | ilen = skb_in->len; | ||
692 | ibuf = skb_in->data; | ||
693 | |||
694 | /* | ||
695 | * Check the sequence number and give up if it differs from | ||
696 | * the value we're expecting. | ||
697 | */ | ||
698 | if (seq != db->seqno) { | ||
699 | if (db->debug) { | ||
700 | printk(KERN_DEBUG "bsd_decomp%d: bad sequence # %d, expected %d\n", | ||
701 | db->unit, seq, db->seqno - 1); | ||
702 | } | ||
703 | return DECOMP_ERROR; | ||
704 | } | ||
705 | |||
706 | ++db->seqno; | ||
707 | db->bytes_out += ilen; | ||
708 | |||
709 | if(skb_tailroom(skb_out) > 0) | ||
710 | *(skb_put(skb_out,1)) = 0; | ||
711 | else | ||
712 | return DECOMP_ERR_NOMEM; | ||
713 | |||
714 | oldcode = CLEAR; | ||
715 | |||
716 | /* | ||
717 | * Keep the checkpoint correctly so that incompressible packets | ||
718 | * clear the dictionary at the proper times. | ||
719 | */ | ||
720 | |||
721 | for (;;) { | ||
722 | if (ilen-- <= 0) { | ||
723 | db->in_count += (skb_out->len - 1); /* don't count the header */ | ||
724 | break; | ||
725 | } | ||
726 | |||
727 | /* | ||
728 | * Accumulate bytes until we have a complete code. | ||
729 | * Then get the next code, relying on the 32-bit, | ||
730 | * unsigned accm to mask the result. | ||
731 | */ | ||
732 | |||
733 | bitno -= 8; | ||
734 | accm |= *ibuf++ << bitno; | ||
735 | if (tgtbitno < bitno) | ||
736 | continue; | ||
737 | |||
738 | incode = accm >> tgtbitno; | ||
739 | accm <<= n_bits; | ||
740 | bitno += n_bits; | ||
741 | |||
742 | /* | ||
743 | * The dictionary must only be cleared at the end of a packet. | ||
744 | */ | ||
745 | |||
746 | if (incode == CLEAR) { | ||
747 | if (ilen > 0) { | ||
748 | if (db->debug) | ||
749 | printk(KERN_DEBUG "bsd_decomp%d: bad CLEAR\n", db->unit); | ||
750 | return DECOMP_FATALERROR; /* probably a bug */ | ||
751 | } | ||
752 | bsd_clear(db); | ||
753 | break; | ||
754 | } | ||
755 | |||
756 | if ((incode > max_ent + 2) || (incode > db->maxmaxcode) | ||
757 | || (incode > max_ent && oldcode == CLEAR)) { | ||
758 | if (db->debug) { | ||
759 | printk(KERN_DEBUG "bsd_decomp%d: bad code 0x%x oldcode=0x%x ", | ||
760 | db->unit, incode, oldcode); | ||
761 | printk(KERN_DEBUG "max_ent=0x%x skb->Len=%d seqno=%d\n", | ||
762 | max_ent, skb_out->len, db->seqno); | ||
763 | } | ||
764 | return DECOMP_FATALERROR; /* probably a bug */ | ||
765 | } | ||
766 | |||
767 | /* Special case for KwKwK string. */ | ||
768 | if (incode > max_ent) { | ||
769 | finchar = oldcode; | ||
770 | extra = 1; | ||
771 | } else { | ||
772 | finchar = incode; | ||
773 | extra = 0; | ||
774 | } | ||
775 | |||
776 | codelen = *(lens_ptr (db, finchar)); | ||
777 | if( skb_tailroom(skb_out) < codelen + extra) { | ||
778 | if (db->debug) { | ||
779 | printk(KERN_DEBUG "bsd_decomp%d: ran out of mru\n", db->unit); | ||
780 | #ifdef DEBUG | ||
781 | printk(KERN_DEBUG " len=%d, finchar=0x%x, codelen=%d,skblen=%d\n", | ||
782 | ilen, finchar, codelen, skb_out->len); | ||
783 | #endif | ||
784 | } | ||
785 | return DECOMP_FATALERROR; | ||
786 | } | ||
787 | |||
788 | /* | ||
789 | * Decode this code and install it in the decompressed buffer. | ||
790 | */ | ||
791 | |||
792 | p = skb_put(skb_out,codelen); | ||
793 | p += codelen; | ||
794 | while (finchar > LAST) { | ||
795 | struct bsd_dict *dictp2 = dict_ptr (db, finchar); | ||
796 | |||
797 | dictp = dict_ptr (db, dictp2->cptr); | ||
798 | |||
799 | #ifdef DEBUG | ||
800 | if (--codelen <= 0 || dictp->codem1 != finchar-1) { | ||
801 | if (codelen <= 0) { | ||
802 | printk(KERN_ERR "bsd_decomp%d: fell off end of chain ", db->unit); | ||
803 | printk(KERN_ERR "0x%x at 0x%x by 0x%x, max_ent=0x%x\n", incode, finchar, dictp2->cptr, max_ent); | ||
804 | } else { | ||
805 | if (dictp->codem1 != finchar-1) { | ||
806 | printk(KERN_ERR "bsd_decomp%d: bad code chain 0x%x finchar=0x%x ",db->unit, incode, finchar); | ||
807 | printk(KERN_ERR "oldcode=0x%x cptr=0x%x codem1=0x%x\n", oldcode, dictp2->cptr, dictp->codem1); | ||
808 | } | ||
809 | } | ||
810 | return DECOMP_FATALERROR; | ||
811 | } | ||
812 | #endif | ||
813 | |||
814 | { | ||
815 | u32 fcode = dictp->fcode; | ||
816 | *--p = (fcode >> 16) & 0xff; | ||
817 | finchar = fcode & 0xffff; | ||
818 | } | ||
819 | } | ||
820 | *--p = finchar; | ||
821 | |||
822 | #ifdef DEBUG | ||
823 | if (--codelen != 0) | ||
824 | printk(KERN_ERR "bsd_decomp%d: short by %d after code 0x%x, max_ent=0x%x\n", db->unit, codelen, incode, max_ent); | ||
825 | #endif | ||
826 | |||
827 | if (extra) /* the KwKwK case again */ | ||
828 | *(skb_put(skb_out,1)) = finchar; | ||
829 | |||
830 | /* | ||
831 | * If not first code in a packet, and | ||
832 | * if not out of code space, then allocate a new code. | ||
833 | * | ||
834 | * Keep the hash table correct so it can be used | ||
835 | * with uncompressed packets. | ||
836 | */ | ||
837 | if (oldcode != CLEAR && max_ent < db->maxmaxcode) { | ||
838 | struct bsd_dict *dictp2, *dictp3; | ||
839 | u16 *lens1, *lens2; | ||
840 | unsigned long fcode; | ||
841 | int hval, disp, indx; | ||
842 | |||
843 | fcode = BSD_KEY(oldcode,finchar); | ||
844 | hval = BSD_HASH(oldcode,finchar,db->hshift); | ||
845 | dictp = dict_ptr (db, hval); | ||
846 | |||
847 | /* look for a free hash table entry */ | ||
848 | if (dictp->codem1 < max_ent) { | ||
849 | disp = (hval == 0) ? 1 : hval; | ||
850 | do { | ||
851 | hval += disp; | ||
852 | if (hval >= db->hsize) | ||
853 | hval -= db->hsize; | ||
854 | dictp = dict_ptr (db, hval); | ||
855 | } while (dictp->codem1 < max_ent); | ||
856 | } | ||
857 | |||
858 | /* | ||
859 | * Invalidate previous hash table entry | ||
860 | * assigned this code, and then take it over | ||
861 | */ | ||
862 | |||
863 | dictp2 = dict_ptr (db, max_ent + 1); | ||
864 | indx = dictp2->cptr; | ||
865 | dictp3 = dict_ptr (db, indx); | ||
866 | |||
867 | if (dictp3->codem1 == max_ent) | ||
868 | dictp3->codem1 = BADCODEM1; | ||
869 | |||
870 | dictp2->cptr = hval; | ||
871 | dictp->codem1 = max_ent; | ||
872 | dictp->fcode = fcode; | ||
873 | db->max_ent = ++max_ent; | ||
874 | |||
875 | /* Update the length of this string. */ | ||
876 | lens1 = lens_ptr (db, max_ent); | ||
877 | lens2 = lens_ptr (db, oldcode); | ||
878 | *lens1 = *lens2 + 1; | ||
879 | |||
880 | /* Expand code size if needed. */ | ||
881 | if (max_ent >= MAXCODE(n_bits) && max_ent < db->maxmaxcode) { | ||
882 | db->n_bits = ++n_bits; | ||
883 | tgtbitno = 32-n_bits; | ||
884 | } | ||
885 | } | ||
886 | oldcode = incode; | ||
887 | } | ||
888 | |||
889 | ++db->comp_count; | ||
890 | ++db->uncomp_count; | ||
891 | db->comp_bytes += skb_in->len - BSD_OVHD; | ||
892 | db->uncomp_bytes += skb_out->len; | ||
893 | |||
894 | if (bsd_check(db)) { | ||
895 | if (db->debug) | ||
896 | printk(KERN_DEBUG "bsd_decomp%d: peer should have cleared dictionary on %d\n", | ||
897 | db->unit, db->seqno - 1); | ||
898 | } | ||
899 | return skb_out->len; | ||
900 | } | ||
901 | |||
902 | /************************************************************* | ||
903 | * Table of addresses for the BSD compression module | ||
904 | *************************************************************/ | ||
905 | |||
906 | static struct isdn_ppp_compressor ippp_bsd_compress = { | ||
907 | .owner = THIS_MODULE, | ||
908 | .num = CI_BSD_COMPRESS, | ||
909 | .alloc = bsd_alloc, | ||
910 | .free = bsd_free, | ||
911 | .init = bsd_init, | ||
912 | .reset = bsd_reset, | ||
913 | .compress = bsd_compress, | ||
914 | .decompress = bsd_decompress, | ||
915 | .incomp = bsd_incomp, | ||
916 | .stat = bsd_stats, | ||
917 | }; | ||
918 | |||
919 | /************************************************************* | ||
920 | * Module support routines | ||
921 | *************************************************************/ | ||
922 | |||
923 | static int __init isdn_bsdcomp_init(void) | ||
924 | { | ||
925 | int answer = isdn_ppp_register_compressor (&ippp_bsd_compress); | ||
926 | if (answer == 0) | ||
927 | printk (KERN_INFO "PPP BSD Compression module registered\n"); | ||
928 | return answer; | ||
929 | } | ||
930 | |||
931 | static void __exit isdn_bsdcomp_exit(void) | ||
932 | { | ||
933 | isdn_ppp_unregister_compressor (&ippp_bsd_compress); | ||
934 | } | ||
935 | |||
936 | module_init(isdn_bsdcomp_init); | ||
937 | 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 000000000000..c406df6f268a --- /dev/null +++ b/drivers/isdn/i4l/isdn_common.c | |||
@@ -0,0 +1,2253 @@ | |||
1 | /* $Id: isdn_common.c,v 1.1.2.3 2004/02/10 01:07:13 keil Exp $ | ||
2 | * | ||
3 | * Linux ISDN subsystem, common used functions (linklevel). | ||
4 | * | ||
5 | * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de) | ||
6 | * Copyright 1995,96 Thinking Objects Software GmbH Wuerzburg | ||
7 | * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de) | ||
8 | * | ||
9 | * This software may be used and distributed according to the terms | ||
10 | * of the GNU General Public License, incorporated herein by reference. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <linux/config.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/init.h> | ||
17 | #include <linux/version.h> | ||
18 | #include <linux/poll.h> | ||
19 | #include <linux/vmalloc.h> | ||
20 | #include <linux/isdn.h> | ||
21 | #include <linux/smp_lock.h> | ||
22 | #include "isdn_common.h" | ||
23 | #include "isdn_tty.h" | ||
24 | #include "isdn_net.h" | ||
25 | #include "isdn_ppp.h" | ||
26 | #ifdef CONFIG_ISDN_AUDIO | ||
27 | #include "isdn_audio.h" | ||
28 | #endif | ||
29 | #ifdef CONFIG_ISDN_DIVERSION_MODULE | ||
30 | #define CONFIG_ISDN_DIVERSION | ||
31 | #endif | ||
32 | #ifdef CONFIG_ISDN_DIVERSION | ||
33 | #include <linux/isdn_divertif.h> | ||
34 | #endif /* CONFIG_ISDN_DIVERSION */ | ||
35 | #include "isdn_v110.h" | ||
36 | |||
37 | /* Debugflags */ | ||
38 | #undef ISDN_DEBUG_STATCALLB | ||
39 | |||
40 | MODULE_DESCRIPTION("ISDN4Linux: link layer"); | ||
41 | MODULE_AUTHOR("Fritz Elfert"); | ||
42 | MODULE_LICENSE("GPL"); | ||
43 | |||
44 | isdn_dev *dev; | ||
45 | |||
46 | static char *isdn_revision = "$Revision: 1.1.2.3 $"; | ||
47 | |||
48 | extern char *isdn_net_revision; | ||
49 | extern char *isdn_tty_revision; | ||
50 | #ifdef CONFIG_ISDN_PPP | ||
51 | extern char *isdn_ppp_revision; | ||
52 | #else | ||
53 | static char *isdn_ppp_revision = ": none $"; | ||
54 | #endif | ||
55 | #ifdef CONFIG_ISDN_AUDIO | ||
56 | extern char *isdn_audio_revision; | ||
57 | #else | ||
58 | static char *isdn_audio_revision = ": none $"; | ||
59 | #endif | ||
60 | extern char *isdn_v110_revision; | ||
61 | |||
62 | #ifdef CONFIG_ISDN_DIVERSION | ||
63 | static isdn_divert_if *divert_if; /* = NULL */ | ||
64 | #endif /* CONFIG_ISDN_DIVERSION */ | ||
65 | |||
66 | |||
67 | static int isdn_writebuf_stub(int, int, const u_char __user *, int); | ||
68 | static void set_global_features(void); | ||
69 | static int isdn_wildmat(char *s, char *p); | ||
70 | |||
71 | |||
72 | static inline void | ||
73 | isdn_lock_driver(isdn_driver_t *drv) | ||
74 | { | ||
75 | try_module_get(drv->interface->owner); | ||
76 | drv->locks++; | ||
77 | } | ||
78 | |||
79 | void | ||
80 | isdn_lock_drivers(void) | ||
81 | { | ||
82 | int i; | ||
83 | |||
84 | for (i = 0; i < ISDN_MAX_DRIVERS; i++) { | ||
85 | if (!dev->drv[i]) | ||
86 | continue; | ||
87 | isdn_lock_driver(dev->drv[i]); | ||
88 | } | ||
89 | } | ||
90 | |||
91 | static inline void | ||
92 | isdn_unlock_driver(isdn_driver_t *drv) | ||
93 | { | ||
94 | if (drv->locks > 0) { | ||
95 | drv->locks--; | ||
96 | module_put(drv->interface->owner); | ||
97 | } | ||
98 | } | ||
99 | |||
100 | void | ||
101 | isdn_unlock_drivers(void) | ||
102 | { | ||
103 | int i; | ||
104 | |||
105 | for (i = 0; i < ISDN_MAX_DRIVERS; i++) { | ||
106 | if (!dev->drv[i]) | ||
107 | continue; | ||
108 | isdn_unlock_driver(dev->drv[i]); | ||
109 | } | ||
110 | } | ||
111 | |||
112 | #if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP) | ||
113 | void | ||
114 | isdn_dumppkt(char *s, u_char * p, int len, int dumplen) | ||
115 | { | ||
116 | int dumpc; | ||
117 | |||
118 | printk(KERN_DEBUG "%s(%d) ", s, len); | ||
119 | for (dumpc = 0; (dumpc < dumplen) && (len); len--, dumpc++) | ||
120 | printk(" %02x", *p++); | ||
121 | printk("\n"); | ||
122 | } | ||
123 | #endif | ||
124 | |||
125 | /* | ||
126 | * I picked the pattern-matching-functions from an old GNU-tar version (1.10) | ||
127 | * It was originally written and put to PD by rs@mirror.TMC.COM (Rich Salz) | ||
128 | */ | ||
129 | static int | ||
130 | isdn_star(char *s, char *p) | ||
131 | { | ||
132 | while (isdn_wildmat(s, p)) { | ||
133 | if (*++s == '\0') | ||
134 | return (2); | ||
135 | } | ||
136 | return (0); | ||
137 | } | ||
138 | |||
139 | /* | ||
140 | * Shell-type Pattern-matching for incoming caller-Ids | ||
141 | * This function gets a string in s and checks, if it matches the pattern | ||
142 | * given in p. | ||
143 | * | ||
144 | * Return: | ||
145 | * 0 = match. | ||
146 | * 1 = no match. | ||
147 | * 2 = no match. Would eventually match, if s would be longer. | ||
148 | * | ||
149 | * Possible Patterns: | ||
150 | * | ||
151 | * '?' matches one character | ||
152 | * '*' matches zero or more characters | ||
153 | * [xyz] matches the set of characters in brackets. | ||
154 | * [^xyz] matches any single character not in the set of characters | ||
155 | */ | ||
156 | |||
157 | static int | ||
158 | isdn_wildmat(char *s, char *p) | ||
159 | { | ||
160 | register int last; | ||
161 | register int matched; | ||
162 | register int reverse; | ||
163 | register int nostar = 1; | ||
164 | |||
165 | if (!(*s) && !(*p)) | ||
166 | return(1); | ||
167 | for (; *p; s++, p++) | ||
168 | switch (*p) { | ||
169 | case '\\': | ||
170 | /* | ||
171 | * Literal match with following character, | ||
172 | * fall through. | ||
173 | */ | ||
174 | p++; | ||
175 | default: | ||
176 | if (*s != *p) | ||
177 | return (*s == '\0')?2:1; | ||
178 | continue; | ||
179 | case '?': | ||
180 | /* Match anything. */ | ||
181 | if (*s == '\0') | ||
182 | return (2); | ||
183 | continue; | ||
184 | case '*': | ||
185 | nostar = 0; | ||
186 | /* Trailing star matches everything. */ | ||
187 | return (*++p ? isdn_star(s, p) : 0); | ||
188 | case '[': | ||
189 | /* [^....] means inverse character class. */ | ||
190 | if ((reverse = (p[1] == '^'))) | ||
191 | p++; | ||
192 | for (last = 0, matched = 0; *++p && (*p != ']'); last = *p) | ||
193 | /* This next line requires a good C compiler. */ | ||
194 | if (*p == '-' ? *s <= *++p && *s >= last : *s == *p) | ||
195 | matched = 1; | ||
196 | if (matched == reverse) | ||
197 | return (1); | ||
198 | continue; | ||
199 | } | ||
200 | return (*s == '\0')?0:nostar; | ||
201 | } | ||
202 | |||
203 | int isdn_msncmp( const char * msn1, const char * msn2 ) | ||
204 | { | ||
205 | char TmpMsn1[ ISDN_MSNLEN ]; | ||
206 | char TmpMsn2[ ISDN_MSNLEN ]; | ||
207 | char *p; | ||
208 | |||
209 | for ( p = TmpMsn1; *msn1 && *msn1 != ':'; ) // Strip off a SPID | ||
210 | *p++ = *msn1++; | ||
211 | *p = '\0'; | ||
212 | |||
213 | for ( p = TmpMsn2; *msn2 && *msn2 != ':'; ) // Strip off a SPID | ||
214 | *p++ = *msn2++; | ||
215 | *p = '\0'; | ||
216 | |||
217 | return isdn_wildmat( TmpMsn1, TmpMsn2 ); | ||
218 | } | ||
219 | |||
220 | int | ||
221 | isdn_dc2minor(int di, int ch) | ||
222 | { | ||
223 | int i; | ||
224 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) | ||
225 | if (dev->chanmap[i] == ch && dev->drvmap[i] == di) | ||
226 | return i; | ||
227 | return -1; | ||
228 | } | ||
229 | |||
230 | static int isdn_timer_cnt1 = 0; | ||
231 | static int isdn_timer_cnt2 = 0; | ||
232 | static int isdn_timer_cnt3 = 0; | ||
233 | |||
234 | static void | ||
235 | isdn_timer_funct(ulong dummy) | ||
236 | { | ||
237 | int tf = dev->tflags; | ||
238 | if (tf & ISDN_TIMER_FAST) { | ||
239 | if (tf & ISDN_TIMER_MODEMREAD) | ||
240 | isdn_tty_readmodem(); | ||
241 | if (tf & ISDN_TIMER_MODEMPLUS) | ||
242 | isdn_tty_modem_escape(); | ||
243 | if (tf & ISDN_TIMER_MODEMXMIT) | ||
244 | isdn_tty_modem_xmit(); | ||
245 | } | ||
246 | if (tf & ISDN_TIMER_SLOW) { | ||
247 | if (++isdn_timer_cnt1 >= ISDN_TIMER_02SEC) { | ||
248 | isdn_timer_cnt1 = 0; | ||
249 | if (tf & ISDN_TIMER_NETDIAL) | ||
250 | isdn_net_dial(); | ||
251 | } | ||
252 | if (++isdn_timer_cnt2 >= ISDN_TIMER_1SEC) { | ||
253 | isdn_timer_cnt2 = 0; | ||
254 | if (tf & ISDN_TIMER_NETHANGUP) | ||
255 | isdn_net_autohup(); | ||
256 | if (++isdn_timer_cnt3 >= ISDN_TIMER_RINGING) { | ||
257 | isdn_timer_cnt3 = 0; | ||
258 | if (tf & ISDN_TIMER_MODEMRING) | ||
259 | isdn_tty_modem_ring(); | ||
260 | } | ||
261 | if (tf & ISDN_TIMER_CARRIER) | ||
262 | isdn_tty_carrier_timeout(); | ||
263 | } | ||
264 | } | ||
265 | if (tf) | ||
266 | mod_timer(&dev->timer, jiffies+ISDN_TIMER_RES); | ||
267 | } | ||
268 | |||
269 | void | ||
270 | isdn_timer_ctrl(int tf, int onoff) | ||
271 | { | ||
272 | unsigned long flags; | ||
273 | int old_tflags; | ||
274 | |||
275 | spin_lock_irqsave(&dev->timerlock, flags); | ||
276 | if ((tf & ISDN_TIMER_SLOW) && (!(dev->tflags & ISDN_TIMER_SLOW))) { | ||
277 | /* If the slow-timer wasn't activated until now */ | ||
278 | isdn_timer_cnt1 = 0; | ||
279 | isdn_timer_cnt2 = 0; | ||
280 | } | ||
281 | old_tflags = dev->tflags; | ||
282 | if (onoff) | ||
283 | dev->tflags |= tf; | ||
284 | else | ||
285 | dev->tflags &= ~tf; | ||
286 | if (dev->tflags && !old_tflags) | ||
287 | mod_timer(&dev->timer, jiffies+ISDN_TIMER_RES); | ||
288 | spin_unlock_irqrestore(&dev->timerlock, flags); | ||
289 | } | ||
290 | |||
291 | /* | ||
292 | * Receive a packet from B-Channel. (Called from low-level-module) | ||
293 | */ | ||
294 | static void | ||
295 | isdn_receive_skb_callback(int di, int channel, struct sk_buff *skb) | ||
296 | { | ||
297 | int i; | ||
298 | |||
299 | if ((i = isdn_dc2minor(di, channel)) == -1) { | ||
300 | dev_kfree_skb(skb); | ||
301 | return; | ||
302 | } | ||
303 | /* Update statistics */ | ||
304 | dev->ibytes[i] += skb->len; | ||
305 | |||
306 | /* First, try to deliver data to network-device */ | ||
307 | if (isdn_net_rcv_skb(i, skb)) | ||
308 | return; | ||
309 | |||
310 | /* V.110 handling | ||
311 | * makes sense for async streams only, so it is | ||
312 | * called after possible net-device delivery. | ||
313 | */ | ||
314 | if (dev->v110[i]) { | ||
315 | atomic_inc(&dev->v110use[i]); | ||
316 | skb = isdn_v110_decode(dev->v110[i], skb); | ||
317 | atomic_dec(&dev->v110use[i]); | ||
318 | if (!skb) | ||
319 | return; | ||
320 | } | ||
321 | |||
322 | /* No network-device found, deliver to tty or raw-channel */ | ||
323 | if (skb->len) { | ||
324 | if (isdn_tty_rcv_skb(i, di, channel, skb)) | ||
325 | return; | ||
326 | wake_up_interruptible(&dev->drv[di]->rcv_waitq[channel]); | ||
327 | } else | ||
328 | dev_kfree_skb(skb); | ||
329 | } | ||
330 | |||
331 | /* | ||
332 | * Intercept command from Linklevel to Lowlevel. | ||
333 | * If layer 2 protocol is V.110 and this is not supported by current | ||
334 | * lowlevel-driver, use driver's transparent mode and handle V.110 in | ||
335 | * linklevel instead. | ||
336 | */ | ||
337 | int | ||
338 | isdn_command(isdn_ctrl *cmd) | ||
339 | { | ||
340 | if (cmd->driver == -1) { | ||
341 | printk(KERN_WARNING "isdn_command command(%x) driver -1\n", cmd->command); | ||
342 | return(1); | ||
343 | } | ||
344 | if (cmd->command == ISDN_CMD_SETL2) { | ||
345 | int idx = isdn_dc2minor(cmd->driver, cmd->arg & 255); | ||
346 | unsigned long l2prot = (cmd->arg >> 8) & 255; | ||
347 | unsigned long features = (dev->drv[cmd->driver]->interface->features | ||
348 | >> ISDN_FEATURE_L2_SHIFT) & | ||
349 | ISDN_FEATURE_L2_MASK; | ||
350 | unsigned long l2_feature = (1 << l2prot); | ||
351 | |||
352 | switch (l2prot) { | ||
353 | case ISDN_PROTO_L2_V11096: | ||
354 | case ISDN_PROTO_L2_V11019: | ||
355 | case ISDN_PROTO_L2_V11038: | ||
356 | /* If V.110 requested, but not supported by | ||
357 | * HL-driver, set emulator-flag and change | ||
358 | * Layer-2 to transparent | ||
359 | */ | ||
360 | if (!(features & l2_feature)) { | ||
361 | dev->v110emu[idx] = l2prot; | ||
362 | cmd->arg = (cmd->arg & 255) | | ||
363 | (ISDN_PROTO_L2_TRANS << 8); | ||
364 | } else | ||
365 | dev->v110emu[idx] = 0; | ||
366 | } | ||
367 | } | ||
368 | return dev->drv[cmd->driver]->interface->command(cmd); | ||
369 | } | ||
370 | |||
371 | void | ||
372 | isdn_all_eaz(int di, int ch) | ||
373 | { | ||
374 | isdn_ctrl cmd; | ||
375 | |||
376 | if (di < 0) | ||
377 | return; | ||
378 | cmd.driver = di; | ||
379 | cmd.arg = ch; | ||
380 | cmd.command = ISDN_CMD_SETEAZ; | ||
381 | cmd.parm.num[0] = '\0'; | ||
382 | isdn_command(&cmd); | ||
383 | } | ||
384 | |||
385 | /* | ||
386 | * Begin of a CAPI like LL<->HL interface, currently used only for | ||
387 | * supplementary service (CAPI 2.0 part III) | ||
388 | */ | ||
389 | #include <linux/isdn/capicmd.h> | ||
390 | |||
391 | int | ||
392 | isdn_capi_rec_hl_msg(capi_msg *cm) { | ||
393 | |||
394 | int di; | ||
395 | int ch; | ||
396 | |||
397 | di = (cm->adr.Controller & 0x7f) -1; | ||
398 | ch = isdn_dc2minor(di, (cm->adr.Controller>>8)& 0x7f); | ||
399 | switch(cm->Command) { | ||
400 | case CAPI_FACILITY: | ||
401 | /* in the moment only handled in tty */ | ||
402 | return(isdn_tty_capi_facility(cm)); | ||
403 | default: | ||
404 | return(-1); | ||
405 | } | ||
406 | } | ||
407 | |||
408 | static int | ||
409 | isdn_status_callback(isdn_ctrl * c) | ||
410 | { | ||
411 | int di; | ||
412 | u_long flags; | ||
413 | int i; | ||
414 | int r; | ||
415 | int retval = 0; | ||
416 | isdn_ctrl cmd; | ||
417 | isdn_net_dev *p; | ||
418 | |||
419 | di = c->driver; | ||
420 | i = isdn_dc2minor(di, c->arg); | ||
421 | switch (c->command) { | ||
422 | case ISDN_STAT_BSENT: | ||
423 | if (i < 0) | ||
424 | return -1; | ||
425 | if (dev->global_flags & ISDN_GLOBAL_STOPPED) | ||
426 | return 0; | ||
427 | if (isdn_net_stat_callback(i, c)) | ||
428 | return 0; | ||
429 | if (isdn_v110_stat_callback(i, c)) | ||
430 | return 0; | ||
431 | if (isdn_tty_stat_callback(i, c)) | ||
432 | return 0; | ||
433 | wake_up_interruptible(&dev->drv[di]->snd_waitq[c->arg]); | ||
434 | break; | ||
435 | case ISDN_STAT_STAVAIL: | ||
436 | dev->drv[di]->stavail += c->arg; | ||
437 | wake_up_interruptible(&dev->drv[di]->st_waitq); | ||
438 | break; | ||
439 | case ISDN_STAT_RUN: | ||
440 | dev->drv[di]->flags |= DRV_FLAG_RUNNING; | ||
441 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) | ||
442 | if (dev->drvmap[i] == di) | ||
443 | isdn_all_eaz(di, dev->chanmap[i]); | ||
444 | set_global_features(); | ||
445 | break; | ||
446 | case ISDN_STAT_STOP: | ||
447 | dev->drv[di]->flags &= ~DRV_FLAG_RUNNING; | ||
448 | break; | ||
449 | case ISDN_STAT_ICALL: | ||
450 | if (i < 0) | ||
451 | return -1; | ||
452 | #ifdef ISDN_DEBUG_STATCALLB | ||
453 | printk(KERN_DEBUG "ICALL (net): %d %ld %s\n", di, c->arg, c->parm.num); | ||
454 | #endif | ||
455 | if (dev->global_flags & ISDN_GLOBAL_STOPPED) { | ||
456 | cmd.driver = di; | ||
457 | cmd.arg = c->arg; | ||
458 | cmd.command = ISDN_CMD_HANGUP; | ||
459 | isdn_command(&cmd); | ||
460 | return 0; | ||
461 | } | ||
462 | /* Try to find a network-interface which will accept incoming call */ | ||
463 | r = ((c->command == ISDN_STAT_ICALLW) ? 0 : isdn_net_find_icall(di, c->arg, i, &c->parm.setup)); | ||
464 | switch (r) { | ||
465 | case 0: | ||
466 | /* No network-device replies. | ||
467 | * Try ttyI's. | ||
468 | * These return 0 on no match, 1 on match and | ||
469 | * 3 on eventually match, if CID is longer. | ||
470 | */ | ||
471 | if (c->command == ISDN_STAT_ICALL) | ||
472 | if ((retval = isdn_tty_find_icall(di, c->arg, &c->parm.setup))) return(retval); | ||
473 | #ifdef CONFIG_ISDN_DIVERSION | ||
474 | if (divert_if) | ||
475 | if ((retval = divert_if->stat_callback(c))) | ||
476 | return(retval); /* processed */ | ||
477 | #endif /* CONFIG_ISDN_DIVERSION */ | ||
478 | if ((!retval) && (dev->drv[di]->flags & DRV_FLAG_REJBUS)) { | ||
479 | /* No tty responding */ | ||
480 | cmd.driver = di; | ||
481 | cmd.arg = c->arg; | ||
482 | cmd.command = ISDN_CMD_HANGUP; | ||
483 | isdn_command(&cmd); | ||
484 | retval = 2; | ||
485 | } | ||
486 | break; | ||
487 | case 1: | ||
488 | /* Schedule connection-setup */ | ||
489 | isdn_net_dial(); | ||
490 | cmd.driver = di; | ||
491 | cmd.arg = c->arg; | ||
492 | cmd.command = ISDN_CMD_ACCEPTD; | ||
493 | for ( p = dev->netdev; p; p = p->next ) | ||
494 | if ( p->local->isdn_channel == cmd.arg ) | ||
495 | { | ||
496 | strcpy( cmd.parm.setup.eazmsn, p->local->msn ); | ||
497 | isdn_command(&cmd); | ||
498 | retval = 1; | ||
499 | break; | ||
500 | } | ||
501 | break; | ||
502 | |||
503 | case 2: /* For calling back, first reject incoming call ... */ | ||
504 | case 3: /* Interface found, but down, reject call actively */ | ||
505 | retval = 2; | ||
506 | printk(KERN_INFO "isdn: Rejecting Call\n"); | ||
507 | cmd.driver = di; | ||
508 | cmd.arg = c->arg; | ||
509 | cmd.command = ISDN_CMD_HANGUP; | ||
510 | isdn_command(&cmd); | ||
511 | if (r == 3) | ||
512 | break; | ||
513 | /* Fall through */ | ||
514 | case 4: | ||
515 | /* ... then start callback. */ | ||
516 | isdn_net_dial(); | ||
517 | break; | ||
518 | case 5: | ||
519 | /* Number would eventually match, if longer */ | ||
520 | retval = 3; | ||
521 | break; | ||
522 | } | ||
523 | #ifdef ISDN_DEBUG_STATCALLB | ||
524 | printk(KERN_DEBUG "ICALL: ret=%d\n", retval); | ||
525 | #endif | ||
526 | return retval; | ||
527 | break; | ||
528 | case ISDN_STAT_CINF: | ||
529 | if (i < 0) | ||
530 | return -1; | ||
531 | #ifdef ISDN_DEBUG_STATCALLB | ||
532 | printk(KERN_DEBUG "CINF: %ld %s\n", c->arg, c->parm.num); | ||
533 | #endif | ||
534 | if (dev->global_flags & ISDN_GLOBAL_STOPPED) | ||
535 | return 0; | ||
536 | if (strcmp(c->parm.num, "0")) | ||
537 | isdn_net_stat_callback(i, c); | ||
538 | isdn_tty_stat_callback(i, c); | ||
539 | break; | ||
540 | case ISDN_STAT_CAUSE: | ||
541 | #ifdef ISDN_DEBUG_STATCALLB | ||
542 | printk(KERN_DEBUG "CAUSE: %ld %s\n", c->arg, c->parm.num); | ||
543 | #endif | ||
544 | printk(KERN_INFO "isdn: %s,ch%ld cause: %s\n", | ||
545 | dev->drvid[di], c->arg, c->parm.num); | ||
546 | isdn_tty_stat_callback(i, c); | ||
547 | #ifdef CONFIG_ISDN_DIVERSION | ||
548 | if (divert_if) | ||
549 | divert_if->stat_callback(c); | ||
550 | #endif /* CONFIG_ISDN_DIVERSION */ | ||
551 | break; | ||
552 | case ISDN_STAT_DISPLAY: | ||
553 | #ifdef ISDN_DEBUG_STATCALLB | ||
554 | printk(KERN_DEBUG "DISPLAY: %ld %s\n", c->arg, c->parm.display); | ||
555 | #endif | ||
556 | isdn_tty_stat_callback(i, c); | ||
557 | #ifdef CONFIG_ISDN_DIVERSION | ||
558 | if (divert_if) | ||
559 | divert_if->stat_callback(c); | ||
560 | #endif /* CONFIG_ISDN_DIVERSION */ | ||
561 | break; | ||
562 | case ISDN_STAT_DCONN: | ||
563 | if (i < 0) | ||
564 | return -1; | ||
565 | #ifdef ISDN_DEBUG_STATCALLB | ||
566 | printk(KERN_DEBUG "DCONN: %ld\n", c->arg); | ||
567 | #endif | ||
568 | if (dev->global_flags & ISDN_GLOBAL_STOPPED) | ||
569 | return 0; | ||
570 | /* Find any net-device, waiting for D-channel setup */ | ||
571 | if (isdn_net_stat_callback(i, c)) | ||
572 | break; | ||
573 | isdn_v110_stat_callback(i, c); | ||
574 | /* Find any ttyI, waiting for D-channel setup */ | ||
575 | if (isdn_tty_stat_callback(i, c)) { | ||
576 | cmd.driver = di; | ||
577 | cmd.arg = c->arg; | ||
578 | cmd.command = ISDN_CMD_ACCEPTB; | ||
579 | isdn_command(&cmd); | ||
580 | break; | ||
581 | } | ||
582 | break; | ||
583 | case ISDN_STAT_DHUP: | ||
584 | if (i < 0) | ||
585 | return -1; | ||
586 | #ifdef ISDN_DEBUG_STATCALLB | ||
587 | printk(KERN_DEBUG "DHUP: %ld\n", c->arg); | ||
588 | #endif | ||
589 | if (dev->global_flags & ISDN_GLOBAL_STOPPED) | ||
590 | return 0; | ||
591 | dev->drv[di]->online &= ~(1 << (c->arg)); | ||
592 | isdn_info_update(); | ||
593 | /* Signal hangup to network-devices */ | ||
594 | if (isdn_net_stat_callback(i, c)) | ||
595 | break; | ||
596 | isdn_v110_stat_callback(i, c); | ||
597 | if (isdn_tty_stat_callback(i, c)) | ||
598 | break; | ||
599 | #ifdef CONFIG_ISDN_DIVERSION | ||
600 | if (divert_if) | ||
601 | divert_if->stat_callback(c); | ||
602 | #endif /* CONFIG_ISDN_DIVERSION */ | ||
603 | break; | ||
604 | break; | ||
605 | case ISDN_STAT_BCONN: | ||
606 | if (i < 0) | ||
607 | return -1; | ||
608 | #ifdef ISDN_DEBUG_STATCALLB | ||
609 | printk(KERN_DEBUG "BCONN: %ld\n", c->arg); | ||
610 | #endif | ||
611 | /* Signal B-channel-connect to network-devices */ | ||
612 | if (dev->global_flags & ISDN_GLOBAL_STOPPED) | ||
613 | return 0; | ||
614 | dev->drv[di]->online |= (1 << (c->arg)); | ||
615 | isdn_info_update(); | ||
616 | if (isdn_net_stat_callback(i, c)) | ||
617 | break; | ||
618 | isdn_v110_stat_callback(i, c); | ||
619 | if (isdn_tty_stat_callback(i, c)) | ||
620 | break; | ||
621 | break; | ||
622 | case ISDN_STAT_BHUP: | ||
623 | if (i < 0) | ||
624 | return -1; | ||
625 | #ifdef ISDN_DEBUG_STATCALLB | ||
626 | printk(KERN_DEBUG "BHUP: %ld\n", c->arg); | ||
627 | #endif | ||
628 | if (dev->global_flags & ISDN_GLOBAL_STOPPED) | ||
629 | return 0; | ||
630 | dev->drv[di]->online &= ~(1 << (c->arg)); | ||
631 | isdn_info_update(); | ||
632 | #ifdef CONFIG_ISDN_X25 | ||
633 | /* Signal hangup to network-devices */ | ||
634 | if (isdn_net_stat_callback(i, c)) | ||
635 | break; | ||
636 | #endif | ||
637 | isdn_v110_stat_callback(i, c); | ||
638 | if (isdn_tty_stat_callback(i, c)) | ||
639 | break; | ||
640 | break; | ||
641 | case ISDN_STAT_NODCH: | ||
642 | if (i < 0) | ||
643 | return -1; | ||
644 | #ifdef ISDN_DEBUG_STATCALLB | ||
645 | printk(KERN_DEBUG "NODCH: %ld\n", c->arg); | ||
646 | #endif | ||
647 | if (dev->global_flags & ISDN_GLOBAL_STOPPED) | ||
648 | return 0; | ||
649 | if (isdn_net_stat_callback(i, c)) | ||
650 | break; | ||
651 | if (isdn_tty_stat_callback(i, c)) | ||
652 | break; | ||
653 | break; | ||
654 | case ISDN_STAT_ADDCH: | ||
655 | spin_lock_irqsave(&dev->lock, flags); | ||
656 | if (isdn_add_channels(dev->drv[di], di, c->arg, 1)) { | ||
657 | spin_unlock_irqrestore(&dev->lock, flags); | ||
658 | return -1; | ||
659 | } | ||
660 | spin_unlock_irqrestore(&dev->lock, flags); | ||
661 | isdn_info_update(); | ||
662 | break; | ||
663 | case ISDN_STAT_DISCH: | ||
664 | spin_lock_irqsave(&dev->lock, flags); | ||
665 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) | ||
666 | if ((dev->drvmap[i] == di) && | ||
667 | (dev->chanmap[i] == c->arg)) { | ||
668 | if (c->parm.num[0]) | ||
669 | dev->usage[i] &= ~ISDN_USAGE_DISABLED; | ||
670 | else | ||
671 | if (USG_NONE(dev->usage[i])) { | ||
672 | dev->usage[i] |= ISDN_USAGE_DISABLED; | ||
673 | } | ||
674 | else | ||
675 | retval = -1; | ||
676 | break; | ||
677 | } | ||
678 | spin_unlock_irqrestore(&dev->lock, flags); | ||
679 | isdn_info_update(); | ||
680 | break; | ||
681 | case ISDN_STAT_UNLOAD: | ||
682 | while (dev->drv[di]->locks > 0) { | ||
683 | isdn_unlock_driver(dev->drv[di]); | ||
684 | } | ||
685 | spin_lock_irqsave(&dev->lock, flags); | ||
686 | isdn_tty_stat_callback(i, c); | ||
687 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) | ||
688 | if (dev->drvmap[i] == di) { | ||
689 | dev->drvmap[i] = -1; | ||
690 | dev->chanmap[i] = -1; | ||
691 | dev->usage[i] &= ~ISDN_USAGE_DISABLED; | ||
692 | } | ||
693 | dev->drivers--; | ||
694 | dev->channels -= dev->drv[di]->channels; | ||
695 | kfree(dev->drv[di]->rcverr); | ||
696 | kfree(dev->drv[di]->rcvcount); | ||
697 | for (i = 0; i < dev->drv[di]->channels; i++) | ||
698 | skb_queue_purge(&dev->drv[di]->rpqueue[i]); | ||
699 | kfree(dev->drv[di]->rpqueue); | ||
700 | kfree(dev->drv[di]->rcv_waitq); | ||
701 | kfree(dev->drv[di]); | ||
702 | dev->drv[di] = NULL; | ||
703 | dev->drvid[di][0] = '\0'; | ||
704 | isdn_info_update(); | ||
705 | set_global_features(); | ||
706 | spin_unlock_irqrestore(&dev->lock, flags); | ||
707 | return 0; | ||
708 | case ISDN_STAT_L1ERR: | ||
709 | break; | ||
710 | case CAPI_PUT_MESSAGE: | ||
711 | return(isdn_capi_rec_hl_msg(&c->parm.cmsg)); | ||
712 | #ifdef CONFIG_ISDN_TTY_FAX | ||
713 | case ISDN_STAT_FAXIND: | ||
714 | isdn_tty_stat_callback(i, c); | ||
715 | break; | ||
716 | #endif | ||
717 | #ifdef CONFIG_ISDN_AUDIO | ||
718 | case ISDN_STAT_AUDIO: | ||
719 | isdn_tty_stat_callback(i, c); | ||
720 | break; | ||
721 | #endif | ||
722 | #ifdef CONFIG_ISDN_DIVERSION | ||
723 | case ISDN_STAT_PROT: | ||
724 | case ISDN_STAT_REDIR: | ||
725 | if (divert_if) | ||
726 | return(divert_if->stat_callback(c)); | ||
727 | #endif /* CONFIG_ISDN_DIVERSION */ | ||
728 | default: | ||
729 | return -1; | ||
730 | } | ||
731 | return 0; | ||
732 | } | ||
733 | |||
734 | /* | ||
735 | * Get integer from char-pointer, set pointer to end of number | ||
736 | */ | ||
737 | int | ||
738 | isdn_getnum(char **p) | ||
739 | { | ||
740 | int v = -1; | ||
741 | |||
742 | while (*p[0] >= '0' && *p[0] <= '9') | ||
743 | v = ((v < 0) ? 0 : (v * 10)) + (int) ((*p[0]++) - '0'); | ||
744 | return v; | ||
745 | } | ||
746 | |||
747 | #define DLE 0x10 | ||
748 | |||
749 | /* | ||
750 | * isdn_readbchan() tries to get data from the read-queue. | ||
751 | * It MUST be called with interrupts off. | ||
752 | * | ||
753 | * Be aware that this is not an atomic operation when sleep != 0, even though | ||
754 | * interrupts are turned off! Well, like that we are currently only called | ||
755 | * on behalf of a read system call on raw device files (which are documented | ||
756 | * to be dangerous and for for debugging purpose only). The inode semaphore | ||
757 | * takes care that this is not called for the same minor device number while | ||
758 | * we are sleeping, but access is not serialized against simultaneous read() | ||
759 | * from the corresponding ttyI device. Can other ugly events, like changes | ||
760 | * of the mapping (di,ch)<->minor, happen during the sleep? --he | ||
761 | */ | ||
762 | int | ||
763 | isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, wait_queue_head_t *sleep) | ||
764 | { | ||
765 | int count; | ||
766 | int count_pull; | ||
767 | int count_put; | ||
768 | int dflag; | ||
769 | struct sk_buff *skb; | ||
770 | u_char *cp; | ||
771 | |||
772 | if (!dev->drv[di]) | ||
773 | return 0; | ||
774 | if (skb_queue_empty(&dev->drv[di]->rpqueue[channel])) { | ||
775 | if (sleep) | ||
776 | interruptible_sleep_on(sleep); | ||
777 | else | ||
778 | return 0; | ||
779 | } | ||
780 | if (len > dev->drv[di]->rcvcount[channel]) | ||
781 | len = dev->drv[di]->rcvcount[channel]; | ||
782 | cp = buf; | ||
783 | count = 0; | ||
784 | while (len) { | ||
785 | if (!(skb = skb_peek(&dev->drv[di]->rpqueue[channel]))) | ||
786 | break; | ||
787 | #ifdef CONFIG_ISDN_AUDIO | ||
788 | if (ISDN_AUDIO_SKB_LOCK(skb)) | ||
789 | break; | ||
790 | ISDN_AUDIO_SKB_LOCK(skb) = 1; | ||
791 | if ((ISDN_AUDIO_SKB_DLECOUNT(skb)) || (dev->drv[di]->DLEflag & (1 << channel))) { | ||
792 | char *p = skb->data; | ||
793 | unsigned long DLEmask = (1 << channel); | ||
794 | |||
795 | dflag = 0; | ||
796 | count_pull = count_put = 0; | ||
797 | while ((count_pull < skb->len) && (len > 0)) { | ||
798 | len--; | ||
799 | if (dev->drv[di]->DLEflag & DLEmask) { | ||
800 | *cp++ = DLE; | ||
801 | dev->drv[di]->DLEflag &= ~DLEmask; | ||
802 | } else { | ||
803 | *cp++ = *p; | ||
804 | if (*p == DLE) { | ||
805 | dev->drv[di]->DLEflag |= DLEmask; | ||
806 | (ISDN_AUDIO_SKB_DLECOUNT(skb))--; | ||
807 | } | ||
808 | p++; | ||
809 | count_pull++; | ||
810 | } | ||
811 | count_put++; | ||
812 | } | ||
813 | if (count_pull >= skb->len) | ||
814 | dflag = 1; | ||
815 | } else { | ||
816 | #endif | ||
817 | /* No DLE's in buff, so simply copy it */ | ||
818 | dflag = 1; | ||
819 | if ((count_pull = skb->len) > len) { | ||
820 | count_pull = len; | ||
821 | dflag = 0; | ||
822 | } | ||
823 | count_put = count_pull; | ||
824 | memcpy(cp, skb->data, count_put); | ||
825 | cp += count_put; | ||
826 | len -= count_put; | ||
827 | #ifdef CONFIG_ISDN_AUDIO | ||
828 | } | ||
829 | #endif | ||
830 | count += count_put; | ||
831 | if (fp) { | ||
832 | memset(fp, 0, count_put); | ||
833 | fp += count_put; | ||
834 | } | ||
835 | if (dflag) { | ||
836 | /* We got all the data in this buff. | ||
837 | * Now we can dequeue it. | ||
838 | */ | ||
839 | if (fp) | ||
840 | *(fp - 1) = 0xff; | ||
841 | #ifdef CONFIG_ISDN_AUDIO | ||
842 | ISDN_AUDIO_SKB_LOCK(skb) = 0; | ||
843 | #endif | ||
844 | skb = skb_dequeue(&dev->drv[di]->rpqueue[channel]); | ||
845 | dev_kfree_skb(skb); | ||
846 | } else { | ||
847 | /* Not yet emptied this buff, so it | ||
848 | * must stay in the queue, for further calls | ||
849 | * but we pull off the data we got until now. | ||
850 | */ | ||
851 | skb_pull(skb, count_pull); | ||
852 | #ifdef CONFIG_ISDN_AUDIO | ||
853 | ISDN_AUDIO_SKB_LOCK(skb) = 0; | ||
854 | #endif | ||
855 | } | ||
856 | dev->drv[di]->rcvcount[channel] -= count_put; | ||
857 | } | ||
858 | return count; | ||
859 | } | ||
860 | |||
861 | static __inline int | ||
862 | isdn_minor2drv(int minor) | ||
863 | { | ||
864 | return (dev->drvmap[minor]); | ||
865 | } | ||
866 | |||
867 | static __inline int | ||
868 | isdn_minor2chan(int minor) | ||
869 | { | ||
870 | return (dev->chanmap[minor]); | ||
871 | } | ||
872 | |||
873 | static char * | ||
874 | isdn_statstr(void) | ||
875 | { | ||
876 | static char istatbuf[2048]; | ||
877 | char *p; | ||
878 | int i; | ||
879 | |||
880 | sprintf(istatbuf, "idmap:\t"); | ||
881 | p = istatbuf + strlen(istatbuf); | ||
882 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
883 | sprintf(p, "%s ", (dev->drvmap[i] < 0) ? "-" : dev->drvid[dev->drvmap[i]]); | ||
884 | p = istatbuf + strlen(istatbuf); | ||
885 | } | ||
886 | sprintf(p, "\nchmap:\t"); | ||
887 | p = istatbuf + strlen(istatbuf); | ||
888 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
889 | sprintf(p, "%d ", dev->chanmap[i]); | ||
890 | p = istatbuf + strlen(istatbuf); | ||
891 | } | ||
892 | sprintf(p, "\ndrmap:\t"); | ||
893 | p = istatbuf + strlen(istatbuf); | ||
894 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
895 | sprintf(p, "%d ", dev->drvmap[i]); | ||
896 | p = istatbuf + strlen(istatbuf); | ||
897 | } | ||
898 | sprintf(p, "\nusage:\t"); | ||
899 | p = istatbuf + strlen(istatbuf); | ||
900 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
901 | sprintf(p, "%d ", dev->usage[i]); | ||
902 | p = istatbuf + strlen(istatbuf); | ||
903 | } | ||
904 | sprintf(p, "\nflags:\t"); | ||
905 | p = istatbuf + strlen(istatbuf); | ||
906 | for (i = 0; i < ISDN_MAX_DRIVERS; i++) { | ||
907 | if (dev->drv[i]) { | ||
908 | sprintf(p, "%ld ", dev->drv[i]->online); | ||
909 | p = istatbuf + strlen(istatbuf); | ||
910 | } else { | ||
911 | sprintf(p, "? "); | ||
912 | p = istatbuf + strlen(istatbuf); | ||
913 | } | ||
914 | } | ||
915 | sprintf(p, "\nphone:\t"); | ||
916 | p = istatbuf + strlen(istatbuf); | ||
917 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
918 | sprintf(p, "%s ", dev->num[i]); | ||
919 | p = istatbuf + strlen(istatbuf); | ||
920 | } | ||
921 | sprintf(p, "\n"); | ||
922 | return istatbuf; | ||
923 | } | ||
924 | |||
925 | /* Module interface-code */ | ||
926 | |||
927 | void | ||
928 | isdn_info_update(void) | ||
929 | { | ||
930 | infostruct *p = dev->infochain; | ||
931 | |||
932 | while (p) { | ||
933 | *(p->private) = 1; | ||
934 | p = (infostruct *) p->next; | ||
935 | } | ||
936 | wake_up_interruptible(&(dev->info_waitq)); | ||
937 | } | ||
938 | |||
939 | static ssize_t | ||
940 | isdn_read(struct file *file, char __user *buf, size_t count, loff_t * off) | ||
941 | { | ||
942 | uint minor = MINOR(file->f_dentry->d_inode->i_rdev); | ||
943 | int len = 0; | ||
944 | int drvidx; | ||
945 | int chidx; | ||
946 | int retval; | ||
947 | char *p; | ||
948 | |||
949 | lock_kernel(); | ||
950 | if (minor == ISDN_MINOR_STATUS) { | ||
951 | if (!file->private_data) { | ||
952 | if (file->f_flags & O_NONBLOCK) { | ||
953 | retval = -EAGAIN; | ||
954 | goto out; | ||
955 | } | ||
956 | interruptible_sleep_on(&(dev->info_waitq)); | ||
957 | } | ||
958 | p = isdn_statstr(); | ||
959 | file->private_data = NULL; | ||
960 | if ((len = strlen(p)) <= count) { | ||
961 | if (copy_to_user(buf, p, len)) { | ||
962 | retval = -EFAULT; | ||
963 | goto out; | ||
964 | } | ||
965 | *off += len; | ||
966 | retval = len; | ||
967 | goto out; | ||
968 | } | ||
969 | retval = 0; | ||
970 | goto out; | ||
971 | } | ||
972 | if (!dev->drivers) { | ||
973 | retval = -ENODEV; | ||
974 | goto out; | ||
975 | } | ||
976 | if (minor <= ISDN_MINOR_BMAX) { | ||
977 | printk(KERN_WARNING "isdn_read minor %d obsolete!\n", minor); | ||
978 | drvidx = isdn_minor2drv(minor); | ||
979 | if (drvidx < 0) { | ||
980 | retval = -ENODEV; | ||
981 | goto out; | ||
982 | } | ||
983 | if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING)) { | ||
984 | retval = -ENODEV; | ||
985 | goto out; | ||
986 | } | ||
987 | chidx = isdn_minor2chan(minor); | ||
988 | if (!(p = kmalloc(count, GFP_KERNEL))) { | ||
989 | retval = -ENOMEM; | ||
990 | goto out; | ||
991 | } | ||
992 | len = isdn_readbchan(drvidx, chidx, p, NULL, count, | ||
993 | &dev->drv[drvidx]->rcv_waitq[chidx]); | ||
994 | *off += len; | ||
995 | if (copy_to_user(buf,p,len)) | ||
996 | len = -EFAULT; | ||
997 | kfree(p); | ||
998 | retval = len; | ||
999 | goto out; | ||
1000 | } | ||
1001 | if (minor <= ISDN_MINOR_CTRLMAX) { | ||
1002 | drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL); | ||
1003 | if (drvidx < 0) { | ||
1004 | retval = -ENODEV; | ||
1005 | goto out; | ||
1006 | } | ||
1007 | if (!dev->drv[drvidx]->stavail) { | ||
1008 | if (file->f_flags & O_NONBLOCK) { | ||
1009 | retval = -EAGAIN; | ||
1010 | goto out; | ||
1011 | } | ||
1012 | interruptible_sleep_on(&(dev->drv[drvidx]->st_waitq)); | ||
1013 | } | ||
1014 | if (dev->drv[drvidx]->interface->readstat) { | ||
1015 | if (count > dev->drv[drvidx]->stavail) | ||
1016 | count = dev->drv[drvidx]->stavail; | ||
1017 | len = dev->drv[drvidx]->interface-> | ||
1018 | readstat(buf, count, drvidx, | ||
1019 | isdn_minor2chan(minor)); | ||
1020 | } else { | ||
1021 | len = 0; | ||
1022 | } | ||
1023 | if (len) | ||
1024 | dev->drv[drvidx]->stavail -= len; | ||
1025 | else | ||
1026 | dev->drv[drvidx]->stavail = 0; | ||
1027 | *off += len; | ||
1028 | retval = len; | ||
1029 | goto out; | ||
1030 | } | ||
1031 | #ifdef CONFIG_ISDN_PPP | ||
1032 | if (minor <= ISDN_MINOR_PPPMAX) { | ||
1033 | retval = isdn_ppp_read(minor - ISDN_MINOR_PPP, file, buf, count); | ||
1034 | goto out; | ||
1035 | } | ||
1036 | #endif | ||
1037 | retval = -ENODEV; | ||
1038 | out: | ||
1039 | unlock_kernel(); | ||
1040 | return retval; | ||
1041 | } | ||
1042 | |||
1043 | static ssize_t | ||
1044 | isdn_write(struct file *file, const char __user *buf, size_t count, loff_t * off) | ||
1045 | { | ||
1046 | uint minor = MINOR(file->f_dentry->d_inode->i_rdev); | ||
1047 | int drvidx; | ||
1048 | int chidx; | ||
1049 | int retval; | ||
1050 | |||
1051 | if (minor == ISDN_MINOR_STATUS) | ||
1052 | return -EPERM; | ||
1053 | if (!dev->drivers) | ||
1054 | return -ENODEV; | ||
1055 | |||
1056 | lock_kernel(); | ||
1057 | if (minor <= ISDN_MINOR_BMAX) { | ||
1058 | printk(KERN_WARNING "isdn_write minor %d obsolete!\n", minor); | ||
1059 | drvidx = isdn_minor2drv(minor); | ||
1060 | if (drvidx < 0) { | ||
1061 | retval = -ENODEV; | ||
1062 | goto out; | ||
1063 | } | ||
1064 | if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING)) { | ||
1065 | retval = -ENODEV; | ||
1066 | goto out; | ||
1067 | } | ||
1068 | chidx = isdn_minor2chan(minor); | ||
1069 | while (isdn_writebuf_stub(drvidx, chidx, buf, count) != count) | ||
1070 | interruptible_sleep_on(&dev->drv[drvidx]->snd_waitq[chidx]); | ||
1071 | retval = count; | ||
1072 | goto out; | ||
1073 | } | ||
1074 | if (minor <= ISDN_MINOR_CTRLMAX) { | ||
1075 | drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL); | ||
1076 | if (drvidx < 0) { | ||
1077 | retval = -ENODEV; | ||
1078 | goto out; | ||
1079 | } | ||
1080 | /* | ||
1081 | * We want to use the isdnctrl device to load the firmware | ||
1082 | * | ||
1083 | if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING)) | ||
1084 | return -ENODEV; | ||
1085 | */ | ||
1086 | if (dev->drv[drvidx]->interface->writecmd) | ||
1087 | retval = dev->drv[drvidx]->interface-> | ||
1088 | writecmd(buf, count, drvidx, isdn_minor2chan(minor)); | ||
1089 | else | ||
1090 | retval = count; | ||
1091 | goto out; | ||
1092 | } | ||
1093 | #ifdef CONFIG_ISDN_PPP | ||
1094 | if (minor <= ISDN_MINOR_PPPMAX) { | ||
1095 | retval = isdn_ppp_write(minor - ISDN_MINOR_PPP, file, buf, count); | ||
1096 | goto out; | ||
1097 | } | ||
1098 | #endif | ||
1099 | retval = -ENODEV; | ||
1100 | out: | ||
1101 | unlock_kernel(); | ||
1102 | return retval; | ||
1103 | } | ||
1104 | |||
1105 | static unsigned int | ||
1106 | isdn_poll(struct file *file, poll_table * wait) | ||
1107 | { | ||
1108 | unsigned int mask = 0; | ||
1109 | unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); | ||
1110 | int drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL); | ||
1111 | |||
1112 | lock_kernel(); | ||
1113 | if (minor == ISDN_MINOR_STATUS) { | ||
1114 | poll_wait(file, &(dev->info_waitq), wait); | ||
1115 | /* mask = POLLOUT | POLLWRNORM; */ | ||
1116 | if (file->private_data) { | ||
1117 | mask |= POLLIN | POLLRDNORM; | ||
1118 | } | ||
1119 | goto out; | ||
1120 | } | ||
1121 | if (minor >= ISDN_MINOR_CTRL && minor <= ISDN_MINOR_CTRLMAX) { | ||
1122 | if (drvidx < 0) { | ||
1123 | /* driver deregistered while file open */ | ||
1124 | mask = POLLHUP; | ||
1125 | goto out; | ||
1126 | } | ||
1127 | poll_wait(file, &(dev->drv[drvidx]->st_waitq), wait); | ||
1128 | mask = POLLOUT | POLLWRNORM; | ||
1129 | if (dev->drv[drvidx]->stavail) { | ||
1130 | mask |= POLLIN | POLLRDNORM; | ||
1131 | } | ||
1132 | goto out; | ||
1133 | } | ||
1134 | #ifdef CONFIG_ISDN_PPP | ||
1135 | if (minor <= ISDN_MINOR_PPPMAX) { | ||
1136 | mask = isdn_ppp_poll(file, wait); | ||
1137 | goto out; | ||
1138 | } | ||
1139 | #endif | ||
1140 | mask = POLLERR; | ||
1141 | out: | ||
1142 | unlock_kernel(); | ||
1143 | return mask; | ||
1144 | } | ||
1145 | |||
1146 | |||
1147 | static int | ||
1148 | isdn_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) | ||
1149 | { | ||
1150 | uint minor = MINOR(inode->i_rdev); | ||
1151 | isdn_ctrl c; | ||
1152 | int drvidx; | ||
1153 | int chidx; | ||
1154 | int ret; | ||
1155 | int i; | ||
1156 | char __user *p; | ||
1157 | char *s; | ||
1158 | union iocpar { | ||
1159 | char name[10]; | ||
1160 | char bname[22]; | ||
1161 | isdn_ioctl_struct iocts; | ||
1162 | isdn_net_ioctl_phone phone; | ||
1163 | isdn_net_ioctl_cfg cfg; | ||
1164 | } iocpar; | ||
1165 | void __user *argp = (void __user *)arg; | ||
1166 | |||
1167 | #define name iocpar.name | ||
1168 | #define bname iocpar.bname | ||
1169 | #define iocts iocpar.iocts | ||
1170 | #define phone iocpar.phone | ||
1171 | #define cfg iocpar.cfg | ||
1172 | |||
1173 | if (minor == ISDN_MINOR_STATUS) { | ||
1174 | switch (cmd) { | ||
1175 | case IIOCGETDVR: | ||
1176 | return (TTY_DV + | ||
1177 | (NET_DV << 8) + | ||
1178 | (INF_DV << 16)); | ||
1179 | case IIOCGETCPS: | ||
1180 | if (arg) { | ||
1181 | ulong __user *p = argp; | ||
1182 | int i; | ||
1183 | if (!access_ok(VERIFY_WRITE, p, | ||
1184 | sizeof(ulong) * ISDN_MAX_CHANNELS * 2)) | ||
1185 | return -EFAULT; | ||
1186 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
1187 | put_user(dev->ibytes[i], p++); | ||
1188 | put_user(dev->obytes[i], p++); | ||
1189 | } | ||
1190 | return 0; | ||
1191 | } else | ||
1192 | return -EINVAL; | ||
1193 | break; | ||
1194 | #ifdef CONFIG_NETDEVICES | ||
1195 | case IIOCNETGPN: | ||
1196 | /* Get peer phone number of a connected | ||
1197 | * isdn network interface */ | ||
1198 | if (arg) { | ||
1199 | if (copy_from_user(&phone, argp, sizeof(phone))) | ||
1200 | return -EFAULT; | ||
1201 | return isdn_net_getpeer(&phone, argp); | ||
1202 | } else | ||
1203 | return -EINVAL; | ||
1204 | #endif | ||
1205 | default: | ||
1206 | return -EINVAL; | ||
1207 | } | ||
1208 | } | ||
1209 | if (!dev->drivers) | ||
1210 | return -ENODEV; | ||
1211 | if (minor <= ISDN_MINOR_BMAX) { | ||
1212 | drvidx = isdn_minor2drv(minor); | ||
1213 | if (drvidx < 0) | ||
1214 | return -ENODEV; | ||
1215 | chidx = isdn_minor2chan(minor); | ||
1216 | if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING)) | ||
1217 | return -ENODEV; | ||
1218 | return 0; | ||
1219 | } | ||
1220 | if (minor <= ISDN_MINOR_CTRLMAX) { | ||
1221 | /* | ||
1222 | * isdn net devices manage lots of configuration variables as linked lists. | ||
1223 | * Those lists must only be manipulated from user space. Some of the ioctl's | ||
1224 | * service routines access user space and are not atomic. Therefor, ioctl's | ||
1225 | * manipulating the lists and ioctl's sleeping while accessing the lists | ||
1226 | * are serialized by means of a semaphore. | ||
1227 | */ | ||
1228 | switch (cmd) { | ||
1229 | case IIOCNETDWRSET: | ||
1230 | printk(KERN_INFO "INFO: ISDN_DW_ABC_EXTENSION not enabled\n"); | ||
1231 | return(-EINVAL); | ||
1232 | case IIOCNETLCR: | ||
1233 | printk(KERN_INFO "INFO: ISDN_ABC_LCR_SUPPORT not enabled\n"); | ||
1234 | return -ENODEV; | ||
1235 | #ifdef CONFIG_NETDEVICES | ||
1236 | case IIOCNETAIF: | ||
1237 | /* Add a network-interface */ | ||
1238 | if (arg) { | ||
1239 | if (copy_from_user(name, argp, sizeof(name))) | ||
1240 | return -EFAULT; | ||
1241 | s = name; | ||
1242 | } else { | ||
1243 | s = NULL; | ||
1244 | } | ||
1245 | ret = down_interruptible(&dev->sem); | ||
1246 | if( ret ) return ret; | ||
1247 | if ((s = isdn_net_new(s, NULL))) { | ||
1248 | if (copy_to_user(argp, s, strlen(s) + 1)){ | ||
1249 | ret = -EFAULT; | ||
1250 | } else { | ||
1251 | ret = 0; | ||
1252 | } | ||
1253 | } else | ||
1254 | ret = -ENODEV; | ||
1255 | up(&dev->sem); | ||
1256 | return ret; | ||
1257 | case IIOCNETASL: | ||
1258 | /* Add a slave to a network-interface */ | ||
1259 | if (arg) { | ||
1260 | if (copy_from_user(bname, argp, sizeof(bname) - 1)) | ||
1261 | return -EFAULT; | ||
1262 | } else | ||
1263 | return -EINVAL; | ||
1264 | ret = down_interruptible(&dev->sem); | ||
1265 | if( ret ) return ret; | ||
1266 | if ((s = isdn_net_newslave(bname))) { | ||
1267 | if (copy_to_user(argp, s, strlen(s) + 1)){ | ||
1268 | ret = -EFAULT; | ||
1269 | } else { | ||
1270 | ret = 0; | ||
1271 | } | ||
1272 | } else | ||
1273 | ret = -ENODEV; | ||
1274 | up(&dev->sem); | ||
1275 | return ret; | ||
1276 | case IIOCNETDIF: | ||
1277 | /* Delete a network-interface */ | ||
1278 | if (arg) { | ||
1279 | if (copy_from_user(name, argp, sizeof(name))) | ||
1280 | return -EFAULT; | ||
1281 | ret = down_interruptible(&dev->sem); | ||
1282 | if( ret ) return ret; | ||
1283 | ret = isdn_net_rm(name); | ||
1284 | up(&dev->sem); | ||
1285 | return ret; | ||
1286 | } else | ||
1287 | return -EINVAL; | ||
1288 | case IIOCNETSCF: | ||
1289 | /* Set configurable parameters of a network-interface */ | ||
1290 | if (arg) { | ||
1291 | if (copy_from_user(&cfg, argp, sizeof(cfg))) | ||
1292 | return -EFAULT; | ||
1293 | return isdn_net_setcfg(&cfg); | ||
1294 | } else | ||
1295 | return -EINVAL; | ||
1296 | case IIOCNETGCF: | ||
1297 | /* Get configurable parameters of a network-interface */ | ||
1298 | if (arg) { | ||
1299 | if (copy_from_user(&cfg, argp, sizeof(cfg))) | ||
1300 | return -EFAULT; | ||
1301 | if (!(ret = isdn_net_getcfg(&cfg))) { | ||
1302 | if (copy_to_user(argp, &cfg, sizeof(cfg))) | ||
1303 | return -EFAULT; | ||
1304 | } | ||
1305 | return ret; | ||
1306 | } else | ||
1307 | return -EINVAL; | ||
1308 | case IIOCNETANM: | ||
1309 | /* Add a phone-number to a network-interface */ | ||
1310 | if (arg) { | ||
1311 | if (copy_from_user(&phone, argp, sizeof(phone))) | ||
1312 | return -EFAULT; | ||
1313 | ret = down_interruptible(&dev->sem); | ||
1314 | if( ret ) return ret; | ||
1315 | ret = isdn_net_addphone(&phone); | ||
1316 | up(&dev->sem); | ||
1317 | return ret; | ||
1318 | } else | ||
1319 | return -EINVAL; | ||
1320 | case IIOCNETGNM: | ||
1321 | /* Get list of phone-numbers of a network-interface */ | ||
1322 | if (arg) { | ||
1323 | if (copy_from_user(&phone, argp, sizeof(phone))) | ||
1324 | return -EFAULT; | ||
1325 | ret = down_interruptible(&dev->sem); | ||
1326 | if( ret ) return ret; | ||
1327 | ret = isdn_net_getphones(&phone, argp); | ||
1328 | up(&dev->sem); | ||
1329 | return ret; | ||
1330 | } else | ||
1331 | return -EINVAL; | ||
1332 | case IIOCNETDNM: | ||
1333 | /* Delete a phone-number of a network-interface */ | ||
1334 | if (arg) { | ||
1335 | if (copy_from_user(&phone, argp, sizeof(phone))) | ||
1336 | return -EFAULT; | ||
1337 | ret = down_interruptible(&dev->sem); | ||
1338 | if( ret ) return ret; | ||
1339 | ret = isdn_net_delphone(&phone); | ||
1340 | up(&dev->sem); | ||
1341 | return ret; | ||
1342 | } else | ||
1343 | return -EINVAL; | ||
1344 | case IIOCNETDIL: | ||
1345 | /* Force dialing of a network-interface */ | ||
1346 | if (arg) { | ||
1347 | if (copy_from_user(name, argp, sizeof(name))) | ||
1348 | return -EFAULT; | ||
1349 | return isdn_net_force_dial(name); | ||
1350 | } else | ||
1351 | return -EINVAL; | ||
1352 | #ifdef CONFIG_ISDN_PPP | ||
1353 | case IIOCNETALN: | ||
1354 | if (!arg) | ||
1355 | return -EINVAL; | ||
1356 | if (copy_from_user(name, argp, sizeof(name))) | ||
1357 | return -EFAULT; | ||
1358 | return isdn_ppp_dial_slave(name); | ||
1359 | case IIOCNETDLN: | ||
1360 | if (!arg) | ||
1361 | return -EINVAL; | ||
1362 | if (copy_from_user(name, argp, sizeof(name))) | ||
1363 | return -EFAULT; | ||
1364 | return isdn_ppp_hangup_slave(name); | ||
1365 | #endif | ||
1366 | case IIOCNETHUP: | ||
1367 | /* Force hangup of a network-interface */ | ||
1368 | if (!arg) | ||
1369 | return -EINVAL; | ||
1370 | if (copy_from_user(name, argp, sizeof(name))) | ||
1371 | return -EFAULT; | ||
1372 | return isdn_net_force_hangup(name); | ||
1373 | break; | ||
1374 | #endif /* CONFIG_NETDEVICES */ | ||
1375 | case IIOCSETVER: | ||
1376 | dev->net_verbose = arg; | ||
1377 | printk(KERN_INFO "isdn: Verbose-Level is %d\n", dev->net_verbose); | ||
1378 | return 0; | ||
1379 | case IIOCSETGST: | ||
1380 | if (arg) | ||
1381 | dev->global_flags |= ISDN_GLOBAL_STOPPED; | ||
1382 | else | ||
1383 | dev->global_flags &= ~ISDN_GLOBAL_STOPPED; | ||
1384 | printk(KERN_INFO "isdn: Global Mode %s\n", | ||
1385 | (dev->global_flags & ISDN_GLOBAL_STOPPED) ? "stopped" : "running"); | ||
1386 | return 0; | ||
1387 | case IIOCSETBRJ: | ||
1388 | drvidx = -1; | ||
1389 | if (arg) { | ||
1390 | int i; | ||
1391 | char *p; | ||
1392 | if (copy_from_user(&iocts, argp, | ||
1393 | sizeof(isdn_ioctl_struct))) | ||
1394 | return -EFAULT; | ||
1395 | if (strlen(iocts.drvid)) { | ||
1396 | if ((p = strchr(iocts.drvid, ','))) | ||
1397 | *p = 0; | ||
1398 | drvidx = -1; | ||
1399 | for (i = 0; i < ISDN_MAX_DRIVERS; i++) | ||
1400 | if (!(strcmp(dev->drvid[i], iocts.drvid))) { | ||
1401 | drvidx = i; | ||
1402 | break; | ||
1403 | } | ||
1404 | } | ||
1405 | } | ||
1406 | if (drvidx == -1) | ||
1407 | return -ENODEV; | ||
1408 | if (iocts.arg) | ||
1409 | dev->drv[drvidx]->flags |= DRV_FLAG_REJBUS; | ||
1410 | else | ||
1411 | dev->drv[drvidx]->flags &= ~DRV_FLAG_REJBUS; | ||
1412 | return 0; | ||
1413 | case IIOCSIGPRF: | ||
1414 | dev->profd = current; | ||
1415 | return 0; | ||
1416 | break; | ||
1417 | case IIOCGETPRF: | ||
1418 | /* Get all Modem-Profiles */ | ||
1419 | if (arg) { | ||
1420 | char __user *p = argp; | ||
1421 | int i; | ||
1422 | |||
1423 | if (!access_ok(VERIFY_WRITE, argp, | ||
1424 | (ISDN_MODEM_NUMREG + ISDN_MSNLEN + ISDN_LMSNLEN) | ||
1425 | * ISDN_MAX_CHANNELS)) | ||
1426 | return -EFAULT; | ||
1427 | |||
1428 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
1429 | if (copy_to_user(p, dev->mdm.info[i].emu.profile, | ||
1430 | ISDN_MODEM_NUMREG)) | ||
1431 | return -EFAULT; | ||
1432 | p += ISDN_MODEM_NUMREG; | ||
1433 | if (copy_to_user(p, dev->mdm.info[i].emu.pmsn, ISDN_MSNLEN)) | ||
1434 | return -EFAULT; | ||
1435 | p += ISDN_MSNLEN; | ||
1436 | if (copy_to_user(p, dev->mdm.info[i].emu.plmsn, ISDN_LMSNLEN)) | ||
1437 | return -EFAULT; | ||
1438 | p += ISDN_LMSNLEN; | ||
1439 | } | ||
1440 | return (ISDN_MODEM_NUMREG + ISDN_MSNLEN + ISDN_LMSNLEN) * ISDN_MAX_CHANNELS; | ||
1441 | } else | ||
1442 | return -EINVAL; | ||
1443 | break; | ||
1444 | case IIOCSETPRF: | ||
1445 | /* Set all Modem-Profiles */ | ||
1446 | if (arg) { | ||
1447 | char __user *p = argp; | ||
1448 | int i; | ||
1449 | |||
1450 | if (!access_ok(VERIFY_READ, argp, | ||
1451 | (ISDN_MODEM_NUMREG + ISDN_MSNLEN + ISDN_LMSNLEN) | ||
1452 | * ISDN_MAX_CHANNELS)) | ||
1453 | return -EFAULT; | ||
1454 | |||
1455 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
1456 | if (copy_from_user(dev->mdm.info[i].emu.profile, p, | ||
1457 | ISDN_MODEM_NUMREG)) | ||
1458 | return -EFAULT; | ||
1459 | p += ISDN_MODEM_NUMREG; | ||
1460 | if (copy_from_user(dev->mdm.info[i].emu.plmsn, p, ISDN_LMSNLEN)) | ||
1461 | return -EFAULT; | ||
1462 | p += ISDN_LMSNLEN; | ||
1463 | if (copy_from_user(dev->mdm.info[i].emu.pmsn, p, ISDN_MSNLEN)) | ||
1464 | return -EFAULT; | ||
1465 | p += ISDN_MSNLEN; | ||
1466 | } | ||
1467 | return 0; | ||
1468 | } else | ||
1469 | return -EINVAL; | ||
1470 | break; | ||
1471 | case IIOCSETMAP: | ||
1472 | case IIOCGETMAP: | ||
1473 | /* Set/Get MSN->EAZ-Mapping for a driver */ | ||
1474 | if (arg) { | ||
1475 | |||
1476 | if (copy_from_user(&iocts, argp, | ||
1477 | sizeof(isdn_ioctl_struct))) | ||
1478 | return -EFAULT; | ||
1479 | if (strlen(iocts.drvid)) { | ||
1480 | drvidx = -1; | ||
1481 | for (i = 0; i < ISDN_MAX_DRIVERS; i++) | ||
1482 | if (!(strcmp(dev->drvid[i], iocts.drvid))) { | ||
1483 | drvidx = i; | ||
1484 | break; | ||
1485 | } | ||
1486 | } else | ||
1487 | drvidx = 0; | ||
1488 | if (drvidx == -1) | ||
1489 | return -ENODEV; | ||
1490 | if (cmd == IIOCSETMAP) { | ||
1491 | int loop = 1; | ||
1492 | |||
1493 | p = (char __user *) iocts.arg; | ||
1494 | i = 0; | ||
1495 | while (loop) { | ||
1496 | int j = 0; | ||
1497 | |||
1498 | while (1) { | ||
1499 | if (!access_ok(VERIFY_READ, p, 1)) | ||
1500 | return -EFAULT; | ||
1501 | get_user(bname[j], p++); | ||
1502 | switch (bname[j]) { | ||
1503 | case '\0': | ||
1504 | loop = 0; | ||
1505 | /* Fall through */ | ||
1506 | case ',': | ||
1507 | bname[j] = '\0'; | ||
1508 | strcpy(dev->drv[drvidx]->msn2eaz[i], bname); | ||
1509 | j = ISDN_MSNLEN; | ||
1510 | break; | ||
1511 | default: | ||
1512 | j++; | ||
1513 | } | ||
1514 | if (j >= ISDN_MSNLEN) | ||
1515 | break; | ||
1516 | } | ||
1517 | if (++i > 9) | ||
1518 | break; | ||
1519 | } | ||
1520 | } else { | ||
1521 | p = (char __user *) iocts.arg; | ||
1522 | for (i = 0; i < 10; i++) { | ||
1523 | sprintf(bname, "%s%s", | ||
1524 | strlen(dev->drv[drvidx]->msn2eaz[i]) ? | ||
1525 | dev->drv[drvidx]->msn2eaz[i] : "_", | ||
1526 | (i < 9) ? "," : "\0"); | ||
1527 | if (copy_to_user(p, bname, strlen(bname) + 1)) | ||
1528 | return -EFAULT; | ||
1529 | p += strlen(bname); | ||
1530 | } | ||
1531 | } | ||
1532 | return 0; | ||
1533 | } else | ||
1534 | return -EINVAL; | ||
1535 | case IIOCDBGVAR: | ||
1536 | if (arg) { | ||
1537 | if (copy_to_user(argp, &dev, sizeof(ulong))) | ||
1538 | return -EFAULT; | ||
1539 | return 0; | ||
1540 | } else | ||
1541 | return -EINVAL; | ||
1542 | break; | ||
1543 | default: | ||
1544 | if ((cmd & IIOCDRVCTL) == IIOCDRVCTL) | ||
1545 | cmd = ((cmd >> _IOC_NRSHIFT) & _IOC_NRMASK) & ISDN_DRVIOCTL_MASK; | ||
1546 | else | ||
1547 | return -EINVAL; | ||
1548 | if (arg) { | ||
1549 | int i; | ||
1550 | char *p; | ||
1551 | if (copy_from_user(&iocts, argp, sizeof(isdn_ioctl_struct))) | ||
1552 | return -EFAULT; | ||
1553 | if (strlen(iocts.drvid)) { | ||
1554 | if ((p = strchr(iocts.drvid, ','))) | ||
1555 | *p = 0; | ||
1556 | drvidx = -1; | ||
1557 | for (i = 0; i < ISDN_MAX_DRIVERS; i++) | ||
1558 | if (!(strcmp(dev->drvid[i], iocts.drvid))) { | ||
1559 | drvidx = i; | ||
1560 | break; | ||
1561 | } | ||
1562 | } else | ||
1563 | drvidx = 0; | ||
1564 | if (drvidx == -1) | ||
1565 | return -ENODEV; | ||
1566 | if (!access_ok(VERIFY_WRITE, argp, | ||
1567 | sizeof(isdn_ioctl_struct))) | ||
1568 | return -EFAULT; | ||
1569 | c.driver = drvidx; | ||
1570 | c.command = ISDN_CMD_IOCTL; | ||
1571 | c.arg = cmd; | ||
1572 | memcpy(c.parm.num, &iocts.arg, sizeof(ulong)); | ||
1573 | ret = isdn_command(&c); | ||
1574 | memcpy(&iocts.arg, c.parm.num, sizeof(ulong)); | ||
1575 | if (copy_to_user(argp, &iocts, sizeof(isdn_ioctl_struct))) | ||
1576 | return -EFAULT; | ||
1577 | return ret; | ||
1578 | } else | ||
1579 | return -EINVAL; | ||
1580 | } | ||
1581 | } | ||
1582 | #ifdef CONFIG_ISDN_PPP | ||
1583 | if (minor <= ISDN_MINOR_PPPMAX) | ||
1584 | return (isdn_ppp_ioctl(minor - ISDN_MINOR_PPP, file, cmd, arg)); | ||
1585 | #endif | ||
1586 | return -ENODEV; | ||
1587 | |||
1588 | #undef name | ||
1589 | #undef bname | ||
1590 | #undef iocts | ||
1591 | #undef phone | ||
1592 | #undef cfg | ||
1593 | } | ||
1594 | |||
1595 | /* | ||
1596 | * Open the device code. | ||
1597 | */ | ||
1598 | static int | ||
1599 | isdn_open(struct inode *ino, struct file *filep) | ||
1600 | { | ||
1601 | uint minor = MINOR(ino->i_rdev); | ||
1602 | int drvidx; | ||
1603 | int chidx; | ||
1604 | int retval = -ENODEV; | ||
1605 | |||
1606 | |||
1607 | if (minor == ISDN_MINOR_STATUS) { | ||
1608 | infostruct *p; | ||
1609 | |||
1610 | if ((p = kmalloc(sizeof(infostruct), GFP_KERNEL))) { | ||
1611 | p->next = (char *) dev->infochain; | ||
1612 | p->private = (char *) &(filep->private_data); | ||
1613 | dev->infochain = p; | ||
1614 | /* At opening we allow a single update */ | ||
1615 | filep->private_data = (char *) 1; | ||
1616 | retval = 0; | ||
1617 | goto out; | ||
1618 | } else { | ||
1619 | retval = -ENOMEM; | ||
1620 | goto out; | ||
1621 | } | ||
1622 | } | ||
1623 | if (!dev->channels) | ||
1624 | goto out; | ||
1625 | if (minor <= ISDN_MINOR_BMAX) { | ||
1626 | printk(KERN_WARNING "isdn_open minor %d obsolete!\n", minor); | ||
1627 | drvidx = isdn_minor2drv(minor); | ||
1628 | if (drvidx < 0) | ||
1629 | goto out; | ||
1630 | chidx = isdn_minor2chan(minor); | ||
1631 | if (!(dev->drv[drvidx]->flags & DRV_FLAG_RUNNING)) | ||
1632 | goto out; | ||
1633 | if (!(dev->drv[drvidx]->online & (1 << chidx))) | ||
1634 | goto out; | ||
1635 | isdn_lock_drivers(); | ||
1636 | retval = 0; | ||
1637 | goto out; | ||
1638 | } | ||
1639 | if (minor <= ISDN_MINOR_CTRLMAX) { | ||
1640 | drvidx = isdn_minor2drv(minor - ISDN_MINOR_CTRL); | ||
1641 | if (drvidx < 0) | ||
1642 | goto out; | ||
1643 | isdn_lock_drivers(); | ||
1644 | retval = 0; | ||
1645 | goto out; | ||
1646 | } | ||
1647 | #ifdef CONFIG_ISDN_PPP | ||
1648 | if (minor <= ISDN_MINOR_PPPMAX) { | ||
1649 | retval = isdn_ppp_open(minor - ISDN_MINOR_PPP, filep); | ||
1650 | if (retval == 0) | ||
1651 | isdn_lock_drivers(); | ||
1652 | goto out; | ||
1653 | } | ||
1654 | #endif | ||
1655 | out: | ||
1656 | nonseekable_open(ino, filep); | ||
1657 | return retval; | ||
1658 | } | ||
1659 | |||
1660 | static int | ||
1661 | isdn_close(struct inode *ino, struct file *filep) | ||
1662 | { | ||
1663 | uint minor = MINOR(ino->i_rdev); | ||
1664 | |||
1665 | lock_kernel(); | ||
1666 | if (minor == ISDN_MINOR_STATUS) { | ||
1667 | infostruct *p = dev->infochain; | ||
1668 | infostruct *q = NULL; | ||
1669 | |||
1670 | while (p) { | ||
1671 | if (p->private == (char *) &(filep->private_data)) { | ||
1672 | if (q) | ||
1673 | q->next = p->next; | ||
1674 | else | ||
1675 | dev->infochain = (infostruct *) (p->next); | ||
1676 | kfree(p); | ||
1677 | goto out; | ||
1678 | } | ||
1679 | q = p; | ||
1680 | p = (infostruct *) (p->next); | ||
1681 | } | ||
1682 | printk(KERN_WARNING "isdn: No private data while closing isdnctrl\n"); | ||
1683 | goto out; | ||
1684 | } | ||
1685 | isdn_unlock_drivers(); | ||
1686 | if (minor <= ISDN_MINOR_BMAX) | ||
1687 | goto out; | ||
1688 | if (minor <= ISDN_MINOR_CTRLMAX) { | ||
1689 | if (dev->profd == current) | ||
1690 | dev->profd = NULL; | ||
1691 | goto out; | ||
1692 | } | ||
1693 | #ifdef CONFIG_ISDN_PPP | ||
1694 | if (minor <= ISDN_MINOR_PPPMAX) | ||
1695 | isdn_ppp_release(minor - ISDN_MINOR_PPP, filep); | ||
1696 | #endif | ||
1697 | |||
1698 | out: | ||
1699 | unlock_kernel(); | ||
1700 | return 0; | ||
1701 | } | ||
1702 | |||
1703 | static struct file_operations isdn_fops = | ||
1704 | { | ||
1705 | .owner = THIS_MODULE, | ||
1706 | .llseek = no_llseek, | ||
1707 | .read = isdn_read, | ||
1708 | .write = isdn_write, | ||
1709 | .poll = isdn_poll, | ||
1710 | .ioctl = isdn_ioctl, | ||
1711 | .open = isdn_open, | ||
1712 | .release = isdn_close, | ||
1713 | }; | ||
1714 | |||
1715 | char * | ||
1716 | isdn_map_eaz2msn(char *msn, int di) | ||
1717 | { | ||
1718 | isdn_driver_t *this = dev->drv[di]; | ||
1719 | int i; | ||
1720 | |||
1721 | if (strlen(msn) == 1) { | ||
1722 | i = msn[0] - '0'; | ||
1723 | if ((i >= 0) && (i <= 9)) | ||
1724 | if (strlen(this->msn2eaz[i])) | ||
1725 | return (this->msn2eaz[i]); | ||
1726 | } | ||
1727 | return (msn); | ||
1728 | } | ||
1729 | |||
1730 | /* | ||
1731 | * Find an unused ISDN-channel, whose feature-flags match the | ||
1732 | * given L2- and L3-protocols. | ||
1733 | */ | ||
1734 | #define L2V (~(ISDN_FEATURE_L2_V11096|ISDN_FEATURE_L2_V11019|ISDN_FEATURE_L2_V11038)) | ||
1735 | |||
1736 | /* | ||
1737 | * This function must be called with holding the dev->lock. | ||
1738 | */ | ||
1739 | int | ||
1740 | isdn_get_free_channel(int usage, int l2_proto, int l3_proto, int pre_dev | ||
1741 | ,int pre_chan, char *msn) | ||
1742 | { | ||
1743 | int i; | ||
1744 | ulong features; | ||
1745 | ulong vfeatures; | ||
1746 | |||
1747 | features = ((1 << l2_proto) | (0x10000 << l3_proto)); | ||
1748 | vfeatures = (((1 << l2_proto) | (0x10000 << l3_proto)) & | ||
1749 | ~(ISDN_FEATURE_L2_V11096|ISDN_FEATURE_L2_V11019|ISDN_FEATURE_L2_V11038)); | ||
1750 | /* If Layer-2 protocol is V.110, accept drivers with | ||
1751 | * transparent feature even if these don't support V.110 | ||
1752 | * because we can emulate this in linklevel. | ||
1753 | */ | ||
1754 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) | ||
1755 | if (USG_NONE(dev->usage[i]) && | ||
1756 | (dev->drvmap[i] != -1)) { | ||
1757 | int d = dev->drvmap[i]; | ||
1758 | if ((dev->usage[i] & ISDN_USAGE_EXCLUSIVE) && | ||
1759 | ((pre_dev != d) || (pre_chan != dev->chanmap[i]))) | ||
1760 | continue; | ||
1761 | if (!strcmp(isdn_map_eaz2msn(msn, d), "-")) | ||
1762 | continue; | ||
1763 | if (dev->usage[i] & ISDN_USAGE_DISABLED) | ||
1764 | continue; /* usage not allowed */ | ||
1765 | if (dev->drv[d]->flags & DRV_FLAG_RUNNING) { | ||
1766 | if (((dev->drv[d]->interface->features & features) == features) || | ||
1767 | (((dev->drv[d]->interface->features & vfeatures) == vfeatures) && | ||
1768 | (dev->drv[d]->interface->features & ISDN_FEATURE_L2_TRANS))) { | ||
1769 | if ((pre_dev < 0) || (pre_chan < 0)) { | ||
1770 | dev->usage[i] &= ISDN_USAGE_EXCLUSIVE; | ||
1771 | dev->usage[i] |= usage; | ||
1772 | isdn_info_update(); | ||
1773 | return i; | ||
1774 | } else { | ||
1775 | if ((pre_dev == d) && (pre_chan == dev->chanmap[i])) { | ||
1776 | dev->usage[i] &= ISDN_USAGE_EXCLUSIVE; | ||
1777 | dev->usage[i] |= usage; | ||
1778 | isdn_info_update(); | ||
1779 | return i; | ||
1780 | } | ||
1781 | } | ||
1782 | } | ||
1783 | } | ||
1784 | } | ||
1785 | return -1; | ||
1786 | } | ||
1787 | |||
1788 | /* | ||
1789 | * Set state of ISDN-channel to 'unused' | ||
1790 | */ | ||
1791 | void | ||
1792 | isdn_free_channel(int di, int ch, int usage) | ||
1793 | { | ||
1794 | int i; | ||
1795 | |||
1796 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) | ||
1797 | if (((!usage) || ((dev->usage[i] & ISDN_USAGE_MASK) == usage)) && | ||
1798 | (dev->drvmap[i] == di) && | ||
1799 | (dev->chanmap[i] == ch)) { | ||
1800 | dev->usage[i] &= (ISDN_USAGE_NONE | ISDN_USAGE_EXCLUSIVE); | ||
1801 | strcpy(dev->num[i], "???"); | ||
1802 | dev->ibytes[i] = 0; | ||
1803 | dev->obytes[i] = 0; | ||
1804 | // 20.10.99 JIM, try to reinitialize v110 ! | ||
1805 | dev->v110emu[i] = 0; | ||
1806 | atomic_set(&(dev->v110use[i]), 0); | ||
1807 | isdn_v110_close(dev->v110[i]); | ||
1808 | dev->v110[i] = NULL; | ||
1809 | // 20.10.99 JIM, try to reinitialize v110 ! | ||
1810 | isdn_info_update(); | ||
1811 | skb_queue_purge(&dev->drv[di]->rpqueue[ch]); | ||
1812 | } | ||
1813 | } | ||
1814 | |||
1815 | /* | ||
1816 | * Cancel Exclusive-Flag for ISDN-channel | ||
1817 | */ | ||
1818 | void | ||
1819 | isdn_unexclusive_channel(int di, int ch) | ||
1820 | { | ||
1821 | int i; | ||
1822 | |||
1823 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) | ||
1824 | if ((dev->drvmap[i] == di) && | ||
1825 | (dev->chanmap[i] == ch)) { | ||
1826 | dev->usage[i] &= ~ISDN_USAGE_EXCLUSIVE; | ||
1827 | isdn_info_update(); | ||
1828 | return; | ||
1829 | } | ||
1830 | } | ||
1831 | |||
1832 | /* | ||
1833 | * writebuf replacement for SKB_ABLE drivers | ||
1834 | */ | ||
1835 | static int | ||
1836 | isdn_writebuf_stub(int drvidx, int chan, const u_char __user * buf, int len) | ||
1837 | { | ||
1838 | int ret; | ||
1839 | int hl = dev->drv[drvidx]->interface->hl_hdrlen; | ||
1840 | struct sk_buff *skb = alloc_skb(hl + len, GFP_ATOMIC); | ||
1841 | |||
1842 | if (!skb) | ||
1843 | return 0; | ||
1844 | skb_reserve(skb, hl); | ||
1845 | copy_from_user(skb_put(skb, len), buf, len); | ||
1846 | ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, 1, skb); | ||
1847 | if (ret <= 0) | ||
1848 | dev_kfree_skb(skb); | ||
1849 | if (ret > 0) | ||
1850 | dev->obytes[isdn_dc2minor(drvidx, chan)] += ret; | ||
1851 | return ret; | ||
1852 | } | ||
1853 | |||
1854 | /* | ||
1855 | * Return: length of data on success, -ERRcode on failure. | ||
1856 | */ | ||
1857 | int | ||
1858 | isdn_writebuf_skb_stub(int drvidx, int chan, int ack, struct sk_buff *skb) | ||
1859 | { | ||
1860 | int ret; | ||
1861 | struct sk_buff *nskb = NULL; | ||
1862 | int v110_ret = skb->len; | ||
1863 | int idx = isdn_dc2minor(drvidx, chan); | ||
1864 | |||
1865 | if (dev->v110[idx]) { | ||
1866 | atomic_inc(&dev->v110use[idx]); | ||
1867 | nskb = isdn_v110_encode(dev->v110[idx], skb); | ||
1868 | atomic_dec(&dev->v110use[idx]); | ||
1869 | if (!nskb) | ||
1870 | return 0; | ||
1871 | v110_ret = *((int *)nskb->data); | ||
1872 | skb_pull(nskb, sizeof(int)); | ||
1873 | if (!nskb->len) { | ||
1874 | dev_kfree_skb(nskb); | ||
1875 | return v110_ret; | ||
1876 | } | ||
1877 | /* V.110 must always be acknowledged */ | ||
1878 | ack = 1; | ||
1879 | ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, nskb); | ||
1880 | } else { | ||
1881 | int hl = dev->drv[drvidx]->interface->hl_hdrlen; | ||
1882 | |||
1883 | if( skb_headroom(skb) < hl ){ | ||
1884 | /* | ||
1885 | * This should only occur when new HL driver with | ||
1886 | * increased hl_hdrlen was loaded after netdevice | ||
1887 | * was created and connected to the new driver. | ||
1888 | * | ||
1889 | * The V.110 branch (re-allocates on its own) does | ||
1890 | * not need this | ||
1891 | */ | ||
1892 | struct sk_buff * skb_tmp; | ||
1893 | |||
1894 | skb_tmp = skb_realloc_headroom(skb, hl); | ||
1895 | printk(KERN_DEBUG "isdn_writebuf_skb_stub: reallocating headroom%s\n", skb_tmp ? "" : " failed"); | ||
1896 | if (!skb_tmp) return -ENOMEM; /* 0 better? */ | ||
1897 | ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, skb_tmp); | ||
1898 | if( ret > 0 ){ | ||
1899 | dev_kfree_skb(skb); | ||
1900 | } else { | ||
1901 | dev_kfree_skb(skb_tmp); | ||
1902 | } | ||
1903 | } else { | ||
1904 | ret = dev->drv[drvidx]->interface->writebuf_skb(drvidx, chan, ack, skb); | ||
1905 | } | ||
1906 | } | ||
1907 | if (ret > 0) { | ||
1908 | dev->obytes[idx] += ret; | ||
1909 | if (dev->v110[idx]) { | ||
1910 | atomic_inc(&dev->v110use[idx]); | ||
1911 | dev->v110[idx]->skbuser++; | ||
1912 | atomic_dec(&dev->v110use[idx]); | ||
1913 | /* For V.110 return unencoded data length */ | ||
1914 | ret = v110_ret; | ||
1915 | /* if the complete frame was send we free the skb; | ||
1916 | if not upper function will requeue the skb */ | ||
1917 | if (ret == skb->len) | ||
1918 | dev_kfree_skb(skb); | ||
1919 | } | ||
1920 | } else | ||
1921 | if (dev->v110[idx]) | ||
1922 | dev_kfree_skb(nskb); | ||
1923 | return ret; | ||
1924 | } | ||
1925 | |||
1926 | int | ||
1927 | isdn_add_channels(isdn_driver_t *d, int drvidx, int n, int adding) | ||
1928 | { | ||
1929 | int j, k, m; | ||
1930 | |||
1931 | init_waitqueue_head(&d->st_waitq); | ||
1932 | if (d->flags & DRV_FLAG_RUNNING) | ||
1933 | return -1; | ||
1934 | if (n < 1) return 0; | ||
1935 | |||
1936 | m = (adding) ? d->channels + n : n; | ||
1937 | |||
1938 | if (dev->channels + n > ISDN_MAX_CHANNELS) { | ||
1939 | printk(KERN_WARNING "register_isdn: Max. %d channels supported\n", | ||
1940 | ISDN_MAX_CHANNELS); | ||
1941 | return -1; | ||
1942 | } | ||
1943 | |||
1944 | if ((adding) && (d->rcverr)) | ||
1945 | kfree(d->rcverr); | ||
1946 | if (!(d->rcverr = kmalloc(sizeof(int) * m, GFP_ATOMIC))) { | ||
1947 | printk(KERN_WARNING "register_isdn: Could not alloc rcverr\n"); | ||
1948 | return -1; | ||
1949 | } | ||
1950 | memset((char *) d->rcverr, 0, sizeof(int) * m); | ||
1951 | |||
1952 | if ((adding) && (d->rcvcount)) | ||
1953 | kfree(d->rcvcount); | ||
1954 | if (!(d->rcvcount = kmalloc(sizeof(int) * m, GFP_ATOMIC))) { | ||
1955 | printk(KERN_WARNING "register_isdn: Could not alloc rcvcount\n"); | ||
1956 | if (!adding) kfree(d->rcverr); | ||
1957 | return -1; | ||
1958 | } | ||
1959 | memset((char *) d->rcvcount, 0, sizeof(int) * m); | ||
1960 | |||
1961 | if ((adding) && (d->rpqueue)) { | ||
1962 | for (j = 0; j < d->channels; j++) | ||
1963 | skb_queue_purge(&d->rpqueue[j]); | ||
1964 | kfree(d->rpqueue); | ||
1965 | } | ||
1966 | if (!(d->rpqueue = kmalloc(sizeof(struct sk_buff_head) * m, GFP_ATOMIC))) { | ||
1967 | printk(KERN_WARNING "register_isdn: Could not alloc rpqueue\n"); | ||
1968 | if (!adding) { | ||
1969 | kfree(d->rcvcount); | ||
1970 | kfree(d->rcverr); | ||
1971 | } | ||
1972 | return -1; | ||
1973 | } | ||
1974 | for (j = 0; j < m; j++) { | ||
1975 | skb_queue_head_init(&d->rpqueue[j]); | ||
1976 | } | ||
1977 | |||
1978 | if ((adding) && (d->rcv_waitq)) | ||
1979 | kfree(d->rcv_waitq); | ||
1980 | d->rcv_waitq = kmalloc(sizeof(wait_queue_head_t) * 2 * m, GFP_ATOMIC); | ||
1981 | if (!d->rcv_waitq) { | ||
1982 | printk(KERN_WARNING "register_isdn: Could not alloc rcv_waitq\n"); | ||
1983 | if (!adding) { | ||
1984 | kfree(d->rpqueue); | ||
1985 | kfree(d->rcvcount); | ||
1986 | kfree(d->rcverr); | ||
1987 | } | ||
1988 | return -1; | ||
1989 | } | ||
1990 | d->snd_waitq = d->rcv_waitq + m; | ||
1991 | for (j = 0; j < m; j++) { | ||
1992 | init_waitqueue_head(&d->rcv_waitq[j]); | ||
1993 | init_waitqueue_head(&d->snd_waitq[j]); | ||
1994 | } | ||
1995 | |||
1996 | dev->channels += n; | ||
1997 | for (j = d->channels; j < m; j++) | ||
1998 | for (k = 0; k < ISDN_MAX_CHANNELS; k++) | ||
1999 | if (dev->chanmap[k] < 0) { | ||
2000 | dev->chanmap[k] = j; | ||
2001 | dev->drvmap[k] = drvidx; | ||
2002 | break; | ||
2003 | } | ||
2004 | d->channels = m; | ||
2005 | return 0; | ||
2006 | } | ||
2007 | |||
2008 | /* | ||
2009 | * Low-level-driver registration | ||
2010 | */ | ||
2011 | |||
2012 | static void | ||
2013 | set_global_features(void) | ||
2014 | { | ||
2015 | int drvidx; | ||
2016 | |||
2017 | dev->global_features = 0; | ||
2018 | for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++) { | ||
2019 | if (!dev->drv[drvidx]) | ||
2020 | continue; | ||
2021 | if (dev->drv[drvidx]->interface) | ||
2022 | dev->global_features |= dev->drv[drvidx]->interface->features; | ||
2023 | } | ||
2024 | } | ||
2025 | |||
2026 | #ifdef CONFIG_ISDN_DIVERSION | ||
2027 | |||
2028 | static char *map_drvname(int di) | ||
2029 | { | ||
2030 | if ((di < 0) || (di >= ISDN_MAX_DRIVERS)) | ||
2031 | return(NULL); | ||
2032 | return(dev->drvid[di]); /* driver name */ | ||
2033 | } /* map_drvname */ | ||
2034 | |||
2035 | static int map_namedrv(char *id) | ||
2036 | { int i; | ||
2037 | |||
2038 | for (i = 0; i < ISDN_MAX_DRIVERS; i++) | ||
2039 | { if (!strcmp(dev->drvid[i],id)) | ||
2040 | return(i); | ||
2041 | } | ||
2042 | return(-1); | ||
2043 | } /* map_namedrv */ | ||
2044 | |||
2045 | int DIVERT_REG_NAME(isdn_divert_if *i_div) | ||
2046 | { | ||
2047 | if (i_div->if_magic != DIVERT_IF_MAGIC) | ||
2048 | return(DIVERT_VER_ERR); | ||
2049 | switch (i_div->cmd) | ||
2050 | { | ||
2051 | case DIVERT_CMD_REL: | ||
2052 | if (divert_if != i_div) | ||
2053 | return(DIVERT_REL_ERR); | ||
2054 | divert_if = NULL; /* free interface */ | ||
2055 | return(DIVERT_NO_ERR); | ||
2056 | |||
2057 | case DIVERT_CMD_REG: | ||
2058 | if (divert_if) | ||
2059 | return(DIVERT_REG_ERR); | ||
2060 | i_div->ll_cmd = isdn_command; /* set command function */ | ||
2061 | i_div->drv_to_name = map_drvname; | ||
2062 | i_div->name_to_drv = map_namedrv; | ||
2063 | divert_if = i_div; /* remember interface */ | ||
2064 | return(DIVERT_NO_ERR); | ||
2065 | |||
2066 | default: | ||
2067 | return(DIVERT_CMD_ERR); | ||
2068 | } | ||
2069 | } /* DIVERT_REG_NAME */ | ||
2070 | |||
2071 | EXPORT_SYMBOL(DIVERT_REG_NAME); | ||
2072 | |||
2073 | #endif /* CONFIG_ISDN_DIVERSION */ | ||
2074 | |||
2075 | |||
2076 | EXPORT_SYMBOL(register_isdn); | ||
2077 | #ifdef CONFIG_ISDN_PPP | ||
2078 | EXPORT_SYMBOL(isdn_ppp_register_compressor); | ||
2079 | EXPORT_SYMBOL(isdn_ppp_unregister_compressor); | ||
2080 | #endif | ||
2081 | |||
2082 | int | ||
2083 | register_isdn(isdn_if * i) | ||
2084 | { | ||
2085 | isdn_driver_t *d; | ||
2086 | int j; | ||
2087 | ulong flags; | ||
2088 | int drvidx; | ||
2089 | |||
2090 | if (dev->drivers >= ISDN_MAX_DRIVERS) { | ||
2091 | printk(KERN_WARNING "register_isdn: Max. %d drivers supported\n", | ||
2092 | ISDN_MAX_DRIVERS); | ||
2093 | return 0; | ||
2094 | } | ||
2095 | if (!i->writebuf_skb) { | ||
2096 | printk(KERN_WARNING "register_isdn: No write routine given.\n"); | ||
2097 | return 0; | ||
2098 | } | ||
2099 | if (!(d = kmalloc(sizeof(isdn_driver_t), GFP_KERNEL))) { | ||
2100 | printk(KERN_WARNING "register_isdn: Could not alloc driver-struct\n"); | ||
2101 | return 0; | ||
2102 | } | ||
2103 | memset((char *) d, 0, sizeof(isdn_driver_t)); | ||
2104 | |||
2105 | d->maxbufsize = i->maxbufsize; | ||
2106 | d->pktcount = 0; | ||
2107 | d->stavail = 0; | ||
2108 | d->flags = DRV_FLAG_LOADED; | ||
2109 | d->online = 0; | ||
2110 | d->interface = i; | ||
2111 | d->channels = 0; | ||
2112 | spin_lock_irqsave(&dev->lock, flags); | ||
2113 | for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++) | ||
2114 | if (!dev->drv[drvidx]) | ||
2115 | break; | ||
2116 | if (isdn_add_channels(d, drvidx, i->channels, 0)) { | ||
2117 | spin_unlock_irqrestore(&dev->lock, flags); | ||
2118 | kfree(d); | ||
2119 | return 0; | ||
2120 | } | ||
2121 | i->channels = drvidx; | ||
2122 | i->rcvcallb_skb = isdn_receive_skb_callback; | ||
2123 | i->statcallb = isdn_status_callback; | ||
2124 | if (!strlen(i->id)) | ||
2125 | sprintf(i->id, "line%d", drvidx); | ||
2126 | for (j = 0; j < drvidx; j++) | ||
2127 | if (!strcmp(i->id, dev->drvid[j])) | ||
2128 | sprintf(i->id, "line%d", drvidx); | ||
2129 | dev->drv[drvidx] = d; | ||
2130 | strcpy(dev->drvid[drvidx], i->id); | ||
2131 | isdn_info_update(); | ||
2132 | dev->drivers++; | ||
2133 | set_global_features(); | ||
2134 | spin_unlock_irqrestore(&dev->lock, flags); | ||
2135 | return 1; | ||
2136 | } | ||
2137 | |||
2138 | /* | ||
2139 | ***************************************************************************** | ||
2140 | * And now the modules code. | ||
2141 | ***************************************************************************** | ||
2142 | */ | ||
2143 | |||
2144 | static char * | ||
2145 | isdn_getrev(const char *revision) | ||
2146 | { | ||
2147 | char *rev; | ||
2148 | char *p; | ||
2149 | |||
2150 | if ((p = strchr(revision, ':'))) { | ||
2151 | rev = p + 2; | ||
2152 | p = strchr(rev, '$'); | ||
2153 | *--p = 0; | ||
2154 | } else | ||
2155 | rev = "???"; | ||
2156 | return rev; | ||
2157 | } | ||
2158 | |||
2159 | /* | ||
2160 | * Allocate and initialize all data, register modem-devices | ||
2161 | */ | ||
2162 | static int __init isdn_init(void) | ||
2163 | { | ||
2164 | int i; | ||
2165 | char tmprev[50]; | ||
2166 | |||
2167 | if (!(dev = (isdn_dev *) vmalloc(sizeof(isdn_dev)))) { | ||
2168 | printk(KERN_WARNING "isdn: Could not allocate device-struct.\n"); | ||
2169 | return -EIO; | ||
2170 | } | ||
2171 | memset((char *) dev, 0, sizeof(isdn_dev)); | ||
2172 | init_timer(&dev->timer); | ||
2173 | dev->timer.function = isdn_timer_funct; | ||
2174 | spin_lock_init(&dev->lock); | ||
2175 | spin_lock_init(&dev->timerlock); | ||
2176 | #ifdef MODULE | ||
2177 | dev->owner = THIS_MODULE; | ||
2178 | #endif | ||
2179 | init_MUTEX(&dev->sem); | ||
2180 | init_waitqueue_head(&dev->info_waitq); | ||
2181 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
2182 | dev->drvmap[i] = -1; | ||
2183 | dev->chanmap[i] = -1; | ||
2184 | dev->m_idx[i] = -1; | ||
2185 | strcpy(dev->num[i], "???"); | ||
2186 | init_waitqueue_head(&dev->mdm.info[i].open_wait); | ||
2187 | init_waitqueue_head(&dev->mdm.info[i].close_wait); | ||
2188 | } | ||
2189 | if (register_chrdev(ISDN_MAJOR, "isdn", &isdn_fops)) { | ||
2190 | printk(KERN_WARNING "isdn: Could not register control devices\n"); | ||
2191 | vfree(dev); | ||
2192 | return -EIO; | ||
2193 | } | ||
2194 | if ((isdn_tty_modem_init()) < 0) { | ||
2195 | printk(KERN_WARNING "isdn: Could not register tty devices\n"); | ||
2196 | vfree(dev); | ||
2197 | unregister_chrdev(ISDN_MAJOR, "isdn"); | ||
2198 | return -EIO; | ||
2199 | } | ||
2200 | #ifdef CONFIG_ISDN_PPP | ||
2201 | if (isdn_ppp_init() < 0) { | ||
2202 | printk(KERN_WARNING "isdn: Could not create PPP-device-structs\n"); | ||
2203 | isdn_tty_exit(); | ||
2204 | unregister_chrdev(ISDN_MAJOR, "isdn"); | ||
2205 | vfree(dev); | ||
2206 | return -EIO; | ||
2207 | } | ||
2208 | #endif /* CONFIG_ISDN_PPP */ | ||
2209 | |||
2210 | strcpy(tmprev, isdn_revision); | ||
2211 | printk(KERN_NOTICE "ISDN subsystem Rev: %s/", isdn_getrev(tmprev)); | ||
2212 | strcpy(tmprev, isdn_tty_revision); | ||
2213 | printk("%s/", isdn_getrev(tmprev)); | ||
2214 | strcpy(tmprev, isdn_net_revision); | ||
2215 | printk("%s/", isdn_getrev(tmprev)); | ||
2216 | strcpy(tmprev, isdn_ppp_revision); | ||
2217 | printk("%s/", isdn_getrev(tmprev)); | ||
2218 | strcpy(tmprev, isdn_audio_revision); | ||
2219 | printk("%s/", isdn_getrev(tmprev)); | ||
2220 | strcpy(tmprev, isdn_v110_revision); | ||
2221 | printk("%s", isdn_getrev(tmprev)); | ||
2222 | |||
2223 | #ifdef MODULE | ||
2224 | printk(" loaded\n"); | ||
2225 | #else | ||
2226 | printk("\n"); | ||
2227 | #endif | ||
2228 | isdn_info_update(); | ||
2229 | return 0; | ||
2230 | } | ||
2231 | |||
2232 | /* | ||
2233 | * Unload module | ||
2234 | */ | ||
2235 | static void __exit isdn_exit(void) | ||
2236 | { | ||
2237 | #ifdef CONFIG_ISDN_PPP | ||
2238 | isdn_ppp_cleanup(); | ||
2239 | #endif | ||
2240 | if (isdn_net_rmall() < 0) { | ||
2241 | printk(KERN_WARNING "isdn: net-device busy, remove cancelled\n"); | ||
2242 | return; | ||
2243 | } | ||
2244 | isdn_tty_exit(); | ||
2245 | unregister_chrdev(ISDN_MAJOR, "isdn"); | ||
2246 | del_timer(&dev->timer); | ||
2247 | /* call vfree with interrupts enabled, else it will hang */ | ||
2248 | vfree(dev); | ||
2249 | printk(KERN_NOTICE "ISDN-subsystem unloaded\n"); | ||
2250 | } | ||
2251 | |||
2252 | module_init(isdn_init); | ||
2253 | 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 000000000000..808135c427ad --- /dev/null +++ b/drivers/isdn/i4l/isdn_common.h | |||
@@ -0,0 +1,47 @@ | |||
1 | /* $Id: isdn_common.h,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $ | ||
2 | * | ||
3 | * header for Linux ISDN subsystem | ||
4 | * common used functions and debugging-switches (linklevel). | ||
5 | * | ||
6 | * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de) | ||
7 | * Copyright 1995,96 by Thinking Objects Software GmbH Wuerzburg | ||
8 | * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de) | ||
9 | * | ||
10 | * This software may be used and distributed according to the terms | ||
11 | * of the GNU General Public License, incorporated herein by reference. | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #undef ISDN_DEBUG_MODEM_OPEN | ||
16 | #undef ISDN_DEBUG_MODEM_IOCTL | ||
17 | #undef ISDN_DEBUG_MODEM_WAITSENT | ||
18 | #undef ISDN_DEBUG_MODEM_HUP | ||
19 | #undef ISDN_DEBUG_MODEM_ICALL | ||
20 | #undef ISDN_DEBUG_MODEM_DUMP | ||
21 | #undef ISDN_DEBUG_MODEM_VOICE | ||
22 | #undef ISDN_DEBUG_AT | ||
23 | #undef ISDN_DEBUG_NET_DUMP | ||
24 | #undef ISDN_DEBUG_NET_DIAL | ||
25 | #undef ISDN_DEBUG_NET_ICALL | ||
26 | |||
27 | /* Prototypes */ | ||
28 | extern void isdn_lock_drivers(void); | ||
29 | extern void isdn_unlock_drivers(void); | ||
30 | extern void isdn_free_channel(int di, int ch, int usage); | ||
31 | extern void isdn_all_eaz(int di, int ch); | ||
32 | extern int isdn_command(isdn_ctrl *); | ||
33 | extern int isdn_dc2minor(int di, int ch); | ||
34 | extern void isdn_info_update(void); | ||
35 | extern char *isdn_map_eaz2msn(char *msn, int di); | ||
36 | extern void isdn_timer_ctrl(int tf, int onoff); | ||
37 | extern void isdn_unexclusive_channel(int di, int ch); | ||
38 | extern int isdn_getnum(char **); | ||
39 | extern int isdn_readbchan(int, int, u_char *, u_char *, int, wait_queue_head_t *); | ||
40 | extern int isdn_get_free_channel(int, int, int, int, int, char *); | ||
41 | extern int isdn_writebuf_skb_stub(int, int, int, struct sk_buff *); | ||
42 | extern int register_isdn(isdn_if * i); | ||
43 | extern int isdn_msncmp( const char *, const char *); | ||
44 | extern int isdn_add_channels(isdn_driver_t *, int, int, int); | ||
45 | #if defined(ISDN_DEBUG_NET_DUMP) || defined(ISDN_DEBUG_MODEM_DUMP) | ||
46 | extern void isdn_dumppkt(char *, u_char *, int, int); | ||
47 | #endif | ||
diff --git a/drivers/isdn/i4l/isdn_concap.c b/drivers/isdn/i4l/isdn_concap.c new file mode 100644 index 000000000000..83a4f5382bc2 --- /dev/null +++ b/drivers/isdn/i4l/isdn_concap.c | |||
@@ -0,0 +1,108 @@ | |||
1 | /* $Id: isdn_concap.c,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $ | ||
2 | * | ||
3 | * Linux ISDN subsystem, protocol encapsulation | ||
4 | * | ||
5 | * This software may be used and distributed according to the terms | ||
6 | * of the GNU General Public License, incorporated herein by reference. | ||
7 | * | ||
8 | */ | ||
9 | |||
10 | /* Stuff to support the concap_proto by isdn4linux. isdn4linux - specific | ||
11 | * stuff goes here. Stuff that depends only on the concap protocol goes to | ||
12 | * another -- protocol specific -- source file. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | |||
17 | #include <linux/isdn.h> | ||
18 | #include "isdn_x25iface.h" | ||
19 | #include "isdn_net.h" | ||
20 | #include <linux/concap.h> | ||
21 | #include "isdn_concap.h" | ||
22 | |||
23 | |||
24 | /* The following set of device service operations are for encapsulation | ||
25 | protocols that require for reliable datalink semantics. That means: | ||
26 | |||
27 | - before any data is to be submitted the connection must explicitly | ||
28 | be set up. | ||
29 | - after the successful set up of the connection is signalled the | ||
30 | connection is considered to be reliably up. | ||
31 | |||
32 | Auto-dialing ist not compatible with this requirements. Thus, auto-dialing | ||
33 | is completely bypassed. | ||
34 | |||
35 | It might be possible to implement a (non standardized) datalink protocol | ||
36 | that provides a reliable data link service while using some auto dialing | ||
37 | mechanism. Such a protocol would need an auxiliary channel (i.e. user-user- | ||
38 | signaling on the D-channel) while the B-channel is down. | ||
39 | */ | ||
40 | |||
41 | |||
42 | int isdn_concap_dl_data_req(struct concap_proto *concap, struct sk_buff *skb) | ||
43 | { | ||
44 | struct net_device *ndev = concap -> net_dev; | ||
45 | isdn_net_dev *nd = ((isdn_net_local *) ndev->priv)->netdev; | ||
46 | isdn_net_local *lp = isdn_net_get_locked_lp(nd); | ||
47 | |||
48 | IX25DEBUG( "isdn_concap_dl_data_req: %s \n", concap->net_dev->name); | ||
49 | if (!lp) { | ||
50 | IX25DEBUG( "isdn_concap_dl_data_req: %s : isdn_net_send_skb returned %d\n", concap -> net_dev -> name, 1); | ||
51 | return 1; | ||
52 | } | ||
53 | lp->huptimer = 0; | ||
54 | isdn_net_writebuf_skb(lp, skb); | ||
55 | spin_unlock_bh(&lp->xmit_lock); | ||
56 | IX25DEBUG( "isdn_concap_dl_data_req: %s : isdn_net_send_skb returned %d\n", concap -> net_dev -> name, 0); | ||
57 | return 0; | ||
58 | } | ||
59 | |||
60 | |||
61 | int isdn_concap_dl_connect_req(struct concap_proto *concap) | ||
62 | { | ||
63 | struct net_device *ndev = concap -> net_dev; | ||
64 | isdn_net_local *lp = (isdn_net_local *) ndev->priv; | ||
65 | int ret; | ||
66 | IX25DEBUG( "isdn_concap_dl_connect_req: %s \n", ndev -> name); | ||
67 | |||
68 | /* dial ... */ | ||
69 | ret = isdn_net_dial_req( lp ); | ||
70 | if ( ret ) IX25DEBUG("dialing failed\n"); | ||
71 | return ret; | ||
72 | } | ||
73 | |||
74 | int isdn_concap_dl_disconn_req(struct concap_proto *concap) | ||
75 | { | ||
76 | IX25DEBUG( "isdn_concap_dl_disconn_req: %s \n", concap -> net_dev -> name); | ||
77 | |||
78 | isdn_net_hangup( concap -> net_dev ); | ||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | struct concap_device_ops isdn_concap_reliable_dl_dops = { | ||
83 | &isdn_concap_dl_data_req, | ||
84 | &isdn_concap_dl_connect_req, | ||
85 | &isdn_concap_dl_disconn_req | ||
86 | }; | ||
87 | |||
88 | struct concap_device_ops isdn_concap_demand_dial_dops = { | ||
89 | NULL, /* set this first entry to something like &isdn_net_start_xmit, | ||
90 | but the entry part of the current isdn_net_start_xmit must be | ||
91 | separated first. */ | ||
92 | /* no connection control for demand dial semantics */ | ||
93 | NULL, | ||
94 | NULL, | ||
95 | }; | ||
96 | |||
97 | /* The following should better go into a dedicated source file such that | ||
98 | this sourcefile does not need to include any protocol specific header | ||
99 | files. For now: | ||
100 | */ | ||
101 | struct concap_proto * isdn_concap_new( int encap ) | ||
102 | { | ||
103 | switch ( encap ) { | ||
104 | case ISDN_NET_ENCAP_X25IFACE: | ||
105 | return isdn_x25iface_proto_new(); | ||
106 | } | ||
107 | return NULL; | ||
108 | } | ||
diff --git a/drivers/isdn/i4l/isdn_concap.h b/drivers/isdn/i4l/isdn_concap.h new file mode 100644 index 000000000000..306eb180438f --- /dev/null +++ b/drivers/isdn/i4l/isdn_concap.h | |||
@@ -0,0 +1,14 @@ | |||
1 | /* $Id: isdn_concap.h,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $ | ||
2 | * | ||
3 | * Linux ISDN subsystem, protocol encapsulation | ||
4 | * | ||
5 | * This software may be used and distributed according to the terms | ||
6 | * of the GNU General Public License, incorporated herein by reference. | ||
7 | * | ||
8 | */ | ||
9 | |||
10 | extern struct concap_device_ops isdn_concap_reliable_dl_dops; | ||
11 | extern struct concap_device_ops isdn_concap_demand_dial_dops; | ||
12 | extern struct concap_proto * isdn_concap_new( int ); | ||
13 | |||
14 | |||
diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c new file mode 100644 index 000000000000..e2b790e34510 --- /dev/null +++ b/drivers/isdn/i4l/isdn_net.c | |||
@@ -0,0 +1,3222 @@ | |||
1 | /* $Id: isdn_net.c,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $ | ||
2 | * | ||
3 | * Linux ISDN subsystem, network interfaces and related functions (linklevel). | ||
4 | * | ||
5 | * Copyright 1994-1998 by Fritz Elfert (fritz@isdn4linux.de) | ||
6 | * Copyright 1995,96 by Thinking Objects Software GmbH Wuerzburg | ||
7 | * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de) | ||
8 | * | ||
9 | * This software may be used and distributed according to the terms | ||
10 | * of the GNU General Public License, incorporated herein by reference. | ||
11 | * | ||
12 | * Data Over Voice (DOV) support added - Guy Ellis 23-Mar-02 | ||
13 | * guy@traverse.com.au | ||
14 | * Outgoing calls - looks for a 'V' in first char of dialed number | ||
15 | * Incoming calls - checks first character of eaz as follows: | ||
16 | * Numeric - accept DATA only - original functionality | ||
17 | * 'V' - accept VOICE (DOV) only | ||
18 | * 'B' - accept BOTH DATA and DOV types | ||
19 | * | ||
20 | * Jan 2001: fix CISCO HDLC Bjoern A. Zeeb <i4l@zabbadoz.net> | ||
21 | * for info on the protocol, see | ||
22 | * http://i4l.zabbadoz.net/i4l/cisco-hdlc.txt | ||
23 | */ | ||
24 | |||
25 | #include <linux/config.h> | ||
26 | #include <linux/isdn.h> | ||
27 | #include <net/arp.h> | ||
28 | #include <net/dst.h> | ||
29 | #include <net/pkt_sched.h> | ||
30 | #include <linux/inetdevice.h> | ||
31 | #include "isdn_common.h" | ||
32 | #include "isdn_net.h" | ||
33 | #ifdef CONFIG_ISDN_PPP | ||
34 | #include "isdn_ppp.h" | ||
35 | #endif | ||
36 | #ifdef CONFIG_ISDN_X25 | ||
37 | #include <linux/concap.h> | ||
38 | #include "isdn_concap.h" | ||
39 | #endif | ||
40 | |||
41 | |||
42 | /* | ||
43 | * Outline of new tbusy handling: | ||
44 | * | ||
45 | * Old method, roughly spoken, consisted of setting tbusy when entering | ||
46 | * isdn_net_start_xmit() and at several other locations and clearing | ||
47 | * it from isdn_net_start_xmit() thread when sending was successful. | ||
48 | * | ||
49 | * With 2.3.x multithreaded network core, to prevent problems, tbusy should | ||
50 | * only be set by the isdn_net_start_xmit() thread and only when a tx-busy | ||
51 | * condition is detected. Other threads (in particular isdn_net_stat_callb()) | ||
52 | * are only allowed to clear tbusy. | ||
53 | * | ||
54 | * -HE | ||
55 | */ | ||
56 | |||
57 | /* | ||
58 | * About SOFTNET: | ||
59 | * Most of the changes were pretty obvious and basically done by HE already. | ||
60 | * | ||
61 | * One problem of the isdn net device code is that is uses struct net_device | ||
62 | * for masters and slaves. However, only master interface are registered to | ||
63 | * the network layer, and therefore, it only makes sense to call netif_* | ||
64 | * functions on them. | ||
65 | * | ||
66 | * --KG | ||
67 | */ | ||
68 | |||
69 | /* | ||
70 | * Find out if the netdevice has been ifup-ed yet. | ||
71 | * For slaves, look at the corresponding master. | ||
72 | */ | ||
73 | static __inline__ int isdn_net_device_started(isdn_net_dev *n) | ||
74 | { | ||
75 | isdn_net_local *lp = n->local; | ||
76 | struct net_device *dev; | ||
77 | |||
78 | if (lp->master) | ||
79 | dev = lp->master; | ||
80 | else | ||
81 | dev = &n->dev; | ||
82 | return netif_running(dev); | ||
83 | } | ||
84 | |||
85 | /* | ||
86 | * wake up the network -> net_device queue. | ||
87 | * For slaves, wake the corresponding master interface. | ||
88 | */ | ||
89 | static __inline__ void isdn_net_device_wake_queue(isdn_net_local *lp) | ||
90 | { | ||
91 | if (lp->master) | ||
92 | netif_wake_queue(lp->master); | ||
93 | else | ||
94 | netif_wake_queue(&lp->netdev->dev); | ||
95 | } | ||
96 | |||
97 | /* | ||
98 | * stop the network -> net_device queue. | ||
99 | * For slaves, stop the corresponding master interface. | ||
100 | */ | ||
101 | static __inline__ void isdn_net_device_stop_queue(isdn_net_local *lp) | ||
102 | { | ||
103 | if (lp->master) | ||
104 | netif_stop_queue(lp->master); | ||
105 | else | ||
106 | netif_stop_queue(&lp->netdev->dev); | ||
107 | } | ||
108 | |||
109 | /* | ||
110 | * find out if the net_device which this lp belongs to (lp can be | ||
111 | * master or slave) is busy. It's busy iff all (master and slave) | ||
112 | * queues are busy | ||
113 | */ | ||
114 | static __inline__ int isdn_net_device_busy(isdn_net_local *lp) | ||
115 | { | ||
116 | isdn_net_local *nlp; | ||
117 | isdn_net_dev *nd; | ||
118 | unsigned long flags; | ||
119 | |||
120 | if (!isdn_net_lp_busy(lp)) | ||
121 | return 0; | ||
122 | |||
123 | if (lp->master) | ||
124 | nd = ((isdn_net_local *) lp->master->priv)->netdev; | ||
125 | else | ||
126 | nd = lp->netdev; | ||
127 | |||
128 | spin_lock_irqsave(&nd->queue_lock, flags); | ||
129 | nlp = lp->next; | ||
130 | while (nlp != lp) { | ||
131 | if (!isdn_net_lp_busy(nlp)) { | ||
132 | spin_unlock_irqrestore(&nd->queue_lock, flags); | ||
133 | return 0; | ||
134 | } | ||
135 | nlp = nlp->next; | ||
136 | } | ||
137 | spin_unlock_irqrestore(&nd->queue_lock, flags); | ||
138 | return 1; | ||
139 | } | ||
140 | |||
141 | static __inline__ void isdn_net_inc_frame_cnt(isdn_net_local *lp) | ||
142 | { | ||
143 | atomic_inc(&lp->frame_cnt); | ||
144 | if (isdn_net_device_busy(lp)) | ||
145 | isdn_net_device_stop_queue(lp); | ||
146 | } | ||
147 | |||
148 | static __inline__ void isdn_net_dec_frame_cnt(isdn_net_local *lp) | ||
149 | { | ||
150 | atomic_dec(&lp->frame_cnt); | ||
151 | |||
152 | if (!(isdn_net_device_busy(lp))) { | ||
153 | if (!skb_queue_empty(&lp->super_tx_queue)) { | ||
154 | schedule_work(&lp->tqueue); | ||
155 | } else { | ||
156 | isdn_net_device_wake_queue(lp); | ||
157 | } | ||
158 | } | ||
159 | } | ||
160 | |||
161 | static __inline__ void isdn_net_zero_frame_cnt(isdn_net_local *lp) | ||
162 | { | ||
163 | atomic_set(&lp->frame_cnt, 0); | ||
164 | } | ||
165 | |||
166 | /* For 2.2.x we leave the transmitter busy timeout at 2 secs, just | ||
167 | * to be safe. | ||
168 | * For 2.3.x we push it up to 20 secs, because call establishment | ||
169 | * (in particular callback) may take such a long time, and we | ||
170 | * don't want confusing messages in the log. However, there is a slight | ||
171 | * possibility that this large timeout will break other things like MPPP, | ||
172 | * which might rely on the tx timeout. If so, we'll find out this way... | ||
173 | */ | ||
174 | |||
175 | #define ISDN_NET_TX_TIMEOUT (20*HZ) | ||
176 | |||
177 | /* Prototypes */ | ||
178 | |||
179 | int isdn_net_force_dial_lp(isdn_net_local *); | ||
180 | static int isdn_net_start_xmit(struct sk_buff *, struct net_device *); | ||
181 | |||
182 | static void isdn_net_ciscohdlck_connected(isdn_net_local *lp); | ||
183 | static void isdn_net_ciscohdlck_disconnected(isdn_net_local *lp); | ||
184 | |||
185 | char *isdn_net_revision = "$Revision: 1.1.2.2 $"; | ||
186 | |||
187 | /* | ||
188 | * Code for raw-networking over ISDN | ||
189 | */ | ||
190 | |||
191 | static void | ||
192 | isdn_net_unreachable(struct net_device *dev, struct sk_buff *skb, char *reason) | ||
193 | { | ||
194 | if(skb) { | ||
195 | |||
196 | u_short proto = ntohs(skb->protocol); | ||
197 | |||
198 | printk(KERN_DEBUG "isdn_net: %s: %s, signalling dst_link_failure %s\n", | ||
199 | dev->name, | ||
200 | (reason != NULL) ? reason : "unknown", | ||
201 | (proto != ETH_P_IP) ? "Protocol != ETH_P_IP" : ""); | ||
202 | |||
203 | dst_link_failure(skb); | ||
204 | } | ||
205 | else { /* dial not triggered by rawIP packet */ | ||
206 | printk(KERN_DEBUG "isdn_net: %s: %s\n", | ||
207 | dev->name, | ||
208 | (reason != NULL) ? reason : "reason unknown"); | ||
209 | } | ||
210 | } | ||
211 | |||
212 | static void | ||
213 | isdn_net_reset(struct net_device *dev) | ||
214 | { | ||
215 | #ifdef CONFIG_ISDN_X25 | ||
216 | struct concap_device_ops * dops = | ||
217 | ( (isdn_net_local *) dev->priv ) -> dops; | ||
218 | struct concap_proto * cprot = | ||
219 | ( (isdn_net_local *) dev->priv ) -> netdev -> cprot; | ||
220 | #endif | ||
221 | #ifdef CONFIG_ISDN_X25 | ||
222 | if( cprot && cprot -> pops && dops ) | ||
223 | cprot -> pops -> restart ( cprot, dev, dops ); | ||
224 | #endif | ||
225 | } | ||
226 | |||
227 | /* Open/initialize the board. */ | ||
228 | static int | ||
229 | isdn_net_open(struct net_device *dev) | ||
230 | { | ||
231 | int i; | ||
232 | struct net_device *p; | ||
233 | struct in_device *in_dev; | ||
234 | |||
235 | /* moved here from isdn_net_reset, because only the master has an | ||
236 | interface associated which is supposed to be started. BTW: | ||
237 | we need to call netif_start_queue, not netif_wake_queue here */ | ||
238 | netif_start_queue(dev); | ||
239 | |||
240 | isdn_net_reset(dev); | ||
241 | /* Fill in the MAC-level header (not needed, but for compatibility... */ | ||
242 | for (i = 0; i < ETH_ALEN - sizeof(u32); i++) | ||
243 | dev->dev_addr[i] = 0xfc; | ||
244 | if ((in_dev = dev->ip_ptr) != NULL) { | ||
245 | /* | ||
246 | * Any address will do - we take the first | ||
247 | */ | ||
248 | struct in_ifaddr *ifa = in_dev->ifa_list; | ||
249 | if (ifa != NULL) | ||
250 | memcpy(dev->dev_addr+2, &ifa->ifa_local, 4); | ||
251 | } | ||
252 | |||
253 | /* If this interface has slaves, start them also */ | ||
254 | |||
255 | if ((p = (((isdn_net_local *) dev->priv)->slave))) { | ||
256 | while (p) { | ||
257 | isdn_net_reset(p); | ||
258 | p = (((isdn_net_local *) p->priv)->slave); | ||
259 | } | ||
260 | } | ||
261 | isdn_lock_drivers(); | ||
262 | return 0; | ||
263 | } | ||
264 | |||
265 | /* | ||
266 | * Assign an ISDN-channel to a net-interface | ||
267 | */ | ||
268 | static void | ||
269 | isdn_net_bind_channel(isdn_net_local * lp, int idx) | ||
270 | { | ||
271 | lp->flags |= ISDN_NET_CONNECTED; | ||
272 | lp->isdn_device = dev->drvmap[idx]; | ||
273 | lp->isdn_channel = dev->chanmap[idx]; | ||
274 | dev->rx_netdev[idx] = lp->netdev; | ||
275 | dev->st_netdev[idx] = lp->netdev; | ||
276 | } | ||
277 | |||
278 | /* | ||
279 | * unbind a net-interface (resets interface after an error) | ||
280 | */ | ||
281 | static void | ||
282 | isdn_net_unbind_channel(isdn_net_local * lp) | ||
283 | { | ||
284 | skb_queue_purge(&lp->super_tx_queue); | ||
285 | |||
286 | if (!lp->master) { /* reset only master device */ | ||
287 | /* Moral equivalent of dev_purge_queues(): | ||
288 | BEWARE! This chunk of code cannot be called from hardware | ||
289 | interrupt handler. I hope it is true. --ANK | ||
290 | */ | ||
291 | qdisc_reset(lp->netdev->dev.qdisc); | ||
292 | } | ||
293 | lp->dialstate = 0; | ||
294 | dev->rx_netdev[isdn_dc2minor(lp->isdn_device, lp->isdn_channel)] = NULL; | ||
295 | dev->st_netdev[isdn_dc2minor(lp->isdn_device, lp->isdn_channel)] = NULL; | ||
296 | isdn_free_channel(lp->isdn_device, lp->isdn_channel, ISDN_USAGE_NET); | ||
297 | lp->flags &= ~ISDN_NET_CONNECTED; | ||
298 | lp->isdn_device = -1; | ||
299 | lp->isdn_channel = -1; | ||
300 | } | ||
301 | |||
302 | /* | ||
303 | * Perform auto-hangup and cps-calculation for net-interfaces. | ||
304 | * | ||
305 | * auto-hangup: | ||
306 | * Increment idle-counter (this counter is reset on any incoming or | ||
307 | * outgoing packet), if counter exceeds configured limit either do a | ||
308 | * hangup immediately or - if configured - wait until just before the next | ||
309 | * charge-info. | ||
310 | * | ||
311 | * cps-calculation (needed for dynamic channel-bundling): | ||
312 | * Since this function is called every second, simply reset the | ||
313 | * byte-counter of the interface after copying it to the cps-variable. | ||
314 | */ | ||
315 | unsigned long last_jiffies = -HZ; | ||
316 | |||
317 | void | ||
318 | isdn_net_autohup(void) | ||
319 | { | ||
320 | isdn_net_dev *p = dev->netdev; | ||
321 | int anymore; | ||
322 | |||
323 | anymore = 0; | ||
324 | while (p) { | ||
325 | isdn_net_local *l = p->local; | ||
326 | if (jiffies == last_jiffies) | ||
327 | l->cps = l->transcount; | ||
328 | else | ||
329 | l->cps = (l->transcount * HZ) / (jiffies - last_jiffies); | ||
330 | l->transcount = 0; | ||
331 | if (dev->net_verbose > 3) | ||
332 | printk(KERN_DEBUG "%s: %d bogocps\n", l->name, l->cps); | ||
333 | if ((l->flags & ISDN_NET_CONNECTED) && (!l->dialstate)) { | ||
334 | anymore = 1; | ||
335 | l->huptimer++; | ||
336 | /* | ||
337 | * if there is some dialmode where timeout-hangup | ||
338 | * should _not_ be done, check for that here | ||
339 | */ | ||
340 | if ((l->onhtime) && | ||
341 | (l->huptimer > l->onhtime)) | ||
342 | { | ||
343 | if (l->hupflags & ISDN_MANCHARGE && | ||
344 | l->hupflags & ISDN_CHARGEHUP) { | ||
345 | while (time_after(jiffies, l->chargetime + l->chargeint)) | ||
346 | l->chargetime += l->chargeint; | ||
347 | if (time_after(jiffies, l->chargetime + l->chargeint - 2 * HZ)) | ||
348 | if (l->outgoing || l->hupflags & ISDN_INHUP) | ||
349 | isdn_net_hangup(&p->dev); | ||
350 | } else if (l->outgoing) { | ||
351 | if (l->hupflags & ISDN_CHARGEHUP) { | ||
352 | if (l->hupflags & ISDN_WAITCHARGE) { | ||
353 | printk(KERN_DEBUG "isdn_net: Hupflags of %s are %X\n", | ||
354 | l->name, l->hupflags); | ||
355 | isdn_net_hangup(&p->dev); | ||
356 | } else if (time_after(jiffies, l->chargetime + l->chargeint)) { | ||
357 | printk(KERN_DEBUG | ||
358 | "isdn_net: %s: chtime = %lu, chint = %d\n", | ||
359 | l->name, l->chargetime, l->chargeint); | ||
360 | isdn_net_hangup(&p->dev); | ||
361 | } | ||
362 | } else | ||
363 | isdn_net_hangup(&p->dev); | ||
364 | } else if (l->hupflags & ISDN_INHUP) | ||
365 | isdn_net_hangup(&p->dev); | ||
366 | } | ||
367 | |||
368 | if(dev->global_flags & ISDN_GLOBAL_STOPPED || (ISDN_NET_DIALMODE(*l) == ISDN_NET_DM_OFF)) { | ||
369 | isdn_net_hangup(&p->dev); | ||
370 | break; | ||
371 | } | ||
372 | } | ||
373 | p = (isdn_net_dev *) p->next; | ||
374 | } | ||
375 | last_jiffies = jiffies; | ||
376 | isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, anymore); | ||
377 | } | ||
378 | |||
379 | static void isdn_net_lp_disconnected(isdn_net_local *lp) | ||
380 | { | ||
381 | isdn_net_rm_from_bundle(lp); | ||
382 | } | ||
383 | |||
384 | /* | ||
385 | * Handle status-messages from ISDN-interfacecard. | ||
386 | * This function is called from within the main-status-dispatcher | ||
387 | * isdn_status_callback, which itself is called from the low-level driver. | ||
388 | * Return: 1 = Event handled, 0 = not for us or unknown Event. | ||
389 | */ | ||
390 | int | ||
391 | isdn_net_stat_callback(int idx, isdn_ctrl *c) | ||
392 | { | ||
393 | isdn_net_dev *p = dev->st_netdev[idx]; | ||
394 | int cmd = c->command; | ||
395 | |||
396 | if (p) { | ||
397 | isdn_net_local *lp = p->local; | ||
398 | #ifdef CONFIG_ISDN_X25 | ||
399 | struct concap_proto *cprot = lp->netdev->cprot; | ||
400 | struct concap_proto_ops *pops = cprot ? cprot->pops : NULL; | ||
401 | #endif | ||
402 | switch (cmd) { | ||
403 | case ISDN_STAT_BSENT: | ||
404 | /* A packet has successfully been sent out */ | ||
405 | if ((lp->flags & ISDN_NET_CONNECTED) && | ||
406 | (!lp->dialstate)) { | ||
407 | isdn_net_dec_frame_cnt(lp); | ||
408 | lp->stats.tx_packets++; | ||
409 | lp->stats.tx_bytes += c->parm.length; | ||
410 | } | ||
411 | return 1; | ||
412 | case ISDN_STAT_DCONN: | ||
413 | /* D-Channel is up */ | ||
414 | switch (lp->dialstate) { | ||
415 | case 4: | ||
416 | case 7: | ||
417 | case 8: | ||
418 | lp->dialstate++; | ||
419 | return 1; | ||
420 | case 12: | ||
421 | lp->dialstate = 5; | ||
422 | return 1; | ||
423 | } | ||
424 | break; | ||
425 | case ISDN_STAT_DHUP: | ||
426 | /* Either D-Channel-hangup or error during dialout */ | ||
427 | #ifdef CONFIG_ISDN_X25 | ||
428 | /* If we are not connencted then dialing had | ||
429 | failed. If there are generic encap protocol | ||
430 | receiver routines signal the closure of | ||
431 | the link*/ | ||
432 | |||
433 | if( !(lp->flags & ISDN_NET_CONNECTED) | ||
434 | && pops && pops -> disconn_ind ) | ||
435 | pops -> disconn_ind(cprot); | ||
436 | #endif /* CONFIG_ISDN_X25 */ | ||
437 | if ((!lp->dialstate) && (lp->flags & ISDN_NET_CONNECTED)) { | ||
438 | if (lp->p_encap == ISDN_NET_ENCAP_CISCOHDLCK) | ||
439 | isdn_net_ciscohdlck_disconnected(lp); | ||
440 | #ifdef CONFIG_ISDN_PPP | ||
441 | if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) | ||
442 | isdn_ppp_free(lp); | ||
443 | #endif | ||
444 | isdn_net_lp_disconnected(lp); | ||
445 | isdn_all_eaz(lp->isdn_device, lp->isdn_channel); | ||
446 | printk(KERN_INFO "%s: remote hangup\n", lp->name); | ||
447 | printk(KERN_INFO "%s: Chargesum is %d\n", lp->name, | ||
448 | lp->charge); | ||
449 | isdn_net_unbind_channel(lp); | ||
450 | return 1; | ||
451 | } | ||
452 | break; | ||
453 | #ifdef CONFIG_ISDN_X25 | ||
454 | case ISDN_STAT_BHUP: | ||
455 | /* B-Channel-hangup */ | ||
456 | /* try if there are generic encap protocol | ||
457 | receiver routines and signal the closure of | ||
458 | the link */ | ||
459 | if( pops && pops -> disconn_ind ){ | ||
460 | pops -> disconn_ind(cprot); | ||
461 | return 1; | ||
462 | } | ||
463 | break; | ||
464 | #endif /* CONFIG_ISDN_X25 */ | ||
465 | case ISDN_STAT_BCONN: | ||
466 | /* B-Channel is up */ | ||
467 | isdn_net_zero_frame_cnt(lp); | ||
468 | switch (lp->dialstate) { | ||
469 | case 5: | ||
470 | case 6: | ||
471 | case 7: | ||
472 | case 8: | ||
473 | case 9: | ||
474 | case 10: | ||
475 | case 12: | ||
476 | if (lp->dialstate <= 6) { | ||
477 | dev->usage[idx] |= ISDN_USAGE_OUTGOING; | ||
478 | isdn_info_update(); | ||
479 | } else | ||
480 | dev->rx_netdev[idx] = p; | ||
481 | lp->dialstate = 0; | ||
482 | isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 1); | ||
483 | if (lp->p_encap == ISDN_NET_ENCAP_CISCOHDLCK) | ||
484 | isdn_net_ciscohdlck_connected(lp); | ||
485 | if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP) { | ||
486 | if (lp->master) { /* is lp a slave? */ | ||
487 | isdn_net_dev *nd = ((isdn_net_local *)lp->master->priv)->netdev; | ||
488 | isdn_net_add_to_bundle(nd, lp); | ||
489 | } | ||
490 | } | ||
491 | printk(KERN_INFO "isdn_net: %s connected\n", lp->name); | ||
492 | /* If first Chargeinfo comes before B-Channel connect, | ||
493 | * we correct the timestamp here. | ||
494 | */ | ||
495 | lp->chargetime = jiffies; | ||
496 | |||
497 | /* reset dial-timeout */ | ||
498 | lp->dialstarted = 0; | ||
499 | lp->dialwait_timer = 0; | ||
500 | |||
501 | #ifdef CONFIG_ISDN_PPP | ||
502 | if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) | ||
503 | isdn_ppp_wakeup_daemon(lp); | ||
504 | #endif | ||
505 | #ifdef CONFIG_ISDN_X25 | ||
506 | /* try if there are generic concap receiver routines */ | ||
507 | if( pops ) | ||
508 | if( pops->connect_ind) | ||
509 | pops->connect_ind(cprot); | ||
510 | #endif /* CONFIG_ISDN_X25 */ | ||
511 | /* ppp needs to do negotiations first */ | ||
512 | if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP) | ||
513 | isdn_net_device_wake_queue(lp); | ||
514 | return 1; | ||
515 | } | ||
516 | break; | ||
517 | case ISDN_STAT_NODCH: | ||
518 | /* No D-Channel avail. */ | ||
519 | if (lp->dialstate == 4) { | ||
520 | lp->dialstate--; | ||
521 | return 1; | ||
522 | } | ||
523 | break; | ||
524 | case ISDN_STAT_CINF: | ||
525 | /* Charge-info from TelCo. Calculate interval between | ||
526 | * charge-infos and set timestamp for last info for | ||
527 | * usage by isdn_net_autohup() | ||
528 | */ | ||
529 | lp->charge++; | ||
530 | if (lp->hupflags & ISDN_HAVECHARGE) { | ||
531 | lp->hupflags &= ~ISDN_WAITCHARGE; | ||
532 | lp->chargeint = jiffies - lp->chargetime - (2 * HZ); | ||
533 | } | ||
534 | if (lp->hupflags & ISDN_WAITCHARGE) | ||
535 | lp->hupflags |= ISDN_HAVECHARGE; | ||
536 | lp->chargetime = jiffies; | ||
537 | printk(KERN_DEBUG "isdn_net: Got CINF chargetime of %s now %lu\n", | ||
538 | lp->name, lp->chargetime); | ||
539 | return 1; | ||
540 | } | ||
541 | } | ||
542 | return 0; | ||
543 | } | ||
544 | |||
545 | /* | ||
546 | * Perform dialout for net-interfaces and timeout-handling for | ||
547 | * D-Channel-up and B-Channel-up Messages. | ||
548 | * This function is initially called from within isdn_net_start_xmit() or | ||
549 | * or isdn_net_find_icall() after initializing the dialstate for an | ||
550 | * interface. If further calls are needed, the function schedules itself | ||
551 | * for a timer-callback via isdn_timer_function(). | ||
552 | * The dialstate is also affected by incoming status-messages from | ||
553 | * the ISDN-Channel which are handled in isdn_net_stat_callback() above. | ||
554 | */ | ||
555 | void | ||
556 | isdn_net_dial(void) | ||
557 | { | ||
558 | isdn_net_dev *p = dev->netdev; | ||
559 | int anymore = 0; | ||
560 | int i; | ||
561 | isdn_ctrl cmd; | ||
562 | u_char *phone_number; | ||
563 | |||
564 | while (p) { | ||
565 | isdn_net_local *lp = p->local; | ||
566 | |||
567 | #ifdef ISDN_DEBUG_NET_DIAL | ||
568 | if (lp->dialstate) | ||
569 | printk(KERN_DEBUG "%s: dialstate=%d\n", lp->name, lp->dialstate); | ||
570 | #endif | ||
571 | switch (lp->dialstate) { | ||
572 | case 0: | ||
573 | /* Nothing to do for this interface */ | ||
574 | break; | ||
575 | case 1: | ||
576 | /* Initiate dialout. Set phone-number-pointer to first number | ||
577 | * of interface. | ||
578 | */ | ||
579 | lp->dial = lp->phone[1]; | ||
580 | if (!lp->dial) { | ||
581 | printk(KERN_WARNING "%s: phone number deleted?\n", | ||
582 | lp->name); | ||
583 | isdn_net_hangup(&p->dev); | ||
584 | break; | ||
585 | } | ||
586 | anymore = 1; | ||
587 | |||
588 | if(lp->dialtimeout > 0) | ||
589 | if(lp->dialstarted == 0 || time_after(jiffies, lp->dialstarted + lp->dialtimeout + lp->dialwait)) { | ||
590 | lp->dialstarted = jiffies; | ||
591 | lp->dialwait_timer = 0; | ||
592 | } | ||
593 | |||
594 | lp->dialstate++; | ||
595 | /* Fall through */ | ||
596 | case 2: | ||
597 | /* Prepare dialing. Clear EAZ, then set EAZ. */ | ||
598 | cmd.driver = lp->isdn_device; | ||
599 | cmd.arg = lp->isdn_channel; | ||
600 | cmd.command = ISDN_CMD_CLREAZ; | ||
601 | isdn_command(&cmd); | ||
602 | sprintf(cmd.parm.num, "%s", isdn_map_eaz2msn(lp->msn, cmd.driver)); | ||
603 | cmd.command = ISDN_CMD_SETEAZ; | ||
604 | isdn_command(&cmd); | ||
605 | lp->dialretry = 0; | ||
606 | anymore = 1; | ||
607 | lp->dialstate++; | ||
608 | /* Fall through */ | ||
609 | case 3: | ||
610 | /* Setup interface, dial current phone-number, switch to next number. | ||
611 | * If list of phone-numbers is exhausted, increment | ||
612 | * retry-counter. | ||
613 | */ | ||
614 | if(dev->global_flags & ISDN_GLOBAL_STOPPED || (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF)) { | ||
615 | char *s; | ||
616 | if (dev->global_flags & ISDN_GLOBAL_STOPPED) | ||
617 | s = "dial suppressed: isdn system stopped"; | ||
618 | else | ||
619 | s = "dial suppressed: dialmode `off'"; | ||
620 | isdn_net_unreachable(&p->dev, NULL, s); | ||
621 | isdn_net_hangup(&p->dev); | ||
622 | break; | ||
623 | } | ||
624 | cmd.driver = lp->isdn_device; | ||
625 | cmd.command = ISDN_CMD_SETL2; | ||
626 | cmd.arg = lp->isdn_channel + (lp->l2_proto << 8); | ||
627 | isdn_command(&cmd); | ||
628 | cmd.driver = lp->isdn_device; | ||
629 | cmd.command = ISDN_CMD_SETL3; | ||
630 | cmd.arg = lp->isdn_channel + (lp->l3_proto << 8); | ||
631 | isdn_command(&cmd); | ||
632 | cmd.driver = lp->isdn_device; | ||
633 | cmd.arg = lp->isdn_channel; | ||
634 | if (!lp->dial) { | ||
635 | printk(KERN_WARNING "%s: phone number deleted?\n", | ||
636 | lp->name); | ||
637 | isdn_net_hangup(&p->dev); | ||
638 | break; | ||
639 | } | ||
640 | if (!strncmp(lp->dial->num, "LEASED", strlen("LEASED"))) { | ||
641 | lp->dialstate = 4; | ||
642 | printk(KERN_INFO "%s: Open leased line ...\n", lp->name); | ||
643 | } else { | ||
644 | if(lp->dialtimeout > 0) | ||
645 | if (time_after(jiffies, lp->dialstarted + lp->dialtimeout)) { | ||
646 | lp->dialwait_timer = jiffies + lp->dialwait; | ||
647 | lp->dialstarted = 0; | ||
648 | isdn_net_unreachable(&p->dev, NULL, "dial: timed out"); | ||
649 | isdn_net_hangup(&p->dev); | ||
650 | break; | ||
651 | } | ||
652 | |||
653 | cmd.driver = lp->isdn_device; | ||
654 | cmd.command = ISDN_CMD_DIAL; | ||
655 | cmd.parm.setup.si2 = 0; | ||
656 | |||
657 | /* check for DOV */ | ||
658 | phone_number = lp->dial->num; | ||
659 | if ((*phone_number == 'v') || | ||
660 | (*phone_number == 'V')) { /* DOV call */ | ||
661 | cmd.parm.setup.si1 = 1; | ||
662 | } else { /* DATA call */ | ||
663 | cmd.parm.setup.si1 = 7; | ||
664 | } | ||
665 | |||
666 | strcpy(cmd.parm.setup.phone, phone_number); | ||
667 | /* | ||
668 | * Switch to next number or back to start if at end of list. | ||
669 | */ | ||
670 | if (!(lp->dial = (isdn_net_phone *) lp->dial->next)) { | ||
671 | lp->dial = lp->phone[1]; | ||
672 | lp->dialretry++; | ||
673 | |||
674 | if (lp->dialretry > lp->dialmax) { | ||
675 | if (lp->dialtimeout == 0) { | ||
676 | lp->dialwait_timer = jiffies + lp->dialwait; | ||
677 | lp->dialstarted = 0; | ||
678 | isdn_net_unreachable(&p->dev, NULL, "dial: tried all numbers dialmax times"); | ||
679 | } | ||
680 | isdn_net_hangup(&p->dev); | ||
681 | break; | ||
682 | } | ||
683 | } | ||
684 | sprintf(cmd.parm.setup.eazmsn, "%s", | ||
685 | isdn_map_eaz2msn(lp->msn, cmd.driver)); | ||
686 | i = isdn_dc2minor(lp->isdn_device, lp->isdn_channel); | ||
687 | if (i >= 0) { | ||
688 | strcpy(dev->num[i], cmd.parm.setup.phone); | ||
689 | dev->usage[i] |= ISDN_USAGE_OUTGOING; | ||
690 | isdn_info_update(); | ||
691 | } | ||
692 | printk(KERN_INFO "%s: dialing %d %s... %s\n", lp->name, | ||
693 | lp->dialretry, cmd.parm.setup.phone, | ||
694 | (cmd.parm.setup.si1 == 1) ? "DOV" : ""); | ||
695 | lp->dtimer = 0; | ||
696 | #ifdef ISDN_DEBUG_NET_DIAL | ||
697 | printk(KERN_DEBUG "dial: d=%d c=%d\n", lp->isdn_device, | ||
698 | lp->isdn_channel); | ||
699 | #endif | ||
700 | isdn_command(&cmd); | ||
701 | } | ||
702 | lp->huptimer = 0; | ||
703 | lp->outgoing = 1; | ||
704 | if (lp->chargeint) { | ||
705 | lp->hupflags |= ISDN_HAVECHARGE; | ||
706 | lp->hupflags &= ~ISDN_WAITCHARGE; | ||
707 | } else { | ||
708 | lp->hupflags |= ISDN_WAITCHARGE; | ||
709 | lp->hupflags &= ~ISDN_HAVECHARGE; | ||
710 | } | ||
711 | anymore = 1; | ||
712 | lp->dialstate = | ||
713 | (lp->cbdelay && | ||
714 | (lp->flags & ISDN_NET_CBOUT)) ? 12 : 4; | ||
715 | break; | ||
716 | case 4: | ||
717 | /* Wait for D-Channel-connect. | ||
718 | * If timeout, switch back to state 3. | ||
719 | * Dialmax-handling moved to state 3. | ||
720 | */ | ||
721 | if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT10) | ||
722 | lp->dialstate = 3; | ||
723 | anymore = 1; | ||
724 | break; | ||
725 | case 5: | ||
726 | /* Got D-Channel-Connect, send B-Channel-request */ | ||
727 | cmd.driver = lp->isdn_device; | ||
728 | cmd.arg = lp->isdn_channel; | ||
729 | cmd.command = ISDN_CMD_ACCEPTB; | ||
730 | anymore = 1; | ||
731 | lp->dtimer = 0; | ||
732 | lp->dialstate++; | ||
733 | isdn_command(&cmd); | ||
734 | break; | ||
735 | case 6: | ||
736 | /* Wait for B- or D-Channel-connect. If timeout, | ||
737 | * switch back to state 3. | ||
738 | */ | ||
739 | #ifdef ISDN_DEBUG_NET_DIAL | ||
740 | printk(KERN_DEBUG "dialtimer2: %d\n", lp->dtimer); | ||
741 | #endif | ||
742 | if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT10) | ||
743 | lp->dialstate = 3; | ||
744 | anymore = 1; | ||
745 | break; | ||
746 | case 7: | ||
747 | /* Got incoming Call, setup L2 and L3 protocols, | ||
748 | * then wait for D-Channel-connect | ||
749 | */ | ||
750 | #ifdef ISDN_DEBUG_NET_DIAL | ||
751 | printk(KERN_DEBUG "dialtimer4: %d\n", lp->dtimer); | ||
752 | #endif | ||
753 | cmd.driver = lp->isdn_device; | ||
754 | cmd.command = ISDN_CMD_SETL2; | ||
755 | cmd.arg = lp->isdn_channel + (lp->l2_proto << 8); | ||
756 | isdn_command(&cmd); | ||
757 | cmd.driver = lp->isdn_device; | ||
758 | cmd.command = ISDN_CMD_SETL3; | ||
759 | cmd.arg = lp->isdn_channel + (lp->l3_proto << 8); | ||
760 | isdn_command(&cmd); | ||
761 | if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT15) | ||
762 | isdn_net_hangup(&p->dev); | ||
763 | else { | ||
764 | anymore = 1; | ||
765 | lp->dialstate++; | ||
766 | } | ||
767 | break; | ||
768 | case 9: | ||
769 | /* Got incoming D-Channel-Connect, send B-Channel-request */ | ||
770 | cmd.driver = lp->isdn_device; | ||
771 | cmd.arg = lp->isdn_channel; | ||
772 | cmd.command = ISDN_CMD_ACCEPTB; | ||
773 | isdn_command(&cmd); | ||
774 | anymore = 1; | ||
775 | lp->dtimer = 0; | ||
776 | lp->dialstate++; | ||
777 | break; | ||
778 | case 8: | ||
779 | case 10: | ||
780 | /* Wait for B- or D-channel-connect */ | ||
781 | #ifdef ISDN_DEBUG_NET_DIAL | ||
782 | printk(KERN_DEBUG "dialtimer4: %d\n", lp->dtimer); | ||
783 | #endif | ||
784 | if (lp->dtimer++ > ISDN_TIMER_DTIMEOUT10) | ||
785 | isdn_net_hangup(&p->dev); | ||
786 | else | ||
787 | anymore = 1; | ||
788 | break; | ||
789 | case 11: | ||
790 | /* Callback Delay */ | ||
791 | if (lp->dtimer++ > lp->cbdelay) | ||
792 | lp->dialstate = 1; | ||
793 | anymore = 1; | ||
794 | break; | ||
795 | case 12: | ||
796 | /* Remote does callback. Hangup after cbdelay, then wait for incoming | ||
797 | * call (in state 4). | ||
798 | */ | ||
799 | if (lp->dtimer++ > lp->cbdelay) | ||
800 | { | ||
801 | printk(KERN_INFO "%s: hangup waiting for callback ...\n", lp->name); | ||
802 | lp->dtimer = 0; | ||
803 | lp->dialstate = 4; | ||
804 | cmd.driver = lp->isdn_device; | ||
805 | cmd.command = ISDN_CMD_HANGUP; | ||
806 | cmd.arg = lp->isdn_channel; | ||
807 | isdn_command(&cmd); | ||
808 | isdn_all_eaz(lp->isdn_device, lp->isdn_channel); | ||
809 | } | ||
810 | anymore = 1; | ||
811 | break; | ||
812 | default: | ||
813 | printk(KERN_WARNING "isdn_net: Illegal dialstate %d for device %s\n", | ||
814 | lp->dialstate, lp->name); | ||
815 | } | ||
816 | p = (isdn_net_dev *) p->next; | ||
817 | } | ||
818 | isdn_timer_ctrl(ISDN_TIMER_NETDIAL, anymore); | ||
819 | } | ||
820 | |||
821 | /* | ||
822 | * Perform hangup for a net-interface. | ||
823 | */ | ||
824 | void | ||
825 | isdn_net_hangup(struct net_device *d) | ||
826 | { | ||
827 | isdn_net_local *lp = (isdn_net_local *) d->priv; | ||
828 | isdn_ctrl cmd; | ||
829 | #ifdef CONFIG_ISDN_X25 | ||
830 | struct concap_proto *cprot = lp->netdev->cprot; | ||
831 | struct concap_proto_ops *pops = cprot ? cprot->pops : NULL; | ||
832 | #endif | ||
833 | |||
834 | if (lp->flags & ISDN_NET_CONNECTED) { | ||
835 | if (lp->slave != NULL) { | ||
836 | isdn_net_local *slp = (isdn_net_local *)lp->slave->priv; | ||
837 | if (slp->flags & ISDN_NET_CONNECTED) { | ||
838 | printk(KERN_INFO | ||
839 | "isdn_net: hang up slave %s before %s\n", | ||
840 | slp->name, lp->name); | ||
841 | isdn_net_hangup(lp->slave); | ||
842 | } | ||
843 | } | ||
844 | printk(KERN_INFO "isdn_net: local hangup %s\n", lp->name); | ||
845 | #ifdef CONFIG_ISDN_PPP | ||
846 | if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) | ||
847 | isdn_ppp_free(lp); | ||
848 | #endif | ||
849 | isdn_net_lp_disconnected(lp); | ||
850 | #ifdef CONFIG_ISDN_X25 | ||
851 | /* try if there are generic encap protocol | ||
852 | receiver routines and signal the closure of | ||
853 | the link */ | ||
854 | if( pops && pops -> disconn_ind ) | ||
855 | pops -> disconn_ind(cprot); | ||
856 | #endif /* CONFIG_ISDN_X25 */ | ||
857 | |||
858 | cmd.driver = lp->isdn_device; | ||
859 | cmd.command = ISDN_CMD_HANGUP; | ||
860 | cmd.arg = lp->isdn_channel; | ||
861 | isdn_command(&cmd); | ||
862 | printk(KERN_INFO "%s: Chargesum is %d\n", lp->name, lp->charge); | ||
863 | isdn_all_eaz(lp->isdn_device, lp->isdn_channel); | ||
864 | } | ||
865 | isdn_net_unbind_channel(lp); | ||
866 | } | ||
867 | |||
868 | typedef struct { | ||
869 | unsigned short source; | ||
870 | unsigned short dest; | ||
871 | } ip_ports; | ||
872 | |||
873 | static void | ||
874 | isdn_net_log_skb(struct sk_buff * skb, isdn_net_local * lp) | ||
875 | { | ||
876 | u_char *p = skb->nh.raw; /* hopefully, this was set correctly */ | ||
877 | unsigned short proto = ntohs(skb->protocol); | ||
878 | int data_ofs; | ||
879 | ip_ports *ipp; | ||
880 | char addinfo[100]; | ||
881 | |||
882 | addinfo[0] = '\0'; | ||
883 | /* This check stolen from 2.1.72 dev_queue_xmit_nit() */ | ||
884 | if (skb->nh.raw < skb->data || skb->nh.raw >= skb->tail) { | ||
885 | /* fall back to old isdn_net_log_packet method() */ | ||
886 | char * buf = skb->data; | ||
887 | |||
888 | printk(KERN_DEBUG "isdn_net: protocol %04x is buggy, dev %s\n", skb->protocol, lp->name); | ||
889 | p = buf; | ||
890 | proto = ETH_P_IP; | ||
891 | switch (lp->p_encap) { | ||
892 | case ISDN_NET_ENCAP_IPTYP: | ||
893 | proto = ntohs(*(unsigned short *) &buf[0]); | ||
894 | p = &buf[2]; | ||
895 | break; | ||
896 | case ISDN_NET_ENCAP_ETHER: | ||
897 | proto = ntohs(*(unsigned short *) &buf[12]); | ||
898 | p = &buf[14]; | ||
899 | break; | ||
900 | case ISDN_NET_ENCAP_CISCOHDLC: | ||
901 | proto = ntohs(*(unsigned short *) &buf[2]); | ||
902 | p = &buf[4]; | ||
903 | break; | ||
904 | #ifdef CONFIG_ISDN_PPP | ||
905 | case ISDN_NET_ENCAP_SYNCPPP: | ||
906 | proto = ntohs(skb->protocol); | ||
907 | p = &buf[IPPP_MAX_HEADER]; | ||
908 | break; | ||
909 | #endif | ||
910 | } | ||
911 | } | ||
912 | data_ofs = ((p[0] & 15) * 4); | ||
913 | switch (proto) { | ||
914 | case ETH_P_IP: | ||
915 | switch (p[9]) { | ||
916 | case 1: | ||
917 | strcpy(addinfo, " ICMP"); | ||
918 | break; | ||
919 | case 2: | ||
920 | strcpy(addinfo, " IGMP"); | ||
921 | break; | ||
922 | case 4: | ||
923 | strcpy(addinfo, " IPIP"); | ||
924 | break; | ||
925 | case 6: | ||
926 | ipp = (ip_ports *) (&p[data_ofs]); | ||
927 | sprintf(addinfo, " TCP, port: %d -> %d", ntohs(ipp->source), | ||
928 | ntohs(ipp->dest)); | ||
929 | break; | ||
930 | case 8: | ||
931 | strcpy(addinfo, " EGP"); | ||
932 | break; | ||
933 | case 12: | ||
934 | strcpy(addinfo, " PUP"); | ||
935 | break; | ||
936 | case 17: | ||
937 | ipp = (ip_ports *) (&p[data_ofs]); | ||
938 | sprintf(addinfo, " UDP, port: %d -> %d", ntohs(ipp->source), | ||
939 | ntohs(ipp->dest)); | ||
940 | break; | ||
941 | case 22: | ||
942 | strcpy(addinfo, " IDP"); | ||
943 | break; | ||
944 | } | ||
945 | printk(KERN_INFO | ||
946 | "OPEN: %d.%d.%d.%d -> %d.%d.%d.%d%s\n", | ||
947 | |||
948 | p[12], p[13], p[14], p[15], | ||
949 | p[16], p[17], p[18], p[19], | ||
950 | addinfo); | ||
951 | break; | ||
952 | case ETH_P_ARP: | ||
953 | printk(KERN_INFO | ||
954 | "OPEN: ARP %d.%d.%d.%d -> *.*.*.* ?%d.%d.%d.%d\n", | ||
955 | p[14], p[15], p[16], p[17], | ||
956 | p[24], p[25], p[26], p[27]); | ||
957 | break; | ||
958 | } | ||
959 | } | ||
960 | |||
961 | /* | ||
962 | * this function is used to send supervisory data, i.e. data which was | ||
963 | * not received from the network layer, but e.g. frames from ipppd, CCP | ||
964 | * reset frames etc. | ||
965 | */ | ||
966 | void isdn_net_write_super(isdn_net_local *lp, struct sk_buff *skb) | ||
967 | { | ||
968 | if (in_irq()) { | ||
969 | // we can't grab the lock from irq context, | ||
970 | // so we just queue the packet | ||
971 | skb_queue_tail(&lp->super_tx_queue, skb); | ||
972 | schedule_work(&lp->tqueue); | ||
973 | return; | ||
974 | } | ||
975 | |||
976 | spin_lock_bh(&lp->xmit_lock); | ||
977 | if (!isdn_net_lp_busy(lp)) { | ||
978 | isdn_net_writebuf_skb(lp, skb); | ||
979 | } else { | ||
980 | skb_queue_tail(&lp->super_tx_queue, skb); | ||
981 | } | ||
982 | spin_unlock_bh(&lp->xmit_lock); | ||
983 | } | ||
984 | |||
985 | /* | ||
986 | * called from tq_immediate | ||
987 | */ | ||
988 | static void isdn_net_softint(void *private) | ||
989 | { | ||
990 | isdn_net_local *lp = private; | ||
991 | struct sk_buff *skb; | ||
992 | |||
993 | spin_lock_bh(&lp->xmit_lock); | ||
994 | while (!isdn_net_lp_busy(lp)) { | ||
995 | skb = skb_dequeue(&lp->super_tx_queue); | ||
996 | if (!skb) | ||
997 | break; | ||
998 | isdn_net_writebuf_skb(lp, skb); | ||
999 | } | ||
1000 | spin_unlock_bh(&lp->xmit_lock); | ||
1001 | } | ||
1002 | |||
1003 | /* | ||
1004 | * all frames sent from the (net) LL to a HL driver should go via this function | ||
1005 | * it's serialized by the caller holding the lp->xmit_lock spinlock | ||
1006 | */ | ||
1007 | void isdn_net_writebuf_skb(isdn_net_local *lp, struct sk_buff *skb) | ||
1008 | { | ||
1009 | int ret; | ||
1010 | int len = skb->len; /* save len */ | ||
1011 | |||
1012 | /* before obtaining the lock the caller should have checked that | ||
1013 | the lp isn't busy */ | ||
1014 | if (isdn_net_lp_busy(lp)) { | ||
1015 | printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__); | ||
1016 | goto error; | ||
1017 | } | ||
1018 | |||
1019 | if (!(lp->flags & ISDN_NET_CONNECTED)) { | ||
1020 | printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__); | ||
1021 | goto error; | ||
1022 | } | ||
1023 | ret = isdn_writebuf_skb_stub(lp->isdn_device, lp->isdn_channel, 1, skb); | ||
1024 | if (ret != len) { | ||
1025 | /* we should never get here */ | ||
1026 | printk(KERN_WARNING "%s: HL driver queue full\n", lp->name); | ||
1027 | goto error; | ||
1028 | } | ||
1029 | |||
1030 | lp->transcount += len; | ||
1031 | isdn_net_inc_frame_cnt(lp); | ||
1032 | return; | ||
1033 | |||
1034 | error: | ||
1035 | dev_kfree_skb(skb); | ||
1036 | lp->stats.tx_errors++; | ||
1037 | |||
1038 | } | ||
1039 | |||
1040 | |||
1041 | /* | ||
1042 | * Helper function for isdn_net_start_xmit. | ||
1043 | * When called, the connection is already established. | ||
1044 | * Based on cps-calculation, check if device is overloaded. | ||
1045 | * If so, and if a slave exists, trigger dialing for it. | ||
1046 | * If any slave is online, deliver packets using a simple round robin | ||
1047 | * scheme. | ||
1048 | * | ||
1049 | * Return: 0 on success, !0 on failure. | ||
1050 | */ | ||
1051 | |||
1052 | static int | ||
1053 | isdn_net_xmit(struct net_device *ndev, struct sk_buff *skb) | ||
1054 | { | ||
1055 | isdn_net_dev *nd; | ||
1056 | isdn_net_local *slp; | ||
1057 | isdn_net_local *lp = (isdn_net_local *) ndev->priv; | ||
1058 | int retv = 0; | ||
1059 | |||
1060 | if (((isdn_net_local *) (ndev->priv))->master) { | ||
1061 | printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__); | ||
1062 | dev_kfree_skb(skb); | ||
1063 | return 0; | ||
1064 | } | ||
1065 | |||
1066 | /* For the other encaps the header has already been built */ | ||
1067 | #ifdef CONFIG_ISDN_PPP | ||
1068 | if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) { | ||
1069 | return isdn_ppp_xmit(skb, ndev); | ||
1070 | } | ||
1071 | #endif | ||
1072 | nd = ((isdn_net_local *) ndev->priv)->netdev; | ||
1073 | lp = isdn_net_get_locked_lp(nd); | ||
1074 | if (!lp) { | ||
1075 | printk(KERN_WARNING "%s: all channels busy - requeuing!\n", ndev->name); | ||
1076 | return 1; | ||
1077 | } | ||
1078 | /* we have our lp locked from now on */ | ||
1079 | |||
1080 | /* Reset hangup-timeout */ | ||
1081 | lp->huptimer = 0; // FIXME? | ||
1082 | isdn_net_writebuf_skb(lp, skb); | ||
1083 | spin_unlock_bh(&lp->xmit_lock); | ||
1084 | |||
1085 | /* the following stuff is here for backwards compatibility. | ||
1086 | * in future, start-up and hangup of slaves (based on current load) | ||
1087 | * should move to userspace and get based on an overall cps | ||
1088 | * calculation | ||
1089 | */ | ||
1090 | if (lp->cps > lp->triggercps) { | ||
1091 | if (lp->slave) { | ||
1092 | if (!lp->sqfull) { | ||
1093 | /* First time overload: set timestamp only */ | ||
1094 | lp->sqfull = 1; | ||
1095 | lp->sqfull_stamp = jiffies; | ||
1096 | } else { | ||
1097 | /* subsequent overload: if slavedelay exceeded, start dialing */ | ||
1098 | if (time_after(jiffies, lp->sqfull_stamp + lp->slavedelay)) { | ||
1099 | slp = lp->slave->priv; | ||
1100 | if (!(slp->flags & ISDN_NET_CONNECTED)) { | ||
1101 | isdn_net_force_dial_lp((isdn_net_local *) lp->slave->priv); | ||
1102 | } | ||
1103 | } | ||
1104 | } | ||
1105 | } | ||
1106 | } else { | ||
1107 | if (lp->sqfull && time_after(jiffies, lp->sqfull_stamp + lp->slavedelay + (10 * HZ))) { | ||
1108 | lp->sqfull = 0; | ||
1109 | } | ||
1110 | /* this is a hack to allow auto-hangup for slaves on moderate loads */ | ||
1111 | nd->queue = nd->local; | ||
1112 | } | ||
1113 | |||
1114 | return retv; | ||
1115 | |||
1116 | } | ||
1117 | |||
1118 | static void | ||
1119 | isdn_net_adjust_hdr(struct sk_buff *skb, struct net_device *dev) | ||
1120 | { | ||
1121 | isdn_net_local *lp = (isdn_net_local *) dev->priv; | ||
1122 | if (!skb) | ||
1123 | return; | ||
1124 | if (lp->p_encap == ISDN_NET_ENCAP_ETHER) { | ||
1125 | int pullsize = (ulong)skb->nh.raw - (ulong)skb->data - ETH_HLEN; | ||
1126 | if (pullsize > 0) { | ||
1127 | printk(KERN_DEBUG "isdn_net: Pull junk %d\n", pullsize); | ||
1128 | skb_pull(skb, pullsize); | ||
1129 | } | ||
1130 | } | ||
1131 | } | ||
1132 | |||
1133 | |||
1134 | void isdn_net_tx_timeout(struct net_device * ndev) | ||
1135 | { | ||
1136 | isdn_net_local *lp = (isdn_net_local *) ndev->priv; | ||
1137 | |||
1138 | printk(KERN_WARNING "isdn_tx_timeout dev %s dialstate %d\n", ndev->name, lp->dialstate); | ||
1139 | if (!lp->dialstate){ | ||
1140 | lp->stats.tx_errors++; | ||
1141 | /* | ||
1142 | * There is a certain probability that this currently | ||
1143 | * works at all because if we always wake up the interface, | ||
1144 | * then upper layer will try to send the next packet | ||
1145 | * immediately. And then, the old clean_up logic in the | ||
1146 | * driver will hopefully continue to work as it used to do. | ||
1147 | * | ||
1148 | * This is rather primitive right know, we better should | ||
1149 | * clean internal queues here, in particular for multilink and | ||
1150 | * ppp, and reset HL driver's channel, too. --HE | ||
1151 | * | ||
1152 | * actually, this may not matter at all, because ISDN hardware | ||
1153 | * should not see transmitter hangs at all IMO | ||
1154 | * changed KERN_DEBUG to KERN_WARNING to find out if this is | ||
1155 | * ever called --KG | ||
1156 | */ | ||
1157 | } | ||
1158 | ndev->trans_start = jiffies; | ||
1159 | netif_wake_queue(ndev); | ||
1160 | } | ||
1161 | |||
1162 | /* | ||
1163 | * Try sending a packet. | ||
1164 | * If this interface isn't connected to a ISDN-Channel, find a free channel, | ||
1165 | * and start dialing. | ||
1166 | */ | ||
1167 | static int | ||
1168 | isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev) | ||
1169 | { | ||
1170 | isdn_net_local *lp = (isdn_net_local *) ndev->priv; | ||
1171 | #ifdef CONFIG_ISDN_X25 | ||
1172 | struct concap_proto * cprot = lp -> netdev -> cprot; | ||
1173 | /* At this point hard_start_xmit() passes control to the encapsulation | ||
1174 | protocol (if present). | ||
1175 | For X.25 auto-dialing is completly bypassed because: | ||
1176 | - It does not conform with the semantics of a reliable datalink | ||
1177 | service as needed by X.25 PLP. | ||
1178 | - I don't want that the interface starts dialing when the network layer | ||
1179 | sends a message which requests to disconnect the lapb link (or if it | ||
1180 | sends any other message not resulting in data transmission). | ||
1181 | Instead, dialing will be initiated by the encapsulation protocol entity | ||
1182 | when a dl_establish request is received from the upper layer. | ||
1183 | */ | ||
1184 | if (cprot && cprot -> pops) { | ||
1185 | int ret = cprot -> pops -> encap_and_xmit ( cprot , skb); | ||
1186 | |||
1187 | if (ret) | ||
1188 | netif_stop_queue(ndev); | ||
1189 | return ret; | ||
1190 | } else | ||
1191 | #endif | ||
1192 | /* auto-dialing xmit function */ | ||
1193 | { | ||
1194 | #ifdef ISDN_DEBUG_NET_DUMP | ||
1195 | u_char *buf; | ||
1196 | #endif | ||
1197 | isdn_net_adjust_hdr(skb, ndev); | ||
1198 | #ifdef ISDN_DEBUG_NET_DUMP | ||
1199 | buf = skb->data; | ||
1200 | isdn_dumppkt("S:", buf, skb->len, 40); | ||
1201 | #endif | ||
1202 | |||
1203 | if (!(lp->flags & ISDN_NET_CONNECTED)) { | ||
1204 | int chi; | ||
1205 | /* only do autodial if allowed by config */ | ||
1206 | if (!(ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_AUTO)) { | ||
1207 | isdn_net_unreachable(ndev, skb, "dial rejected: interface not in dialmode `auto'"); | ||
1208 | dev_kfree_skb(skb); | ||
1209 | return 0; | ||
1210 | } | ||
1211 | if (lp->phone[1]) { | ||
1212 | ulong flags; | ||
1213 | |||
1214 | if(lp->dialwait_timer <= 0) | ||
1215 | if(lp->dialstarted > 0 && lp->dialtimeout > 0 && time_before(jiffies, lp->dialstarted + lp->dialtimeout + lp->dialwait)) | ||
1216 | lp->dialwait_timer = lp->dialstarted + lp->dialtimeout + lp->dialwait; | ||
1217 | |||
1218 | if(lp->dialwait_timer > 0) { | ||
1219 | if(time_before(jiffies, lp->dialwait_timer)) { | ||
1220 | isdn_net_unreachable(ndev, skb, "dial rejected: retry-time not reached"); | ||
1221 | dev_kfree_skb(skb); | ||
1222 | return 0; | ||
1223 | } else | ||
1224 | lp->dialwait_timer = 0; | ||
1225 | } | ||
1226 | /* Grab a free ISDN-Channel */ | ||
1227 | spin_lock_irqsave(&dev->lock, flags); | ||
1228 | if (((chi = | ||
1229 | isdn_get_free_channel( | ||
1230 | ISDN_USAGE_NET, | ||
1231 | lp->l2_proto, | ||
1232 | lp->l3_proto, | ||
1233 | lp->pre_device, | ||
1234 | lp->pre_channel, | ||
1235 | lp->msn) | ||
1236 | ) < 0) && | ||
1237 | ((chi = | ||
1238 | isdn_get_free_channel( | ||
1239 | ISDN_USAGE_NET, | ||
1240 | lp->l2_proto, | ||
1241 | lp->l3_proto, | ||
1242 | lp->pre_device, | ||
1243 | lp->pre_channel^1, | ||
1244 | lp->msn) | ||
1245 | ) < 0)) { | ||
1246 | spin_unlock_irqrestore(&dev->lock, flags); | ||
1247 | isdn_net_unreachable(ndev, skb, | ||
1248 | "No channel"); | ||
1249 | dev_kfree_skb(skb); | ||
1250 | return 0; | ||
1251 | } | ||
1252 | /* Log packet, which triggered dialing */ | ||
1253 | if (dev->net_verbose) | ||
1254 | isdn_net_log_skb(skb, lp); | ||
1255 | lp->dialstate = 1; | ||
1256 | /* Connect interface with channel */ | ||
1257 | isdn_net_bind_channel(lp, chi); | ||
1258 | #ifdef CONFIG_ISDN_PPP | ||
1259 | if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) { | ||
1260 | /* no 'first_skb' handling for syncPPP */ | ||
1261 | if (isdn_ppp_bind(lp) < 0) { | ||
1262 | dev_kfree_skb(skb); | ||
1263 | isdn_net_unbind_channel(lp); | ||
1264 | spin_unlock_irqrestore(&dev->lock, flags); | ||
1265 | return 0; /* STN (skb to nirvana) ;) */ | ||
1266 | } | ||
1267 | #ifdef CONFIG_IPPP_FILTER | ||
1268 | if (isdn_ppp_autodial_filter(skb, lp)) { | ||
1269 | isdn_ppp_free(lp); | ||
1270 | isdn_net_unbind_channel(lp); | ||
1271 | spin_unlock_irqrestore(&dev->lock, flags); | ||
1272 | isdn_net_unreachable(ndev, skb, "dial rejected: packet filtered"); | ||
1273 | dev_kfree_skb(skb); | ||
1274 | return 0; | ||
1275 | } | ||
1276 | #endif | ||
1277 | spin_unlock_irqrestore(&dev->lock, flags); | ||
1278 | isdn_net_dial(); /* Initiate dialing */ | ||
1279 | netif_stop_queue(ndev); | ||
1280 | return 1; /* let upper layer requeue skb packet */ | ||
1281 | } | ||
1282 | #endif | ||
1283 | /* Initiate dialing */ | ||
1284 | spin_unlock_irqrestore(&dev->lock, flags); | ||
1285 | isdn_net_dial(); | ||
1286 | isdn_net_device_stop_queue(lp); | ||
1287 | return 1; | ||
1288 | } else { | ||
1289 | isdn_net_unreachable(ndev, skb, | ||
1290 | "No phone number"); | ||
1291 | dev_kfree_skb(skb); | ||
1292 | return 0; | ||
1293 | } | ||
1294 | } else { | ||
1295 | /* Device is connected to an ISDN channel */ | ||
1296 | ndev->trans_start = jiffies; | ||
1297 | if (!lp->dialstate) { | ||
1298 | /* ISDN connection is established, try sending */ | ||
1299 | int ret; | ||
1300 | ret = (isdn_net_xmit(ndev, skb)); | ||
1301 | if(ret) netif_stop_queue(ndev); | ||
1302 | return ret; | ||
1303 | } else | ||
1304 | netif_stop_queue(ndev); | ||
1305 | } | ||
1306 | } | ||
1307 | return 1; | ||
1308 | } | ||
1309 | |||
1310 | /* | ||
1311 | * Shutdown a net-interface. | ||
1312 | */ | ||
1313 | static int | ||
1314 | isdn_net_close(struct net_device *dev) | ||
1315 | { | ||
1316 | struct net_device *p; | ||
1317 | #ifdef CONFIG_ISDN_X25 | ||
1318 | struct concap_proto * cprot = | ||
1319 | ( (isdn_net_local *) dev->priv ) -> netdev -> cprot; | ||
1320 | /* printk(KERN_DEBUG "isdn_net_close %s\n" , dev-> name ); */ | ||
1321 | #endif | ||
1322 | |||
1323 | #ifdef CONFIG_ISDN_X25 | ||
1324 | if( cprot && cprot -> pops ) cprot -> pops -> close( cprot ); | ||
1325 | #endif | ||
1326 | netif_stop_queue(dev); | ||
1327 | if ((p = (((isdn_net_local *) dev->priv)->slave))) { | ||
1328 | /* If this interface has slaves, stop them also */ | ||
1329 | while (p) { | ||
1330 | #ifdef CONFIG_ISDN_X25 | ||
1331 | cprot = ( (isdn_net_local *) p->priv ) | ||
1332 | -> netdev -> cprot; | ||
1333 | if( cprot && cprot -> pops ) | ||
1334 | cprot -> pops -> close( cprot ); | ||
1335 | #endif | ||
1336 | isdn_net_hangup(p); | ||
1337 | p = (((isdn_net_local *) p->priv)->slave); | ||
1338 | } | ||
1339 | } | ||
1340 | isdn_net_hangup(dev); | ||
1341 | isdn_unlock_drivers(); | ||
1342 | return 0; | ||
1343 | } | ||
1344 | |||
1345 | /* | ||
1346 | * Get statistics | ||
1347 | */ | ||
1348 | static struct net_device_stats * | ||
1349 | isdn_net_get_stats(struct net_device *dev) | ||
1350 | { | ||
1351 | isdn_net_local *lp = (isdn_net_local *) dev->priv; | ||
1352 | return &lp->stats; | ||
1353 | } | ||
1354 | |||
1355 | /* This is simply a copy from std. eth.c EXCEPT we pull ETH_HLEN | ||
1356 | * instead of dev->hard_header_len off. This is done because the | ||
1357 | * lowlevel-driver has already pulled off its stuff when we get | ||
1358 | * here and this routine only gets called with p_encap == ETHER. | ||
1359 | * Determine the packet's protocol ID. The rule here is that we | ||
1360 | * assume 802.3 if the type field is short enough to be a length. | ||
1361 | * This is normal practice and works for any 'now in use' protocol. | ||
1362 | */ | ||
1363 | |||
1364 | static unsigned short | ||
1365 | isdn_net_type_trans(struct sk_buff *skb, struct net_device *dev) | ||
1366 | { | ||
1367 | struct ethhdr *eth; | ||
1368 | unsigned char *rawp; | ||
1369 | |||
1370 | skb->mac.raw = skb->data; | ||
1371 | skb_pull(skb, ETH_HLEN); | ||
1372 | eth = eth_hdr(skb); | ||
1373 | |||
1374 | if (*eth->h_dest & 1) { | ||
1375 | if (memcmp(eth->h_dest, dev->broadcast, ETH_ALEN) == 0) | ||
1376 | skb->pkt_type = PACKET_BROADCAST; | ||
1377 | else | ||
1378 | skb->pkt_type = PACKET_MULTICAST; | ||
1379 | } | ||
1380 | /* | ||
1381 | * This ALLMULTI check should be redundant by 1.4 | ||
1382 | * so don't forget to remove it. | ||
1383 | */ | ||
1384 | |||
1385 | else if (dev->flags & (IFF_PROMISC /*| IFF_ALLMULTI*/)) { | ||
1386 | if (memcmp(eth->h_dest, dev->dev_addr, ETH_ALEN)) | ||
1387 | skb->pkt_type = PACKET_OTHERHOST; | ||
1388 | } | ||
1389 | if (ntohs(eth->h_proto) >= 1536) | ||
1390 | return eth->h_proto; | ||
1391 | |||
1392 | rawp = skb->data; | ||
1393 | |||
1394 | /* | ||
1395 | * This is a magic hack to spot IPX packets. Older Novell breaks | ||
1396 | * the protocol design and runs IPX over 802.3 without an 802.2 LLC | ||
1397 | * layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This | ||
1398 | * won't work for fault tolerant netware but does for the rest. | ||
1399 | */ | ||
1400 | if (*(unsigned short *) rawp == 0xFFFF) | ||
1401 | return htons(ETH_P_802_3); | ||
1402 | /* | ||
1403 | * Real 802.2 LLC | ||
1404 | */ | ||
1405 | return htons(ETH_P_802_2); | ||
1406 | } | ||
1407 | |||
1408 | |||
1409 | /* | ||
1410 | * CISCO HDLC keepalive specific stuff | ||
1411 | */ | ||
1412 | static struct sk_buff* | ||
1413 | isdn_net_ciscohdlck_alloc_skb(isdn_net_local *lp, int len) | ||
1414 | { | ||
1415 | unsigned short hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen; | ||
1416 | struct sk_buff *skb; | ||
1417 | |||
1418 | skb = alloc_skb(hl + len, GFP_ATOMIC); | ||
1419 | if (skb) | ||
1420 | skb_reserve(skb, hl); | ||
1421 | else | ||
1422 | printk("isdn out of mem at %s:%d!\n", __FILE__, __LINE__); | ||
1423 | return skb; | ||
1424 | } | ||
1425 | |||
1426 | /* cisco hdlck device private ioctls */ | ||
1427 | int | ||
1428 | isdn_ciscohdlck_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) | ||
1429 | { | ||
1430 | isdn_net_local *lp = (isdn_net_local *) dev->priv; | ||
1431 | unsigned long len = 0; | ||
1432 | unsigned long expires = 0; | ||
1433 | int tmp = 0; | ||
1434 | int period = lp->cisco_keepalive_period; | ||
1435 | s8 debserint = lp->cisco_debserint; | ||
1436 | int rc = 0; | ||
1437 | |||
1438 | if (lp->p_encap != ISDN_NET_ENCAP_CISCOHDLCK) | ||
1439 | return -EINVAL; | ||
1440 | |||
1441 | switch (cmd) { | ||
1442 | /* get/set keepalive period */ | ||
1443 | case SIOCGKEEPPERIOD: | ||
1444 | len = (unsigned long)sizeof(lp->cisco_keepalive_period); | ||
1445 | if (copy_to_user(ifr->ifr_data, | ||
1446 | &lp->cisco_keepalive_period, len)) | ||
1447 | rc = -EFAULT; | ||
1448 | break; | ||
1449 | case SIOCSKEEPPERIOD: | ||
1450 | tmp = lp->cisco_keepalive_period; | ||
1451 | len = (unsigned long)sizeof(lp->cisco_keepalive_period); | ||
1452 | if (copy_from_user(&period, ifr->ifr_data, len)) | ||
1453 | rc = -EFAULT; | ||
1454 | if ((period > 0) && (period <= 32767)) | ||
1455 | lp->cisco_keepalive_period = period; | ||
1456 | else | ||
1457 | rc = -EINVAL; | ||
1458 | if (!rc && (tmp != lp->cisco_keepalive_period)) { | ||
1459 | expires = (unsigned long)(jiffies + | ||
1460 | lp->cisco_keepalive_period * HZ); | ||
1461 | mod_timer(&lp->cisco_timer, expires); | ||
1462 | printk(KERN_INFO "%s: Keepalive period set " | ||
1463 | "to %d seconds.\n", | ||
1464 | lp->name, lp->cisco_keepalive_period); | ||
1465 | } | ||
1466 | break; | ||
1467 | |||
1468 | /* get/set debugging */ | ||
1469 | case SIOCGDEBSERINT: | ||
1470 | len = (unsigned long)sizeof(lp->cisco_debserint); | ||
1471 | if (copy_to_user(ifr->ifr_data, | ||
1472 | &lp->cisco_debserint, len)) | ||
1473 | rc = -EFAULT; | ||
1474 | break; | ||
1475 | case SIOCSDEBSERINT: | ||
1476 | len = (unsigned long)sizeof(lp->cisco_debserint); | ||
1477 | if (copy_from_user(&debserint, | ||
1478 | ifr->ifr_data, len)) | ||
1479 | rc = -EFAULT; | ||
1480 | if ((debserint >= 0) && (debserint <= 64)) | ||
1481 | lp->cisco_debserint = debserint; | ||
1482 | else | ||
1483 | rc = -EINVAL; | ||
1484 | break; | ||
1485 | |||
1486 | default: | ||
1487 | rc = -EINVAL; | ||
1488 | break; | ||
1489 | } | ||
1490 | return (rc); | ||
1491 | } | ||
1492 | |||
1493 | /* called via cisco_timer.function */ | ||
1494 | static void | ||
1495 | isdn_net_ciscohdlck_slarp_send_keepalive(unsigned long data) | ||
1496 | { | ||
1497 | isdn_net_local *lp = (isdn_net_local *) data; | ||
1498 | struct sk_buff *skb; | ||
1499 | unsigned char *p; | ||
1500 | unsigned long last_cisco_myseq = lp->cisco_myseq; | ||
1501 | int myseq_diff = 0; | ||
1502 | |||
1503 | if (!(lp->flags & ISDN_NET_CONNECTED) || lp->dialstate) { | ||
1504 | printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__); | ||
1505 | return; | ||
1506 | } | ||
1507 | lp->cisco_myseq++; | ||
1508 | |||
1509 | myseq_diff = (lp->cisco_myseq - lp->cisco_mineseen); | ||
1510 | if ((lp->cisco_line_state) && ((myseq_diff >= 3)||(myseq_diff <= -3))) { | ||
1511 | /* line up -> down */ | ||
1512 | lp->cisco_line_state = 0; | ||
1513 | printk (KERN_WARNING | ||
1514 | "UPDOWN: Line protocol on Interface %s," | ||
1515 | " changed state to down\n", lp->name); | ||
1516 | /* should stop routing higher-level data accross */ | ||
1517 | } else if ((!lp->cisco_line_state) && | ||
1518 | (myseq_diff >= 0) && (myseq_diff <= 2)) { | ||
1519 | /* line down -> up */ | ||
1520 | lp->cisco_line_state = 1; | ||
1521 | printk (KERN_WARNING | ||
1522 | "UPDOWN: Line protocol on Interface %s," | ||
1523 | " changed state to up\n", lp->name); | ||
1524 | /* restart routing higher-level data accross */ | ||
1525 | } | ||
1526 | |||
1527 | if (lp->cisco_debserint) | ||
1528 | printk (KERN_DEBUG "%s: HDLC " | ||
1529 | "myseq %lu, mineseen %lu%c, yourseen %lu, %s\n", | ||
1530 | lp->name, last_cisco_myseq, lp->cisco_mineseen, | ||
1531 | ((last_cisco_myseq == lp->cisco_mineseen) ? '*' : 040), | ||
1532 | lp->cisco_yourseq, | ||
1533 | ((lp->cisco_line_state) ? "line up" : "line down")); | ||
1534 | |||
1535 | skb = isdn_net_ciscohdlck_alloc_skb(lp, 4 + 14); | ||
1536 | if (!skb) | ||
1537 | return; | ||
1538 | |||
1539 | p = skb_put(skb, 4 + 14); | ||
1540 | |||
1541 | /* cisco header */ | ||
1542 | p += put_u8 (p, CISCO_ADDR_UNICAST); | ||
1543 | p += put_u8 (p, CISCO_CTRL); | ||
1544 | p += put_u16(p, CISCO_TYPE_SLARP); | ||
1545 | |||
1546 | /* slarp keepalive */ | ||
1547 | p += put_u32(p, CISCO_SLARP_KEEPALIVE); | ||
1548 | p += put_u32(p, lp->cisco_myseq); | ||
1549 | p += put_u32(p, lp->cisco_yourseq); | ||
1550 | p += put_u16(p, 0xffff); // reliablity, always 0xffff | ||
1551 | |||
1552 | isdn_net_write_super(lp, skb); | ||
1553 | |||
1554 | lp->cisco_timer.expires = jiffies + lp->cisco_keepalive_period * HZ; | ||
1555 | |||
1556 | add_timer(&lp->cisco_timer); | ||
1557 | } | ||
1558 | |||
1559 | static void | ||
1560 | isdn_net_ciscohdlck_slarp_send_request(isdn_net_local *lp) | ||
1561 | { | ||
1562 | struct sk_buff *skb; | ||
1563 | unsigned char *p; | ||
1564 | |||
1565 | skb = isdn_net_ciscohdlck_alloc_skb(lp, 4 + 14); | ||
1566 | if (!skb) | ||
1567 | return; | ||
1568 | |||
1569 | p = skb_put(skb, 4 + 14); | ||
1570 | |||
1571 | /* cisco header */ | ||
1572 | p += put_u8 (p, CISCO_ADDR_UNICAST); | ||
1573 | p += put_u8 (p, CISCO_CTRL); | ||
1574 | p += put_u16(p, CISCO_TYPE_SLARP); | ||
1575 | |||
1576 | /* slarp request */ | ||
1577 | p += put_u32(p, CISCO_SLARP_REQUEST); | ||
1578 | p += put_u32(p, 0); // address | ||
1579 | p += put_u32(p, 0); // netmask | ||
1580 | p += put_u16(p, 0); // unused | ||
1581 | |||
1582 | isdn_net_write_super(lp, skb); | ||
1583 | } | ||
1584 | |||
1585 | static void | ||
1586 | isdn_net_ciscohdlck_connected(isdn_net_local *lp) | ||
1587 | { | ||
1588 | lp->cisco_myseq = 0; | ||
1589 | lp->cisco_mineseen = 0; | ||
1590 | lp->cisco_yourseq = 0; | ||
1591 | lp->cisco_keepalive_period = ISDN_TIMER_KEEPINT; | ||
1592 | lp->cisco_last_slarp_in = 0; | ||
1593 | lp->cisco_line_state = 0; | ||
1594 | lp->cisco_debserint = 0; | ||
1595 | |||
1596 | /* send slarp request because interface/seq.no.s reset */ | ||
1597 | isdn_net_ciscohdlck_slarp_send_request(lp); | ||
1598 | |||
1599 | init_timer(&lp->cisco_timer); | ||
1600 | lp->cisco_timer.data = (unsigned long) lp; | ||
1601 | lp->cisco_timer.function = isdn_net_ciscohdlck_slarp_send_keepalive; | ||
1602 | lp->cisco_timer.expires = jiffies + lp->cisco_keepalive_period * HZ; | ||
1603 | add_timer(&lp->cisco_timer); | ||
1604 | } | ||
1605 | |||
1606 | static void | ||
1607 | isdn_net_ciscohdlck_disconnected(isdn_net_local *lp) | ||
1608 | { | ||
1609 | del_timer(&lp->cisco_timer); | ||
1610 | } | ||
1611 | |||
1612 | static void | ||
1613 | isdn_net_ciscohdlck_slarp_send_reply(isdn_net_local *lp) | ||
1614 | { | ||
1615 | struct sk_buff *skb; | ||
1616 | unsigned char *p; | ||
1617 | struct in_device *in_dev = NULL; | ||
1618 | u32 addr = 0; /* local ipv4 address */ | ||
1619 | u32 mask = 0; /* local netmask */ | ||
1620 | |||
1621 | if ((in_dev = lp->netdev->dev.ip_ptr) != NULL) { | ||
1622 | /* take primary(first) address of interface */ | ||
1623 | struct in_ifaddr *ifa = in_dev->ifa_list; | ||
1624 | if (ifa != NULL) { | ||
1625 | addr = ifa->ifa_local; | ||
1626 | mask = ifa->ifa_mask; | ||
1627 | } | ||
1628 | } | ||
1629 | |||
1630 | skb = isdn_net_ciscohdlck_alloc_skb(lp, 4 + 14); | ||
1631 | if (!skb) | ||
1632 | return; | ||
1633 | |||
1634 | p = skb_put(skb, 4 + 14); | ||
1635 | |||
1636 | /* cisco header */ | ||
1637 | p += put_u8 (p, CISCO_ADDR_UNICAST); | ||
1638 | p += put_u8 (p, CISCO_CTRL); | ||
1639 | p += put_u16(p, CISCO_TYPE_SLARP); | ||
1640 | |||
1641 | /* slarp reply, send own ip/netmask; if values are nonsense remote | ||
1642 | * should think we are unable to provide it with an address via SLARP */ | ||
1643 | p += put_u32(p, CISCO_SLARP_REPLY); | ||
1644 | p += put_u32(p, addr); // address | ||
1645 | p += put_u32(p, mask); // netmask | ||
1646 | p += put_u16(p, 0); // unused | ||
1647 | |||
1648 | isdn_net_write_super(lp, skb); | ||
1649 | } | ||
1650 | |||
1651 | static void | ||
1652 | isdn_net_ciscohdlck_slarp_in(isdn_net_local *lp, struct sk_buff *skb) | ||
1653 | { | ||
1654 | unsigned char *p; | ||
1655 | int period; | ||
1656 | u32 code; | ||
1657 | u32 my_seq, addr; | ||
1658 | u32 your_seq, mask; | ||
1659 | u32 local; | ||
1660 | u16 unused; | ||
1661 | |||
1662 | if (skb->len < 14) | ||
1663 | return; | ||
1664 | |||
1665 | p = skb->data; | ||
1666 | p += get_u32(p, &code); | ||
1667 | |||
1668 | switch (code) { | ||
1669 | case CISCO_SLARP_REQUEST: | ||
1670 | lp->cisco_yourseq = 0; | ||
1671 | isdn_net_ciscohdlck_slarp_send_reply(lp); | ||
1672 | break; | ||
1673 | case CISCO_SLARP_REPLY: | ||
1674 | addr = ntohl(*(u32 *)p); | ||
1675 | mask = ntohl(*(u32 *)(p+4)); | ||
1676 | if (mask != 0xfffffffc) | ||
1677 | goto slarp_reply_out; | ||
1678 | if ((addr & 3) == 0 || (addr & 3) == 3) | ||
1679 | goto slarp_reply_out; | ||
1680 | local = addr ^ 3; | ||
1681 | printk(KERN_INFO "%s: got slarp reply: " | ||
1682 | "remote ip: %d.%d.%d.%d, " | ||
1683 | "local ip: %d.%d.%d.%d " | ||
1684 | "mask: %d.%d.%d.%d\n", | ||
1685 | lp->name, | ||
1686 | HIPQUAD(addr), | ||
1687 | HIPQUAD(local), | ||
1688 | HIPQUAD(mask)); | ||
1689 | break; | ||
1690 | slarp_reply_out: | ||
1691 | printk(KERN_INFO "%s: got invalid slarp " | ||
1692 | "reply (%d.%d.%d.%d/%d.%d.%d.%d) " | ||
1693 | "- ignored\n", lp->name, | ||
1694 | HIPQUAD(addr), HIPQUAD(mask)); | ||
1695 | break; | ||
1696 | case CISCO_SLARP_KEEPALIVE: | ||
1697 | period = (int)((jiffies - lp->cisco_last_slarp_in | ||
1698 | + HZ/2 - 1) / HZ); | ||
1699 | if (lp->cisco_debserint && | ||
1700 | (period != lp->cisco_keepalive_period) && | ||
1701 | lp->cisco_last_slarp_in) { | ||
1702 | printk(KERN_DEBUG "%s: Keepalive period mismatch - " | ||
1703 | "is %d but should be %d.\n", | ||
1704 | lp->name, period, lp->cisco_keepalive_period); | ||
1705 | } | ||
1706 | lp->cisco_last_slarp_in = jiffies; | ||
1707 | p += get_u32(p, &my_seq); | ||
1708 | p += get_u32(p, &your_seq); | ||
1709 | p += get_u16(p, &unused); | ||
1710 | lp->cisco_yourseq = my_seq; | ||
1711 | lp->cisco_mineseen = your_seq; | ||
1712 | break; | ||
1713 | } | ||
1714 | } | ||
1715 | |||
1716 | static void | ||
1717 | isdn_net_ciscohdlck_receive(isdn_net_local *lp, struct sk_buff *skb) | ||
1718 | { | ||
1719 | unsigned char *p; | ||
1720 | u8 addr; | ||
1721 | u8 ctrl; | ||
1722 | u16 type; | ||
1723 | |||
1724 | if (skb->len < 4) | ||
1725 | goto out_free; | ||
1726 | |||
1727 | p = skb->data; | ||
1728 | p += get_u8 (p, &addr); | ||
1729 | p += get_u8 (p, &ctrl); | ||
1730 | p += get_u16(p, &type); | ||
1731 | skb_pull(skb, 4); | ||
1732 | |||
1733 | if (addr != CISCO_ADDR_UNICAST && addr != CISCO_ADDR_BROADCAST) { | ||
1734 | printk(KERN_WARNING "%s: Unknown Cisco addr 0x%02x\n", | ||
1735 | lp->name, addr); | ||
1736 | goto out_free; | ||
1737 | } | ||
1738 | if (ctrl != CISCO_CTRL) { | ||
1739 | printk(KERN_WARNING "%s: Unknown Cisco ctrl 0x%02x\n", | ||
1740 | lp->name, ctrl); | ||
1741 | goto out_free; | ||
1742 | } | ||
1743 | |||
1744 | switch (type) { | ||
1745 | case CISCO_TYPE_SLARP: | ||
1746 | isdn_net_ciscohdlck_slarp_in(lp, skb); | ||
1747 | goto out_free; | ||
1748 | case CISCO_TYPE_CDP: | ||
1749 | if (lp->cisco_debserint) | ||
1750 | printk(KERN_DEBUG "%s: Received CDP packet. use " | ||
1751 | "\"no cdp enable\" on cisco.\n", lp->name); | ||
1752 | goto out_free; | ||
1753 | default: | ||
1754 | /* no special cisco protocol */ | ||
1755 | skb->protocol = htons(type); | ||
1756 | netif_rx(skb); | ||
1757 | return; | ||
1758 | } | ||
1759 | |||
1760 | out_free: | ||
1761 | kfree_skb(skb); | ||
1762 | } | ||
1763 | |||
1764 | /* | ||
1765 | * Got a packet from ISDN-Channel. | ||
1766 | */ | ||
1767 | static void | ||
1768 | isdn_net_receive(struct net_device *ndev, struct sk_buff *skb) | ||
1769 | { | ||
1770 | isdn_net_local *lp = (isdn_net_local *) ndev->priv; | ||
1771 | isdn_net_local *olp = lp; /* original 'lp' */ | ||
1772 | #ifdef CONFIG_ISDN_X25 | ||
1773 | struct concap_proto *cprot = lp -> netdev -> cprot; | ||
1774 | #endif | ||
1775 | lp->transcount += skb->len; | ||
1776 | |||
1777 | lp->stats.rx_packets++; | ||
1778 | lp->stats.rx_bytes += skb->len; | ||
1779 | if (lp->master) { | ||
1780 | /* Bundling: If device is a slave-device, deliver to master, also | ||
1781 | * handle master's statistics and hangup-timeout | ||
1782 | */ | ||
1783 | ndev = lp->master; | ||
1784 | lp = (isdn_net_local *) ndev->priv; | ||
1785 | lp->stats.rx_packets++; | ||
1786 | lp->stats.rx_bytes += skb->len; | ||
1787 | } | ||
1788 | skb->dev = ndev; | ||
1789 | skb->input_dev = ndev; | ||
1790 | skb->pkt_type = PACKET_HOST; | ||
1791 | skb->mac.raw = skb->data; | ||
1792 | #ifdef ISDN_DEBUG_NET_DUMP | ||
1793 | isdn_dumppkt("R:", skb->data, skb->len, 40); | ||
1794 | #endif | ||
1795 | switch (lp->p_encap) { | ||
1796 | case ISDN_NET_ENCAP_ETHER: | ||
1797 | /* Ethernet over ISDN */ | ||
1798 | olp->huptimer = 0; | ||
1799 | lp->huptimer = 0; | ||
1800 | skb->protocol = isdn_net_type_trans(skb, ndev); | ||
1801 | break; | ||
1802 | case ISDN_NET_ENCAP_UIHDLC: | ||
1803 | /* HDLC with UI-frame (for ispa with -h1 option) */ | ||
1804 | olp->huptimer = 0; | ||
1805 | lp->huptimer = 0; | ||
1806 | skb_pull(skb, 2); | ||
1807 | /* Fall through */ | ||
1808 | case ISDN_NET_ENCAP_RAWIP: | ||
1809 | /* RAW-IP without MAC-Header */ | ||
1810 | olp->huptimer = 0; | ||
1811 | lp->huptimer = 0; | ||
1812 | skb->protocol = htons(ETH_P_IP); | ||
1813 | break; | ||
1814 | case ISDN_NET_ENCAP_CISCOHDLCK: | ||
1815 | isdn_net_ciscohdlck_receive(lp, skb); | ||
1816 | return; | ||
1817 | case ISDN_NET_ENCAP_CISCOHDLC: | ||
1818 | /* CISCO-HDLC IP with type field and fake I-frame-header */ | ||
1819 | skb_pull(skb, 2); | ||
1820 | /* Fall through */ | ||
1821 | case ISDN_NET_ENCAP_IPTYP: | ||
1822 | /* IP with type field */ | ||
1823 | olp->huptimer = 0; | ||
1824 | lp->huptimer = 0; | ||
1825 | skb->protocol = *(unsigned short *) &(skb->data[0]); | ||
1826 | skb_pull(skb, 2); | ||
1827 | if (*(unsigned short *) skb->data == 0xFFFF) | ||
1828 | skb->protocol = htons(ETH_P_802_3); | ||
1829 | break; | ||
1830 | #ifdef CONFIG_ISDN_PPP | ||
1831 | case ISDN_NET_ENCAP_SYNCPPP: | ||
1832 | /* huptimer is done in isdn_ppp_push_higher */ | ||
1833 | isdn_ppp_receive(lp->netdev, olp, skb); | ||
1834 | return; | ||
1835 | #endif | ||
1836 | |||
1837 | default: | ||
1838 | #ifdef CONFIG_ISDN_X25 | ||
1839 | /* try if there are generic sync_device receiver routines */ | ||
1840 | if(cprot) if(cprot -> pops) | ||
1841 | if( cprot -> pops -> data_ind){ | ||
1842 | cprot -> pops -> data_ind(cprot,skb); | ||
1843 | return; | ||
1844 | }; | ||
1845 | #endif /* CONFIG_ISDN_X25 */ | ||
1846 | printk(KERN_WARNING "%s: unknown encapsulation, dropping\n", | ||
1847 | lp->name); | ||
1848 | kfree_skb(skb); | ||
1849 | return; | ||
1850 | } | ||
1851 | |||
1852 | netif_rx(skb); | ||
1853 | return; | ||
1854 | } | ||
1855 | |||
1856 | /* | ||
1857 | * A packet arrived via ISDN. Search interface-chain for a corresponding | ||
1858 | * interface. If found, deliver packet to receiver-function and return 1, | ||
1859 | * else return 0. | ||
1860 | */ | ||
1861 | int | ||
1862 | isdn_net_rcv_skb(int idx, struct sk_buff *skb) | ||
1863 | { | ||
1864 | isdn_net_dev *p = dev->rx_netdev[idx]; | ||
1865 | |||
1866 | if (p) { | ||
1867 | isdn_net_local *lp = p->local; | ||
1868 | if ((lp->flags & ISDN_NET_CONNECTED) && | ||
1869 | (!lp->dialstate)) { | ||
1870 | isdn_net_receive(&p->dev, skb); | ||
1871 | return 1; | ||
1872 | } | ||
1873 | } | ||
1874 | return 0; | ||
1875 | } | ||
1876 | |||
1877 | static int | ||
1878 | my_eth_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, | ||
1879 | void *daddr, void *saddr, unsigned len) | ||
1880 | { | ||
1881 | struct ethhdr *eth = (struct ethhdr *) skb_push(skb, ETH_HLEN); | ||
1882 | |||
1883 | /* | ||
1884 | * Set the protocol type. For a packet of type ETH_P_802_3 we | ||
1885 | * put the length here instead. It is up to the 802.2 layer to | ||
1886 | * carry protocol information. | ||
1887 | */ | ||
1888 | |||
1889 | if (type != ETH_P_802_3) | ||
1890 | eth->h_proto = htons(type); | ||
1891 | else | ||
1892 | eth->h_proto = htons(len); | ||
1893 | |||
1894 | /* | ||
1895 | * Set the source hardware address. | ||
1896 | */ | ||
1897 | if (saddr) | ||
1898 | memcpy(eth->h_source, saddr, dev->addr_len); | ||
1899 | else | ||
1900 | memcpy(eth->h_source, dev->dev_addr, dev->addr_len); | ||
1901 | |||
1902 | /* | ||
1903 | * Anyway, the loopback-device should never use this function... | ||
1904 | */ | ||
1905 | |||
1906 | if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) { | ||
1907 | memset(eth->h_dest, 0, dev->addr_len); | ||
1908 | return ETH_HLEN /*(dev->hard_header_len)*/; | ||
1909 | } | ||
1910 | if (daddr) { | ||
1911 | memcpy(eth->h_dest, daddr, dev->addr_len); | ||
1912 | return ETH_HLEN /*dev->hard_header_len*/; | ||
1913 | } | ||
1914 | return -ETH_HLEN /*dev->hard_header_len*/; | ||
1915 | } | ||
1916 | |||
1917 | /* | ||
1918 | * build an header | ||
1919 | * depends on encaps that is being used. | ||
1920 | */ | ||
1921 | |||
1922 | static int | ||
1923 | isdn_net_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, | ||
1924 | void *daddr, void *saddr, unsigned plen) | ||
1925 | { | ||
1926 | isdn_net_local *lp = dev->priv; | ||
1927 | unsigned char *p; | ||
1928 | ushort len = 0; | ||
1929 | |||
1930 | switch (lp->p_encap) { | ||
1931 | case ISDN_NET_ENCAP_ETHER: | ||
1932 | len = my_eth_header(skb, dev, type, daddr, saddr, plen); | ||
1933 | break; | ||
1934 | #ifdef CONFIG_ISDN_PPP | ||
1935 | case ISDN_NET_ENCAP_SYNCPPP: | ||
1936 | /* stick on a fake header to keep fragmentation code happy. */ | ||
1937 | len = IPPP_MAX_HEADER; | ||
1938 | skb_push(skb,len); | ||
1939 | break; | ||
1940 | #endif | ||
1941 | case ISDN_NET_ENCAP_RAWIP: | ||
1942 | printk(KERN_WARNING "isdn_net_header called with RAW_IP!\n"); | ||
1943 | len = 0; | ||
1944 | break; | ||
1945 | case ISDN_NET_ENCAP_IPTYP: | ||
1946 | /* ethernet type field */ | ||
1947 | *((ushort *) skb_push(skb, 2)) = htons(type); | ||
1948 | len = 2; | ||
1949 | break; | ||
1950 | case ISDN_NET_ENCAP_UIHDLC: | ||
1951 | /* HDLC with UI-Frames (for ispa with -h1 option) */ | ||
1952 | *((ushort *) skb_push(skb, 2)) = htons(0x0103); | ||
1953 | len = 2; | ||
1954 | break; | ||
1955 | case ISDN_NET_ENCAP_CISCOHDLC: | ||
1956 | case ISDN_NET_ENCAP_CISCOHDLCK: | ||
1957 | p = skb_push(skb, 4); | ||
1958 | p += put_u8 (p, CISCO_ADDR_UNICAST); | ||
1959 | p += put_u8 (p, CISCO_CTRL); | ||
1960 | p += put_u16(p, type); | ||
1961 | len = 4; | ||
1962 | break; | ||
1963 | #ifdef CONFIG_ISDN_X25 | ||
1964 | default: | ||
1965 | /* try if there are generic concap protocol routines */ | ||
1966 | if( lp-> netdev -> cprot ){ | ||
1967 | printk(KERN_WARNING "isdn_net_header called with concap_proto!\n"); | ||
1968 | len = 0; | ||
1969 | break; | ||
1970 | } | ||
1971 | break; | ||
1972 | #endif /* CONFIG_ISDN_X25 */ | ||
1973 | } | ||
1974 | return len; | ||
1975 | } | ||
1976 | |||
1977 | /* We don't need to send arp, because we have point-to-point connections. */ | ||
1978 | static int | ||
1979 | isdn_net_rebuild_header(struct sk_buff *skb) | ||
1980 | { | ||
1981 | struct net_device *dev = skb->dev; | ||
1982 | isdn_net_local *lp = dev->priv; | ||
1983 | int ret = 0; | ||
1984 | |||
1985 | if (lp->p_encap == ISDN_NET_ENCAP_ETHER) { | ||
1986 | struct ethhdr *eth = (struct ethhdr *) skb->data; | ||
1987 | |||
1988 | /* | ||
1989 | * Only ARP/IP is currently supported | ||
1990 | */ | ||
1991 | |||
1992 | if (eth->h_proto != htons(ETH_P_IP)) { | ||
1993 | printk(KERN_WARNING | ||
1994 | "isdn_net: %s don't know how to resolve type %d addresses?\n", | ||
1995 | dev->name, (int) eth->h_proto); | ||
1996 | memcpy(eth->h_source, dev->dev_addr, dev->addr_len); | ||
1997 | return 0; | ||
1998 | } | ||
1999 | /* | ||
2000 | * Try to get ARP to resolve the header. | ||
2001 | */ | ||
2002 | #ifdef CONFIG_INET | ||
2003 | ret = arp_find(eth->h_dest, skb); | ||
2004 | #endif | ||
2005 | } | ||
2006 | return ret; | ||
2007 | } | ||
2008 | |||
2009 | /* | ||
2010 | * Interface-setup. (just after registering a new interface) | ||
2011 | */ | ||
2012 | static int | ||
2013 | isdn_net_init(struct net_device *ndev) | ||
2014 | { | ||
2015 | ushort max_hlhdr_len = 0; | ||
2016 | isdn_net_local *lp = (isdn_net_local *) ndev->priv; | ||
2017 | int drvidx, i; | ||
2018 | |||
2019 | ether_setup(ndev); | ||
2020 | lp->org_hhc = ndev->hard_header_cache; | ||
2021 | lp->org_hcu = ndev->header_cache_update; | ||
2022 | |||
2023 | /* Setup the generic properties */ | ||
2024 | |||
2025 | ndev->hard_header = NULL; | ||
2026 | ndev->hard_header_cache = NULL; | ||
2027 | ndev->header_cache_update = NULL; | ||
2028 | ndev->mtu = 1500; | ||
2029 | ndev->flags = IFF_NOARP|IFF_POINTOPOINT; | ||
2030 | ndev->type = ARPHRD_ETHER; | ||
2031 | ndev->addr_len = ETH_ALEN; | ||
2032 | |||
2033 | /* for clients with MPPP maybe higher values better */ | ||
2034 | ndev->tx_queue_len = 30; | ||
2035 | |||
2036 | for (i = 0; i < ETH_ALEN; i++) | ||
2037 | ndev->broadcast[i] = 0xff; | ||
2038 | |||
2039 | /* The ISDN-specific entries in the device structure. */ | ||
2040 | ndev->open = &isdn_net_open; | ||
2041 | ndev->hard_start_xmit = &isdn_net_start_xmit; | ||
2042 | |||
2043 | /* | ||
2044 | * up till binding we ask the protocol layer to reserve as much | ||
2045 | * as we might need for HL layer | ||
2046 | */ | ||
2047 | |||
2048 | for (drvidx = 0; drvidx < ISDN_MAX_DRIVERS; drvidx++) | ||
2049 | if (dev->drv[drvidx]) | ||
2050 | if (max_hlhdr_len < dev->drv[drvidx]->interface->hl_hdrlen) | ||
2051 | max_hlhdr_len = dev->drv[drvidx]->interface->hl_hdrlen; | ||
2052 | |||
2053 | ndev->hard_header_len = ETH_HLEN + max_hlhdr_len; | ||
2054 | ndev->stop = &isdn_net_close; | ||
2055 | ndev->get_stats = &isdn_net_get_stats; | ||
2056 | ndev->rebuild_header = &isdn_net_rebuild_header; | ||
2057 | ndev->do_ioctl = NULL; | ||
2058 | return 0; | ||
2059 | } | ||
2060 | |||
2061 | static void | ||
2062 | isdn_net_swapbind(int drvidx) | ||
2063 | { | ||
2064 | isdn_net_dev *p; | ||
2065 | |||
2066 | #ifdef ISDN_DEBUG_NET_ICALL | ||
2067 | printk(KERN_DEBUG "n_fi: swapping ch of %d\n", drvidx); | ||
2068 | #endif | ||
2069 | p = dev->netdev; | ||
2070 | while (p) { | ||
2071 | if (p->local->pre_device == drvidx) | ||
2072 | switch (p->local->pre_channel) { | ||
2073 | case 0: | ||
2074 | p->local->pre_channel = 1; | ||
2075 | break; | ||
2076 | case 1: | ||
2077 | p->local->pre_channel = 0; | ||
2078 | break; | ||
2079 | } | ||
2080 | p = (isdn_net_dev *) p->next; | ||
2081 | } | ||
2082 | } | ||
2083 | |||
2084 | static void | ||
2085 | isdn_net_swap_usage(int i1, int i2) | ||
2086 | { | ||
2087 | int u1 = dev->usage[i1] & ISDN_USAGE_EXCLUSIVE; | ||
2088 | int u2 = dev->usage[i2] & ISDN_USAGE_EXCLUSIVE; | ||
2089 | |||
2090 | #ifdef ISDN_DEBUG_NET_ICALL | ||
2091 | printk(KERN_DEBUG "n_fi: usage of %d and %d\n", i1, i2); | ||
2092 | #endif | ||
2093 | dev->usage[i1] &= ~ISDN_USAGE_EXCLUSIVE; | ||
2094 | dev->usage[i1] |= u2; | ||
2095 | dev->usage[i2] &= ~ISDN_USAGE_EXCLUSIVE; | ||
2096 | dev->usage[i2] |= u1; | ||
2097 | isdn_info_update(); | ||
2098 | } | ||
2099 | |||
2100 | /* | ||
2101 | * An incoming call-request has arrived. | ||
2102 | * Search the interface-chain for an appropriate interface. | ||
2103 | * If found, connect the interface to the ISDN-channel and initiate | ||
2104 | * D- and B-Channel-setup. If secure-flag is set, accept only | ||
2105 | * configured phone-numbers. If callback-flag is set, initiate | ||
2106 | * callback-dialing. | ||
2107 | * | ||
2108 | * Return-Value: 0 = No appropriate interface for this call. | ||
2109 | * 1 = Call accepted | ||
2110 | * 2 = Reject call, wait cbdelay, then call back | ||
2111 | * 3 = Reject call | ||
2112 | * 4 = Wait cbdelay, then call back | ||
2113 | * 5 = No appropriate interface for this call, | ||
2114 | * would eventually match if CID was longer. | ||
2115 | */ | ||
2116 | |||
2117 | int | ||
2118 | isdn_net_find_icall(int di, int ch, int idx, setup_parm *setup) | ||
2119 | { | ||
2120 | char *eaz; | ||
2121 | int si1; | ||
2122 | int si2; | ||
2123 | int ematch; | ||
2124 | int wret; | ||
2125 | int swapped; | ||
2126 | int sidx = 0; | ||
2127 | u_long flags; | ||
2128 | isdn_net_dev *p; | ||
2129 | isdn_net_phone *n; | ||
2130 | char nr[32]; | ||
2131 | char *my_eaz; | ||
2132 | |||
2133 | /* Search name in netdev-chain */ | ||
2134 | if (!setup->phone[0]) { | ||
2135 | nr[0] = '0'; | ||
2136 | nr[1] = '\0'; | ||
2137 | printk(KERN_INFO "isdn_net: Incoming call without OAD, assuming '0'\n"); | ||
2138 | } else | ||
2139 | strcpy(nr, setup->phone); | ||
2140 | si1 = (int) setup->si1; | ||
2141 | si2 = (int) setup->si2; | ||
2142 | if (!setup->eazmsn[0]) { | ||
2143 | printk(KERN_WARNING "isdn_net: Incoming call without CPN, assuming '0'\n"); | ||
2144 | eaz = "0"; | ||
2145 | } else | ||
2146 | eaz = setup->eazmsn; | ||
2147 | if (dev->net_verbose > 1) | ||
2148 | printk(KERN_INFO "isdn_net: call from %s,%d,%d -> %s\n", nr, si1, si2, eaz); | ||
2149 | /* Accept DATA and VOICE calls at this stage | ||
2150 | * local eaz is checked later for allowed call types | ||
2151 | */ | ||
2152 | if ((si1 != 7) && (si1 != 1)) { | ||
2153 | if (dev->net_verbose > 1) | ||
2154 | printk(KERN_INFO "isdn_net: Service-Indicator not 1 or 7, ignored\n"); | ||
2155 | return 0; | ||
2156 | } | ||
2157 | n = (isdn_net_phone *) 0; | ||
2158 | p = dev->netdev; | ||
2159 | ematch = wret = swapped = 0; | ||
2160 | #ifdef ISDN_DEBUG_NET_ICALL | ||
2161 | printk(KERN_DEBUG "n_fi: di=%d ch=%d idx=%d usg=%d\n", di, ch, idx, | ||
2162 | dev->usage[idx]); | ||
2163 | #endif | ||
2164 | while (p) { | ||
2165 | int matchret; | ||
2166 | isdn_net_local *lp = p->local; | ||
2167 | |||
2168 | /* If last check has triggered as binding-swap, revert it */ | ||
2169 | switch (swapped) { | ||
2170 | case 2: | ||
2171 | isdn_net_swap_usage(idx, sidx); | ||
2172 | /* fall through */ | ||
2173 | case 1: | ||
2174 | isdn_net_swapbind(di); | ||
2175 | break; | ||
2176 | } | ||
2177 | swapped = 0; | ||
2178 | /* check acceptable call types for DOV */ | ||
2179 | my_eaz = isdn_map_eaz2msn(lp->msn, di); | ||
2180 | if (si1 == 1) { /* it's a DOV call, check if we allow it */ | ||
2181 | if (*my_eaz == 'v' || *my_eaz == 'V' || | ||
2182 | *my_eaz == 'b' || *my_eaz == 'B') | ||
2183 | my_eaz++; /* skip to allow a match */ | ||
2184 | else | ||
2185 | my_eaz = NULL; /* force non match */ | ||
2186 | } else { /* it's a DATA call, check if we allow it */ | ||
2187 | if (*my_eaz == 'b' || *my_eaz == 'B') | ||
2188 | my_eaz++; /* skip to allow a match */ | ||
2189 | } | ||
2190 | if (my_eaz) | ||
2191 | matchret = isdn_msncmp(eaz, my_eaz); | ||
2192 | else | ||
2193 | matchret = 1; | ||
2194 | if (!matchret) | ||
2195 | ematch = 1; | ||
2196 | |||
2197 | /* Remember if more numbers eventually can match */ | ||
2198 | if (matchret > wret) | ||
2199 | wret = matchret; | ||
2200 | #ifdef ISDN_DEBUG_NET_ICALL | ||
2201 | printk(KERN_DEBUG "n_fi: if='%s', l.msn=%s, l.flags=%d, l.dstate=%d\n", | ||
2202 | lp->name, lp->msn, lp->flags, lp->dialstate); | ||
2203 | #endif | ||
2204 | if ((!matchret) && /* EAZ is matching */ | ||
2205 | (((!(lp->flags & ISDN_NET_CONNECTED)) && /* but not connected */ | ||
2206 | (USG_NONE(dev->usage[idx]))) || /* and ch. unused or */ | ||
2207 | ((((lp->dialstate == 4) || (lp->dialstate == 12)) && /* if dialing */ | ||
2208 | (!(lp->flags & ISDN_NET_CALLBACK))) /* but no callback */ | ||
2209 | ))) | ||
2210 | { | ||
2211 | #ifdef ISDN_DEBUG_NET_ICALL | ||
2212 | printk(KERN_DEBUG "n_fi: match1, pdev=%d pch=%d\n", | ||
2213 | lp->pre_device, lp->pre_channel); | ||
2214 | #endif | ||
2215 | if (dev->usage[idx] & ISDN_USAGE_EXCLUSIVE) { | ||
2216 | if ((lp->pre_channel != ch) || | ||
2217 | (lp->pre_device != di)) { | ||
2218 | /* Here we got a problem: | ||
2219 | * If using an ICN-Card, an incoming call is always signaled on | ||
2220 | * on the first channel of the card, if both channels are | ||
2221 | * down. However this channel may be bound exclusive. If the | ||
2222 | * second channel is free, this call should be accepted. | ||
2223 | * The solution is horribly but it runs, so what: | ||
2224 | * We exchange the exclusive bindings of the two channels, the | ||
2225 | * corresponding variables in the interface-structs. | ||
2226 | */ | ||
2227 | if (ch == 0) { | ||
2228 | sidx = isdn_dc2minor(di, 1); | ||
2229 | #ifdef ISDN_DEBUG_NET_ICALL | ||
2230 | printk(KERN_DEBUG "n_fi: ch is 0\n"); | ||
2231 | #endif | ||
2232 | if (USG_NONE(dev->usage[sidx])) { | ||
2233 | /* Second Channel is free, now see if it is bound | ||
2234 | * exclusive too. */ | ||
2235 | if (dev->usage[sidx] & ISDN_USAGE_EXCLUSIVE) { | ||
2236 | #ifdef ISDN_DEBUG_NET_ICALL | ||
2237 | printk(KERN_DEBUG "n_fi: 2nd channel is down and bound\n"); | ||
2238 | #endif | ||
2239 | /* Yes, swap bindings only, if the original | ||
2240 | * binding is bound to channel 1 of this driver */ | ||
2241 | if ((lp->pre_device == di) && | ||
2242 | (lp->pre_channel == 1)) { | ||
2243 | isdn_net_swapbind(di); | ||
2244 | swapped = 1; | ||
2245 | } else { | ||
2246 | /* ... else iterate next device */ | ||
2247 | p = (isdn_net_dev *) p->next; | ||
2248 | continue; | ||
2249 | } | ||
2250 | } else { | ||
2251 | #ifdef ISDN_DEBUG_NET_ICALL | ||
2252 | printk(KERN_DEBUG "n_fi: 2nd channel is down and unbound\n"); | ||
2253 | #endif | ||
2254 | /* No, swap always and swap excl-usage also */ | ||
2255 | isdn_net_swap_usage(idx, sidx); | ||
2256 | isdn_net_swapbind(di); | ||
2257 | swapped = 2; | ||
2258 | } | ||
2259 | /* Now check for exclusive binding again */ | ||
2260 | #ifdef ISDN_DEBUG_NET_ICALL | ||
2261 | printk(KERN_DEBUG "n_fi: final check\n"); | ||
2262 | #endif | ||
2263 | if ((dev->usage[idx] & ISDN_USAGE_EXCLUSIVE) && | ||
2264 | ((lp->pre_channel != ch) || | ||
2265 | (lp->pre_device != di))) { | ||
2266 | #ifdef ISDN_DEBUG_NET_ICALL | ||
2267 | printk(KERN_DEBUG "n_fi: final check failed\n"); | ||
2268 | #endif | ||
2269 | p = (isdn_net_dev *) p->next; | ||
2270 | continue; | ||
2271 | } | ||
2272 | } | ||
2273 | } else { | ||
2274 | /* We are already on the second channel, so nothing to do */ | ||
2275 | #ifdef ISDN_DEBUG_NET_ICALL | ||
2276 | printk(KERN_DEBUG "n_fi: already on 2nd channel\n"); | ||
2277 | #endif | ||
2278 | } | ||
2279 | } | ||
2280 | } | ||
2281 | #ifdef ISDN_DEBUG_NET_ICALL | ||
2282 | printk(KERN_DEBUG "n_fi: match2\n"); | ||
2283 | #endif | ||
2284 | n = lp->phone[0]; | ||
2285 | if (lp->flags & ISDN_NET_SECURE) { | ||
2286 | while (n) { | ||
2287 | if (!isdn_msncmp(nr, n->num)) | ||
2288 | break; | ||
2289 | n = (isdn_net_phone *) n->next; | ||
2290 | } | ||
2291 | } | ||
2292 | if (n || (!(lp->flags & ISDN_NET_SECURE))) { | ||
2293 | #ifdef ISDN_DEBUG_NET_ICALL | ||
2294 | printk(KERN_DEBUG "n_fi: match3\n"); | ||
2295 | #endif | ||
2296 | /* matching interface found */ | ||
2297 | |||
2298 | /* | ||
2299 | * Is the state STOPPED? | ||
2300 | * If so, no dialin is allowed, | ||
2301 | * so reject actively. | ||
2302 | * */ | ||
2303 | if (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF) { | ||
2304 | printk(KERN_INFO "incoming call, interface %s `stopped' -> rejected\n", | ||
2305 | lp->name); | ||
2306 | return 3; | ||
2307 | } | ||
2308 | /* | ||
2309 | * Is the interface up? | ||
2310 | * If not, reject the call actively. | ||
2311 | */ | ||
2312 | if (!isdn_net_device_started(p)) { | ||
2313 | printk(KERN_INFO "%s: incoming call, interface down -> rejected\n", | ||
2314 | lp->name); | ||
2315 | return 3; | ||
2316 | } | ||
2317 | /* Interface is up, now see if it's a slave. If so, see if | ||
2318 | * it's master and parent slave is online. If not, reject the call. | ||
2319 | */ | ||
2320 | if (lp->master) { | ||
2321 | isdn_net_local *mlp = (isdn_net_local *) lp->master->priv; | ||
2322 | printk(KERN_DEBUG "ICALLslv: %s\n", lp->name); | ||
2323 | printk(KERN_DEBUG "master=%s\n", mlp->name); | ||
2324 | if (mlp->flags & ISDN_NET_CONNECTED) { | ||
2325 | printk(KERN_DEBUG "master online\n"); | ||
2326 | /* Master is online, find parent-slave (master if first slave) */ | ||
2327 | while (mlp->slave) { | ||
2328 | if ((isdn_net_local *) mlp->slave->priv == lp) | ||
2329 | break; | ||
2330 | mlp = (isdn_net_local *) mlp->slave->priv; | ||
2331 | } | ||
2332 | } else | ||
2333 | printk(KERN_DEBUG "master offline\n"); | ||
2334 | /* Found parent, if it's offline iterate next device */ | ||
2335 | printk(KERN_DEBUG "mlpf: %d\n", mlp->flags & ISDN_NET_CONNECTED); | ||
2336 | if (!(mlp->flags & ISDN_NET_CONNECTED)) { | ||
2337 | p = (isdn_net_dev *) p->next; | ||
2338 | continue; | ||
2339 | } | ||
2340 | } | ||
2341 | if (lp->flags & ISDN_NET_CALLBACK) { | ||
2342 | int chi; | ||
2343 | /* | ||
2344 | * Is the state MANUAL? | ||
2345 | * If so, no callback can be made, | ||
2346 | * so reject actively. | ||
2347 | * */ | ||
2348 | if (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF) { | ||
2349 | printk(KERN_INFO "incoming call for callback, interface %s `off' -> rejected\n", | ||
2350 | lp->name); | ||
2351 | return 3; | ||
2352 | } | ||
2353 | printk(KERN_DEBUG "%s: call from %s -> %s, start callback\n", | ||
2354 | lp->name, nr, eaz); | ||
2355 | if (lp->phone[1]) { | ||
2356 | /* Grab a free ISDN-Channel */ | ||
2357 | spin_lock_irqsave(&dev->lock, flags); | ||
2358 | if ((chi = | ||
2359 | isdn_get_free_channel( | ||
2360 | ISDN_USAGE_NET, | ||
2361 | lp->l2_proto, | ||
2362 | lp->l3_proto, | ||
2363 | lp->pre_device, | ||
2364 | lp->pre_channel, | ||
2365 | lp->msn) | ||
2366 | ) < 0) { | ||
2367 | |||
2368 | printk(KERN_WARNING "isdn_net_find_icall: No channel for %s\n", lp->name); | ||
2369 | spin_unlock_irqrestore(&dev->lock, flags); | ||
2370 | return 0; | ||
2371 | } | ||
2372 | /* Setup dialstate. */ | ||
2373 | lp->dtimer = 0; | ||
2374 | lp->dialstate = 11; | ||
2375 | /* Connect interface with channel */ | ||
2376 | isdn_net_bind_channel(lp, chi); | ||
2377 | #ifdef CONFIG_ISDN_PPP | ||
2378 | if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) | ||
2379 | if (isdn_ppp_bind(lp) < 0) { | ||
2380 | spin_unlock_irqrestore(&dev->lock, flags); | ||
2381 | isdn_net_unbind_channel(lp); | ||
2382 | return 0; | ||
2383 | } | ||
2384 | #endif | ||
2385 | spin_unlock_irqrestore(&dev->lock, flags); | ||
2386 | /* Initiate dialing by returning 2 or 4 */ | ||
2387 | return (lp->flags & ISDN_NET_CBHUP) ? 2 : 4; | ||
2388 | } else | ||
2389 | printk(KERN_WARNING "isdn_net: %s: No phone number\n", lp->name); | ||
2390 | return 0; | ||
2391 | } else { | ||
2392 | printk(KERN_DEBUG "%s: call from %s -> %s accepted\n", lp->name, nr, | ||
2393 | eaz); | ||
2394 | /* if this interface is dialing, it does it probably on a different | ||
2395 | device, so free this device */ | ||
2396 | if ((lp->dialstate == 4) || (lp->dialstate == 12)) { | ||
2397 | #ifdef CONFIG_ISDN_PPP | ||
2398 | if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) | ||
2399 | isdn_ppp_free(lp); | ||
2400 | #endif | ||
2401 | isdn_net_lp_disconnected(lp); | ||
2402 | isdn_free_channel(lp->isdn_device, lp->isdn_channel, | ||
2403 | ISDN_USAGE_NET); | ||
2404 | } | ||
2405 | spin_lock_irqsave(&dev->lock, flags); | ||
2406 | dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE; | ||
2407 | dev->usage[idx] |= ISDN_USAGE_NET; | ||
2408 | strcpy(dev->num[idx], nr); | ||
2409 | isdn_info_update(); | ||
2410 | dev->st_netdev[idx] = lp->netdev; | ||
2411 | lp->isdn_device = di; | ||
2412 | lp->isdn_channel = ch; | ||
2413 | lp->ppp_slot = -1; | ||
2414 | lp->flags |= ISDN_NET_CONNECTED; | ||
2415 | lp->dialstate = 7; | ||
2416 | lp->dtimer = 0; | ||
2417 | lp->outgoing = 0; | ||
2418 | lp->huptimer = 0; | ||
2419 | lp->hupflags |= ISDN_WAITCHARGE; | ||
2420 | lp->hupflags &= ~ISDN_HAVECHARGE; | ||
2421 | #ifdef CONFIG_ISDN_PPP | ||
2422 | if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) { | ||
2423 | if (isdn_ppp_bind(lp) < 0) { | ||
2424 | isdn_net_unbind_channel(lp); | ||
2425 | spin_unlock_irqrestore(&dev->lock, flags); | ||
2426 | return 0; | ||
2427 | } | ||
2428 | } | ||
2429 | #endif | ||
2430 | spin_unlock_irqrestore(&dev->lock, flags); | ||
2431 | return 1; | ||
2432 | } | ||
2433 | } | ||
2434 | } | ||
2435 | p = (isdn_net_dev *) p->next; | ||
2436 | } | ||
2437 | /* If none of configured EAZ/MSN matched and not verbose, be silent */ | ||
2438 | if (!ematch || dev->net_verbose) | ||
2439 | printk(KERN_INFO "isdn_net: call from %s -> %d %s ignored\n", nr, di, eaz); | ||
2440 | return (wret == 2)?5:0; | ||
2441 | } | ||
2442 | |||
2443 | /* | ||
2444 | * Search list of net-interfaces for an interface with given name. | ||
2445 | */ | ||
2446 | isdn_net_dev * | ||
2447 | isdn_net_findif(char *name) | ||
2448 | { | ||
2449 | isdn_net_dev *p = dev->netdev; | ||
2450 | |||
2451 | while (p) { | ||
2452 | if (!strcmp(p->local->name, name)) | ||
2453 | return p; | ||
2454 | p = (isdn_net_dev *) p->next; | ||
2455 | } | ||
2456 | return (isdn_net_dev *) NULL; | ||
2457 | } | ||
2458 | |||
2459 | /* | ||
2460 | * Force a net-interface to dial out. | ||
2461 | * This is called from the userlevel-routine below or | ||
2462 | * from isdn_net_start_xmit(). | ||
2463 | */ | ||
2464 | int | ||
2465 | isdn_net_force_dial_lp(isdn_net_local * lp) | ||
2466 | { | ||
2467 | if ((!(lp->flags & ISDN_NET_CONNECTED)) && !lp->dialstate) { | ||
2468 | int chi; | ||
2469 | if (lp->phone[1]) { | ||
2470 | ulong flags; | ||
2471 | |||
2472 | /* Grab a free ISDN-Channel */ | ||
2473 | spin_lock_irqsave(&dev->lock, flags); | ||
2474 | if ((chi = isdn_get_free_channel( | ||
2475 | ISDN_USAGE_NET, | ||
2476 | lp->l2_proto, | ||
2477 | lp->l3_proto, | ||
2478 | lp->pre_device, | ||
2479 | lp->pre_channel, | ||
2480 | lp->msn)) < 0) { | ||
2481 | printk(KERN_WARNING "isdn_net_force_dial: No channel for %s\n", lp->name); | ||
2482 | spin_unlock_irqrestore(&dev->lock, flags); | ||
2483 | return -EAGAIN; | ||
2484 | } | ||
2485 | lp->dialstate = 1; | ||
2486 | /* Connect interface with channel */ | ||
2487 | isdn_net_bind_channel(lp, chi); | ||
2488 | #ifdef CONFIG_ISDN_PPP | ||
2489 | if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) | ||
2490 | if (isdn_ppp_bind(lp) < 0) { | ||
2491 | isdn_net_unbind_channel(lp); | ||
2492 | spin_unlock_irqrestore(&dev->lock, flags); | ||
2493 | return -EAGAIN; | ||
2494 | } | ||
2495 | #endif | ||
2496 | /* Initiate dialing */ | ||
2497 | spin_unlock_irqrestore(&dev->lock, flags); | ||
2498 | isdn_net_dial(); | ||
2499 | return 0; | ||
2500 | } else | ||
2501 | return -EINVAL; | ||
2502 | } else | ||
2503 | return -EBUSY; | ||
2504 | } | ||
2505 | |||
2506 | /* | ||
2507 | * This is called from certain upper protocol layers (multilink ppp | ||
2508 | * and x25iface encapsulation module) that want to initiate dialing | ||
2509 | * themselves. | ||
2510 | */ | ||
2511 | int | ||
2512 | isdn_net_dial_req(isdn_net_local * lp) | ||
2513 | { | ||
2514 | /* is there a better error code? */ | ||
2515 | if (!(ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_AUTO)) return -EBUSY; | ||
2516 | |||
2517 | return isdn_net_force_dial_lp(lp); | ||
2518 | } | ||
2519 | |||
2520 | /* | ||
2521 | * Force a net-interface to dial out. | ||
2522 | * This is always called from within userspace (ISDN_IOCTL_NET_DIAL). | ||
2523 | */ | ||
2524 | int | ||
2525 | isdn_net_force_dial(char *name) | ||
2526 | { | ||
2527 | isdn_net_dev *p = isdn_net_findif(name); | ||
2528 | |||
2529 | if (!p) | ||
2530 | return -ENODEV; | ||
2531 | return (isdn_net_force_dial_lp(p->local)); | ||
2532 | } | ||
2533 | |||
2534 | /* | ||
2535 | * Allocate a new network-interface and initialize its data structures. | ||
2536 | */ | ||
2537 | char * | ||
2538 | isdn_net_new(char *name, struct net_device *master) | ||
2539 | { | ||
2540 | isdn_net_dev *netdev; | ||
2541 | |||
2542 | /* Avoid creating an existing interface */ | ||
2543 | if (isdn_net_findif(name)) { | ||
2544 | printk(KERN_WARNING "isdn_net: interface %s already exists\n", name); | ||
2545 | return NULL; | ||
2546 | } | ||
2547 | if (!(netdev = (isdn_net_dev *) kmalloc(sizeof(isdn_net_dev), GFP_KERNEL))) { | ||
2548 | printk(KERN_WARNING "isdn_net: Could not allocate net-device\n"); | ||
2549 | return NULL; | ||
2550 | } | ||
2551 | memset(netdev, 0, sizeof(isdn_net_dev)); | ||
2552 | if (!(netdev->local = (isdn_net_local *) kmalloc(sizeof(isdn_net_local), GFP_KERNEL))) { | ||
2553 | printk(KERN_WARNING "isdn_net: Could not allocate device locals\n"); | ||
2554 | kfree(netdev); | ||
2555 | return NULL; | ||
2556 | } | ||
2557 | memset(netdev->local, 0, sizeof(isdn_net_local)); | ||
2558 | if (name == NULL) | ||
2559 | strcpy(netdev->local->name, " "); | ||
2560 | else | ||
2561 | strcpy(netdev->local->name, name); | ||
2562 | strcpy(netdev->dev.name, netdev->local->name); | ||
2563 | netdev->dev.priv = netdev->local; | ||
2564 | netdev->dev.init = isdn_net_init; | ||
2565 | netdev->local->p_encap = ISDN_NET_ENCAP_RAWIP; | ||
2566 | if (master) { | ||
2567 | /* Device shall be a slave */ | ||
2568 | struct net_device *p = (((isdn_net_local *) master->priv)->slave); | ||
2569 | struct net_device *q = master; | ||
2570 | |||
2571 | netdev->local->master = master; | ||
2572 | /* Put device at end of slave-chain */ | ||
2573 | while (p) { | ||
2574 | q = p; | ||
2575 | p = (((isdn_net_local *) p->priv)->slave); | ||
2576 | } | ||
2577 | ((isdn_net_local *) q->priv)->slave = &(netdev->dev); | ||
2578 | } else { | ||
2579 | /* Device shall be a master */ | ||
2580 | /* | ||
2581 | * Watchdog timer (currently) for master only. | ||
2582 | */ | ||
2583 | netdev->dev.tx_timeout = isdn_net_tx_timeout; | ||
2584 | netdev->dev.watchdog_timeo = ISDN_NET_TX_TIMEOUT; | ||
2585 | if (register_netdev(&netdev->dev) != 0) { | ||
2586 | printk(KERN_WARNING "isdn_net: Could not register net-device\n"); | ||
2587 | kfree(netdev->local); | ||
2588 | kfree(netdev); | ||
2589 | return NULL; | ||
2590 | } | ||
2591 | } | ||
2592 | netdev->local->magic = ISDN_NET_MAGIC; | ||
2593 | |||
2594 | netdev->queue = netdev->local; | ||
2595 | spin_lock_init(&netdev->queue_lock); | ||
2596 | |||
2597 | netdev->local->last = netdev->local; | ||
2598 | netdev->local->netdev = netdev; | ||
2599 | netdev->local->next = netdev->local; | ||
2600 | |||
2601 | INIT_WORK(&netdev->local->tqueue, (void *)(void *) isdn_net_softint, netdev->local); | ||
2602 | spin_lock_init(&netdev->local->xmit_lock); | ||
2603 | |||
2604 | netdev->local->isdn_device = -1; | ||
2605 | netdev->local->isdn_channel = -1; | ||
2606 | netdev->local->pre_device = -1; | ||
2607 | netdev->local->pre_channel = -1; | ||
2608 | netdev->local->exclusive = -1; | ||
2609 | netdev->local->ppp_slot = -1; | ||
2610 | netdev->local->pppbind = -1; | ||
2611 | skb_queue_head_init(&netdev->local->super_tx_queue); | ||
2612 | netdev->local->l2_proto = ISDN_PROTO_L2_X75I; | ||
2613 | netdev->local->l3_proto = ISDN_PROTO_L3_TRANS; | ||
2614 | netdev->local->triggercps = 6000; | ||
2615 | netdev->local->slavedelay = 10 * HZ; | ||
2616 | netdev->local->hupflags = ISDN_INHUP; /* Do hangup even on incoming calls */ | ||
2617 | netdev->local->onhtime = 10; /* Default hangup-time for saving costs | ||
2618 | of those who forget configuring this */ | ||
2619 | netdev->local->dialmax = 1; | ||
2620 | netdev->local->flags = ISDN_NET_CBHUP | ISDN_NET_DM_MANUAL; /* Hangup before Callback, manual dial */ | ||
2621 | netdev->local->cbdelay = 25; /* Wait 5 secs before Callback */ | ||
2622 | netdev->local->dialtimeout = -1; /* Infinite Dial-Timeout */ | ||
2623 | netdev->local->dialwait = 5 * HZ; /* Wait 5 sec. after failed dial */ | ||
2624 | netdev->local->dialstarted = 0; /* Jiffies of last dial-start */ | ||
2625 | netdev->local->dialwait_timer = 0; /* Jiffies of earliest next dial-start */ | ||
2626 | |||
2627 | /* Put into to netdev-chain */ | ||
2628 | netdev->next = (void *) dev->netdev; | ||
2629 | dev->netdev = netdev; | ||
2630 | return netdev->dev.name; | ||
2631 | } | ||
2632 | |||
2633 | char * | ||
2634 | isdn_net_newslave(char *parm) | ||
2635 | { | ||
2636 | char *p = strchr(parm, ','); | ||
2637 | isdn_net_dev *n; | ||
2638 | char newname[10]; | ||
2639 | |||
2640 | if (p) { | ||
2641 | /* Slave-Name MUST not be empty */ | ||
2642 | if (!strlen(p + 1)) | ||
2643 | return NULL; | ||
2644 | strcpy(newname, p + 1); | ||
2645 | *p = 0; | ||
2646 | /* Master must already exist */ | ||
2647 | if (!(n = isdn_net_findif(parm))) | ||
2648 | return NULL; | ||
2649 | /* Master must be a real interface, not a slave */ | ||
2650 | if (n->local->master) | ||
2651 | return NULL; | ||
2652 | /* Master must not be started yet */ | ||
2653 | if (isdn_net_device_started(n)) | ||
2654 | return NULL; | ||
2655 | return (isdn_net_new(newname, &(n->dev))); | ||
2656 | } | ||
2657 | return NULL; | ||
2658 | } | ||
2659 | |||
2660 | /* | ||
2661 | * Set interface-parameters. | ||
2662 | * Always set all parameters, so the user-level application is responsible | ||
2663 | * for not overwriting existing setups. It has to get the current | ||
2664 | * setup first, if only selected parameters are to be changed. | ||
2665 | */ | ||
2666 | int | ||
2667 | isdn_net_setcfg(isdn_net_ioctl_cfg * cfg) | ||
2668 | { | ||
2669 | isdn_net_dev *p = isdn_net_findif(cfg->name); | ||
2670 | ulong features; | ||
2671 | int i; | ||
2672 | int drvidx; | ||
2673 | int chidx; | ||
2674 | char drvid[25]; | ||
2675 | |||
2676 | if (p) { | ||
2677 | isdn_net_local *lp = p->local; | ||
2678 | |||
2679 | /* See if any registered driver supports the features we want */ | ||
2680 | features = ((1 << cfg->l2_proto) << ISDN_FEATURE_L2_SHIFT) | | ||
2681 | ((1 << cfg->l3_proto) << ISDN_FEATURE_L3_SHIFT); | ||
2682 | for (i = 0; i < ISDN_MAX_DRIVERS; i++) | ||
2683 | if (dev->drv[i]) | ||
2684 | if ((dev->drv[i]->interface->features & features) == features) | ||
2685 | break; | ||
2686 | if (i == ISDN_MAX_DRIVERS) { | ||
2687 | printk(KERN_WARNING "isdn_net: No driver with selected features\n"); | ||
2688 | return -ENODEV; | ||
2689 | } | ||
2690 | if (lp->p_encap != cfg->p_encap){ | ||
2691 | #ifdef CONFIG_ISDN_X25 | ||
2692 | struct concap_proto * cprot = p -> cprot; | ||
2693 | #endif | ||
2694 | if (isdn_net_device_started(p)) { | ||
2695 | printk(KERN_WARNING "%s: cannot change encap when if is up\n", | ||
2696 | lp->name); | ||
2697 | return -EBUSY; | ||
2698 | } | ||
2699 | #ifdef CONFIG_ISDN_X25 | ||
2700 | if( cprot && cprot -> pops ) | ||
2701 | cprot -> pops -> proto_del ( cprot ); | ||
2702 | p -> cprot = NULL; | ||
2703 | lp -> dops = NULL; | ||
2704 | /* ... , prepare for configuration of new one ... */ | ||
2705 | switch ( cfg -> p_encap ){ | ||
2706 | case ISDN_NET_ENCAP_X25IFACE: | ||
2707 | lp -> dops = &isdn_concap_reliable_dl_dops; | ||
2708 | } | ||
2709 | /* ... and allocate new one ... */ | ||
2710 | p -> cprot = isdn_concap_new( cfg -> p_encap ); | ||
2711 | /* p -> cprot == NULL now if p_encap is not supported | ||
2712 | by means of the concap_proto mechanism */ | ||
2713 | /* the protocol is not configured yet; this will | ||
2714 | happen later when isdn_net_reset() is called */ | ||
2715 | #endif | ||
2716 | } | ||
2717 | switch ( cfg->p_encap ) { | ||
2718 | case ISDN_NET_ENCAP_SYNCPPP: | ||
2719 | #ifndef CONFIG_ISDN_PPP | ||
2720 | printk(KERN_WARNING "%s: SyncPPP support not configured\n", | ||
2721 | lp->name); | ||
2722 | return -EINVAL; | ||
2723 | #else | ||
2724 | p->dev.type = ARPHRD_PPP; /* change ARP type */ | ||
2725 | p->dev.addr_len = 0; | ||
2726 | p->dev.do_ioctl = isdn_ppp_dev_ioctl; | ||
2727 | #endif | ||
2728 | break; | ||
2729 | case ISDN_NET_ENCAP_X25IFACE: | ||
2730 | #ifndef CONFIG_ISDN_X25 | ||
2731 | printk(KERN_WARNING "%s: isdn-x25 support not configured\n", | ||
2732 | p->local->name); | ||
2733 | return -EINVAL; | ||
2734 | #else | ||
2735 | p->dev.type = ARPHRD_X25; /* change ARP type */ | ||
2736 | p->dev.addr_len = 0; | ||
2737 | #endif | ||
2738 | break; | ||
2739 | case ISDN_NET_ENCAP_CISCOHDLCK: | ||
2740 | p->dev.do_ioctl = isdn_ciscohdlck_dev_ioctl; | ||
2741 | break; | ||
2742 | default: | ||
2743 | if( cfg->p_encap >= 0 && | ||
2744 | cfg->p_encap <= ISDN_NET_ENCAP_MAX_ENCAP ) | ||
2745 | break; | ||
2746 | printk(KERN_WARNING | ||
2747 | "%s: encapsulation protocol %d not supported\n", | ||
2748 | p->local->name, cfg->p_encap); | ||
2749 | return -EINVAL; | ||
2750 | } | ||
2751 | if (strlen(cfg->drvid)) { | ||
2752 | /* A bind has been requested ... */ | ||
2753 | char *c, | ||
2754 | *e; | ||
2755 | |||
2756 | drvidx = -1; | ||
2757 | chidx = -1; | ||
2758 | strcpy(drvid, cfg->drvid); | ||
2759 | if ((c = strchr(drvid, ','))) { | ||
2760 | /* The channel-number is appended to the driver-Id with a comma */ | ||
2761 | chidx = (int) simple_strtoul(c + 1, &e, 10); | ||
2762 | if (e == c) | ||
2763 | chidx = -1; | ||
2764 | *c = '\0'; | ||
2765 | } | ||
2766 | for (i = 0; i < ISDN_MAX_DRIVERS; i++) | ||
2767 | /* Lookup driver-Id in array */ | ||
2768 | if (!(strcmp(dev->drvid[i], drvid))) { | ||
2769 | drvidx = i; | ||
2770 | break; | ||
2771 | } | ||
2772 | if ((drvidx == -1) || (chidx == -1)) | ||
2773 | /* Either driver-Id or channel-number invalid */ | ||
2774 | return -ENODEV; | ||
2775 | } else { | ||
2776 | /* Parameters are valid, so get them */ | ||
2777 | drvidx = lp->pre_device; | ||
2778 | chidx = lp->pre_channel; | ||
2779 | } | ||
2780 | if (cfg->exclusive > 0) { | ||
2781 | unsigned long flags; | ||
2782 | |||
2783 | /* If binding is exclusive, try to grab the channel */ | ||
2784 | spin_lock_irqsave(&dev->lock, flags); | ||
2785 | if ((i = isdn_get_free_channel(ISDN_USAGE_NET, | ||
2786 | lp->l2_proto, lp->l3_proto, drvidx, | ||
2787 | chidx, lp->msn)) < 0) { | ||
2788 | /* Grab failed, because desired channel is in use */ | ||
2789 | lp->exclusive = -1; | ||
2790 | spin_unlock_irqrestore(&dev->lock, flags); | ||
2791 | return -EBUSY; | ||
2792 | } | ||
2793 | /* All went ok, so update isdninfo */ | ||
2794 | dev->usage[i] = ISDN_USAGE_EXCLUSIVE; | ||
2795 | isdn_info_update(); | ||
2796 | spin_unlock_irqrestore(&dev->lock, flags); | ||
2797 | lp->exclusive = i; | ||
2798 | } else { | ||
2799 | /* Non-exclusive binding or unbind. */ | ||
2800 | lp->exclusive = -1; | ||
2801 | if ((lp->pre_device != -1) && (cfg->exclusive == -1)) { | ||
2802 | isdn_unexclusive_channel(lp->pre_device, lp->pre_channel); | ||
2803 | isdn_free_channel(lp->pre_device, lp->pre_channel, ISDN_USAGE_NET); | ||
2804 | drvidx = -1; | ||
2805 | chidx = -1; | ||
2806 | } | ||
2807 | } | ||
2808 | strcpy(lp->msn, cfg->eaz); | ||
2809 | lp->pre_device = drvidx; | ||
2810 | lp->pre_channel = chidx; | ||
2811 | lp->onhtime = cfg->onhtime; | ||
2812 | lp->charge = cfg->charge; | ||
2813 | lp->l2_proto = cfg->l2_proto; | ||
2814 | lp->l3_proto = cfg->l3_proto; | ||
2815 | lp->cbdelay = cfg->cbdelay; | ||
2816 | lp->dialmax = cfg->dialmax; | ||
2817 | lp->triggercps = cfg->triggercps; | ||
2818 | lp->slavedelay = cfg->slavedelay * HZ; | ||
2819 | lp->pppbind = cfg->pppbind; | ||
2820 | lp->dialtimeout = cfg->dialtimeout >= 0 ? cfg->dialtimeout * HZ : -1; | ||
2821 | lp->dialwait = cfg->dialwait * HZ; | ||
2822 | if (cfg->secure) | ||
2823 | lp->flags |= ISDN_NET_SECURE; | ||
2824 | else | ||
2825 | lp->flags &= ~ISDN_NET_SECURE; | ||
2826 | if (cfg->cbhup) | ||
2827 | lp->flags |= ISDN_NET_CBHUP; | ||
2828 | else | ||
2829 | lp->flags &= ~ISDN_NET_CBHUP; | ||
2830 | switch (cfg->callback) { | ||
2831 | case 0: | ||
2832 | lp->flags &= ~(ISDN_NET_CALLBACK | ISDN_NET_CBOUT); | ||
2833 | break; | ||
2834 | case 1: | ||
2835 | lp->flags |= ISDN_NET_CALLBACK; | ||
2836 | lp->flags &= ~ISDN_NET_CBOUT; | ||
2837 | break; | ||
2838 | case 2: | ||
2839 | lp->flags |= ISDN_NET_CBOUT; | ||
2840 | lp->flags &= ~ISDN_NET_CALLBACK; | ||
2841 | break; | ||
2842 | } | ||
2843 | lp->flags &= ~ISDN_NET_DIALMODE_MASK; /* first all bits off */ | ||
2844 | if (cfg->dialmode && !(cfg->dialmode & ISDN_NET_DIALMODE_MASK)) { | ||
2845 | /* old isdnctrl version, where only 0 or 1 is given */ | ||
2846 | printk(KERN_WARNING | ||
2847 | "Old isdnctrl version detected! Please update.\n"); | ||
2848 | lp->flags |= ISDN_NET_DM_OFF; /* turn on `off' bit */ | ||
2849 | } | ||
2850 | else { | ||
2851 | lp->flags |= cfg->dialmode; /* turn on selected bits */ | ||
2852 | } | ||
2853 | if (cfg->chargehup) | ||
2854 | lp->hupflags |= ISDN_CHARGEHUP; | ||
2855 | else | ||
2856 | lp->hupflags &= ~ISDN_CHARGEHUP; | ||
2857 | if (cfg->ihup) | ||
2858 | lp->hupflags |= ISDN_INHUP; | ||
2859 | else | ||
2860 | lp->hupflags &= ~ISDN_INHUP; | ||
2861 | if (cfg->chargeint > 10) { | ||
2862 | lp->hupflags |= ISDN_CHARGEHUP | ISDN_HAVECHARGE | ISDN_MANCHARGE; | ||
2863 | lp->chargeint = cfg->chargeint * HZ; | ||
2864 | } | ||
2865 | if (cfg->p_encap != lp->p_encap) { | ||
2866 | if (cfg->p_encap == ISDN_NET_ENCAP_RAWIP) { | ||
2867 | p->dev.hard_header = NULL; | ||
2868 | p->dev.hard_header_cache = NULL; | ||
2869 | p->dev.header_cache_update = NULL; | ||
2870 | p->dev.flags = IFF_NOARP|IFF_POINTOPOINT; | ||
2871 | } else { | ||
2872 | p->dev.hard_header = isdn_net_header; | ||
2873 | if (cfg->p_encap == ISDN_NET_ENCAP_ETHER) { | ||
2874 | p->dev.hard_header_cache = lp->org_hhc; | ||
2875 | p->dev.header_cache_update = lp->org_hcu; | ||
2876 | p->dev.flags = IFF_BROADCAST | IFF_MULTICAST; | ||
2877 | } else { | ||
2878 | p->dev.hard_header_cache = NULL; | ||
2879 | p->dev.header_cache_update = NULL; | ||
2880 | p->dev.flags = IFF_NOARP|IFF_POINTOPOINT; | ||
2881 | } | ||
2882 | } | ||
2883 | } | ||
2884 | lp->p_encap = cfg->p_encap; | ||
2885 | return 0; | ||
2886 | } | ||
2887 | return -ENODEV; | ||
2888 | } | ||
2889 | |||
2890 | /* | ||
2891 | * Perform get-interface-parameters.ioctl | ||
2892 | */ | ||
2893 | int | ||
2894 | isdn_net_getcfg(isdn_net_ioctl_cfg * cfg) | ||
2895 | { | ||
2896 | isdn_net_dev *p = isdn_net_findif(cfg->name); | ||
2897 | |||
2898 | if (p) { | ||
2899 | isdn_net_local *lp = p->local; | ||
2900 | |||
2901 | strcpy(cfg->eaz, lp->msn); | ||
2902 | cfg->exclusive = lp->exclusive; | ||
2903 | if (lp->pre_device >= 0) { | ||
2904 | sprintf(cfg->drvid, "%s,%d", dev->drvid[lp->pre_device], | ||
2905 | lp->pre_channel); | ||
2906 | } else | ||
2907 | cfg->drvid[0] = '\0'; | ||
2908 | cfg->onhtime = lp->onhtime; | ||
2909 | cfg->charge = lp->charge; | ||
2910 | cfg->l2_proto = lp->l2_proto; | ||
2911 | cfg->l3_proto = lp->l3_proto; | ||
2912 | cfg->p_encap = lp->p_encap; | ||
2913 | cfg->secure = (lp->flags & ISDN_NET_SECURE) ? 1 : 0; | ||
2914 | cfg->callback = 0; | ||
2915 | if (lp->flags & ISDN_NET_CALLBACK) | ||
2916 | cfg->callback = 1; | ||
2917 | if (lp->flags & ISDN_NET_CBOUT) | ||
2918 | cfg->callback = 2; | ||
2919 | cfg->cbhup = (lp->flags & ISDN_NET_CBHUP) ? 1 : 0; | ||
2920 | cfg->dialmode = lp->flags & ISDN_NET_DIALMODE_MASK; | ||
2921 | cfg->chargehup = (lp->hupflags & 4) ? 1 : 0; | ||
2922 | cfg->ihup = (lp->hupflags & 8) ? 1 : 0; | ||
2923 | cfg->cbdelay = lp->cbdelay; | ||
2924 | cfg->dialmax = lp->dialmax; | ||
2925 | cfg->triggercps = lp->triggercps; | ||
2926 | cfg->slavedelay = lp->slavedelay / HZ; | ||
2927 | cfg->chargeint = (lp->hupflags & ISDN_CHARGEHUP) ? | ||
2928 | (lp->chargeint / HZ) : 0; | ||
2929 | cfg->pppbind = lp->pppbind; | ||
2930 | cfg->dialtimeout = lp->dialtimeout >= 0 ? lp->dialtimeout / HZ : -1; | ||
2931 | cfg->dialwait = lp->dialwait / HZ; | ||
2932 | if (lp->slave) | ||
2933 | strcpy(cfg->slave, ((isdn_net_local *) lp->slave->priv)->name); | ||
2934 | else | ||
2935 | cfg->slave[0] = '\0'; | ||
2936 | if (lp->master) | ||
2937 | strcpy(cfg->master, ((isdn_net_local *) lp->master->priv)->name); | ||
2938 | else | ||
2939 | cfg->master[0] = '\0'; | ||
2940 | return 0; | ||
2941 | } | ||
2942 | return -ENODEV; | ||
2943 | } | ||
2944 | |||
2945 | /* | ||
2946 | * Add a phone-number to an interface. | ||
2947 | */ | ||
2948 | int | ||
2949 | isdn_net_addphone(isdn_net_ioctl_phone * phone) | ||
2950 | { | ||
2951 | isdn_net_dev *p = isdn_net_findif(phone->name); | ||
2952 | isdn_net_phone *n; | ||
2953 | |||
2954 | if (p) { | ||
2955 | if (!(n = (isdn_net_phone *) kmalloc(sizeof(isdn_net_phone), GFP_KERNEL))) | ||
2956 | return -ENOMEM; | ||
2957 | strcpy(n->num, phone->phone); | ||
2958 | n->next = p->local->phone[phone->outgoing & 1]; | ||
2959 | p->local->phone[phone->outgoing & 1] = n; | ||
2960 | return 0; | ||
2961 | } | ||
2962 | return -ENODEV; | ||
2963 | } | ||
2964 | |||
2965 | /* | ||
2966 | * Copy a string of all phone-numbers of an interface to user space. | ||
2967 | * This might sleep and must be called with the isdn semaphore down. | ||
2968 | */ | ||
2969 | int | ||
2970 | isdn_net_getphones(isdn_net_ioctl_phone * phone, char __user *phones) | ||
2971 | { | ||
2972 | isdn_net_dev *p = isdn_net_findif(phone->name); | ||
2973 | int inout = phone->outgoing & 1; | ||
2974 | int more = 0; | ||
2975 | int count = 0; | ||
2976 | isdn_net_phone *n; | ||
2977 | |||
2978 | if (!p) | ||
2979 | return -ENODEV; | ||
2980 | inout &= 1; | ||
2981 | for (n = p->local->phone[inout]; n; n = n->next) { | ||
2982 | if (more) { | ||
2983 | put_user(' ', phones++); | ||
2984 | count++; | ||
2985 | } | ||
2986 | if (copy_to_user(phones, n->num, strlen(n->num) + 1)) { | ||
2987 | return -EFAULT; | ||
2988 | } | ||
2989 | phones += strlen(n->num); | ||
2990 | count += strlen(n->num); | ||
2991 | more = 1; | ||
2992 | } | ||
2993 | put_user(0, phones); | ||
2994 | count++; | ||
2995 | return count; | ||
2996 | } | ||
2997 | |||
2998 | /* | ||
2999 | * Copy a string containing the peer's phone number of a connected interface | ||
3000 | * to user space. | ||
3001 | */ | ||
3002 | int | ||
3003 | isdn_net_getpeer(isdn_net_ioctl_phone *phone, isdn_net_ioctl_phone __user *peer) | ||
3004 | { | ||
3005 | isdn_net_dev *p = isdn_net_findif(phone->name); | ||
3006 | int ch, dv, idx; | ||
3007 | |||
3008 | if (!p) return -ENODEV; | ||
3009 | /* | ||
3010 | * Theoretical race: while this executes, the remote number might | ||
3011 | * become invalid (hang up) or change (new connection), resulting | ||
3012 | * in (partially) wrong number copied to user. This race | ||
3013 | * currently ignored. | ||
3014 | */ | ||
3015 | ch = p->local->isdn_channel; | ||
3016 | dv = p->local->isdn_device; | ||
3017 | if(ch<0 && dv<0) return -ENOTCONN; | ||
3018 | idx = isdn_dc2minor(dv, ch); | ||
3019 | if (idx<0) return -ENODEV; | ||
3020 | /* for pre-bound channels, we need this extra check */ | ||
3021 | if ( strncmp(dev->num[idx],"???",3) == 0 ) return -ENOTCONN; | ||
3022 | strncpy(phone->phone,dev->num[idx],ISDN_MSNLEN); | ||
3023 | phone->outgoing=USG_OUTGOING(dev->usage[idx]); | ||
3024 | if ( copy_to_user(peer,phone,sizeof(*peer)) ) return -EFAULT; | ||
3025 | return 0; | ||
3026 | } | ||
3027 | /* | ||
3028 | * Delete a phone-number from an interface. | ||
3029 | */ | ||
3030 | int | ||
3031 | isdn_net_delphone(isdn_net_ioctl_phone * phone) | ||
3032 | { | ||
3033 | isdn_net_dev *p = isdn_net_findif(phone->name); | ||
3034 | int inout = phone->outgoing & 1; | ||
3035 | isdn_net_phone *n; | ||
3036 | isdn_net_phone *m; | ||
3037 | |||
3038 | if (p) { | ||
3039 | n = p->local->phone[inout]; | ||
3040 | m = NULL; | ||
3041 | while (n) { | ||
3042 | if (!strcmp(n->num, phone->phone)) { | ||
3043 | if (p->local->dial == n) | ||
3044 | p->local->dial = n->next; | ||
3045 | if (m) | ||
3046 | m->next = n->next; | ||
3047 | else | ||
3048 | p->local->phone[inout] = n->next; | ||
3049 | kfree(n); | ||
3050 | return 0; | ||
3051 | } | ||
3052 | m = n; | ||
3053 | n = (isdn_net_phone *) n->next; | ||
3054 | } | ||
3055 | return -EINVAL; | ||
3056 | } | ||
3057 | return -ENODEV; | ||
3058 | } | ||
3059 | |||
3060 | /* | ||
3061 | * Delete all phone-numbers of an interface. | ||
3062 | */ | ||
3063 | static int | ||
3064 | isdn_net_rmallphone(isdn_net_dev * p) | ||
3065 | { | ||
3066 | isdn_net_phone *n; | ||
3067 | isdn_net_phone *m; | ||
3068 | int i; | ||
3069 | |||
3070 | for (i = 0; i < 2; i++) { | ||
3071 | n = p->local->phone[i]; | ||
3072 | while (n) { | ||
3073 | m = n->next; | ||
3074 | kfree(n); | ||
3075 | n = m; | ||
3076 | } | ||
3077 | p->local->phone[i] = NULL; | ||
3078 | } | ||
3079 | p->local->dial = NULL; | ||
3080 | return 0; | ||
3081 | } | ||
3082 | |||
3083 | /* | ||
3084 | * Force a hangup of a network-interface. | ||
3085 | */ | ||
3086 | int | ||
3087 | isdn_net_force_hangup(char *name) | ||
3088 | { | ||
3089 | isdn_net_dev *p = isdn_net_findif(name); | ||
3090 | struct net_device *q; | ||
3091 | |||
3092 | if (p) { | ||
3093 | if (p->local->isdn_device < 0) | ||
3094 | return 1; | ||
3095 | q = p->local->slave; | ||
3096 | /* If this interface has slaves, do a hangup for them also. */ | ||
3097 | while (q) { | ||
3098 | isdn_net_hangup(q); | ||
3099 | q = (((isdn_net_local *) q->priv)->slave); | ||
3100 | } | ||
3101 | isdn_net_hangup(&p->dev); | ||
3102 | return 0; | ||
3103 | } | ||
3104 | return -ENODEV; | ||
3105 | } | ||
3106 | |||
3107 | /* | ||
3108 | * Helper-function for isdn_net_rm: Do the real work. | ||
3109 | */ | ||
3110 | static int | ||
3111 | isdn_net_realrm(isdn_net_dev * p, isdn_net_dev * q) | ||
3112 | { | ||
3113 | u_long flags; | ||
3114 | |||
3115 | if (isdn_net_device_started(p)) { | ||
3116 | return -EBUSY; | ||
3117 | } | ||
3118 | #ifdef CONFIG_ISDN_X25 | ||
3119 | if( p -> cprot && p -> cprot -> pops ) | ||
3120 | p -> cprot -> pops -> proto_del ( p -> cprot ); | ||
3121 | #endif | ||
3122 | /* Free all phone-entries */ | ||
3123 | isdn_net_rmallphone(p); | ||
3124 | /* If interface is bound exclusive, free channel-usage */ | ||
3125 | if (p->local->exclusive != -1) | ||
3126 | isdn_unexclusive_channel(p->local->pre_device, p->local->pre_channel); | ||
3127 | if (p->local->master) { | ||
3128 | /* It's a slave-device, so update master's slave-pointer if necessary */ | ||
3129 | if (((isdn_net_local *) (p->local->master->priv))->slave == &p->dev) | ||
3130 | ((isdn_net_local *) (p->local->master->priv))->slave = p->local->slave; | ||
3131 | } else { | ||
3132 | /* Unregister only if it's a master-device */ | ||
3133 | p->dev.hard_header_cache = p->local->org_hhc; | ||
3134 | p->dev.header_cache_update = p->local->org_hcu; | ||
3135 | unregister_netdev(&p->dev); | ||
3136 | } | ||
3137 | /* Unlink device from chain */ | ||
3138 | spin_lock_irqsave(&dev->lock, flags); | ||
3139 | if (q) | ||
3140 | q->next = p->next; | ||
3141 | else | ||
3142 | dev->netdev = p->next; | ||
3143 | if (p->local->slave) { | ||
3144 | /* If this interface has a slave, remove it also */ | ||
3145 | char *slavename = ((isdn_net_local *) (p->local->slave->priv))->name; | ||
3146 | isdn_net_dev *n = dev->netdev; | ||
3147 | q = NULL; | ||
3148 | while (n) { | ||
3149 | if (!strcmp(n->local->name, slavename)) { | ||
3150 | spin_unlock_irqrestore(&dev->lock, flags); | ||
3151 | isdn_net_realrm(n, q); | ||
3152 | spin_lock_irqsave(&dev->lock, flags); | ||
3153 | break; | ||
3154 | } | ||
3155 | q = n; | ||
3156 | n = (isdn_net_dev *) n->next; | ||
3157 | } | ||
3158 | } | ||
3159 | spin_unlock_irqrestore(&dev->lock, flags); | ||
3160 | /* If no more net-devices remain, disable auto-hangup timer */ | ||
3161 | if (dev->netdev == NULL) | ||
3162 | isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 0); | ||
3163 | kfree(p->local); | ||
3164 | kfree(p); | ||
3165 | |||
3166 | return 0; | ||
3167 | } | ||
3168 | |||
3169 | /* | ||
3170 | * Remove a single network-interface. | ||
3171 | */ | ||
3172 | int | ||
3173 | isdn_net_rm(char *name) | ||
3174 | { | ||
3175 | u_long flags; | ||
3176 | isdn_net_dev *p; | ||
3177 | isdn_net_dev *q; | ||
3178 | |||
3179 | /* Search name in netdev-chain */ | ||
3180 | spin_lock_irqsave(&dev->lock, flags); | ||
3181 | p = dev->netdev; | ||
3182 | q = NULL; | ||
3183 | while (p) { | ||
3184 | if (!strcmp(p->local->name, name)) { | ||
3185 | spin_unlock_irqrestore(&dev->lock, flags); | ||
3186 | return (isdn_net_realrm(p, q)); | ||
3187 | } | ||
3188 | q = p; | ||
3189 | p = (isdn_net_dev *) p->next; | ||
3190 | } | ||
3191 | spin_unlock_irqrestore(&dev->lock, flags); | ||
3192 | /* If no more net-devices remain, disable auto-hangup timer */ | ||
3193 | if (dev->netdev == NULL) | ||
3194 | isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, 0); | ||
3195 | return -ENODEV; | ||
3196 | } | ||
3197 | |||
3198 | /* | ||
3199 | * Remove all network-interfaces | ||
3200 | */ | ||
3201 | int | ||
3202 | isdn_net_rmall(void) | ||
3203 | { | ||
3204 | u_long flags; | ||
3205 | int ret; | ||
3206 | |||
3207 | /* Walk through netdev-chain */ | ||
3208 | spin_lock_irqsave(&dev->lock, flags); | ||
3209 | while (dev->netdev) { | ||
3210 | if (!dev->netdev->local->master) { | ||
3211 | /* Remove master-devices only, slaves get removed with their master */ | ||
3212 | spin_unlock_irqrestore(&dev->lock, flags); | ||
3213 | if ((ret = isdn_net_realrm(dev->netdev, NULL))) { | ||
3214 | return ret; | ||
3215 | } | ||
3216 | spin_lock_irqsave(&dev->lock, flags); | ||
3217 | } | ||
3218 | } | ||
3219 | dev->netdev = NULL; | ||
3220 | spin_unlock_irqrestore(&dev->lock, flags); | ||
3221 | return 0; | ||
3222 | } | ||
diff --git a/drivers/isdn/i4l/isdn_net.h b/drivers/isdn/i4l/isdn_net.h new file mode 100644 index 000000000000..bc2f0dd962ea --- /dev/null +++ b/drivers/isdn/i4l/isdn_net.h | |||
@@ -0,0 +1,190 @@ | |||
1 | /* $Id: isdn_net.h,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $ | ||
2 | * | ||
3 | * header for Linux ISDN subsystem, network related functions (linklevel). | ||
4 | * | ||
5 | * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de) | ||
6 | * Copyright 1995,96 by Thinking Objects Software GmbH Wuerzburg | ||
7 | * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de) | ||
8 | * | ||
9 | * This software may be used and distributed according to the terms | ||
10 | * of the GNU General Public License, incorporated herein by reference. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | /* Definitions for hupflags: */ | ||
15 | #define ISDN_WAITCHARGE 1 /* did not get a charge info yet */ | ||
16 | #define ISDN_HAVECHARGE 2 /* We know a charge info */ | ||
17 | #define ISDN_CHARGEHUP 4 /* We want to use the charge mechanism */ | ||
18 | #define ISDN_INHUP 8 /* Even if incoming, close after huptimeout */ | ||
19 | #define ISDN_MANCHARGE 16 /* Charge Interval manually set */ | ||
20 | |||
21 | /* | ||
22 | * Definitions for Cisco-HDLC header. | ||
23 | */ | ||
24 | |||
25 | #define CISCO_ADDR_UNICAST 0x0f | ||
26 | #define CISCO_ADDR_BROADCAST 0x8f | ||
27 | #define CISCO_CTRL 0x00 | ||
28 | #define CISCO_TYPE_CDP 0x2000 | ||
29 | #define CISCO_TYPE_SLARP 0x8035 | ||
30 | #define CISCO_SLARP_REQUEST 0 | ||
31 | #define CISCO_SLARP_REPLY 1 | ||
32 | #define CISCO_SLARP_KEEPALIVE 2 | ||
33 | |||
34 | extern char *isdn_net_new(char *, struct net_device *); | ||
35 | extern char *isdn_net_newslave(char *); | ||
36 | extern int isdn_net_rm(char *); | ||
37 | extern int isdn_net_rmall(void); | ||
38 | extern int isdn_net_stat_callback(int, isdn_ctrl *); | ||
39 | extern int isdn_net_setcfg(isdn_net_ioctl_cfg *); | ||
40 | extern int isdn_net_getcfg(isdn_net_ioctl_cfg *); | ||
41 | extern int isdn_net_addphone(isdn_net_ioctl_phone *); | ||
42 | extern int isdn_net_getphones(isdn_net_ioctl_phone *, char __user *); | ||
43 | extern int isdn_net_getpeer(isdn_net_ioctl_phone *, isdn_net_ioctl_phone __user *); | ||
44 | extern int isdn_net_delphone(isdn_net_ioctl_phone *); | ||
45 | extern int isdn_net_find_icall(int, int, int, setup_parm *); | ||
46 | extern void isdn_net_hangup(struct net_device *); | ||
47 | extern void isdn_net_dial(void); | ||
48 | extern void isdn_net_autohup(void); | ||
49 | extern int isdn_net_force_hangup(char *); | ||
50 | extern int isdn_net_force_dial(char *); | ||
51 | extern isdn_net_dev *isdn_net_findif(char *); | ||
52 | extern int isdn_net_rcv_skb(int, struct sk_buff *); | ||
53 | extern int isdn_net_dial_req(isdn_net_local *); | ||
54 | extern void isdn_net_writebuf_skb(isdn_net_local *lp, struct sk_buff *skb); | ||
55 | extern void isdn_net_write_super(isdn_net_local *lp, struct sk_buff *skb); | ||
56 | |||
57 | #define ISDN_NET_MAX_QUEUE_LENGTH 2 | ||
58 | |||
59 | /* | ||
60 | * is this particular channel busy? | ||
61 | */ | ||
62 | static __inline__ int isdn_net_lp_busy(isdn_net_local *lp) | ||
63 | { | ||
64 | if (atomic_read(&lp->frame_cnt) < ISDN_NET_MAX_QUEUE_LENGTH) | ||
65 | return 0; | ||
66 | else | ||
67 | return 1; | ||
68 | } | ||
69 | |||
70 | /* | ||
71 | * For the given net device, this will get a non-busy channel out of the | ||
72 | * corresponding bundle. The returned channel is locked. | ||
73 | */ | ||
74 | static __inline__ isdn_net_local * isdn_net_get_locked_lp(isdn_net_dev *nd) | ||
75 | { | ||
76 | unsigned long flags; | ||
77 | isdn_net_local *lp; | ||
78 | |||
79 | spin_lock_irqsave(&nd->queue_lock, flags); | ||
80 | lp = nd->queue; /* get lp on top of queue */ | ||
81 | spin_lock(&nd->queue->xmit_lock); | ||
82 | while (isdn_net_lp_busy(nd->queue)) { | ||
83 | spin_unlock(&nd->queue->xmit_lock); | ||
84 | nd->queue = nd->queue->next; | ||
85 | if (nd->queue == lp) { /* not found -- should never happen */ | ||
86 | lp = NULL; | ||
87 | goto errout; | ||
88 | } | ||
89 | spin_lock(&nd->queue->xmit_lock); | ||
90 | } | ||
91 | lp = nd->queue; | ||
92 | nd->queue = nd->queue->next; | ||
93 | local_bh_disable(); | ||
94 | errout: | ||
95 | spin_unlock_irqrestore(&nd->queue_lock, flags); | ||
96 | return lp; | ||
97 | } | ||
98 | |||
99 | /* | ||
100 | * add a channel to a bundle | ||
101 | */ | ||
102 | static __inline__ void isdn_net_add_to_bundle(isdn_net_dev *nd, isdn_net_local *nlp) | ||
103 | { | ||
104 | isdn_net_local *lp; | ||
105 | unsigned long flags; | ||
106 | |||
107 | spin_lock_irqsave(&nd->queue_lock, flags); | ||
108 | |||
109 | lp = nd->queue; | ||
110 | // printk(KERN_DEBUG "%s: lp:%s(%p) nlp:%s(%p) last(%p)\n", | ||
111 | // __FUNCTION__, lp->name, lp, nlp->name, nlp, lp->last); | ||
112 | nlp->last = lp->last; | ||
113 | lp->last->next = nlp; | ||
114 | lp->last = nlp; | ||
115 | nlp->next = lp; | ||
116 | nd->queue = nlp; | ||
117 | |||
118 | spin_unlock_irqrestore(&nd->queue_lock, flags); | ||
119 | } | ||
120 | /* | ||
121 | * remove a channel from the bundle it belongs to | ||
122 | */ | ||
123 | static __inline__ void isdn_net_rm_from_bundle(isdn_net_local *lp) | ||
124 | { | ||
125 | isdn_net_local *master_lp = lp; | ||
126 | unsigned long flags; | ||
127 | |||
128 | if (lp->master) | ||
129 | master_lp = (isdn_net_local *) lp->master->priv; | ||
130 | |||
131 | // printk(KERN_DEBUG "%s: lp:%s(%p) mlp:%s(%p) last(%p) next(%p) mndq(%p)\n", | ||
132 | // __FUNCTION__, lp->name, lp, master_lp->name, master_lp, lp->last, lp->next, master_lp->netdev->queue); | ||
133 | spin_lock_irqsave(&master_lp->netdev->queue_lock, flags); | ||
134 | lp->last->next = lp->next; | ||
135 | lp->next->last = lp->last; | ||
136 | if (master_lp->netdev->queue == lp) { | ||
137 | master_lp->netdev->queue = lp->next; | ||
138 | if (lp->next == lp) { /* last in queue */ | ||
139 | master_lp->netdev->queue = master_lp->netdev->local; | ||
140 | } | ||
141 | } | ||
142 | lp->next = lp->last = lp; /* (re)set own pointers */ | ||
143 | // printk(KERN_DEBUG "%s: mndq(%p)\n", | ||
144 | // __FUNCTION__, master_lp->netdev->queue); | ||
145 | spin_unlock_irqrestore(&master_lp->netdev->queue_lock, flags); | ||
146 | } | ||
147 | |||
148 | static inline int | ||
149 | put_u8(unsigned char *p, u8 x) | ||
150 | { | ||
151 | *p = x; | ||
152 | return 1; | ||
153 | } | ||
154 | |||
155 | static inline int | ||
156 | put_u16(unsigned char *p, u16 x) | ||
157 | { | ||
158 | *((u16 *)p) = htons(x); | ||
159 | return 2; | ||
160 | } | ||
161 | |||
162 | static inline int | ||
163 | put_u32(unsigned char *p, u32 x) | ||
164 | { | ||
165 | *((u32 *)p) = htonl(x); | ||
166 | return 4; | ||
167 | } | ||
168 | |||
169 | static inline int | ||
170 | get_u8(unsigned char *p, u8 *x) | ||
171 | { | ||
172 | *x = *p; | ||
173 | return 1; | ||
174 | } | ||
175 | |||
176 | static inline int | ||
177 | get_u16(unsigned char *p, u16 *x) | ||
178 | { | ||
179 | *x = ntohs(*((u16 *)p)); | ||
180 | return 2; | ||
181 | } | ||
182 | |||
183 | static inline int | ||
184 | get_u32(unsigned char *p, u32 *x) | ||
185 | { | ||
186 | *x = ntohl(*((u32 *)p)); | ||
187 | return 4; | ||
188 | } | ||
189 | |||
190 | |||
diff --git a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c new file mode 100644 index 000000000000..3c092117a8ea --- /dev/null +++ b/drivers/isdn/i4l/isdn_ppp.c | |||
@@ -0,0 +1,3020 @@ | |||
1 | /* $Id: isdn_ppp.c,v 1.1.2.3 2004/02/10 01:07:13 keil Exp $ | ||
2 | * | ||
3 | * Linux ISDN subsystem, functions for synchronous PPP (linklevel). | ||
4 | * | ||
5 | * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de) | ||
6 | * | ||
7 | * This software may be used and distributed according to the terms | ||
8 | * of the GNU General Public License, incorporated herein by reference. | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #include <linux/config.h> | ||
13 | #include <linux/isdn.h> | ||
14 | #include <linux/poll.h> | ||
15 | #include <linux/ppp-comp.h> | ||
16 | #ifdef CONFIG_IPPP_FILTER | ||
17 | #include <linux/filter.h> | ||
18 | #endif | ||
19 | |||
20 | #include "isdn_common.h" | ||
21 | #include "isdn_ppp.h" | ||
22 | #include "isdn_net.h" | ||
23 | |||
24 | #ifndef PPP_IPX | ||
25 | #define PPP_IPX 0x002b | ||
26 | #endif | ||
27 | |||
28 | /* Prototypes */ | ||
29 | static int isdn_ppp_fill_rq(unsigned char *buf, int len, int proto, int slot); | ||
30 | static int isdn_ppp_closewait(int slot); | ||
31 | static void isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, | ||
32 | struct sk_buff *skb, int proto); | ||
33 | static int isdn_ppp_if_get_unit(char *namebuf); | ||
34 | static int isdn_ppp_set_compressor(struct ippp_struct *is,struct isdn_ppp_comp_data *); | ||
35 | static struct sk_buff *isdn_ppp_decompress(struct sk_buff *, | ||
36 | struct ippp_struct *,struct ippp_struct *,int *proto); | ||
37 | static void isdn_ppp_receive_ccp(isdn_net_dev * net_dev, isdn_net_local * lp, | ||
38 | struct sk_buff *skb,int proto); | ||
39 | static struct sk_buff *isdn_ppp_compress(struct sk_buff *skb_in,int *proto, | ||
40 | struct ippp_struct *is,struct ippp_struct *master,int type); | ||
41 | static void isdn_ppp_send_ccp(isdn_net_dev *net_dev, isdn_net_local *lp, | ||
42 | struct sk_buff *skb); | ||
43 | |||
44 | /* New CCP stuff */ | ||
45 | static void isdn_ppp_ccp_kickup(struct ippp_struct *is); | ||
46 | static void isdn_ppp_ccp_xmit_reset(struct ippp_struct *is, int proto, | ||
47 | unsigned char code, unsigned char id, | ||
48 | unsigned char *data, int len); | ||
49 | static struct ippp_ccp_reset *isdn_ppp_ccp_reset_alloc(struct ippp_struct *is); | ||
50 | static void isdn_ppp_ccp_reset_free(struct ippp_struct *is); | ||
51 | static void isdn_ppp_ccp_reset_free_state(struct ippp_struct *is, | ||
52 | unsigned char id); | ||
53 | static void isdn_ppp_ccp_timer_callback(unsigned long closure); | ||
54 | static struct ippp_ccp_reset_state *isdn_ppp_ccp_reset_alloc_state(struct ippp_struct *is, | ||
55 | unsigned char id); | ||
56 | static void isdn_ppp_ccp_reset_trans(struct ippp_struct *is, | ||
57 | struct isdn_ppp_resetparams *rp); | ||
58 | static void isdn_ppp_ccp_reset_ack_rcvd(struct ippp_struct *is, | ||
59 | unsigned char id); | ||
60 | |||
61 | |||
62 | |||
63 | #ifdef CONFIG_ISDN_MPP | ||
64 | static ippp_bundle * isdn_ppp_bundle_arr = NULL; | ||
65 | |||
66 | static int isdn_ppp_mp_bundle_array_init(void); | ||
67 | static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to ); | ||
68 | static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, | ||
69 | struct sk_buff *skb); | ||
70 | static void isdn_ppp_mp_cleanup( isdn_net_local * lp ); | ||
71 | |||
72 | static int isdn_ppp_bundle(struct ippp_struct *, int unit); | ||
73 | #endif /* CONFIG_ISDN_MPP */ | ||
74 | |||
75 | char *isdn_ppp_revision = "$Revision: 1.1.2.3 $"; | ||
76 | |||
77 | static struct ippp_struct *ippp_table[ISDN_MAX_CHANNELS]; | ||
78 | |||
79 | static struct isdn_ppp_compressor *ipc_head = NULL; | ||
80 | |||
81 | /* | ||
82 | * frame log (debug) | ||
83 | */ | ||
84 | static void | ||
85 | isdn_ppp_frame_log(char *info, char *data, int len, int maxlen,int unit,int slot) | ||
86 | { | ||
87 | int cnt, | ||
88 | j, | ||
89 | i; | ||
90 | char buf[80]; | ||
91 | |||
92 | if (len < maxlen) | ||
93 | maxlen = len; | ||
94 | |||
95 | for (i = 0, cnt = 0; cnt < maxlen; i++) { | ||
96 | for (j = 0; j < 16 && cnt < maxlen; j++, cnt++) | ||
97 | sprintf(buf + j * 3, "%02x ", (unsigned char) data[cnt]); | ||
98 | printk(KERN_DEBUG "[%d/%d].%s[%d]: %s\n",unit,slot, info, i, buf); | ||
99 | } | ||
100 | } | ||
101 | |||
102 | /* | ||
103 | * unbind isdn_net_local <=> ippp-device | ||
104 | * note: it can happen, that we hangup/free the master before the slaves | ||
105 | * in this case we bind another lp to the master device | ||
106 | */ | ||
107 | int | ||
108 | isdn_ppp_free(isdn_net_local * lp) | ||
109 | { | ||
110 | struct ippp_struct *is; | ||
111 | |||
112 | if (lp->ppp_slot < 0 || lp->ppp_slot > ISDN_MAX_CHANNELS) { | ||
113 | printk(KERN_ERR "%s: ppp_slot(%d) out of range\n", | ||
114 | __FUNCTION__, lp->ppp_slot); | ||
115 | return 0; | ||
116 | } | ||
117 | |||
118 | #ifdef CONFIG_ISDN_MPP | ||
119 | spin_lock(&lp->netdev->pb->lock); | ||
120 | #endif | ||
121 | isdn_net_rm_from_bundle(lp); | ||
122 | #ifdef CONFIG_ISDN_MPP | ||
123 | if (lp->netdev->pb->ref_ct == 1) /* last link in queue? */ | ||
124 | isdn_ppp_mp_cleanup(lp); | ||
125 | |||
126 | lp->netdev->pb->ref_ct--; | ||
127 | spin_unlock(&lp->netdev->pb->lock); | ||
128 | #endif /* CONFIG_ISDN_MPP */ | ||
129 | if (lp->ppp_slot < 0 || lp->ppp_slot > ISDN_MAX_CHANNELS) { | ||
130 | printk(KERN_ERR "%s: ppp_slot(%d) now invalid\n", | ||
131 | __FUNCTION__, lp->ppp_slot); | ||
132 | return 0; | ||
133 | } | ||
134 | is = ippp_table[lp->ppp_slot]; | ||
135 | if ((is->state & IPPP_CONNECT)) | ||
136 | isdn_ppp_closewait(lp->ppp_slot); /* force wakeup on ippp device */ | ||
137 | else if (is->state & IPPP_ASSIGNED) | ||
138 | is->state = IPPP_OPEN; /* fallback to 'OPEN but not ASSIGNED' state */ | ||
139 | |||
140 | if (is->debug & 0x1) | ||
141 | printk(KERN_DEBUG "isdn_ppp_free %d %lx %lx\n", lp->ppp_slot, (long) lp, (long) is->lp); | ||
142 | |||
143 | is->lp = NULL; /* link is down .. set lp to NULL */ | ||
144 | lp->ppp_slot = -1; /* is this OK ?? */ | ||
145 | |||
146 | return 0; | ||
147 | } | ||
148 | |||
149 | /* | ||
150 | * bind isdn_net_local <=> ippp-device | ||
151 | * | ||
152 | * This function is allways called with holding dev->lock so | ||
153 | * no additional lock is needed | ||
154 | */ | ||
155 | int | ||
156 | isdn_ppp_bind(isdn_net_local * lp) | ||
157 | { | ||
158 | int i; | ||
159 | int unit = 0; | ||
160 | struct ippp_struct *is; | ||
161 | int retval; | ||
162 | |||
163 | if (lp->pppbind < 0) { /* device bounded to ippp device ? */ | ||
164 | isdn_net_dev *net_dev = dev->netdev; | ||
165 | char exclusive[ISDN_MAX_CHANNELS]; /* exclusive flags */ | ||
166 | memset(exclusive, 0, ISDN_MAX_CHANNELS); | ||
167 | while (net_dev) { /* step through net devices to find exclusive minors */ | ||
168 | isdn_net_local *lp = net_dev->local; | ||
169 | if (lp->pppbind >= 0) | ||
170 | exclusive[lp->pppbind] = 1; | ||
171 | net_dev = net_dev->next; | ||
172 | } | ||
173 | /* | ||
174 | * search a free device / slot | ||
175 | */ | ||
176 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
177 | if (ippp_table[i]->state == IPPP_OPEN && !exclusive[ippp_table[i]->minor]) { /* OPEN, but not connected! */ | ||
178 | break; | ||
179 | } | ||
180 | } | ||
181 | } else { | ||
182 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
183 | if (ippp_table[i]->minor == lp->pppbind && | ||
184 | (ippp_table[i]->state & IPPP_OPEN) == IPPP_OPEN) | ||
185 | break; | ||
186 | } | ||
187 | } | ||
188 | |||
189 | if (i >= ISDN_MAX_CHANNELS) { | ||
190 | printk(KERN_WARNING "isdn_ppp_bind: Can't find a (free) connection to the ipppd daemon.\n"); | ||
191 | retval = -1; | ||
192 | goto out; | ||
193 | } | ||
194 | unit = isdn_ppp_if_get_unit(lp->name); /* get unit number from interface name .. ugly! */ | ||
195 | if (unit < 0) { | ||
196 | printk(KERN_ERR "isdn_ppp_bind: illegal interface name %s.\n", lp->name); | ||
197 | retval = -1; | ||
198 | goto out; | ||
199 | } | ||
200 | |||
201 | lp->ppp_slot = i; | ||
202 | is = ippp_table[i]; | ||
203 | is->lp = lp; | ||
204 | is->unit = unit; | ||
205 | is->state = IPPP_OPEN | IPPP_ASSIGNED; /* assigned to a netdevice but not connected */ | ||
206 | #ifdef CONFIG_ISDN_MPP | ||
207 | retval = isdn_ppp_mp_init(lp, NULL); | ||
208 | if (retval < 0) | ||
209 | goto out; | ||
210 | #endif /* CONFIG_ISDN_MPP */ | ||
211 | |||
212 | retval = lp->ppp_slot; | ||
213 | |||
214 | out: | ||
215 | return retval; | ||
216 | } | ||
217 | |||
218 | /* | ||
219 | * kick the ipppd on the device | ||
220 | * (wakes up daemon after B-channel connect) | ||
221 | */ | ||
222 | |||
223 | void | ||
224 | isdn_ppp_wakeup_daemon(isdn_net_local * lp) | ||
225 | { | ||
226 | if (lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS) { | ||
227 | printk(KERN_ERR "%s: ppp_slot(%d) out of range\n", | ||
228 | __FUNCTION__, lp->ppp_slot); | ||
229 | return; | ||
230 | } | ||
231 | ippp_table[lp->ppp_slot]->state = IPPP_OPEN | IPPP_CONNECT | IPPP_NOBLOCK; | ||
232 | wake_up_interruptible(&ippp_table[lp->ppp_slot]->wq); | ||
233 | } | ||
234 | |||
235 | /* | ||
236 | * there was a hangup on the netdevice | ||
237 | * force wakeup of the ippp device | ||
238 | * go into 'device waits for release' state | ||
239 | */ | ||
240 | static int | ||
241 | isdn_ppp_closewait(int slot) | ||
242 | { | ||
243 | struct ippp_struct *is; | ||
244 | |||
245 | if (slot < 0 || slot >= ISDN_MAX_CHANNELS) { | ||
246 | printk(KERN_ERR "%s: slot(%d) out of range\n", | ||
247 | __FUNCTION__, slot); | ||
248 | return 0; | ||
249 | } | ||
250 | is = ippp_table[slot]; | ||
251 | if (is->state) | ||
252 | wake_up_interruptible(&is->wq); | ||
253 | is->state = IPPP_CLOSEWAIT; | ||
254 | return 1; | ||
255 | } | ||
256 | |||
257 | /* | ||
258 | * isdn_ppp_find_slot / isdn_ppp_free_slot | ||
259 | */ | ||
260 | |||
261 | static int | ||
262 | isdn_ppp_get_slot(void) | ||
263 | { | ||
264 | int i; | ||
265 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
266 | if (!ippp_table[i]->state) | ||
267 | return i; | ||
268 | } | ||
269 | return -1; | ||
270 | } | ||
271 | |||
272 | /* | ||
273 | * isdn_ppp_open | ||
274 | */ | ||
275 | |||
276 | int | ||
277 | isdn_ppp_open(int min, struct file *file) | ||
278 | { | ||
279 | int slot; | ||
280 | struct ippp_struct *is; | ||
281 | |||
282 | if (min < 0 || min > ISDN_MAX_CHANNELS) | ||
283 | return -ENODEV; | ||
284 | |||
285 | slot = isdn_ppp_get_slot(); | ||
286 | if (slot < 0) { | ||
287 | return -EBUSY; | ||
288 | } | ||
289 | is = file->private_data = ippp_table[slot]; | ||
290 | |||
291 | printk(KERN_DEBUG "ippp, open, slot: %d, minor: %d, state: %04x\n", | ||
292 | slot, min, is->state); | ||
293 | |||
294 | /* compression stuff */ | ||
295 | is->link_compressor = is->compressor = NULL; | ||
296 | is->link_decompressor = is->decompressor = NULL; | ||
297 | is->link_comp_stat = is->comp_stat = NULL; | ||
298 | is->link_decomp_stat = is->decomp_stat = NULL; | ||
299 | is->compflags = 0; | ||
300 | |||
301 | is->reset = isdn_ppp_ccp_reset_alloc(is); | ||
302 | |||
303 | is->lp = NULL; | ||
304 | is->mp_seqno = 0; /* MP sequence number */ | ||
305 | is->pppcfg = 0; /* ppp configuration */ | ||
306 | is->mpppcfg = 0; /* mppp configuration */ | ||
307 | is->last_link_seqno = -1; /* MP: maybe set to Bundle-MIN, when joining a bundle ?? */ | ||
308 | is->unit = -1; /* set, when we have our interface */ | ||
309 | is->mru = 1524; /* MRU, default 1524 */ | ||
310 | is->maxcid = 16; /* VJ: maxcid */ | ||
311 | is->tk = current; | ||
312 | init_waitqueue_head(&is->wq); | ||
313 | is->first = is->rq + NUM_RCV_BUFFS - 1; /* receive queue */ | ||
314 | is->last = is->rq; | ||
315 | is->minor = min; | ||
316 | #ifdef CONFIG_ISDN_PPP_VJ | ||
317 | /* | ||
318 | * VJ header compression init | ||
319 | */ | ||
320 | is->slcomp = slhc_init(16, 16); /* not necessary for 2. link in bundle */ | ||
321 | #endif | ||
322 | #ifdef CONFIG_IPPP_FILTER | ||
323 | is->pass_filter = NULL; | ||
324 | is->active_filter = NULL; | ||
325 | #endif | ||
326 | is->state = IPPP_OPEN; | ||
327 | |||
328 | return 0; | ||
329 | } | ||
330 | |||
331 | /* | ||
332 | * release ippp device | ||
333 | */ | ||
334 | void | ||
335 | isdn_ppp_release(int min, struct file *file) | ||
336 | { | ||
337 | int i; | ||
338 | struct ippp_struct *is; | ||
339 | |||
340 | if (min < 0 || min >= ISDN_MAX_CHANNELS) | ||
341 | return; | ||
342 | is = file->private_data; | ||
343 | |||
344 | if (!is) { | ||
345 | printk(KERN_ERR "%s: no file->private_data\n", __FUNCTION__); | ||
346 | return; | ||
347 | } | ||
348 | if (is->debug & 0x1) | ||
349 | printk(KERN_DEBUG "ippp: release, minor: %d %lx\n", min, (long) is->lp); | ||
350 | |||
351 | if (is->lp) { /* a lp address says: this link is still up */ | ||
352 | isdn_net_dev *p = is->lp->netdev; | ||
353 | |||
354 | if (!p) { | ||
355 | printk(KERN_ERR "%s: no lp->netdev\n", __FUNCTION__); | ||
356 | return; | ||
357 | } | ||
358 | is->state &= ~IPPP_CONNECT; /* -> effect: no call of wakeup */ | ||
359 | /* | ||
360 | * isdn_net_hangup() calls isdn_ppp_free() | ||
361 | * isdn_ppp_free() sets is->lp to NULL and lp->ppp_slot to -1 | ||
362 | * removing the IPPP_CONNECT flag omits calling of isdn_ppp_wakeup_daemon() | ||
363 | */ | ||
364 | isdn_net_hangup(&p->dev); | ||
365 | } | ||
366 | for (i = 0; i < NUM_RCV_BUFFS; i++) { | ||
367 | if (is->rq[i].buf) { | ||
368 | kfree(is->rq[i].buf); | ||
369 | is->rq[i].buf = NULL; | ||
370 | } | ||
371 | } | ||
372 | is->first = is->rq + NUM_RCV_BUFFS - 1; /* receive queue */ | ||
373 | is->last = is->rq; | ||
374 | |||
375 | #ifdef CONFIG_ISDN_PPP_VJ | ||
376 | /* TODO: if this was the previous master: link the slcomp to the new master */ | ||
377 | slhc_free(is->slcomp); | ||
378 | is->slcomp = NULL; | ||
379 | #endif | ||
380 | #ifdef CONFIG_IPPP_FILTER | ||
381 | if (is->pass_filter) { | ||
382 | kfree(is->pass_filter); | ||
383 | is->pass_filter = NULL; | ||
384 | } | ||
385 | if (is->active_filter) { | ||
386 | kfree(is->active_filter); | ||
387 | is->active_filter = NULL; | ||
388 | } | ||
389 | #endif | ||
390 | |||
391 | /* TODO: if this was the previous master: link the stuff to the new master */ | ||
392 | if(is->comp_stat) | ||
393 | is->compressor->free(is->comp_stat); | ||
394 | if(is->link_comp_stat) | ||
395 | is->link_compressor->free(is->link_comp_stat); | ||
396 | if(is->link_decomp_stat) | ||
397 | is->link_decompressor->free(is->link_decomp_stat); | ||
398 | if(is->decomp_stat) | ||
399 | is->decompressor->free(is->decomp_stat); | ||
400 | is->compressor = is->link_compressor = NULL; | ||
401 | is->decompressor = is->link_decompressor = NULL; | ||
402 | is->comp_stat = is->link_comp_stat = NULL; | ||
403 | is->decomp_stat = is->link_decomp_stat = NULL; | ||
404 | |||
405 | /* Clean up if necessary */ | ||
406 | if(is->reset) | ||
407 | isdn_ppp_ccp_reset_free(is); | ||
408 | |||
409 | /* this slot is ready for new connections */ | ||
410 | is->state = 0; | ||
411 | } | ||
412 | |||
413 | /* | ||
414 | * get_arg .. ioctl helper | ||
415 | */ | ||
416 | static int | ||
417 | get_arg(void __user *b, void *val, int len) | ||
418 | { | ||
419 | if (len <= 0) | ||
420 | len = sizeof(void *); | ||
421 | if (copy_from_user(val, b, len)) | ||
422 | return -EFAULT; | ||
423 | return 0; | ||
424 | } | ||
425 | |||
426 | /* | ||
427 | * set arg .. ioctl helper | ||
428 | */ | ||
429 | static int | ||
430 | set_arg(void __user *b, void *val,int len) | ||
431 | { | ||
432 | if(len <= 0) | ||
433 | len = sizeof(void *); | ||
434 | if (copy_to_user(b, val, len)) | ||
435 | return -EFAULT; | ||
436 | return 0; | ||
437 | } | ||
438 | |||
439 | static int get_filter(void __user *arg, struct sock_filter **p) | ||
440 | { | ||
441 | struct sock_fprog uprog; | ||
442 | struct sock_filter *code = NULL; | ||
443 | int len, err; | ||
444 | |||
445 | if (copy_from_user(&uprog, arg, sizeof(uprog))) | ||
446 | return -EFAULT; | ||
447 | |||
448 | if (!uprog.len) { | ||
449 | *p = NULL; | ||
450 | return 0; | ||
451 | } | ||
452 | |||
453 | /* uprog.len is unsigned short, so no overflow here */ | ||
454 | len = uprog.len * sizeof(struct sock_filter); | ||
455 | code = kmalloc(len, GFP_KERNEL); | ||
456 | if (code == NULL) | ||
457 | return -ENOMEM; | ||
458 | |||
459 | if (copy_from_user(code, uprog.filter, len)) { | ||
460 | kfree(code); | ||
461 | return -EFAULT; | ||
462 | } | ||
463 | |||
464 | err = sk_chk_filter(code, uprog.len); | ||
465 | if (err) { | ||
466 | kfree(code); | ||
467 | return err; | ||
468 | } | ||
469 | |||
470 | *p = code; | ||
471 | return uprog.len; | ||
472 | } | ||
473 | |||
474 | /* | ||
475 | * ippp device ioctl | ||
476 | */ | ||
477 | int | ||
478 | isdn_ppp_ioctl(int min, struct file *file, unsigned int cmd, unsigned long arg) | ||
479 | { | ||
480 | unsigned long val; | ||
481 | int r,i,j; | ||
482 | struct ippp_struct *is; | ||
483 | isdn_net_local *lp; | ||
484 | struct isdn_ppp_comp_data data; | ||
485 | void __user *argp = (void __user *)arg; | ||
486 | |||
487 | is = (struct ippp_struct *) file->private_data; | ||
488 | lp = is->lp; | ||
489 | |||
490 | if (is->debug & 0x1) | ||
491 | printk(KERN_DEBUG "isdn_ppp_ioctl: minor: %d cmd: %x state: %x\n", min, cmd, is->state); | ||
492 | |||
493 | if (!(is->state & IPPP_OPEN)) | ||
494 | return -EINVAL; | ||
495 | |||
496 | switch (cmd) { | ||
497 | case PPPIOCBUNDLE: | ||
498 | #ifdef CONFIG_ISDN_MPP | ||
499 | if (!(is->state & IPPP_CONNECT)) | ||
500 | return -EINVAL; | ||
501 | if ((r = get_arg(argp, &val, sizeof(val) ))) | ||
502 | return r; | ||
503 | printk(KERN_DEBUG "iPPP-bundle: minor: %d, slave unit: %d, master unit: %d\n", | ||
504 | (int) min, (int) is->unit, (int) val); | ||
505 | return isdn_ppp_bundle(is, val); | ||
506 | #else | ||
507 | return -1; | ||
508 | #endif | ||
509 | break; | ||
510 | case PPPIOCGUNIT: /* get ppp/isdn unit number */ | ||
511 | if ((r = set_arg(argp, &is->unit, sizeof(is->unit) ))) | ||
512 | return r; | ||
513 | break; | ||
514 | case PPPIOCGIFNAME: | ||
515 | if(!lp) | ||
516 | return -EINVAL; | ||
517 | if ((r = set_arg(argp, lp->name, strlen(lp->name)))) | ||
518 | return r; | ||
519 | break; | ||
520 | case PPPIOCGMPFLAGS: /* get configuration flags */ | ||
521 | if ((r = set_arg(argp, &is->mpppcfg, sizeof(is->mpppcfg) ))) | ||
522 | return r; | ||
523 | break; | ||
524 | case PPPIOCSMPFLAGS: /* set configuration flags */ | ||
525 | if ((r = get_arg(argp, &val, sizeof(val) ))) | ||
526 | return r; | ||
527 | is->mpppcfg = val; | ||
528 | break; | ||
529 | case PPPIOCGFLAGS: /* get configuration flags */ | ||
530 | if ((r = set_arg(argp, &is->pppcfg,sizeof(is->pppcfg) ))) | ||
531 | return r; | ||
532 | break; | ||
533 | case PPPIOCSFLAGS: /* set configuration flags */ | ||
534 | if ((r = get_arg(argp, &val, sizeof(val) ))) { | ||
535 | return r; | ||
536 | } | ||
537 | if (val & SC_ENABLE_IP && !(is->pppcfg & SC_ENABLE_IP) && (is->state & IPPP_CONNECT)) { | ||
538 | if (lp) { | ||
539 | /* OK .. we are ready to send buffers */ | ||
540 | is->pppcfg = val; /* isdn_ppp_xmit test for SC_ENABLE_IP !!! */ | ||
541 | netif_wake_queue(&lp->netdev->dev); | ||
542 | break; | ||
543 | } | ||
544 | } | ||
545 | is->pppcfg = val; | ||
546 | break; | ||
547 | case PPPIOCGIDLE: /* get idle time information */ | ||
548 | if (lp) { | ||
549 | struct ppp_idle pidle; | ||
550 | pidle.xmit_idle = pidle.recv_idle = lp->huptimer; | ||
551 | if ((r = set_arg(argp, &pidle,sizeof(struct ppp_idle)))) | ||
552 | return r; | ||
553 | } | ||
554 | break; | ||
555 | case PPPIOCSMRU: /* set receive unit size for PPP */ | ||
556 | if ((r = get_arg(argp, &val, sizeof(val) ))) | ||
557 | return r; | ||
558 | is->mru = val; | ||
559 | break; | ||
560 | case PPPIOCSMPMRU: | ||
561 | break; | ||
562 | case PPPIOCSMPMTU: | ||
563 | break; | ||
564 | case PPPIOCSMAXCID: /* set the maximum compression slot id */ | ||
565 | if ((r = get_arg(argp, &val, sizeof(val) ))) | ||
566 | return r; | ||
567 | val++; | ||
568 | if (is->maxcid != val) { | ||
569 | #ifdef CONFIG_ISDN_PPP_VJ | ||
570 | struct slcompress *sltmp; | ||
571 | #endif | ||
572 | if (is->debug & 0x1) | ||
573 | printk(KERN_DEBUG "ippp, ioctl: changed MAXCID to %ld\n", val); | ||
574 | is->maxcid = val; | ||
575 | #ifdef CONFIG_ISDN_PPP_VJ | ||
576 | sltmp = slhc_init(16, val); | ||
577 | if (!sltmp) { | ||
578 | printk(KERN_ERR "ippp, can't realloc slhc struct\n"); | ||
579 | return -ENOMEM; | ||
580 | } | ||
581 | if (is->slcomp) | ||
582 | slhc_free(is->slcomp); | ||
583 | is->slcomp = sltmp; | ||
584 | #endif | ||
585 | } | ||
586 | break; | ||
587 | case PPPIOCGDEBUG: | ||
588 | if ((r = set_arg(argp, &is->debug, sizeof(is->debug) ))) | ||
589 | return r; | ||
590 | break; | ||
591 | case PPPIOCSDEBUG: | ||
592 | if ((r = get_arg(argp, &val, sizeof(val) ))) | ||
593 | return r; | ||
594 | is->debug = val; | ||
595 | break; | ||
596 | case PPPIOCGCOMPRESSORS: | ||
597 | { | ||
598 | unsigned long protos[8] = {0,}; | ||
599 | struct isdn_ppp_compressor *ipc = ipc_head; | ||
600 | while(ipc) { | ||
601 | j = ipc->num / (sizeof(long)*8); | ||
602 | i = ipc->num % (sizeof(long)*8); | ||
603 | if(j < 8) | ||
604 | protos[j] |= (0x1<<i); | ||
605 | ipc = ipc->next; | ||
606 | } | ||
607 | if ((r = set_arg(argp,protos,8*sizeof(long) ))) | ||
608 | return r; | ||
609 | } | ||
610 | break; | ||
611 | case PPPIOCSCOMPRESSOR: | ||
612 | if ((r = get_arg(argp, &data, sizeof(struct isdn_ppp_comp_data)))) | ||
613 | return r; | ||
614 | return isdn_ppp_set_compressor(is, &data); | ||
615 | case PPPIOCGCALLINFO: | ||
616 | { | ||
617 | struct pppcallinfo pci; | ||
618 | memset((char *) &pci,0,sizeof(struct pppcallinfo)); | ||
619 | if(lp) | ||
620 | { | ||
621 | strncpy(pci.local_num,lp->msn,63); | ||
622 | if(lp->dial) { | ||
623 | strncpy(pci.remote_num,lp->dial->num,63); | ||
624 | } | ||
625 | pci.charge_units = lp->charge; | ||
626 | if(lp->outgoing) | ||
627 | pci.calltype = CALLTYPE_OUTGOING; | ||
628 | else | ||
629 | pci.calltype = CALLTYPE_INCOMING; | ||
630 | if(lp->flags & ISDN_NET_CALLBACK) | ||
631 | pci.calltype |= CALLTYPE_CALLBACK; | ||
632 | } | ||
633 | return set_arg(argp,&pci,sizeof(struct pppcallinfo)); | ||
634 | } | ||
635 | #ifdef CONFIG_IPPP_FILTER | ||
636 | case PPPIOCSPASS: | ||
637 | { | ||
638 | struct sock_filter *code; | ||
639 | int len = get_filter(argp, &code); | ||
640 | if (len < 0) | ||
641 | return len; | ||
642 | kfree(is->pass_filter); | ||
643 | is->pass_filter = code; | ||
644 | is->pass_len = len; | ||
645 | break; | ||
646 | } | ||
647 | case PPPIOCSACTIVE: | ||
648 | { | ||
649 | struct sock_filter *code; | ||
650 | int len = get_filter(argp, &code); | ||
651 | if (len < 0) | ||
652 | return len; | ||
653 | kfree(is->active_filter); | ||
654 | is->active_filter = code; | ||
655 | is->active_len = len; | ||
656 | break; | ||
657 | } | ||
658 | #endif /* CONFIG_IPPP_FILTER */ | ||
659 | default: | ||
660 | break; | ||
661 | } | ||
662 | return 0; | ||
663 | } | ||
664 | |||
665 | unsigned int | ||
666 | isdn_ppp_poll(struct file *file, poll_table * wait) | ||
667 | { | ||
668 | u_int mask; | ||
669 | struct ippp_buf_queue *bf, *bl; | ||
670 | u_long flags; | ||
671 | struct ippp_struct *is; | ||
672 | |||
673 | is = file->private_data; | ||
674 | |||
675 | if (is->debug & 0x2) | ||
676 | printk(KERN_DEBUG "isdn_ppp_poll: minor: %d\n", | ||
677 | MINOR(file->f_dentry->d_inode->i_rdev)); | ||
678 | |||
679 | /* just registers wait_queue hook. This doesn't really wait. */ | ||
680 | poll_wait(file, &is->wq, wait); | ||
681 | |||
682 | if (!(is->state & IPPP_OPEN)) { | ||
683 | if(is->state == IPPP_CLOSEWAIT) | ||
684 | return POLLHUP; | ||
685 | printk(KERN_DEBUG "isdn_ppp: device not open\n"); | ||
686 | return POLLERR; | ||
687 | } | ||
688 | /* we're always ready to send .. */ | ||
689 | mask = POLLOUT | POLLWRNORM; | ||
690 | |||
691 | spin_lock_irqsave(&is->buflock, flags); | ||
692 | bl = is->last; | ||
693 | bf = is->first; | ||
694 | /* | ||
695 | * if IPPP_NOBLOCK is set we return even if we have nothing to read | ||
696 | */ | ||
697 | if (bf->next != bl || (is->state & IPPP_NOBLOCK)) { | ||
698 | is->state &= ~IPPP_NOBLOCK; | ||
699 | mask |= POLLIN | POLLRDNORM; | ||
700 | } | ||
701 | spin_unlock_irqrestore(&is->buflock, flags); | ||
702 | return mask; | ||
703 | } | ||
704 | |||
705 | /* | ||
706 | * fill up isdn_ppp_read() queue .. | ||
707 | */ | ||
708 | |||
709 | static int | ||
710 | isdn_ppp_fill_rq(unsigned char *buf, int len, int proto, int slot) | ||
711 | { | ||
712 | struct ippp_buf_queue *bf, *bl; | ||
713 | u_long flags; | ||
714 | u_char *nbuf; | ||
715 | struct ippp_struct *is; | ||
716 | |||
717 | if (slot < 0 || slot >= ISDN_MAX_CHANNELS) { | ||
718 | printk(KERN_WARNING "ippp: illegal slot(%d).\n", slot); | ||
719 | return 0; | ||
720 | } | ||
721 | is = ippp_table[slot]; | ||
722 | |||
723 | if (!(is->state & IPPP_CONNECT)) { | ||
724 | printk(KERN_DEBUG "ippp: device not activated.\n"); | ||
725 | return 0; | ||
726 | } | ||
727 | nbuf = (unsigned char *) kmalloc(len + 4, GFP_ATOMIC); | ||
728 | if (!nbuf) { | ||
729 | printk(KERN_WARNING "ippp: Can't alloc buf\n"); | ||
730 | return 0; | ||
731 | } | ||
732 | nbuf[0] = PPP_ALLSTATIONS; | ||
733 | nbuf[1] = PPP_UI; | ||
734 | nbuf[2] = proto >> 8; | ||
735 | nbuf[3] = proto & 0xff; | ||
736 | memcpy(nbuf + 4, buf, len); | ||
737 | |||
738 | spin_lock_irqsave(&is->buflock, flags); | ||
739 | bf = is->first; | ||
740 | bl = is->last; | ||
741 | |||
742 | if (bf == bl) { | ||
743 | printk(KERN_WARNING "ippp: Queue is full; discarding first buffer\n"); | ||
744 | bf = bf->next; | ||
745 | kfree(bf->buf); | ||
746 | is->first = bf; | ||
747 | } | ||
748 | bl->buf = (char *) nbuf; | ||
749 | bl->len = len + 4; | ||
750 | |||
751 | is->last = bl->next; | ||
752 | spin_unlock_irqrestore(&is->buflock, flags); | ||
753 | wake_up_interruptible(&is->wq); | ||
754 | return len; | ||
755 | } | ||
756 | |||
757 | /* | ||
758 | * read() .. non-blocking: ipppd calls it only after select() | ||
759 | * reports, that there is data | ||
760 | */ | ||
761 | |||
762 | int | ||
763 | isdn_ppp_read(int min, struct file *file, char __user *buf, int count) | ||
764 | { | ||
765 | struct ippp_struct *is; | ||
766 | struct ippp_buf_queue *b; | ||
767 | u_long flags; | ||
768 | u_char *save_buf; | ||
769 | |||
770 | is = file->private_data; | ||
771 | |||
772 | if (!(is->state & IPPP_OPEN)) | ||
773 | return 0; | ||
774 | |||
775 | if (!access_ok(VERIFY_WRITE, buf, count)) | ||
776 | return -EFAULT; | ||
777 | |||
778 | spin_lock_irqsave(&is->buflock, flags); | ||
779 | b = is->first->next; | ||
780 | save_buf = b->buf; | ||
781 | if (!save_buf) { | ||
782 | spin_unlock_irqrestore(&is->buflock, flags); | ||
783 | return -EAGAIN; | ||
784 | } | ||
785 | if (b->len < count) | ||
786 | count = b->len; | ||
787 | b->buf = NULL; | ||
788 | is->first = b; | ||
789 | |||
790 | spin_unlock_irqrestore(&is->buflock, flags); | ||
791 | copy_to_user(buf, save_buf, count); | ||
792 | kfree(save_buf); | ||
793 | |||
794 | return count; | ||
795 | } | ||
796 | |||
797 | /* | ||
798 | * ipppd wanna write a packet to the card .. non-blocking | ||
799 | */ | ||
800 | |||
801 | int | ||
802 | isdn_ppp_write(int min, struct file *file, const char __user *buf, int count) | ||
803 | { | ||
804 | isdn_net_local *lp; | ||
805 | struct ippp_struct *is; | ||
806 | int proto; | ||
807 | unsigned char protobuf[4]; | ||
808 | |||
809 | is = file->private_data; | ||
810 | |||
811 | if (!(is->state & IPPP_CONNECT)) | ||
812 | return 0; | ||
813 | |||
814 | lp = is->lp; | ||
815 | |||
816 | /* -> push it directly to the lowlevel interface */ | ||
817 | |||
818 | if (!lp) | ||
819 | printk(KERN_DEBUG "isdn_ppp_write: lp == NULL\n"); | ||
820 | else { | ||
821 | /* | ||
822 | * Don't reset huptimer for | ||
823 | * LCP packets. (Echo requests). | ||
824 | */ | ||
825 | if (copy_from_user(protobuf, buf, 4)) | ||
826 | return -EFAULT; | ||
827 | proto = PPP_PROTOCOL(protobuf); | ||
828 | if (proto != PPP_LCP) | ||
829 | lp->huptimer = 0; | ||
830 | |||
831 | if (lp->isdn_device < 0 || lp->isdn_channel < 0) | ||
832 | return 0; | ||
833 | |||
834 | if ((dev->drv[lp->isdn_device]->flags & DRV_FLAG_RUNNING) && | ||
835 | lp->dialstate == 0 && | ||
836 | (lp->flags & ISDN_NET_CONNECTED)) { | ||
837 | unsigned short hl; | ||
838 | struct sk_buff *skb; | ||
839 | /* | ||
840 | * we need to reserve enought space in front of | ||
841 | * sk_buff. old call to dev_alloc_skb only reserved | ||
842 | * 16 bytes, now we are looking what the driver want | ||
843 | */ | ||
844 | hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen; | ||
845 | skb = alloc_skb(hl+count, GFP_ATOMIC); | ||
846 | if (!skb) { | ||
847 | printk(KERN_WARNING "isdn_ppp_write: out of memory!\n"); | ||
848 | return count; | ||
849 | } | ||
850 | skb_reserve(skb, hl); | ||
851 | if (copy_from_user(skb_put(skb, count), buf, count)) | ||
852 | { | ||
853 | kfree_skb(skb); | ||
854 | return -EFAULT; | ||
855 | } | ||
856 | if (is->debug & 0x40) { | ||
857 | printk(KERN_DEBUG "ppp xmit: len %d\n", (int) skb->len); | ||
858 | isdn_ppp_frame_log("xmit", skb->data, skb->len, 32,is->unit,lp->ppp_slot); | ||
859 | } | ||
860 | |||
861 | isdn_ppp_send_ccp(lp->netdev,lp,skb); /* keeps CCP/compression states in sync */ | ||
862 | |||
863 | isdn_net_write_super(lp, skb); | ||
864 | } | ||
865 | } | ||
866 | return count; | ||
867 | } | ||
868 | |||
869 | /* | ||
870 | * init memory, structures etc. | ||
871 | */ | ||
872 | |||
873 | int | ||
874 | isdn_ppp_init(void) | ||
875 | { | ||
876 | int i, | ||
877 | j; | ||
878 | |||
879 | #ifdef CONFIG_ISDN_MPP | ||
880 | if( isdn_ppp_mp_bundle_array_init() < 0 ) | ||
881 | return -ENOMEM; | ||
882 | #endif /* CONFIG_ISDN_MPP */ | ||
883 | |||
884 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
885 | if (!(ippp_table[i] = (struct ippp_struct *) | ||
886 | kmalloc(sizeof(struct ippp_struct), GFP_KERNEL))) { | ||
887 | printk(KERN_WARNING "isdn_ppp_init: Could not alloc ippp_table\n"); | ||
888 | for (j = 0; j < i; j++) | ||
889 | kfree(ippp_table[j]); | ||
890 | return -1; | ||
891 | } | ||
892 | memset((char *) ippp_table[i], 0, sizeof(struct ippp_struct)); | ||
893 | spin_lock_init(&ippp_table[i]->buflock); | ||
894 | ippp_table[i]->state = 0; | ||
895 | ippp_table[i]->first = ippp_table[i]->rq + NUM_RCV_BUFFS - 1; | ||
896 | ippp_table[i]->last = ippp_table[i]->rq; | ||
897 | |||
898 | for (j = 0; j < NUM_RCV_BUFFS; j++) { | ||
899 | ippp_table[i]->rq[j].buf = NULL; | ||
900 | ippp_table[i]->rq[j].last = ippp_table[i]->rq + | ||
901 | (NUM_RCV_BUFFS + j - 1) % NUM_RCV_BUFFS; | ||
902 | ippp_table[i]->rq[j].next = ippp_table[i]->rq + (j + 1) % NUM_RCV_BUFFS; | ||
903 | } | ||
904 | } | ||
905 | return 0; | ||
906 | } | ||
907 | |||
908 | void | ||
909 | isdn_ppp_cleanup(void) | ||
910 | { | ||
911 | int i; | ||
912 | |||
913 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) | ||
914 | kfree(ippp_table[i]); | ||
915 | |||
916 | #ifdef CONFIG_ISDN_MPP | ||
917 | if (isdn_ppp_bundle_arr) | ||
918 | kfree(isdn_ppp_bundle_arr); | ||
919 | #endif /* CONFIG_ISDN_MPP */ | ||
920 | |||
921 | } | ||
922 | |||
923 | /* | ||
924 | * check for address/control field and skip if allowed | ||
925 | * retval != 0 -> discard packet silently | ||
926 | */ | ||
927 | static int isdn_ppp_skip_ac(struct ippp_struct *is, struct sk_buff *skb) | ||
928 | { | ||
929 | if (skb->len < 1) | ||
930 | return -1; | ||
931 | |||
932 | if (skb->data[0] == 0xff) { | ||
933 | if (skb->len < 2) | ||
934 | return -1; | ||
935 | |||
936 | if (skb->data[1] != 0x03) | ||
937 | return -1; | ||
938 | |||
939 | // skip address/control (AC) field | ||
940 | skb_pull(skb, 2); | ||
941 | } else { | ||
942 | if (is->pppcfg & SC_REJ_COMP_AC) | ||
943 | // if AC compression was not negotiated, but used, discard packet | ||
944 | return -1; | ||
945 | } | ||
946 | return 0; | ||
947 | } | ||
948 | |||
949 | /* | ||
950 | * get the PPP protocol header and pull skb | ||
951 | * retval < 0 -> discard packet silently | ||
952 | */ | ||
953 | static int isdn_ppp_strip_proto(struct sk_buff *skb) | ||
954 | { | ||
955 | int proto; | ||
956 | |||
957 | if (skb->len < 1) | ||
958 | return -1; | ||
959 | |||
960 | if (skb->data[0] & 0x1) { | ||
961 | // protocol field is compressed | ||
962 | proto = skb->data[0]; | ||
963 | skb_pull(skb, 1); | ||
964 | } else { | ||
965 | if (skb->len < 2) | ||
966 | return -1; | ||
967 | proto = ((int) skb->data[0] << 8) + skb->data[1]; | ||
968 | skb_pull(skb, 2); | ||
969 | } | ||
970 | return proto; | ||
971 | } | ||
972 | |||
973 | |||
974 | /* | ||
975 | * handler for incoming packets on a syncPPP interface | ||
976 | */ | ||
977 | void isdn_ppp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb) | ||
978 | { | ||
979 | struct ippp_struct *is; | ||
980 | int slot; | ||
981 | int proto; | ||
982 | |||
983 | if (net_dev->local->master) | ||
984 | BUG(); // we're called with the master device always | ||
985 | |||
986 | slot = lp->ppp_slot; | ||
987 | if (slot < 0 || slot >= ISDN_MAX_CHANNELS) { | ||
988 | printk(KERN_ERR "isdn_ppp_receive: lp->ppp_slot(%d)\n", | ||
989 | lp->ppp_slot); | ||
990 | kfree_skb(skb); | ||
991 | return; | ||
992 | } | ||
993 | is = ippp_table[slot]; | ||
994 | |||
995 | if (is->debug & 0x4) { | ||
996 | printk(KERN_DEBUG "ippp_receive: is:%08lx lp:%08lx slot:%d unit:%d len:%d\n", | ||
997 | (long)is,(long)lp,lp->ppp_slot,is->unit,(int) skb->len); | ||
998 | isdn_ppp_frame_log("receive", skb->data, skb->len, 32,is->unit,lp->ppp_slot); | ||
999 | } | ||
1000 | |||
1001 | if (isdn_ppp_skip_ac(is, skb) < 0) { | ||
1002 | kfree_skb(skb); | ||
1003 | return; | ||
1004 | } | ||
1005 | proto = isdn_ppp_strip_proto(skb); | ||
1006 | if (proto < 0) { | ||
1007 | kfree_skb(skb); | ||
1008 | return; | ||
1009 | } | ||
1010 | |||
1011 | #ifdef CONFIG_ISDN_MPP | ||
1012 | if (is->compflags & SC_LINK_DECOMP_ON) { | ||
1013 | skb = isdn_ppp_decompress(skb, is, NULL, &proto); | ||
1014 | if (!skb) // decompression error | ||
1015 | return; | ||
1016 | } | ||
1017 | |||
1018 | if (!(is->mpppcfg & SC_REJ_MP_PROT)) { // we agreed to receive MPPP | ||
1019 | if (proto == PPP_MP) { | ||
1020 | isdn_ppp_mp_receive(net_dev, lp, skb); | ||
1021 | return; | ||
1022 | } | ||
1023 | } | ||
1024 | #endif | ||
1025 | isdn_ppp_push_higher(net_dev, lp, skb, proto); | ||
1026 | } | ||
1027 | |||
1028 | /* | ||
1029 | * we receive a reassembled frame, MPPP has been taken care of before. | ||
1030 | * address/control and protocol have been stripped from the skb | ||
1031 | * note: net_dev has to be master net_dev | ||
1032 | */ | ||
1033 | static void | ||
1034 | isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb, int proto) | ||
1035 | { | ||
1036 | struct net_device *dev = &net_dev->dev; | ||
1037 | struct ippp_struct *is, *mis; | ||
1038 | isdn_net_local *mlp = NULL; | ||
1039 | int slot; | ||
1040 | |||
1041 | slot = lp->ppp_slot; | ||
1042 | if (slot < 0 || slot >= ISDN_MAX_CHANNELS) { | ||
1043 | printk(KERN_ERR "isdn_ppp_push_higher: lp->ppp_slot(%d)\n", | ||
1044 | lp->ppp_slot); | ||
1045 | goto drop_packet; | ||
1046 | } | ||
1047 | is = ippp_table[slot]; | ||
1048 | |||
1049 | if (lp->master) { // FIXME? | ||
1050 | mlp = (isdn_net_local *) lp->master->priv; | ||
1051 | slot = mlp->ppp_slot; | ||
1052 | if (slot < 0 || slot > ISDN_MAX_CHANNELS) { | ||
1053 | printk(KERN_ERR "isdn_ppp_push_higher: master->ppp_slot(%d)\n", | ||
1054 | lp->ppp_slot); | ||
1055 | goto drop_packet; | ||
1056 | } | ||
1057 | } | ||
1058 | mis = ippp_table[slot]; | ||
1059 | |||
1060 | if (is->debug & 0x10) { | ||
1061 | printk(KERN_DEBUG "push, skb %d %04x\n", (int) skb->len, proto); | ||
1062 | isdn_ppp_frame_log("rpush", skb->data, skb->len, 32,is->unit,lp->ppp_slot); | ||
1063 | } | ||
1064 | if (mis->compflags & SC_DECOMP_ON) { | ||
1065 | skb = isdn_ppp_decompress(skb, is, mis, &proto); | ||
1066 | if (!skb) // decompression error | ||
1067 | return; | ||
1068 | } | ||
1069 | switch (proto) { | ||
1070 | case PPP_IPX: /* untested */ | ||
1071 | if (is->debug & 0x20) | ||
1072 | printk(KERN_DEBUG "isdn_ppp: IPX\n"); | ||
1073 | skb->protocol = htons(ETH_P_IPX); | ||
1074 | break; | ||
1075 | case PPP_IP: | ||
1076 | if (is->debug & 0x20) | ||
1077 | printk(KERN_DEBUG "isdn_ppp: IP\n"); | ||
1078 | skb->protocol = htons(ETH_P_IP); | ||
1079 | break; | ||
1080 | case PPP_COMP: | ||
1081 | case PPP_COMPFRAG: | ||
1082 | printk(KERN_INFO "isdn_ppp: unexpected compressed frame dropped\n"); | ||
1083 | goto drop_packet; | ||
1084 | #ifdef CONFIG_ISDN_PPP_VJ | ||
1085 | case PPP_VJC_UNCOMP: | ||
1086 | if (is->debug & 0x20) | ||
1087 | printk(KERN_DEBUG "isdn_ppp: VJC_UNCOMP\n"); | ||
1088 | if (net_dev->local->ppp_slot < 0) { | ||
1089 | printk(KERN_ERR "%s: net_dev->local->ppp_slot(%d) out of range\n", | ||
1090 | __FUNCTION__, net_dev->local->ppp_slot); | ||
1091 | goto drop_packet; | ||
1092 | } | ||
1093 | if (slhc_remember(ippp_table[net_dev->local->ppp_slot]->slcomp, skb->data, skb->len) <= 0) { | ||
1094 | printk(KERN_WARNING "isdn_ppp: received illegal VJC_UNCOMP frame!\n"); | ||
1095 | goto drop_packet; | ||
1096 | } | ||
1097 | skb->protocol = htons(ETH_P_IP); | ||
1098 | break; | ||
1099 | case PPP_VJC_COMP: | ||
1100 | if (is->debug & 0x20) | ||
1101 | printk(KERN_DEBUG "isdn_ppp: VJC_COMP\n"); | ||
1102 | { | ||
1103 | struct sk_buff *skb_old = skb; | ||
1104 | int pkt_len; | ||
1105 | skb = dev_alloc_skb(skb_old->len + 128); | ||
1106 | |||
1107 | if (!skb) { | ||
1108 | printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name); | ||
1109 | skb = skb_old; | ||
1110 | goto drop_packet; | ||
1111 | } | ||
1112 | skb_put(skb, skb_old->len + 128); | ||
1113 | memcpy(skb->data, skb_old->data, skb_old->len); | ||
1114 | if (net_dev->local->ppp_slot < 0) { | ||
1115 | printk(KERN_ERR "%s: net_dev->local->ppp_slot(%d) out of range\n", | ||
1116 | __FUNCTION__, net_dev->local->ppp_slot); | ||
1117 | goto drop_packet; | ||
1118 | } | ||
1119 | pkt_len = slhc_uncompress(ippp_table[net_dev->local->ppp_slot]->slcomp, | ||
1120 | skb->data, skb_old->len); | ||
1121 | kfree_skb(skb_old); | ||
1122 | if (pkt_len < 0) | ||
1123 | goto drop_packet; | ||
1124 | |||
1125 | skb_trim(skb, pkt_len); | ||
1126 | skb->protocol = htons(ETH_P_IP); | ||
1127 | } | ||
1128 | break; | ||
1129 | #endif | ||
1130 | case PPP_CCP: | ||
1131 | case PPP_CCPFRAG: | ||
1132 | isdn_ppp_receive_ccp(net_dev,lp,skb,proto); | ||
1133 | /* Dont pop up ResetReq/Ack stuff to the daemon any | ||
1134 | longer - the job is done already */ | ||
1135 | if(skb->data[0] == CCP_RESETREQ || | ||
1136 | skb->data[0] == CCP_RESETACK) | ||
1137 | break; | ||
1138 | /* fall through */ | ||
1139 | default: | ||
1140 | isdn_ppp_fill_rq(skb->data, skb->len, proto, lp->ppp_slot); /* push data to pppd device */ | ||
1141 | kfree_skb(skb); | ||
1142 | return; | ||
1143 | } | ||
1144 | |||
1145 | #ifdef CONFIG_IPPP_FILTER | ||
1146 | /* check if the packet passes the pass and active filters | ||
1147 | * the filter instructions are constructed assuming | ||
1148 | * a four-byte PPP header on each packet (which is still present) */ | ||
1149 | skb_push(skb, 4); | ||
1150 | |||
1151 | { | ||
1152 | u_int16_t *p = (u_int16_t *) skb->data; | ||
1153 | |||
1154 | *p = 0; /* indicate inbound in DLT_LINUX_SLL */ | ||
1155 | } | ||
1156 | |||
1157 | if (is->pass_filter | ||
1158 | && sk_run_filter(skb, is->pass_filter, is->pass_len) == 0) { | ||
1159 | if (is->debug & 0x2) | ||
1160 | printk(KERN_DEBUG "IPPP: inbound frame filtered.\n"); | ||
1161 | kfree_skb(skb); | ||
1162 | return; | ||
1163 | } | ||
1164 | if (!(is->active_filter | ||
1165 | && sk_run_filter(skb, is->active_filter, | ||
1166 | is->active_len) == 0)) { | ||
1167 | if (is->debug & 0x2) | ||
1168 | printk(KERN_DEBUG "IPPP: link-active filter: reseting huptimer.\n"); | ||
1169 | lp->huptimer = 0; | ||
1170 | if (mlp) | ||
1171 | mlp->huptimer = 0; | ||
1172 | } | ||
1173 | skb_pull(skb, 4); | ||
1174 | #else /* CONFIG_IPPP_FILTER */ | ||
1175 | lp->huptimer = 0; | ||
1176 | if (mlp) | ||
1177 | mlp->huptimer = 0; | ||
1178 | #endif /* CONFIG_IPPP_FILTER */ | ||
1179 | skb->dev = dev; | ||
1180 | skb->input_dev = dev; | ||
1181 | skb->mac.raw = skb->data; | ||
1182 | netif_rx(skb); | ||
1183 | /* net_dev->local->stats.rx_packets++; done in isdn_net.c */ | ||
1184 | return; | ||
1185 | |||
1186 | drop_packet: | ||
1187 | net_dev->local->stats.rx_dropped++; | ||
1188 | kfree_skb(skb); | ||
1189 | } | ||
1190 | |||
1191 | /* | ||
1192 | * isdn_ppp_skb_push .. | ||
1193 | * checks whether we have enough space at the beginning of the skb | ||
1194 | * and allocs a new SKB if necessary | ||
1195 | */ | ||
1196 | static unsigned char *isdn_ppp_skb_push(struct sk_buff **skb_p,int len) | ||
1197 | { | ||
1198 | struct sk_buff *skb = *skb_p; | ||
1199 | |||
1200 | if(skb_headroom(skb) < len) { | ||
1201 | struct sk_buff *nskb = skb_realloc_headroom(skb, len); | ||
1202 | |||
1203 | if (!nskb) { | ||
1204 | printk(KERN_ERR "isdn_ppp_skb_push: can't realloc headroom!\n"); | ||
1205 | dev_kfree_skb(skb); | ||
1206 | return NULL; | ||
1207 | } | ||
1208 | printk(KERN_DEBUG "isdn_ppp_skb_push:under %d %d\n",skb_headroom(skb),len); | ||
1209 | dev_kfree_skb(skb); | ||
1210 | *skb_p = nskb; | ||
1211 | return skb_push(nskb, len); | ||
1212 | } | ||
1213 | return skb_push(skb,len); | ||
1214 | } | ||
1215 | |||
1216 | /* | ||
1217 | * send ppp frame .. we expect a PIDCOMPressable proto -- | ||
1218 | * (here: currently always PPP_IP,PPP_VJC_COMP,PPP_VJC_UNCOMP) | ||
1219 | * | ||
1220 | * VJ compression may change skb pointer!!! .. requeue with old | ||
1221 | * skb isn't allowed!! | ||
1222 | */ | ||
1223 | |||
1224 | int | ||
1225 | isdn_ppp_xmit(struct sk_buff *skb, struct net_device *netdev) | ||
1226 | { | ||
1227 | isdn_net_local *lp,*mlp; | ||
1228 | isdn_net_dev *nd; | ||
1229 | unsigned int proto = PPP_IP; /* 0x21 */ | ||
1230 | struct ippp_struct *ipt,*ipts; | ||
1231 | int slot, retval = 0; | ||
1232 | |||
1233 | mlp = (isdn_net_local *) (netdev->priv); | ||
1234 | nd = mlp->netdev; /* get master lp */ | ||
1235 | |||
1236 | slot = mlp->ppp_slot; | ||
1237 | if (slot < 0 || slot >= ISDN_MAX_CHANNELS) { | ||
1238 | printk(KERN_ERR "isdn_ppp_xmit: lp->ppp_slot(%d)\n", | ||
1239 | mlp->ppp_slot); | ||
1240 | kfree_skb(skb); | ||
1241 | goto out; | ||
1242 | } | ||
1243 | ipts = ippp_table[slot]; | ||
1244 | |||
1245 | if (!(ipts->pppcfg & SC_ENABLE_IP)) { /* PPP connected ? */ | ||
1246 | if (ipts->debug & 0x1) | ||
1247 | printk(KERN_INFO "%s: IP frame delayed.\n", netdev->name); | ||
1248 | retval = 1; | ||
1249 | goto out; | ||
1250 | } | ||
1251 | |||
1252 | switch (ntohs(skb->protocol)) { | ||
1253 | case ETH_P_IP: | ||
1254 | proto = PPP_IP; | ||
1255 | break; | ||
1256 | case ETH_P_IPX: | ||
1257 | proto = PPP_IPX; /* untested */ | ||
1258 | break; | ||
1259 | default: | ||
1260 | printk(KERN_ERR "isdn_ppp: skipped unsupported protocol: %#x.\n", | ||
1261 | skb->protocol); | ||
1262 | dev_kfree_skb(skb); | ||
1263 | goto out; | ||
1264 | } | ||
1265 | |||
1266 | lp = isdn_net_get_locked_lp(nd); | ||
1267 | if (!lp) { | ||
1268 | printk(KERN_WARNING "%s: all channels busy - requeuing!\n", netdev->name); | ||
1269 | retval = 1; | ||
1270 | goto out; | ||
1271 | } | ||
1272 | /* we have our lp locked from now on */ | ||
1273 | |||
1274 | slot = lp->ppp_slot; | ||
1275 | if (slot < 0 || slot > ISDN_MAX_CHANNELS) { | ||
1276 | printk(KERN_ERR "isdn_ppp_xmit: lp->ppp_slot(%d)\n", | ||
1277 | lp->ppp_slot); | ||
1278 | kfree_skb(skb); | ||
1279 | goto unlock; | ||
1280 | } | ||
1281 | ipt = ippp_table[slot]; | ||
1282 | |||
1283 | /* | ||
1284 | * after this line .. requeueing in the device queue is no longer allowed!!! | ||
1285 | */ | ||
1286 | |||
1287 | /* Pull off the fake header we stuck on earlier to keep | ||
1288 | * the fragmentation code happy. | ||
1289 | */ | ||
1290 | skb_pull(skb,IPPP_MAX_HEADER); | ||
1291 | |||
1292 | #ifdef CONFIG_IPPP_FILTER | ||
1293 | /* check if we should pass this packet | ||
1294 | * the filter instructions are constructed assuming | ||
1295 | * a four-byte PPP header on each packet */ | ||
1296 | skb_push(skb, 4); | ||
1297 | |||
1298 | { | ||
1299 | u_int16_t *p = (u_int16_t *) skb->data; | ||
1300 | |||
1301 | *p++ = htons(4); /* indicate outbound in DLT_LINUX_SLL */ | ||
1302 | *p = htons(proto); | ||
1303 | } | ||
1304 | |||
1305 | if (ipt->pass_filter | ||
1306 | && sk_run_filter(skb, ipt->pass_filter, ipt->pass_len) == 0) { | ||
1307 | if (ipt->debug & 0x4) | ||
1308 | printk(KERN_DEBUG "IPPP: outbound frame filtered.\n"); | ||
1309 | kfree_skb(skb); | ||
1310 | goto unlock; | ||
1311 | } | ||
1312 | if (!(ipt->active_filter | ||
1313 | && sk_run_filter(skb, ipt->active_filter, | ||
1314 | ipt->active_len) == 0)) { | ||
1315 | if (ipt->debug & 0x4) | ||
1316 | printk(KERN_DEBUG "IPPP: link-active filter: reseting huptimer.\n"); | ||
1317 | lp->huptimer = 0; | ||
1318 | } | ||
1319 | skb_pull(skb, 4); | ||
1320 | #else /* CONFIG_IPPP_FILTER */ | ||
1321 | lp->huptimer = 0; | ||
1322 | #endif /* CONFIG_IPPP_FILTER */ | ||
1323 | |||
1324 | if (ipt->debug & 0x4) | ||
1325 | printk(KERN_DEBUG "xmit skb, len %d\n", (int) skb->len); | ||
1326 | if (ipts->debug & 0x40) | ||
1327 | isdn_ppp_frame_log("xmit0", skb->data, skb->len, 32,ipts->unit,lp->ppp_slot); | ||
1328 | |||
1329 | #ifdef CONFIG_ISDN_PPP_VJ | ||
1330 | if (proto == PPP_IP && ipts->pppcfg & SC_COMP_TCP) { /* ipts here? probably yes, but check this again */ | ||
1331 | struct sk_buff *new_skb; | ||
1332 | unsigned short hl; | ||
1333 | /* | ||
1334 | * we need to reserve enought space in front of | ||
1335 | * sk_buff. old call to dev_alloc_skb only reserved | ||
1336 | * 16 bytes, now we are looking what the driver want. | ||
1337 | */ | ||
1338 | hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen + IPPP_MAX_HEADER; | ||
1339 | /* | ||
1340 | * Note: hl might still be insufficient because the method | ||
1341 | * above does not account for a possibible MPPP slave channel | ||
1342 | * which had larger HL header space requirements than the | ||
1343 | * master. | ||
1344 | */ | ||
1345 | new_skb = alloc_skb(hl+skb->len, GFP_ATOMIC); | ||
1346 | if (new_skb) { | ||
1347 | u_char *buf; | ||
1348 | int pktlen; | ||
1349 | |||
1350 | skb_reserve(new_skb, hl); | ||
1351 | new_skb->dev = skb->dev; | ||
1352 | skb_put(new_skb, skb->len); | ||
1353 | buf = skb->data; | ||
1354 | |||
1355 | pktlen = slhc_compress(ipts->slcomp, skb->data, skb->len, new_skb->data, | ||
1356 | &buf, !(ipts->pppcfg & SC_NO_TCP_CCID)); | ||
1357 | |||
1358 | if (buf != skb->data) { | ||
1359 | if (new_skb->data != buf) | ||
1360 | printk(KERN_ERR "isdn_ppp: FATAL error after slhc_compress!!\n"); | ||
1361 | dev_kfree_skb(skb); | ||
1362 | skb = new_skb; | ||
1363 | } else { | ||
1364 | dev_kfree_skb(new_skb); | ||
1365 | } | ||
1366 | |||
1367 | skb_trim(skb, pktlen); | ||
1368 | if (skb->data[0] & SL_TYPE_COMPRESSED_TCP) { /* cslip? style -> PPP */ | ||
1369 | proto = PPP_VJC_COMP; | ||
1370 | skb->data[0] ^= SL_TYPE_COMPRESSED_TCP; | ||
1371 | } else { | ||
1372 | if (skb->data[0] >= SL_TYPE_UNCOMPRESSED_TCP) | ||
1373 | proto = PPP_VJC_UNCOMP; | ||
1374 | skb->data[0] = (skb->data[0] & 0x0f) | 0x40; | ||
1375 | } | ||
1376 | } | ||
1377 | } | ||
1378 | #endif | ||
1379 | |||
1380 | /* | ||
1381 | * normal (single link) or bundle compression | ||
1382 | */ | ||
1383 | if(ipts->compflags & SC_COMP_ON) { | ||
1384 | /* We send compressed only if both down- und upstream | ||
1385 | compression is negotiated, that means, CCP is up */ | ||
1386 | if(ipts->compflags & SC_DECOMP_ON) { | ||
1387 | skb = isdn_ppp_compress(skb,&proto,ipt,ipts,0); | ||
1388 | } else { | ||
1389 | printk(KERN_DEBUG "isdn_ppp: CCP not yet up - sending as-is\n"); | ||
1390 | } | ||
1391 | } | ||
1392 | |||
1393 | if (ipt->debug & 0x24) | ||
1394 | printk(KERN_DEBUG "xmit2 skb, len %d, proto %04x\n", (int) skb->len, proto); | ||
1395 | |||
1396 | #ifdef CONFIG_ISDN_MPP | ||
1397 | if (ipt->mpppcfg & SC_MP_PROT) { | ||
1398 | /* we get mp_seqno from static isdn_net_local */ | ||
1399 | long mp_seqno = ipts->mp_seqno; | ||
1400 | ipts->mp_seqno++; | ||
1401 | if (ipt->mpppcfg & SC_OUT_SHORT_SEQ) { | ||
1402 | unsigned char *data = isdn_ppp_skb_push(&skb, 3); | ||
1403 | if(!data) | ||
1404 | goto unlock; | ||
1405 | mp_seqno &= 0xfff; | ||
1406 | data[0] = MP_BEGIN_FRAG | MP_END_FRAG | ((mp_seqno >> 8) & 0xf); /* (B)egin & (E)ndbit .. */ | ||
1407 | data[1] = mp_seqno & 0xff; | ||
1408 | data[2] = proto; /* PID compression */ | ||
1409 | } else { | ||
1410 | unsigned char *data = isdn_ppp_skb_push(&skb, 5); | ||
1411 | if(!data) | ||
1412 | goto unlock; | ||
1413 | data[0] = MP_BEGIN_FRAG | MP_END_FRAG; /* (B)egin & (E)ndbit .. */ | ||
1414 | data[1] = (mp_seqno >> 16) & 0xff; /* sequence number: 24bit */ | ||
1415 | data[2] = (mp_seqno >> 8) & 0xff; | ||
1416 | data[3] = (mp_seqno >> 0) & 0xff; | ||
1417 | data[4] = proto; /* PID compression */ | ||
1418 | } | ||
1419 | proto = PPP_MP; /* MP Protocol, 0x003d */ | ||
1420 | } | ||
1421 | #endif | ||
1422 | |||
1423 | /* | ||
1424 | * 'link in bundle' compression ... | ||
1425 | */ | ||
1426 | if(ipt->compflags & SC_LINK_COMP_ON) | ||
1427 | skb = isdn_ppp_compress(skb,&proto,ipt,ipts,1); | ||
1428 | |||
1429 | if( (ipt->pppcfg & SC_COMP_PROT) && (proto <= 0xff) ) { | ||
1430 | unsigned char *data = isdn_ppp_skb_push(&skb,1); | ||
1431 | if(!data) | ||
1432 | goto unlock; | ||
1433 | data[0] = proto & 0xff; | ||
1434 | } | ||
1435 | else { | ||
1436 | unsigned char *data = isdn_ppp_skb_push(&skb,2); | ||
1437 | if(!data) | ||
1438 | goto unlock; | ||
1439 | data[0] = (proto >> 8) & 0xff; | ||
1440 | data[1] = proto & 0xff; | ||
1441 | } | ||
1442 | if(!(ipt->pppcfg & SC_COMP_AC)) { | ||
1443 | unsigned char *data = isdn_ppp_skb_push(&skb,2); | ||
1444 | if(!data) | ||
1445 | goto unlock; | ||
1446 | data[0] = 0xff; /* All Stations */ | ||
1447 | data[1] = 0x03; /* Unnumbered information */ | ||
1448 | } | ||
1449 | |||
1450 | /* tx-stats are now updated via BSENT-callback */ | ||
1451 | |||
1452 | if (ipts->debug & 0x40) { | ||
1453 | printk(KERN_DEBUG "skb xmit: len: %d\n", (int) skb->len); | ||
1454 | isdn_ppp_frame_log("xmit", skb->data, skb->len, 32,ipt->unit,lp->ppp_slot); | ||
1455 | } | ||
1456 | |||
1457 | isdn_net_writebuf_skb(lp, skb); | ||
1458 | |||
1459 | unlock: | ||
1460 | spin_unlock_bh(&lp->xmit_lock); | ||
1461 | out: | ||
1462 | return retval; | ||
1463 | } | ||
1464 | |||
1465 | #ifdef CONFIG_IPPP_FILTER | ||
1466 | /* | ||
1467 | * check if this packet may trigger auto-dial. | ||
1468 | */ | ||
1469 | |||
1470 | int isdn_ppp_autodial_filter(struct sk_buff *skb, isdn_net_local *lp) | ||
1471 | { | ||
1472 | struct ippp_struct *is = ippp_table[lp->ppp_slot]; | ||
1473 | u_int16_t proto; | ||
1474 | int drop = 0; | ||
1475 | |||
1476 | switch (ntohs(skb->protocol)) { | ||
1477 | case ETH_P_IP: | ||
1478 | proto = PPP_IP; | ||
1479 | break; | ||
1480 | case ETH_P_IPX: | ||
1481 | proto = PPP_IPX; | ||
1482 | break; | ||
1483 | default: | ||
1484 | printk(KERN_ERR "isdn_ppp_autodial_filter: unsupported protocol 0x%x.\n", | ||
1485 | skb->protocol); | ||
1486 | return 1; | ||
1487 | } | ||
1488 | |||
1489 | /* the filter instructions are constructed assuming | ||
1490 | * a four-byte PPP header on each packet. we have to | ||
1491 | * temporarily remove part of the fake header stuck on | ||
1492 | * earlier. | ||
1493 | */ | ||
1494 | skb_pull(skb, IPPP_MAX_HEADER - 4); | ||
1495 | |||
1496 | { | ||
1497 | u_int16_t *p = (u_int16_t *) skb->data; | ||
1498 | |||
1499 | *p++ = htons(4); /* indicate outbound in DLT_LINUX_SLL */ | ||
1500 | *p = htons(proto); | ||
1501 | } | ||
1502 | |||
1503 | drop |= is->pass_filter | ||
1504 | && sk_run_filter(skb, is->pass_filter, is->pass_len) == 0; | ||
1505 | drop |= is->active_filter | ||
1506 | && sk_run_filter(skb, is->active_filter, is->active_len) == 0; | ||
1507 | |||
1508 | skb_push(skb, IPPP_MAX_HEADER - 4); | ||
1509 | return drop; | ||
1510 | } | ||
1511 | #endif | ||
1512 | #ifdef CONFIG_ISDN_MPP | ||
1513 | |||
1514 | /* this is _not_ rfc1990 header, but something we convert both short and long | ||
1515 | * headers to for convinience's sake: | ||
1516 | * byte 0 is flags as in rfc1990 | ||
1517 | * bytes 1...4 is 24-bit seqence number converted to host byte order | ||
1518 | */ | ||
1519 | #define MP_HEADER_LEN 5 | ||
1520 | |||
1521 | #define MP_LONGSEQ_MASK 0x00ffffff | ||
1522 | #define MP_SHORTSEQ_MASK 0x00000fff | ||
1523 | #define MP_LONGSEQ_MAX MP_LONGSEQ_MASK | ||
1524 | #define MP_SHORTSEQ_MAX MP_SHORTSEQ_MASK | ||
1525 | #define MP_LONGSEQ_MAXBIT ((MP_LONGSEQ_MASK+1)>>1) | ||
1526 | #define MP_SHORTSEQ_MAXBIT ((MP_SHORTSEQ_MASK+1)>>1) | ||
1527 | |||
1528 | /* sequence-wrap safe comparisions (for long sequence)*/ | ||
1529 | #define MP_LT(a,b) ((a-b)&MP_LONGSEQ_MAXBIT) | ||
1530 | #define MP_LE(a,b) !((b-a)&MP_LONGSEQ_MAXBIT) | ||
1531 | #define MP_GT(a,b) ((b-a)&MP_LONGSEQ_MAXBIT) | ||
1532 | #define MP_GE(a,b) !((a-b)&MP_LONGSEQ_MAXBIT) | ||
1533 | |||
1534 | #define MP_SEQ(f) ((*(u32*)(f->data+1))) | ||
1535 | #define MP_FLAGS(f) (f->data[0]) | ||
1536 | |||
1537 | static int isdn_ppp_mp_bundle_array_init(void) | ||
1538 | { | ||
1539 | int i; | ||
1540 | int sz = ISDN_MAX_CHANNELS*sizeof(ippp_bundle); | ||
1541 | if( (isdn_ppp_bundle_arr = (ippp_bundle*)kmalloc(sz, | ||
1542 | GFP_KERNEL)) == NULL ) | ||
1543 | return -ENOMEM; | ||
1544 | memset(isdn_ppp_bundle_arr, 0, sz); | ||
1545 | for( i = 0; i < ISDN_MAX_CHANNELS; i++ ) | ||
1546 | spin_lock_init(&isdn_ppp_bundle_arr[i].lock); | ||
1547 | return 0; | ||
1548 | } | ||
1549 | |||
1550 | static ippp_bundle * isdn_ppp_mp_bundle_alloc(void) | ||
1551 | { | ||
1552 | int i; | ||
1553 | for( i = 0; i < ISDN_MAX_CHANNELS; i++ ) | ||
1554 | if (isdn_ppp_bundle_arr[i].ref_ct <= 0) | ||
1555 | return (isdn_ppp_bundle_arr + i); | ||
1556 | return NULL; | ||
1557 | } | ||
1558 | |||
1559 | static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to ) | ||
1560 | { | ||
1561 | struct ippp_struct * is; | ||
1562 | |||
1563 | if (lp->ppp_slot < 0) { | ||
1564 | printk(KERN_ERR "%s: lp->ppp_slot(%d) out of range\n", | ||
1565 | __FUNCTION__, lp->ppp_slot); | ||
1566 | return(-EINVAL); | ||
1567 | } | ||
1568 | |||
1569 | is = ippp_table[lp->ppp_slot]; | ||
1570 | if (add_to) { | ||
1571 | if( lp->netdev->pb ) | ||
1572 | lp->netdev->pb->ref_ct--; | ||
1573 | lp->netdev->pb = add_to; | ||
1574 | } else { /* first link in a bundle */ | ||
1575 | is->mp_seqno = 0; | ||
1576 | if ((lp->netdev->pb = isdn_ppp_mp_bundle_alloc()) == NULL) | ||
1577 | return -ENOMEM; | ||
1578 | lp->next = lp->last = lp; /* nobody else in a queue */ | ||
1579 | lp->netdev->pb->frags = NULL; | ||
1580 | lp->netdev->pb->frames = 0; | ||
1581 | lp->netdev->pb->seq = UINT_MAX; | ||
1582 | } | ||
1583 | lp->netdev->pb->ref_ct++; | ||
1584 | |||
1585 | is->last_link_seqno = 0; | ||
1586 | return 0; | ||
1587 | } | ||
1588 | |||
1589 | static u32 isdn_ppp_mp_get_seq( int short_seq, | ||
1590 | struct sk_buff * skb, u32 last_seq ); | ||
1591 | static struct sk_buff * isdn_ppp_mp_discard( ippp_bundle * mp, | ||
1592 | struct sk_buff * from, struct sk_buff * to ); | ||
1593 | static void isdn_ppp_mp_reassembly( isdn_net_dev * net_dev, isdn_net_local * lp, | ||
1594 | struct sk_buff * from, struct sk_buff * to ); | ||
1595 | static void isdn_ppp_mp_free_skb( ippp_bundle * mp, struct sk_buff * skb ); | ||
1596 | static void isdn_ppp_mp_print_recv_pkt( int slot, struct sk_buff * skb ); | ||
1597 | |||
1598 | static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, | ||
1599 | struct sk_buff *skb) | ||
1600 | { | ||
1601 | struct ippp_struct *is; | ||
1602 | isdn_net_local * lpq; | ||
1603 | ippp_bundle * mp; | ||
1604 | isdn_mppp_stats * stats; | ||
1605 | struct sk_buff * newfrag, * frag, * start, *nextf; | ||
1606 | u32 newseq, minseq, thisseq; | ||
1607 | unsigned long flags; | ||
1608 | int slot; | ||
1609 | |||
1610 | spin_lock_irqsave(&net_dev->pb->lock, flags); | ||
1611 | mp = net_dev->pb; | ||
1612 | stats = &mp->stats; | ||
1613 | slot = lp->ppp_slot; | ||
1614 | if (slot < 0 || slot > ISDN_MAX_CHANNELS) { | ||
1615 | printk(KERN_ERR "%s: lp->ppp_slot(%d)\n", | ||
1616 | __FUNCTION__, lp->ppp_slot); | ||
1617 | stats->frame_drops++; | ||
1618 | dev_kfree_skb(skb); | ||
1619 | spin_unlock_irqrestore(&mp->lock, flags); | ||
1620 | return; | ||
1621 | } | ||
1622 | is = ippp_table[slot]; | ||
1623 | if( ++mp->frames > stats->max_queue_len ) | ||
1624 | stats->max_queue_len = mp->frames; | ||
1625 | |||
1626 | if (is->debug & 0x8) | ||
1627 | isdn_ppp_mp_print_recv_pkt(lp->ppp_slot, skb); | ||
1628 | |||
1629 | newseq = isdn_ppp_mp_get_seq(is->mpppcfg & SC_IN_SHORT_SEQ, | ||
1630 | skb, is->last_link_seqno); | ||
1631 | |||
1632 | |||
1633 | /* if this packet seq # is less than last already processed one, | ||
1634 | * toss it right away, but check for sequence start case first | ||
1635 | */ | ||
1636 | if( mp->seq > MP_LONGSEQ_MAX && (newseq & MP_LONGSEQ_MAXBIT) ) { | ||
1637 | mp->seq = newseq; /* the first packet: required for | ||
1638 | * rfc1990 non-compliant clients -- | ||
1639 | * prevents constant packet toss */ | ||
1640 | } else if( MP_LT(newseq, mp->seq) ) { | ||
1641 | stats->frame_drops++; | ||
1642 | isdn_ppp_mp_free_skb(mp, skb); | ||
1643 | spin_unlock_irqrestore(&mp->lock, flags); | ||
1644 | return; | ||
1645 | } | ||
1646 | |||
1647 | /* find the minimum received sequence number over all links */ | ||
1648 | is->last_link_seqno = minseq = newseq; | ||
1649 | for (lpq = net_dev->queue;;) { | ||
1650 | slot = lpq->ppp_slot; | ||
1651 | if (slot < 0 || slot > ISDN_MAX_CHANNELS) { | ||
1652 | printk(KERN_ERR "%s: lpq->ppp_slot(%d)\n", | ||
1653 | __FUNCTION__, lpq->ppp_slot); | ||
1654 | } else { | ||
1655 | u32 lls = ippp_table[slot]->last_link_seqno; | ||
1656 | if (MP_LT(lls, minseq)) | ||
1657 | minseq = lls; | ||
1658 | } | ||
1659 | if ((lpq = lpq->next) == net_dev->queue) | ||
1660 | break; | ||
1661 | } | ||
1662 | if (MP_LT(minseq, mp->seq)) | ||
1663 | minseq = mp->seq; /* can't go beyond already processed | ||
1664 | * packets */ | ||
1665 | newfrag = skb; | ||
1666 | |||
1667 | /* if this new fragment is before the first one, then enqueue it now. */ | ||
1668 | if ((frag = mp->frags) == NULL || MP_LT(newseq, MP_SEQ(frag))) { | ||
1669 | newfrag->next = frag; | ||
1670 | mp->frags = frag = newfrag; | ||
1671 | newfrag = NULL; | ||
1672 | } | ||
1673 | |||
1674 | start = MP_FLAGS(frag) & MP_BEGIN_FRAG && | ||
1675 | MP_SEQ(frag) == mp->seq ? frag : NULL; | ||
1676 | |||
1677 | /* | ||
1678 | * main fragment traversing loop | ||
1679 | * | ||
1680 | * try to accomplish several tasks: | ||
1681 | * - insert new fragment into the proper sequence slot (once that's done | ||
1682 | * newfrag will be set to NULL) | ||
1683 | * - reassemble any complete fragment sequence (non-null 'start' | ||
1684 | * indicates there is a continguous sequence present) | ||
1685 | * - discard any incomplete sequences that are below minseq -- due | ||
1686 | * to the fact that sender always increment sequence number, if there | ||
1687 | * is an incomplete sequence below minseq, no new fragments would | ||
1688 | * come to complete such sequence and it should be discarded | ||
1689 | * | ||
1690 | * loop completes when we accomplished the following tasks: | ||
1691 | * - new fragment is inserted in the proper sequence ('newfrag' is | ||
1692 | * set to NULL) | ||
1693 | * - we hit a gap in the sequence, so no reassembly/processing is | ||
1694 | * possible ('start' would be set to NULL) | ||
1695 | * | ||
1696 | * algorightm for this code is derived from code in the book | ||
1697 | * 'PPP Design And Debugging' by James Carlson (Addison-Wesley) | ||
1698 | */ | ||
1699 | while (start != NULL || newfrag != NULL) { | ||
1700 | |||
1701 | thisseq = MP_SEQ(frag); | ||
1702 | nextf = frag->next; | ||
1703 | |||
1704 | /* drop any duplicate fragments */ | ||
1705 | if (newfrag != NULL && thisseq == newseq) { | ||
1706 | isdn_ppp_mp_free_skb(mp, newfrag); | ||
1707 | newfrag = NULL; | ||
1708 | } | ||
1709 | |||
1710 | /* insert new fragment before next element if possible. */ | ||
1711 | if (newfrag != NULL && (nextf == NULL || | ||
1712 | MP_LT(newseq, MP_SEQ(nextf)))) { | ||
1713 | newfrag->next = nextf; | ||
1714 | frag->next = nextf = newfrag; | ||
1715 | newfrag = NULL; | ||
1716 | } | ||
1717 | |||
1718 | if (start != NULL) { | ||
1719 | /* check for misplaced start */ | ||
1720 | if (start != frag && (MP_FLAGS(frag) & MP_BEGIN_FRAG)) { | ||
1721 | printk(KERN_WARNING"isdn_mppp(seq %d): new " | ||
1722 | "BEGIN flag with no prior END", thisseq); | ||
1723 | stats->seqerrs++; | ||
1724 | stats->frame_drops++; | ||
1725 | start = isdn_ppp_mp_discard(mp, start,frag); | ||
1726 | nextf = frag->next; | ||
1727 | } | ||
1728 | } else if (MP_LE(thisseq, minseq)) { | ||
1729 | if (MP_FLAGS(frag) & MP_BEGIN_FRAG) | ||
1730 | start = frag; | ||
1731 | else { | ||
1732 | if (MP_FLAGS(frag) & MP_END_FRAG) | ||
1733 | stats->frame_drops++; | ||
1734 | if( mp->frags == frag ) | ||
1735 | mp->frags = nextf; | ||
1736 | isdn_ppp_mp_free_skb(mp, frag); | ||
1737 | frag = nextf; | ||
1738 | continue; | ||
1739 | } | ||
1740 | } | ||
1741 | |||
1742 | /* if start is non-null and we have end fragment, then | ||
1743 | * we have full reassembly sequence -- reassemble | ||
1744 | * and process packet now | ||
1745 | */ | ||
1746 | if (start != NULL && (MP_FLAGS(frag) & MP_END_FRAG)) { | ||
1747 | minseq = mp->seq = (thisseq+1) & MP_LONGSEQ_MASK; | ||
1748 | /* Reassemble the packet then dispatch it */ | ||
1749 | isdn_ppp_mp_reassembly(net_dev, lp, start, nextf); | ||
1750 | |||
1751 | start = NULL; | ||
1752 | frag = NULL; | ||
1753 | |||
1754 | mp->frags = nextf; | ||
1755 | } | ||
1756 | |||
1757 | /* check if need to update start pointer: if we just | ||
1758 | * reassembled the packet and sequence is contiguous | ||
1759 | * then next fragment should be the start of new reassembly | ||
1760 | * if sequence is contiguous, but we haven't reassembled yet, | ||
1761 | * keep going. | ||
1762 | * if sequence is not contiguous, either clear everyting | ||
1763 | * below low watermark and set start to the next frag or | ||
1764 | * clear start ptr. | ||
1765 | */ | ||
1766 | if (nextf != NULL && | ||
1767 | ((thisseq+1) & MP_LONGSEQ_MASK) == MP_SEQ(nextf)) { | ||
1768 | /* if we just reassembled and the next one is here, | ||
1769 | * then start another reassembly. */ | ||
1770 | |||
1771 | if (frag == NULL) { | ||
1772 | if (MP_FLAGS(nextf) & MP_BEGIN_FRAG) | ||
1773 | start = nextf; | ||
1774 | else | ||
1775 | { | ||
1776 | printk(KERN_WARNING"isdn_mppp(seq %d):" | ||
1777 | " END flag with no following " | ||
1778 | "BEGIN", thisseq); | ||
1779 | stats->seqerrs++; | ||
1780 | } | ||
1781 | } | ||
1782 | |||
1783 | } else { | ||
1784 | if ( nextf != NULL && frag != NULL && | ||
1785 | MP_LT(thisseq, minseq)) { | ||
1786 | /* we've got a break in the sequence | ||
1787 | * and we not at the end yet | ||
1788 | * and we did not just reassembled | ||
1789 | *(if we did, there wouldn't be anything before) | ||
1790 | * and we below the low watermark | ||
1791 | * discard all the frames below low watermark | ||
1792 | * and start over */ | ||
1793 | stats->frame_drops++; | ||
1794 | mp->frags = isdn_ppp_mp_discard(mp,start,nextf); | ||
1795 | } | ||
1796 | /* break in the sequence, no reassembly */ | ||
1797 | start = NULL; | ||
1798 | } | ||
1799 | |||
1800 | frag = nextf; | ||
1801 | } /* while -- main loop */ | ||
1802 | |||
1803 | if (mp->frags == NULL) | ||
1804 | mp->frags = frag; | ||
1805 | |||
1806 | /* rather straighforward way to deal with (not very) possible | ||
1807 | * queue overflow */ | ||
1808 | if (mp->frames > MP_MAX_QUEUE_LEN) { | ||
1809 | stats->overflows++; | ||
1810 | while (mp->frames > MP_MAX_QUEUE_LEN) { | ||
1811 | frag = mp->frags->next; | ||
1812 | isdn_ppp_mp_free_skb(mp, mp->frags); | ||
1813 | mp->frags = frag; | ||
1814 | } | ||
1815 | } | ||
1816 | spin_unlock_irqrestore(&mp->lock, flags); | ||
1817 | } | ||
1818 | |||
1819 | static void isdn_ppp_mp_cleanup( isdn_net_local * lp ) | ||
1820 | { | ||
1821 | struct sk_buff * frag = lp->netdev->pb->frags; | ||
1822 | struct sk_buff * nextfrag; | ||
1823 | while( frag ) { | ||
1824 | nextfrag = frag->next; | ||
1825 | isdn_ppp_mp_free_skb(lp->netdev->pb, frag); | ||
1826 | frag = nextfrag; | ||
1827 | } | ||
1828 | lp->netdev->pb->frags = NULL; | ||
1829 | } | ||
1830 | |||
1831 | static u32 isdn_ppp_mp_get_seq( int short_seq, | ||
1832 | struct sk_buff * skb, u32 last_seq ) | ||
1833 | { | ||
1834 | u32 seq; | ||
1835 | int flags = skb->data[0] & (MP_BEGIN_FRAG | MP_END_FRAG); | ||
1836 | |||
1837 | if( !short_seq ) | ||
1838 | { | ||
1839 | seq = ntohl(*(u32*)skb->data) & MP_LONGSEQ_MASK; | ||
1840 | skb_push(skb,1); | ||
1841 | } | ||
1842 | else | ||
1843 | { | ||
1844 | /* convert 12-bit short seq number to 24-bit long one | ||
1845 | */ | ||
1846 | seq = ntohs(*(u16*)skb->data) & MP_SHORTSEQ_MASK; | ||
1847 | |||
1848 | /* check for seqence wrap */ | ||
1849 | if( !(seq & MP_SHORTSEQ_MAXBIT) && | ||
1850 | (last_seq & MP_SHORTSEQ_MAXBIT) && | ||
1851 | (unsigned long)last_seq <= MP_LONGSEQ_MAX ) | ||
1852 | seq |= (last_seq + MP_SHORTSEQ_MAX+1) & | ||
1853 | (~MP_SHORTSEQ_MASK & MP_LONGSEQ_MASK); | ||
1854 | else | ||
1855 | seq |= last_seq & (~MP_SHORTSEQ_MASK & MP_LONGSEQ_MASK); | ||
1856 | |||
1857 | skb_push(skb, 3); /* put converted seqence back in skb */ | ||
1858 | } | ||
1859 | *(u32*)(skb->data+1) = seq; /* put seqence back in _host_ byte | ||
1860 | * order */ | ||
1861 | skb->data[0] = flags; /* restore flags */ | ||
1862 | return seq; | ||
1863 | } | ||
1864 | |||
1865 | struct sk_buff * isdn_ppp_mp_discard( ippp_bundle * mp, | ||
1866 | struct sk_buff * from, struct sk_buff * to ) | ||
1867 | { | ||
1868 | if( from ) | ||
1869 | while (from != to) { | ||
1870 | struct sk_buff * next = from->next; | ||
1871 | isdn_ppp_mp_free_skb(mp, from); | ||
1872 | from = next; | ||
1873 | } | ||
1874 | return from; | ||
1875 | } | ||
1876 | |||
1877 | void isdn_ppp_mp_reassembly( isdn_net_dev * net_dev, isdn_net_local * lp, | ||
1878 | struct sk_buff * from, struct sk_buff * to ) | ||
1879 | { | ||
1880 | ippp_bundle * mp = net_dev->pb; | ||
1881 | int proto; | ||
1882 | struct sk_buff * skb; | ||
1883 | unsigned int tot_len; | ||
1884 | |||
1885 | if (lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS) { | ||
1886 | printk(KERN_ERR "%s: lp->ppp_slot(%d) out of range\n", | ||
1887 | __FUNCTION__, lp->ppp_slot); | ||
1888 | return; | ||
1889 | } | ||
1890 | if( MP_FLAGS(from) == (MP_BEGIN_FRAG | MP_END_FRAG) ) { | ||
1891 | if( ippp_table[lp->ppp_slot]->debug & 0x40 ) | ||
1892 | printk(KERN_DEBUG "isdn_mppp: reassembly: frame %d, " | ||
1893 | "len %d\n", MP_SEQ(from), from->len ); | ||
1894 | skb = from; | ||
1895 | skb_pull(skb, MP_HEADER_LEN); | ||
1896 | mp->frames--; | ||
1897 | } else { | ||
1898 | struct sk_buff * frag; | ||
1899 | int n; | ||
1900 | |||
1901 | for(tot_len=n=0, frag=from; frag != to; frag=frag->next, n++) | ||
1902 | tot_len += frag->len - MP_HEADER_LEN; | ||
1903 | |||
1904 | if( ippp_table[lp->ppp_slot]->debug & 0x40 ) | ||
1905 | printk(KERN_DEBUG"isdn_mppp: reassembling frames %d " | ||
1906 | "to %d, len %d\n", MP_SEQ(from), | ||
1907 | (MP_SEQ(from)+n-1) & MP_LONGSEQ_MASK, tot_len ); | ||
1908 | if( (skb = dev_alloc_skb(tot_len)) == NULL ) { | ||
1909 | printk(KERN_ERR "isdn_mppp: cannot allocate sk buff " | ||
1910 | "of size %d\n", tot_len); | ||
1911 | isdn_ppp_mp_discard(mp, from, to); | ||
1912 | return; | ||
1913 | } | ||
1914 | |||
1915 | while( from != to ) { | ||
1916 | unsigned int len = from->len - MP_HEADER_LEN; | ||
1917 | |||
1918 | memcpy(skb_put(skb,len), from->data+MP_HEADER_LEN, len); | ||
1919 | frag = from->next; | ||
1920 | isdn_ppp_mp_free_skb(mp, from); | ||
1921 | from = frag; | ||
1922 | } | ||
1923 | } | ||
1924 | proto = isdn_ppp_strip_proto(skb); | ||
1925 | isdn_ppp_push_higher(net_dev, lp, skb, proto); | ||
1926 | } | ||
1927 | |||
1928 | static void isdn_ppp_mp_free_skb(ippp_bundle * mp, struct sk_buff * skb) | ||
1929 | { | ||
1930 | dev_kfree_skb(skb); | ||
1931 | mp->frames--; | ||
1932 | } | ||
1933 | |||
1934 | static void isdn_ppp_mp_print_recv_pkt( int slot, struct sk_buff * skb ) | ||
1935 | { | ||
1936 | printk(KERN_DEBUG "mp_recv: %d/%d -> %02x %02x %02x %02x %02x %02x\n", | ||
1937 | slot, (int) skb->len, | ||
1938 | (int) skb->data[0], (int) skb->data[1], (int) skb->data[2], | ||
1939 | (int) skb->data[3], (int) skb->data[4], (int) skb->data[5]); | ||
1940 | } | ||
1941 | |||
1942 | static int | ||
1943 | isdn_ppp_bundle(struct ippp_struct *is, int unit) | ||
1944 | { | ||
1945 | char ifn[IFNAMSIZ + 1]; | ||
1946 | isdn_net_dev *p; | ||
1947 | isdn_net_local *lp, *nlp; | ||
1948 | int rc; | ||
1949 | unsigned long flags; | ||
1950 | |||
1951 | sprintf(ifn, "ippp%d", unit); | ||
1952 | p = isdn_net_findif(ifn); | ||
1953 | if (!p) { | ||
1954 | printk(KERN_ERR "ippp_bundle: cannot find %s\n", ifn); | ||
1955 | return -EINVAL; | ||
1956 | } | ||
1957 | |||
1958 | spin_lock_irqsave(&p->pb->lock, flags); | ||
1959 | |||
1960 | nlp = is->lp; | ||
1961 | lp = p->queue; | ||
1962 | if( nlp->ppp_slot < 0 || nlp->ppp_slot >= ISDN_MAX_CHANNELS || | ||
1963 | lp->ppp_slot < 0 || lp->ppp_slot >= ISDN_MAX_CHANNELS ) { | ||
1964 | printk(KERN_ERR "ippp_bundle: binding to invalid slot %d\n", | ||
1965 | nlp->ppp_slot < 0 || nlp->ppp_slot >= ISDN_MAX_CHANNELS ? | ||
1966 | nlp->ppp_slot : lp->ppp_slot ); | ||
1967 | rc = -EINVAL; | ||
1968 | goto out; | ||
1969 | } | ||
1970 | |||
1971 | isdn_net_add_to_bundle(p, nlp); | ||
1972 | |||
1973 | ippp_table[nlp->ppp_slot]->unit = ippp_table[lp->ppp_slot]->unit; | ||
1974 | |||
1975 | /* maybe also SC_CCP stuff */ | ||
1976 | ippp_table[nlp->ppp_slot]->pppcfg |= ippp_table[lp->ppp_slot]->pppcfg & | ||
1977 | (SC_ENABLE_IP | SC_NO_TCP_CCID | SC_REJ_COMP_TCP); | ||
1978 | ippp_table[nlp->ppp_slot]->mpppcfg |= ippp_table[lp->ppp_slot]->mpppcfg & | ||
1979 | (SC_MP_PROT | SC_REJ_MP_PROT | SC_OUT_SHORT_SEQ | SC_IN_SHORT_SEQ); | ||
1980 | rc = isdn_ppp_mp_init(nlp, p->pb); | ||
1981 | out: | ||
1982 | spin_unlock_irqrestore(&p->pb->lock, flags); | ||
1983 | return rc; | ||
1984 | } | ||
1985 | |||
1986 | #endif /* CONFIG_ISDN_MPP */ | ||
1987 | |||
1988 | /* | ||
1989 | * network device ioctl handlers | ||
1990 | */ | ||
1991 | |||
1992 | static int | ||
1993 | isdn_ppp_dev_ioctl_stats(int slot, struct ifreq *ifr, struct net_device *dev) | ||
1994 | { | ||
1995 | struct ppp_stats __user *res = ifr->ifr_data; | ||
1996 | struct ppp_stats t; | ||
1997 | isdn_net_local *lp = (isdn_net_local *) dev->priv; | ||
1998 | |||
1999 | if (!access_ok(VERIFY_WRITE, res, sizeof(struct ppp_stats))) | ||
2000 | return -EFAULT; | ||
2001 | |||
2002 | /* build a temporary stat struct and copy it to user space */ | ||
2003 | |||
2004 | memset(&t, 0, sizeof(struct ppp_stats)); | ||
2005 | if (dev->flags & IFF_UP) { | ||
2006 | t.p.ppp_ipackets = lp->stats.rx_packets; | ||
2007 | t.p.ppp_ibytes = lp->stats.rx_bytes; | ||
2008 | t.p.ppp_ierrors = lp->stats.rx_errors; | ||
2009 | t.p.ppp_opackets = lp->stats.tx_packets; | ||
2010 | t.p.ppp_obytes = lp->stats.tx_bytes; | ||
2011 | t.p.ppp_oerrors = lp->stats.tx_errors; | ||
2012 | #ifdef CONFIG_ISDN_PPP_VJ | ||
2013 | if (slot >= 0 && ippp_table[slot]->slcomp) { | ||
2014 | struct slcompress *slcomp = ippp_table[slot]->slcomp; | ||
2015 | t.vj.vjs_packets = slcomp->sls_o_compressed + slcomp->sls_o_uncompressed; | ||
2016 | t.vj.vjs_compressed = slcomp->sls_o_compressed; | ||
2017 | t.vj.vjs_searches = slcomp->sls_o_searches; | ||
2018 | t.vj.vjs_misses = slcomp->sls_o_misses; | ||
2019 | t.vj.vjs_errorin = slcomp->sls_i_error; | ||
2020 | t.vj.vjs_tossed = slcomp->sls_i_tossed; | ||
2021 | t.vj.vjs_uncompressedin = slcomp->sls_i_uncompressed; | ||
2022 | t.vj.vjs_compressedin = slcomp->sls_i_compressed; | ||
2023 | } | ||
2024 | #endif | ||
2025 | } | ||
2026 | if (copy_to_user(res, &t, sizeof(struct ppp_stats))) | ||
2027 | return -EFAULT; | ||
2028 | return 0; | ||
2029 | } | ||
2030 | |||
2031 | int | ||
2032 | isdn_ppp_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) | ||
2033 | { | ||
2034 | int error=0; | ||
2035 | int len; | ||
2036 | isdn_net_local *lp = (isdn_net_local *) dev->priv; | ||
2037 | |||
2038 | |||
2039 | if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP) | ||
2040 | return -EINVAL; | ||
2041 | |||
2042 | switch (cmd) { | ||
2043 | #define PPP_VERSION "2.3.7" | ||
2044 | case SIOCGPPPVER: | ||
2045 | len = strlen(PPP_VERSION) + 1; | ||
2046 | if (copy_to_user(ifr->ifr_data, PPP_VERSION, len)) | ||
2047 | error = -EFAULT; | ||
2048 | break; | ||
2049 | |||
2050 | case SIOCGPPPSTATS: | ||
2051 | error = isdn_ppp_dev_ioctl_stats(lp->ppp_slot, ifr, dev); | ||
2052 | break; | ||
2053 | default: | ||
2054 | error = -EINVAL; | ||
2055 | break; | ||
2056 | } | ||
2057 | return error; | ||
2058 | } | ||
2059 | |||
2060 | static int | ||
2061 | isdn_ppp_if_get_unit(char *name) | ||
2062 | { | ||
2063 | int len, | ||
2064 | i, | ||
2065 | unit = 0, | ||
2066 | deci; | ||
2067 | |||
2068 | len = strlen(name); | ||
2069 | |||
2070 | if (strncmp("ippp", name, 4) || len > 8) | ||
2071 | return -1; | ||
2072 | |||
2073 | for (i = 0, deci = 1; i < len; i++, deci *= 10) { | ||
2074 | char a = name[len - i - 1]; | ||
2075 | if (a >= '0' && a <= '9') | ||
2076 | unit += (a - '0') * deci; | ||
2077 | else | ||
2078 | break; | ||
2079 | } | ||
2080 | if (!i || len - i != 4) | ||
2081 | unit = -1; | ||
2082 | |||
2083 | return unit; | ||
2084 | } | ||
2085 | |||
2086 | |||
2087 | int | ||
2088 | isdn_ppp_dial_slave(char *name) | ||
2089 | { | ||
2090 | #ifdef CONFIG_ISDN_MPP | ||
2091 | isdn_net_dev *ndev; | ||
2092 | isdn_net_local *lp; | ||
2093 | struct net_device *sdev; | ||
2094 | |||
2095 | if (!(ndev = isdn_net_findif(name))) | ||
2096 | return 1; | ||
2097 | lp = ndev->local; | ||
2098 | if (!(lp->flags & ISDN_NET_CONNECTED)) | ||
2099 | return 5; | ||
2100 | |||
2101 | sdev = lp->slave; | ||
2102 | while (sdev) { | ||
2103 | isdn_net_local *mlp = (isdn_net_local *) sdev->priv; | ||
2104 | if (!(mlp->flags & ISDN_NET_CONNECTED)) | ||
2105 | break; | ||
2106 | sdev = mlp->slave; | ||
2107 | } | ||
2108 | if (!sdev) | ||
2109 | return 2; | ||
2110 | |||
2111 | isdn_net_dial_req((isdn_net_local *) sdev->priv); | ||
2112 | return 0; | ||
2113 | #else | ||
2114 | return -1; | ||
2115 | #endif | ||
2116 | } | ||
2117 | |||
2118 | int | ||
2119 | isdn_ppp_hangup_slave(char *name) | ||
2120 | { | ||
2121 | #ifdef CONFIG_ISDN_MPP | ||
2122 | isdn_net_dev *ndev; | ||
2123 | isdn_net_local *lp; | ||
2124 | struct net_device *sdev; | ||
2125 | |||
2126 | if (!(ndev = isdn_net_findif(name))) | ||
2127 | return 1; | ||
2128 | lp = ndev->local; | ||
2129 | if (!(lp->flags & ISDN_NET_CONNECTED)) | ||
2130 | return 5; | ||
2131 | |||
2132 | sdev = lp->slave; | ||
2133 | while (sdev) { | ||
2134 | isdn_net_local *mlp = (isdn_net_local *) sdev->priv; | ||
2135 | |||
2136 | if (mlp->slave) { /* find last connected link in chain */ | ||
2137 | isdn_net_local *nlp = (isdn_net_local *) mlp->slave->priv; | ||
2138 | |||
2139 | if (!(nlp->flags & ISDN_NET_CONNECTED)) | ||
2140 | break; | ||
2141 | } else if (mlp->flags & ISDN_NET_CONNECTED) | ||
2142 | break; | ||
2143 | |||
2144 | sdev = mlp->slave; | ||
2145 | } | ||
2146 | if (!sdev) | ||
2147 | return 2; | ||
2148 | |||
2149 | isdn_net_hangup(sdev); | ||
2150 | return 0; | ||
2151 | #else | ||
2152 | return -1; | ||
2153 | #endif | ||
2154 | } | ||
2155 | |||
2156 | /* | ||
2157 | * PPP compression stuff | ||
2158 | */ | ||
2159 | |||
2160 | |||
2161 | /* Push an empty CCP Data Frame up to the daemon to wake it up and let it | ||
2162 | generate a CCP Reset-Request or tear down CCP altogether */ | ||
2163 | |||
2164 | static void isdn_ppp_ccp_kickup(struct ippp_struct *is) | ||
2165 | { | ||
2166 | isdn_ppp_fill_rq(NULL, 0, PPP_COMP, is->lp->ppp_slot); | ||
2167 | } | ||
2168 | |||
2169 | /* In-kernel handling of CCP Reset-Request and Reset-Ack is necessary, | ||
2170 | but absolutely nontrivial. The most abstruse problem we are facing is | ||
2171 | that the generation, reception and all the handling of timeouts and | ||
2172 | resends including proper request id management should be entirely left | ||
2173 | to the (de)compressor, but indeed is not covered by the current API to | ||
2174 | the (de)compressor. The API is a prototype version from PPP where only | ||
2175 | some (de)compressors have yet been implemented and all of them are | ||
2176 | rather simple in their reset handling. Especially, their is only one | ||
2177 | outstanding ResetAck at a time with all of them and ResetReq/-Acks do | ||
2178 | not have parameters. For this very special case it was sufficient to | ||
2179 | just return an error code from the decompressor and have a single | ||
2180 | reset() entry to communicate all the necessary information between | ||
2181 | the framework and the (de)compressor. Bad enough, LZS is different | ||
2182 | (and any other compressor may be different, too). It has multiple | ||
2183 | histories (eventually) and needs to Reset each of them independently | ||
2184 | and thus uses multiple outstanding Acks and history numbers as an | ||
2185 | additional parameter to Reqs/Acks. | ||
2186 | All that makes it harder to port the reset state engine into the | ||
2187 | kernel because it is not just the same simple one as in (i)pppd but | ||
2188 | it must be able to pass additional parameters and have multiple out- | ||
2189 | standing Acks. We are trying to achieve the impossible by handling | ||
2190 | reset transactions independent by their id. The id MUST change when | ||
2191 | the data portion changes, thus any (de)compressor who uses more than | ||
2192 | one resettable state must provide and recognize individual ids for | ||
2193 | each individual reset transaction. The framework itself does _only_ | ||
2194 | differentiate them by id, because it has no other semantics like the | ||
2195 | (de)compressor might. | ||
2196 | This looks like a major redesign of the interface would be nice, | ||
2197 | but I don't have an idea how to do it better. */ | ||
2198 | |||
2199 | /* Send a CCP Reset-Request or Reset-Ack directly from the kernel. This is | ||
2200 | getting that lengthy because there is no simple "send-this-frame-out" | ||
2201 | function above but every wrapper does a bit different. Hope I guess | ||
2202 | correct in this hack... */ | ||
2203 | |||
2204 | static void isdn_ppp_ccp_xmit_reset(struct ippp_struct *is, int proto, | ||
2205 | unsigned char code, unsigned char id, | ||
2206 | unsigned char *data, int len) | ||
2207 | { | ||
2208 | struct sk_buff *skb; | ||
2209 | unsigned char *p; | ||
2210 | int hl; | ||
2211 | int cnt = 0; | ||
2212 | isdn_net_local *lp = is->lp; | ||
2213 | |||
2214 | /* Alloc large enough skb */ | ||
2215 | hl = dev->drv[lp->isdn_device]->interface->hl_hdrlen; | ||
2216 | skb = alloc_skb(len + hl + 16,GFP_ATOMIC); | ||
2217 | if(!skb) { | ||
2218 | printk(KERN_WARNING | ||
2219 | "ippp: CCP cannot send reset - out of memory\n"); | ||
2220 | return; | ||
2221 | } | ||
2222 | skb_reserve(skb, hl); | ||
2223 | |||
2224 | /* We may need to stuff an address and control field first */ | ||
2225 | if(!(is->pppcfg & SC_COMP_AC)) { | ||
2226 | p = skb_put(skb, 2); | ||
2227 | *p++ = 0xff; | ||
2228 | *p++ = 0x03; | ||
2229 | } | ||
2230 | |||
2231 | /* Stuff proto, code, id and length */ | ||
2232 | p = skb_put(skb, 6); | ||
2233 | *p++ = (proto >> 8); | ||
2234 | *p++ = (proto & 0xff); | ||
2235 | *p++ = code; | ||
2236 | *p++ = id; | ||
2237 | cnt = 4 + len; | ||
2238 | *p++ = (cnt >> 8); | ||
2239 | *p++ = (cnt & 0xff); | ||
2240 | |||
2241 | /* Now stuff remaining bytes */ | ||
2242 | if(len) { | ||
2243 | p = skb_put(skb, len); | ||
2244 | memcpy(p, data, len); | ||
2245 | } | ||
2246 | |||
2247 | /* skb is now ready for xmit */ | ||
2248 | printk(KERN_DEBUG "Sending CCP Frame:\n"); | ||
2249 | isdn_ppp_frame_log("ccp-xmit", skb->data, skb->len, 32, is->unit,lp->ppp_slot); | ||
2250 | |||
2251 | isdn_net_write_super(lp, skb); | ||
2252 | } | ||
2253 | |||
2254 | /* Allocate the reset state vector */ | ||
2255 | static struct ippp_ccp_reset *isdn_ppp_ccp_reset_alloc(struct ippp_struct *is) | ||
2256 | { | ||
2257 | struct ippp_ccp_reset *r; | ||
2258 | r = kmalloc(sizeof(struct ippp_ccp_reset), GFP_KERNEL); | ||
2259 | if(!r) { | ||
2260 | printk(KERN_ERR "ippp_ccp: failed to allocate reset data" | ||
2261 | " structure - no mem\n"); | ||
2262 | return NULL; | ||
2263 | } | ||
2264 | memset(r, 0, sizeof(struct ippp_ccp_reset)); | ||
2265 | printk(KERN_DEBUG "ippp_ccp: allocated reset data structure %p\n", r); | ||
2266 | is->reset = r; | ||
2267 | return r; | ||
2268 | } | ||
2269 | |||
2270 | /* Destroy the reset state vector. Kill all pending timers first. */ | ||
2271 | static void isdn_ppp_ccp_reset_free(struct ippp_struct *is) | ||
2272 | { | ||
2273 | unsigned int id; | ||
2274 | |||
2275 | printk(KERN_DEBUG "ippp_ccp: freeing reset data structure %p\n", | ||
2276 | is->reset); | ||
2277 | for(id = 0; id < 256; id++) { | ||
2278 | if(is->reset->rs[id]) { | ||
2279 | isdn_ppp_ccp_reset_free_state(is, (unsigned char)id); | ||
2280 | } | ||
2281 | } | ||
2282 | kfree(is->reset); | ||
2283 | is->reset = NULL; | ||
2284 | } | ||
2285 | |||
2286 | /* Free a given state and clear everything up for later reallocation */ | ||
2287 | static void isdn_ppp_ccp_reset_free_state(struct ippp_struct *is, | ||
2288 | unsigned char id) | ||
2289 | { | ||
2290 | struct ippp_ccp_reset_state *rs; | ||
2291 | |||
2292 | if(is->reset->rs[id]) { | ||
2293 | printk(KERN_DEBUG "ippp_ccp: freeing state for id %d\n", id); | ||
2294 | rs = is->reset->rs[id]; | ||
2295 | /* Make sure the kernel will not call back later */ | ||
2296 | if(rs->ta) | ||
2297 | del_timer(&rs->timer); | ||
2298 | is->reset->rs[id] = NULL; | ||
2299 | kfree(rs); | ||
2300 | } else { | ||
2301 | printk(KERN_WARNING "ippp_ccp: id %d is not allocated\n", id); | ||
2302 | } | ||
2303 | } | ||
2304 | |||
2305 | /* The timer callback function which is called when a ResetReq has timed out, | ||
2306 | aka has never been answered by a ResetAck */ | ||
2307 | static void isdn_ppp_ccp_timer_callback(unsigned long closure) | ||
2308 | { | ||
2309 | struct ippp_ccp_reset_state *rs = | ||
2310 | (struct ippp_ccp_reset_state *)closure; | ||
2311 | |||
2312 | if(!rs) { | ||
2313 | printk(KERN_ERR "ippp_ccp: timer cb with zero closure.\n"); | ||
2314 | return; | ||
2315 | } | ||
2316 | if(rs->ta && rs->state == CCPResetSentReq) { | ||
2317 | /* We are correct here */ | ||
2318 | if(!rs->expra) { | ||
2319 | /* Hmm, there is no Ack really expected. We can clean | ||
2320 | up the state now, it will be reallocated if the | ||
2321 | decompressor insists on another reset */ | ||
2322 | rs->ta = 0; | ||
2323 | isdn_ppp_ccp_reset_free_state(rs->is, rs->id); | ||
2324 | return; | ||
2325 | } | ||
2326 | printk(KERN_DEBUG "ippp_ccp: CCP Reset timed out for id %d\n", | ||
2327 | rs->id); | ||
2328 | /* Push it again */ | ||
2329 | isdn_ppp_ccp_xmit_reset(rs->is, PPP_CCP, CCP_RESETREQ, rs->id, | ||
2330 | rs->data, rs->dlen); | ||
2331 | /* Restart timer */ | ||
2332 | rs->timer.expires = jiffies + HZ*5; | ||
2333 | add_timer(&rs->timer); | ||
2334 | } else { | ||
2335 | printk(KERN_WARNING "ippp_ccp: timer cb in wrong state %d\n", | ||
2336 | rs->state); | ||
2337 | } | ||
2338 | } | ||
2339 | |||
2340 | /* Allocate a new reset transaction state */ | ||
2341 | static struct ippp_ccp_reset_state *isdn_ppp_ccp_reset_alloc_state(struct ippp_struct *is, | ||
2342 | unsigned char id) | ||
2343 | { | ||
2344 | struct ippp_ccp_reset_state *rs; | ||
2345 | if(is->reset->rs[id]) { | ||
2346 | printk(KERN_WARNING "ippp_ccp: old state exists for id %d\n", | ||
2347 | id); | ||
2348 | return NULL; | ||
2349 | } else { | ||
2350 | rs = kmalloc(sizeof(struct ippp_ccp_reset_state), GFP_KERNEL); | ||
2351 | if(!rs) | ||
2352 | return NULL; | ||
2353 | memset(rs, 0, sizeof(struct ippp_ccp_reset_state)); | ||
2354 | rs->state = CCPResetIdle; | ||
2355 | rs->is = is; | ||
2356 | rs->id = id; | ||
2357 | rs->timer.data = (unsigned long)rs; | ||
2358 | rs->timer.function = isdn_ppp_ccp_timer_callback; | ||
2359 | is->reset->rs[id] = rs; | ||
2360 | } | ||
2361 | return rs; | ||
2362 | } | ||
2363 | |||
2364 | |||
2365 | /* A decompressor wants a reset with a set of parameters - do what is | ||
2366 | necessary to fulfill it */ | ||
2367 | static void isdn_ppp_ccp_reset_trans(struct ippp_struct *is, | ||
2368 | struct isdn_ppp_resetparams *rp) | ||
2369 | { | ||
2370 | struct ippp_ccp_reset_state *rs; | ||
2371 | |||
2372 | if(rp->valid) { | ||
2373 | /* The decompressor defines parameters by itself */ | ||
2374 | if(rp->rsend) { | ||
2375 | /* And he wants us to send a request */ | ||
2376 | if(!(rp->idval)) { | ||
2377 | printk(KERN_ERR "ippp_ccp: decompressor must" | ||
2378 | " specify reset id\n"); | ||
2379 | return; | ||
2380 | } | ||
2381 | if(is->reset->rs[rp->id]) { | ||
2382 | /* There is already a transaction in existence | ||
2383 | for this id. May be still waiting for a | ||
2384 | Ack or may be wrong. */ | ||
2385 | rs = is->reset->rs[rp->id]; | ||
2386 | if(rs->state == CCPResetSentReq && rs->ta) { | ||
2387 | printk(KERN_DEBUG "ippp_ccp: reset" | ||
2388 | " trans still in progress" | ||
2389 | " for id %d\n", rp->id); | ||
2390 | } else { | ||
2391 | printk(KERN_WARNING "ippp_ccp: reset" | ||
2392 | " trans in wrong state %d for" | ||
2393 | " id %d\n", rs->state, rp->id); | ||
2394 | } | ||
2395 | } else { | ||
2396 | /* Ok, this is a new transaction */ | ||
2397 | printk(KERN_DEBUG "ippp_ccp: new trans for id" | ||
2398 | " %d to be started\n", rp->id); | ||
2399 | rs = isdn_ppp_ccp_reset_alloc_state(is, rp->id); | ||
2400 | if(!rs) { | ||
2401 | printk(KERN_ERR "ippp_ccp: out of mem" | ||
2402 | " allocing ccp trans\n"); | ||
2403 | return; | ||
2404 | } | ||
2405 | rs->state = CCPResetSentReq; | ||
2406 | rs->expra = rp->expra; | ||
2407 | if(rp->dtval) { | ||
2408 | rs->dlen = rp->dlen; | ||
2409 | memcpy(rs->data, rp->data, rp->dlen); | ||
2410 | } | ||
2411 | /* HACK TODO - add link comp here */ | ||
2412 | isdn_ppp_ccp_xmit_reset(is, PPP_CCP, | ||
2413 | CCP_RESETREQ, rs->id, | ||
2414 | rs->data, rs->dlen); | ||
2415 | /* Start the timer */ | ||
2416 | rs->timer.expires = jiffies + 5*HZ; | ||
2417 | add_timer(&rs->timer); | ||
2418 | rs->ta = 1; | ||
2419 | } | ||
2420 | } else { | ||
2421 | printk(KERN_DEBUG "ippp_ccp: no reset sent\n"); | ||
2422 | } | ||
2423 | } else { | ||
2424 | /* The reset params are invalid. The decompressor does not | ||
2425 | care about them, so we just send the minimal requests | ||
2426 | and increase ids only when an Ack is received for a | ||
2427 | given id */ | ||
2428 | if(is->reset->rs[is->reset->lastid]) { | ||
2429 | /* There is already a transaction in existence | ||
2430 | for this id. May be still waiting for a | ||
2431 | Ack or may be wrong. */ | ||
2432 | rs = is->reset->rs[is->reset->lastid]; | ||
2433 | if(rs->state == CCPResetSentReq && rs->ta) { | ||
2434 | printk(KERN_DEBUG "ippp_ccp: reset" | ||
2435 | " trans still in progress" | ||
2436 | " for id %d\n", rp->id); | ||
2437 | } else { | ||
2438 | printk(KERN_WARNING "ippp_ccp: reset" | ||
2439 | " trans in wrong state %d for" | ||
2440 | " id %d\n", rs->state, rp->id); | ||
2441 | } | ||
2442 | } else { | ||
2443 | printk(KERN_DEBUG "ippp_ccp: new trans for id" | ||
2444 | " %d to be started\n", is->reset->lastid); | ||
2445 | rs = isdn_ppp_ccp_reset_alloc_state(is, | ||
2446 | is->reset->lastid); | ||
2447 | if(!rs) { | ||
2448 | printk(KERN_ERR "ippp_ccp: out of mem" | ||
2449 | " allocing ccp trans\n"); | ||
2450 | return; | ||
2451 | } | ||
2452 | rs->state = CCPResetSentReq; | ||
2453 | /* We always expect an Ack if the decompressor doesn't | ||
2454 | know better */ | ||
2455 | rs->expra = 1; | ||
2456 | rs->dlen = 0; | ||
2457 | /* HACK TODO - add link comp here */ | ||
2458 | isdn_ppp_ccp_xmit_reset(is, PPP_CCP, CCP_RESETREQ, | ||
2459 | rs->id, NULL, 0); | ||
2460 | /* Start the timer */ | ||
2461 | rs->timer.expires = jiffies + 5*HZ; | ||
2462 | add_timer(&rs->timer); | ||
2463 | rs->ta = 1; | ||
2464 | } | ||
2465 | } | ||
2466 | } | ||
2467 | |||
2468 | /* An Ack was received for this id. This means we stop the timer and clean | ||
2469 | up the state prior to calling the decompressors reset routine. */ | ||
2470 | static void isdn_ppp_ccp_reset_ack_rcvd(struct ippp_struct *is, | ||
2471 | unsigned char id) | ||
2472 | { | ||
2473 | struct ippp_ccp_reset_state *rs = is->reset->rs[id]; | ||
2474 | |||
2475 | if(rs) { | ||
2476 | if(rs->ta && rs->state == CCPResetSentReq) { | ||
2477 | /* Great, we are correct */ | ||
2478 | if(!rs->expra) | ||
2479 | printk(KERN_DEBUG "ippp_ccp: ResetAck received" | ||
2480 | " for id %d but not expected\n", id); | ||
2481 | } else { | ||
2482 | printk(KERN_INFO "ippp_ccp: ResetAck received out of" | ||
2483 | "sync for id %d\n", id); | ||
2484 | } | ||
2485 | if(rs->ta) { | ||
2486 | rs->ta = 0; | ||
2487 | del_timer(&rs->timer); | ||
2488 | } | ||
2489 | isdn_ppp_ccp_reset_free_state(is, id); | ||
2490 | } else { | ||
2491 | printk(KERN_INFO "ippp_ccp: ResetAck received for unknown id" | ||
2492 | " %d\n", id); | ||
2493 | } | ||
2494 | /* Make sure the simple reset stuff uses a new id next time */ | ||
2495 | is->reset->lastid++; | ||
2496 | } | ||
2497 | |||
2498 | /* | ||
2499 | * decompress packet | ||
2500 | * | ||
2501 | * if master = 0, we're trying to uncompress an per-link compressed packet, | ||
2502 | * as opposed to an compressed reconstructed-from-MPPP packet. | ||
2503 | * proto is updated to protocol field of uncompressed packet. | ||
2504 | * | ||
2505 | * retval: decompressed packet, | ||
2506 | * same packet if uncompressed, | ||
2507 | * NULL if decompression error | ||
2508 | */ | ||
2509 | |||
2510 | static struct sk_buff *isdn_ppp_decompress(struct sk_buff *skb,struct ippp_struct *is,struct ippp_struct *master, | ||
2511 | int *proto) | ||
2512 | { | ||
2513 | void *stat = NULL; | ||
2514 | struct isdn_ppp_compressor *ipc = NULL; | ||
2515 | struct sk_buff *skb_out; | ||
2516 | int len; | ||
2517 | struct ippp_struct *ri; | ||
2518 | struct isdn_ppp_resetparams rsparm; | ||
2519 | unsigned char rsdata[IPPP_RESET_MAXDATABYTES]; | ||
2520 | |||
2521 | if(!master) { | ||
2522 | // per-link decompression | ||
2523 | stat = is->link_decomp_stat; | ||
2524 | ipc = is->link_decompressor; | ||
2525 | ri = is; | ||
2526 | } else { | ||
2527 | stat = master->decomp_stat; | ||
2528 | ipc = master->decompressor; | ||
2529 | ri = master; | ||
2530 | } | ||
2531 | |||
2532 | if (!ipc) { | ||
2533 | // no decompressor -> we can't decompress. | ||
2534 | printk(KERN_DEBUG "ippp: no decompressor defined!\n"); | ||
2535 | return skb; | ||
2536 | } | ||
2537 | if (!stat) // if we have a compressor, stat has been set as well | ||
2538 | BUG(); | ||
2539 | |||
2540 | if((master && *proto == PPP_COMP) || (!master && *proto == PPP_COMPFRAG) ) { | ||
2541 | // compressed packets are compressed by their protocol type | ||
2542 | |||
2543 | // Set up reset params for the decompressor | ||
2544 | memset(&rsparm, 0, sizeof(rsparm)); | ||
2545 | rsparm.data = rsdata; | ||
2546 | rsparm.maxdlen = IPPP_RESET_MAXDATABYTES; | ||
2547 | |||
2548 | skb_out = dev_alloc_skb(is->mru + PPP_HDRLEN); | ||
2549 | len = ipc->decompress(stat, skb, skb_out, &rsparm); | ||
2550 | kfree_skb(skb); | ||
2551 | if (len <= 0) { | ||
2552 | switch(len) { | ||
2553 | case DECOMP_ERROR: | ||
2554 | printk(KERN_INFO "ippp: decomp wants reset %s params\n", | ||
2555 | rsparm.valid ? "with" : "without"); | ||
2556 | |||
2557 | isdn_ppp_ccp_reset_trans(ri, &rsparm); | ||
2558 | break; | ||
2559 | case DECOMP_FATALERROR: | ||
2560 | ri->pppcfg |= SC_DC_FERROR; | ||
2561 | /* Kick ipppd to recognize the error */ | ||
2562 | isdn_ppp_ccp_kickup(ri); | ||
2563 | break; | ||
2564 | } | ||
2565 | kfree_skb(skb_out); | ||
2566 | return NULL; | ||
2567 | } | ||
2568 | *proto = isdn_ppp_strip_proto(skb_out); | ||
2569 | if (*proto < 0) { | ||
2570 | kfree_skb(skb_out); | ||
2571 | return NULL; | ||
2572 | } | ||
2573 | return skb_out; | ||
2574 | } else { | ||
2575 | // uncompressed packets are fed through the decompressor to | ||
2576 | // update the decompressor state | ||
2577 | ipc->incomp(stat, skb, *proto); | ||
2578 | return skb; | ||
2579 | } | ||
2580 | } | ||
2581 | |||
2582 | /* | ||
2583 | * compress a frame | ||
2584 | * type=0: normal/bundle compression | ||
2585 | * =1: link compression | ||
2586 | * returns original skb if we haven't compressed the frame | ||
2587 | * and a new skb pointer if we've done it | ||
2588 | */ | ||
2589 | static struct sk_buff *isdn_ppp_compress(struct sk_buff *skb_in,int *proto, | ||
2590 | struct ippp_struct *is,struct ippp_struct *master,int type) | ||
2591 | { | ||
2592 | int ret; | ||
2593 | int new_proto; | ||
2594 | struct isdn_ppp_compressor *compressor; | ||
2595 | void *stat; | ||
2596 | struct sk_buff *skb_out; | ||
2597 | |||
2598 | /* we do not compress control protocols */ | ||
2599 | if(*proto < 0 || *proto > 0x3fff) { | ||
2600 | return skb_in; | ||
2601 | } | ||
2602 | |||
2603 | if(type) { /* type=1 => Link compression */ | ||
2604 | return skb_in; | ||
2605 | } | ||
2606 | else { | ||
2607 | if(!master) { | ||
2608 | compressor = is->compressor; | ||
2609 | stat = is->comp_stat; | ||
2610 | } | ||
2611 | else { | ||
2612 | compressor = master->compressor; | ||
2613 | stat = master->comp_stat; | ||
2614 | } | ||
2615 | new_proto = PPP_COMP; | ||
2616 | } | ||
2617 | |||
2618 | if(!compressor) { | ||
2619 | printk(KERN_ERR "isdn_ppp: No compressor set!\n"); | ||
2620 | return skb_in; | ||
2621 | } | ||
2622 | if(!stat) { | ||
2623 | printk(KERN_ERR "isdn_ppp: Compressor not initialized?\n"); | ||
2624 | return skb_in; | ||
2625 | } | ||
2626 | |||
2627 | /* Allow for at least 150 % expansion (for now) */ | ||
2628 | skb_out = alloc_skb(skb_in->len + skb_in->len/2 + 32 + | ||
2629 | skb_headroom(skb_in), GFP_ATOMIC); | ||
2630 | if(!skb_out) | ||
2631 | return skb_in; | ||
2632 | skb_reserve(skb_out, skb_headroom(skb_in)); | ||
2633 | |||
2634 | ret = (compressor->compress)(stat,skb_in,skb_out,*proto); | ||
2635 | if(!ret) { | ||
2636 | dev_kfree_skb(skb_out); | ||
2637 | return skb_in; | ||
2638 | } | ||
2639 | |||
2640 | dev_kfree_skb(skb_in); | ||
2641 | *proto = new_proto; | ||
2642 | return skb_out; | ||
2643 | } | ||
2644 | |||
2645 | /* | ||
2646 | * we received a CCP frame .. | ||
2647 | * not a clean solution, but we MUST handle a few cases in the kernel | ||
2648 | */ | ||
2649 | static void isdn_ppp_receive_ccp(isdn_net_dev *net_dev, isdn_net_local *lp, | ||
2650 | struct sk_buff *skb,int proto) | ||
2651 | { | ||
2652 | struct ippp_struct *is; | ||
2653 | struct ippp_struct *mis; | ||
2654 | int len; | ||
2655 | struct isdn_ppp_resetparams rsparm; | ||
2656 | unsigned char rsdata[IPPP_RESET_MAXDATABYTES]; | ||
2657 | |||
2658 | printk(KERN_DEBUG "Received CCP frame from peer slot(%d)\n", | ||
2659 | lp->ppp_slot); | ||
2660 | if (lp->ppp_slot < 0 || lp->ppp_slot > ISDN_MAX_CHANNELS) { | ||
2661 | printk(KERN_ERR "%s: lp->ppp_slot(%d) out of range\n", | ||
2662 | __FUNCTION__, lp->ppp_slot); | ||
2663 | return; | ||
2664 | } | ||
2665 | is = ippp_table[lp->ppp_slot]; | ||
2666 | isdn_ppp_frame_log("ccp-rcv", skb->data, skb->len, 32, is->unit,lp->ppp_slot); | ||
2667 | |||
2668 | if(lp->master) { | ||
2669 | int slot = ((isdn_net_local *) (lp->master->priv))->ppp_slot; | ||
2670 | if (slot < 0 || slot > ISDN_MAX_CHANNELS) { | ||
2671 | printk(KERN_ERR "%s: slot(%d) out of range\n", | ||
2672 | __FUNCTION__, slot); | ||
2673 | return; | ||
2674 | } | ||
2675 | mis = ippp_table[slot]; | ||
2676 | } else | ||
2677 | mis = is; | ||
2678 | |||
2679 | switch(skb->data[0]) { | ||
2680 | case CCP_CONFREQ: | ||
2681 | if(is->debug & 0x10) | ||
2682 | printk(KERN_DEBUG "Disable compression here!\n"); | ||
2683 | if(proto == PPP_CCP) | ||
2684 | mis->compflags &= ~SC_COMP_ON; | ||
2685 | else | ||
2686 | is->compflags &= ~SC_LINK_COMP_ON; | ||
2687 | break; | ||
2688 | case CCP_TERMREQ: | ||
2689 | case CCP_TERMACK: | ||
2690 | if(is->debug & 0x10) | ||
2691 | printk(KERN_DEBUG "Disable (de)compression here!\n"); | ||
2692 | if(proto == PPP_CCP) | ||
2693 | mis->compflags &= ~(SC_DECOMP_ON|SC_COMP_ON); | ||
2694 | else | ||
2695 | is->compflags &= ~(SC_LINK_DECOMP_ON|SC_LINK_COMP_ON); | ||
2696 | break; | ||
2697 | case CCP_CONFACK: | ||
2698 | /* if we RECEIVE an ackowledge we enable the decompressor */ | ||
2699 | if(is->debug & 0x10) | ||
2700 | printk(KERN_DEBUG "Enable decompression here!\n"); | ||
2701 | if(proto == PPP_CCP) { | ||
2702 | if (!mis->decompressor) | ||
2703 | break; | ||
2704 | mis->compflags |= SC_DECOMP_ON; | ||
2705 | } else { | ||
2706 | if (!is->decompressor) | ||
2707 | break; | ||
2708 | is->compflags |= SC_LINK_DECOMP_ON; | ||
2709 | } | ||
2710 | break; | ||
2711 | |||
2712 | case CCP_RESETACK: | ||
2713 | printk(KERN_DEBUG "Received ResetAck from peer\n"); | ||
2714 | len = (skb->data[2] << 8) | skb->data[3]; | ||
2715 | len -= 4; | ||
2716 | |||
2717 | if(proto == PPP_CCP) { | ||
2718 | /* If a reset Ack was outstanding for this id, then | ||
2719 | clean up the state engine */ | ||
2720 | isdn_ppp_ccp_reset_ack_rcvd(mis, skb->data[1]); | ||
2721 | if(mis->decompressor && mis->decomp_stat) | ||
2722 | mis->decompressor-> | ||
2723 | reset(mis->decomp_stat, | ||
2724 | skb->data[0], | ||
2725 | skb->data[1], | ||
2726 | len ? &skb->data[4] : NULL, | ||
2727 | len, NULL); | ||
2728 | /* TODO: This is not easy to decide here */ | ||
2729 | mis->compflags &= ~SC_DECOMP_DISCARD; | ||
2730 | } | ||
2731 | else { | ||
2732 | isdn_ppp_ccp_reset_ack_rcvd(is, skb->data[1]); | ||
2733 | if(is->link_decompressor && is->link_decomp_stat) | ||
2734 | is->link_decompressor-> | ||
2735 | reset(is->link_decomp_stat, | ||
2736 | skb->data[0], | ||
2737 | skb->data[1], | ||
2738 | len ? &skb->data[4] : NULL, | ||
2739 | len, NULL); | ||
2740 | /* TODO: neither here */ | ||
2741 | is->compflags &= ~SC_LINK_DECOMP_DISCARD; | ||
2742 | } | ||
2743 | break; | ||
2744 | |||
2745 | case CCP_RESETREQ: | ||
2746 | printk(KERN_DEBUG "Received ResetReq from peer\n"); | ||
2747 | /* Receiving a ResetReq means we must reset our compressor */ | ||
2748 | /* Set up reset params for the reset entry */ | ||
2749 | memset(&rsparm, 0, sizeof(rsparm)); | ||
2750 | rsparm.data = rsdata; | ||
2751 | rsparm.maxdlen = IPPP_RESET_MAXDATABYTES; | ||
2752 | /* Isolate data length */ | ||
2753 | len = (skb->data[2] << 8) | skb->data[3]; | ||
2754 | len -= 4; | ||
2755 | if(proto == PPP_CCP) { | ||
2756 | if(mis->compressor && mis->comp_stat) | ||
2757 | mis->compressor-> | ||
2758 | reset(mis->comp_stat, | ||
2759 | skb->data[0], | ||
2760 | skb->data[1], | ||
2761 | len ? &skb->data[4] : NULL, | ||
2762 | len, &rsparm); | ||
2763 | } | ||
2764 | else { | ||
2765 | if(is->link_compressor && is->link_comp_stat) | ||
2766 | is->link_compressor-> | ||
2767 | reset(is->link_comp_stat, | ||
2768 | skb->data[0], | ||
2769 | skb->data[1], | ||
2770 | len ? &skb->data[4] : NULL, | ||
2771 | len, &rsparm); | ||
2772 | } | ||
2773 | /* Ack the Req as specified by rsparm */ | ||
2774 | if(rsparm.valid) { | ||
2775 | /* Compressor reset handler decided how to answer */ | ||
2776 | if(rsparm.rsend) { | ||
2777 | /* We should send a Frame */ | ||
2778 | isdn_ppp_ccp_xmit_reset(is, proto, CCP_RESETACK, | ||
2779 | rsparm.idval ? rsparm.id | ||
2780 | : skb->data[1], | ||
2781 | rsparm.dtval ? | ||
2782 | rsparm.data : NULL, | ||
2783 | rsparm.dtval ? | ||
2784 | rsparm.dlen : 0); | ||
2785 | } else { | ||
2786 | printk(KERN_DEBUG "ResetAck suppressed\n"); | ||
2787 | } | ||
2788 | } else { | ||
2789 | /* We answer with a straight reflected Ack */ | ||
2790 | isdn_ppp_ccp_xmit_reset(is, proto, CCP_RESETACK, | ||
2791 | skb->data[1], | ||
2792 | len ? &skb->data[4] : NULL, | ||
2793 | len); | ||
2794 | } | ||
2795 | break; | ||
2796 | } | ||
2797 | } | ||
2798 | |||
2799 | |||
2800 | /* | ||
2801 | * Daemon sends a CCP frame ... | ||
2802 | */ | ||
2803 | |||
2804 | /* TODO: Clean this up with new Reset semantics */ | ||
2805 | |||
2806 | /* I believe the CCP handling as-is is done wrong. Compressed frames | ||
2807 | * should only be sent/received after CCP reaches UP state, which means | ||
2808 | * both sides have sent CONF_ACK. Currently, we handle both directions | ||
2809 | * independently, which means we may accept compressed frames too early | ||
2810 | * (supposedly not a problem), but may also mean we send compressed frames | ||
2811 | * too early, which may turn out to be a problem. | ||
2812 | * This part of state machine should actually be handled by (i)pppd, but | ||
2813 | * that's too big of a change now. --kai | ||
2814 | */ | ||
2815 | |||
2816 | /* Actually, we might turn this into an advantage: deal with the RFC in | ||
2817 | * the old tradition of beeing generous on what we accept, but beeing | ||
2818 | * strict on what we send. Thus we should just | ||
2819 | * - accept compressed frames as soon as decompression is negotiated | ||
2820 | * - send compressed frames only when decomp *and* comp are negotiated | ||
2821 | * - drop rx compressed frames if we cannot decomp (instead of pushing them | ||
2822 | * up to ipppd) | ||
2823 | * and I tried to modify this file according to that. --abp | ||
2824 | */ | ||
2825 | |||
2826 | static void isdn_ppp_send_ccp(isdn_net_dev *net_dev, isdn_net_local *lp, struct sk_buff *skb) | ||
2827 | { | ||
2828 | struct ippp_struct *mis,*is; | ||
2829 | int proto, slot = lp->ppp_slot; | ||
2830 | unsigned char *data; | ||
2831 | |||
2832 | if(!skb || skb->len < 3) | ||
2833 | return; | ||
2834 | if (slot < 0 || slot >= ISDN_MAX_CHANNELS) { | ||
2835 | printk(KERN_ERR "%s: lp->ppp_slot(%d) out of range\n", | ||
2836 | __FUNCTION__, slot); | ||
2837 | return; | ||
2838 | } | ||
2839 | is = ippp_table[slot]; | ||
2840 | /* Daemon may send with or without address and control field comp */ | ||
2841 | data = skb->data; | ||
2842 | if(!(is->pppcfg & SC_COMP_AC) && data[0] == 0xff && data[1] == 0x03) { | ||
2843 | data += 2; | ||
2844 | if(skb->len < 5) | ||
2845 | return; | ||
2846 | } | ||
2847 | |||
2848 | proto = ((int)data[0]<<8)+data[1]; | ||
2849 | if(proto != PPP_CCP && proto != PPP_CCPFRAG) | ||
2850 | return; | ||
2851 | |||
2852 | printk(KERN_DEBUG "Received CCP frame from daemon:\n"); | ||
2853 | isdn_ppp_frame_log("ccp-xmit", skb->data, skb->len, 32, is->unit,lp->ppp_slot); | ||
2854 | |||
2855 | if (lp->master) { | ||
2856 | slot = ((isdn_net_local *) (lp->master->priv))->ppp_slot; | ||
2857 | if (slot < 0 || slot > ISDN_MAX_CHANNELS) { | ||
2858 | printk(KERN_ERR "%s: slot(%d) out of range\n", | ||
2859 | __FUNCTION__, slot); | ||
2860 | return; | ||
2861 | } | ||
2862 | mis = ippp_table[slot]; | ||
2863 | } else | ||
2864 | mis = is; | ||
2865 | if (mis != is) | ||
2866 | printk(KERN_DEBUG "isdn_ppp: Ouch! Master CCP sends on slave slot!\n"); | ||
2867 | |||
2868 | switch(data[2]) { | ||
2869 | case CCP_CONFREQ: | ||
2870 | if(is->debug & 0x10) | ||
2871 | printk(KERN_DEBUG "Disable decompression here!\n"); | ||
2872 | if(proto == PPP_CCP) | ||
2873 | is->compflags &= ~SC_DECOMP_ON; | ||
2874 | else | ||
2875 | is->compflags &= ~SC_LINK_DECOMP_ON; | ||
2876 | break; | ||
2877 | case CCP_TERMREQ: | ||
2878 | case CCP_TERMACK: | ||
2879 | if(is->debug & 0x10) | ||
2880 | printk(KERN_DEBUG "Disable (de)compression here!\n"); | ||
2881 | if(proto == PPP_CCP) | ||
2882 | is->compflags &= ~(SC_DECOMP_ON|SC_COMP_ON); | ||
2883 | else | ||
2884 | is->compflags &= ~(SC_LINK_DECOMP_ON|SC_LINK_COMP_ON); | ||
2885 | break; | ||
2886 | case CCP_CONFACK: | ||
2887 | /* if we SEND an ackowledge we can/must enable the compressor */ | ||
2888 | if(is->debug & 0x10) | ||
2889 | printk(KERN_DEBUG "Enable compression here!\n"); | ||
2890 | if(proto == PPP_CCP) { | ||
2891 | if (!is->compressor) | ||
2892 | break; | ||
2893 | is->compflags |= SC_COMP_ON; | ||
2894 | } else { | ||
2895 | if (!is->compressor) | ||
2896 | break; | ||
2897 | is->compflags |= SC_LINK_COMP_ON; | ||
2898 | } | ||
2899 | break; | ||
2900 | case CCP_RESETACK: | ||
2901 | /* If we send a ACK we should reset our compressor */ | ||
2902 | if(is->debug & 0x10) | ||
2903 | printk(KERN_DEBUG "Reset decompression state here!\n"); | ||
2904 | printk(KERN_DEBUG "ResetAck from daemon passed by\n"); | ||
2905 | if(proto == PPP_CCP) { | ||
2906 | /* link to master? */ | ||
2907 | if(is->compressor && is->comp_stat) | ||
2908 | is->compressor->reset(is->comp_stat, 0, 0, | ||
2909 | NULL, 0, NULL); | ||
2910 | is->compflags &= ~SC_COMP_DISCARD; | ||
2911 | } | ||
2912 | else { | ||
2913 | if(is->link_compressor && is->link_comp_stat) | ||
2914 | is->link_compressor->reset(is->link_comp_stat, | ||
2915 | 0, 0, NULL, 0, NULL); | ||
2916 | is->compflags &= ~SC_LINK_COMP_DISCARD; | ||
2917 | } | ||
2918 | break; | ||
2919 | case CCP_RESETREQ: | ||
2920 | /* Just let it pass by */ | ||
2921 | printk(KERN_DEBUG "ResetReq from daemon passed by\n"); | ||
2922 | break; | ||
2923 | } | ||
2924 | } | ||
2925 | |||
2926 | int isdn_ppp_register_compressor(struct isdn_ppp_compressor *ipc) | ||
2927 | { | ||
2928 | ipc->next = ipc_head; | ||
2929 | ipc->prev = NULL; | ||
2930 | if(ipc_head) { | ||
2931 | ipc_head->prev = ipc; | ||
2932 | } | ||
2933 | ipc_head = ipc; | ||
2934 | return 0; | ||
2935 | } | ||
2936 | |||
2937 | int isdn_ppp_unregister_compressor(struct isdn_ppp_compressor *ipc) | ||
2938 | { | ||
2939 | if(ipc->prev) | ||
2940 | ipc->prev->next = ipc->next; | ||
2941 | else | ||
2942 | ipc_head = ipc->next; | ||
2943 | if(ipc->next) | ||
2944 | ipc->next->prev = ipc->prev; | ||
2945 | ipc->prev = ipc->next = NULL; | ||
2946 | return 0; | ||
2947 | } | ||
2948 | |||
2949 | static int isdn_ppp_set_compressor(struct ippp_struct *is, struct isdn_ppp_comp_data *data) | ||
2950 | { | ||
2951 | struct isdn_ppp_compressor *ipc = ipc_head; | ||
2952 | int ret; | ||
2953 | void *stat; | ||
2954 | int num = data->num; | ||
2955 | |||
2956 | if(is->debug & 0x10) | ||
2957 | printk(KERN_DEBUG "[%d] Set %s type %d\n",is->unit, | ||
2958 | (data->flags&IPPP_COMP_FLAG_XMIT)?"compressor":"decompressor",num); | ||
2959 | |||
2960 | /* If is has no valid reset state vector, we cannot allocate a | ||
2961 | decompressor. The decompressor would cause reset transactions | ||
2962 | sooner or later, and they need that vector. */ | ||
2963 | |||
2964 | if(!(data->flags & IPPP_COMP_FLAG_XMIT) && !is->reset) { | ||
2965 | printk(KERN_ERR "ippp_ccp: no reset data structure - can't" | ||
2966 | " allow decompression.\n"); | ||
2967 | return -ENOMEM; | ||
2968 | } | ||
2969 | |||
2970 | while(ipc) { | ||
2971 | if(ipc->num == num) { | ||
2972 | stat = ipc->alloc(data); | ||
2973 | if(stat) { | ||
2974 | ret = ipc->init(stat,data,is->unit,0); | ||
2975 | if(!ret) { | ||
2976 | printk(KERN_ERR "Can't init (de)compression!\n"); | ||
2977 | ipc->free(stat); | ||
2978 | stat = NULL; | ||
2979 | break; | ||
2980 | } | ||
2981 | } | ||
2982 | else { | ||
2983 | printk(KERN_ERR "Can't alloc (de)compression!\n"); | ||
2984 | break; | ||
2985 | } | ||
2986 | |||
2987 | if(data->flags & IPPP_COMP_FLAG_XMIT) { | ||
2988 | if(data->flags & IPPP_COMP_FLAG_LINK) { | ||
2989 | if(is->link_comp_stat) | ||
2990 | is->link_compressor->free(is->link_comp_stat); | ||
2991 | is->link_comp_stat = stat; | ||
2992 | is->link_compressor = ipc; | ||
2993 | } | ||
2994 | else { | ||
2995 | if(is->comp_stat) | ||
2996 | is->compressor->free(is->comp_stat); | ||
2997 | is->comp_stat = stat; | ||
2998 | is->compressor = ipc; | ||
2999 | } | ||
3000 | } | ||
3001 | else { | ||
3002 | if(data->flags & IPPP_COMP_FLAG_LINK) { | ||
3003 | if(is->link_decomp_stat) | ||
3004 | is->link_decompressor->free(is->link_decomp_stat); | ||
3005 | is->link_decomp_stat = stat; | ||
3006 | is->link_decompressor = ipc; | ||
3007 | } | ||
3008 | else { | ||
3009 | if(is->decomp_stat) | ||
3010 | is->decompressor->free(is->decomp_stat); | ||
3011 | is->decomp_stat = stat; | ||
3012 | is->decompressor = ipc; | ||
3013 | } | ||
3014 | } | ||
3015 | return 0; | ||
3016 | } | ||
3017 | ipc = ipc->next; | ||
3018 | } | ||
3019 | return -EINVAL; | ||
3020 | } | ||
diff --git a/drivers/isdn/i4l/isdn_ppp.h b/drivers/isdn/i4l/isdn_ppp.h new file mode 100644 index 000000000000..8cc05c7ccf78 --- /dev/null +++ b/drivers/isdn/i4l/isdn_ppp.h | |||
@@ -0,0 +1,43 @@ | |||
1 | /* $Id: isdn_ppp.h,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $ | ||
2 | * | ||
3 | * header for Linux ISDN subsystem, functions for synchronous PPP (linklevel). | ||
4 | * | ||
5 | * Copyright 1995,96 by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de) | ||
6 | * | ||
7 | * This software may be used and distributed according to the terms | ||
8 | * of the GNU General Public License, incorporated herein by reference. | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #include <linux/ppp_defs.h> /* for PPP_PROTOCOL */ | ||
13 | #include <linux/isdn_ppp.h> /* for isdn_ppp info */ | ||
14 | |||
15 | extern int isdn_ppp_read(int, struct file *, char __user *, int); | ||
16 | extern int isdn_ppp_write(int, struct file *, const char __user *, int); | ||
17 | extern int isdn_ppp_open(int, struct file *); | ||
18 | extern int isdn_ppp_init(void); | ||
19 | extern void isdn_ppp_cleanup(void); | ||
20 | extern int isdn_ppp_free(isdn_net_local *); | ||
21 | extern int isdn_ppp_bind(isdn_net_local *); | ||
22 | extern int isdn_ppp_autodial_filter(struct sk_buff *, isdn_net_local *); | ||
23 | extern int isdn_ppp_xmit(struct sk_buff *, struct net_device *); | ||
24 | extern void isdn_ppp_receive(isdn_net_dev *, isdn_net_local *, struct sk_buff *); | ||
25 | extern int isdn_ppp_dev_ioctl(struct net_device *, struct ifreq *, int); | ||
26 | extern unsigned int isdn_ppp_poll(struct file *, struct poll_table_struct *); | ||
27 | extern int isdn_ppp_ioctl(int, struct file *, unsigned int, unsigned long); | ||
28 | extern void isdn_ppp_release(int, struct file *); | ||
29 | extern int isdn_ppp_dial_slave(char *); | ||
30 | extern void isdn_ppp_wakeup_daemon(isdn_net_local *); | ||
31 | |||
32 | extern int isdn_ppp_register_compressor(struct isdn_ppp_compressor *ipc); | ||
33 | extern int isdn_ppp_unregister_compressor(struct isdn_ppp_compressor *ipc); | ||
34 | |||
35 | #define IPPP_OPEN 0x01 | ||
36 | #define IPPP_CONNECT 0x02 | ||
37 | #define IPPP_CLOSEWAIT 0x04 | ||
38 | #define IPPP_NOBLOCK 0x08 | ||
39 | #define IPPP_ASSIGNED 0x10 | ||
40 | |||
41 | #define IPPP_MAX_HEADER 10 | ||
42 | |||
43 | |||
diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c new file mode 100644 index 000000000000..e21007eca0f0 --- /dev/null +++ b/drivers/isdn/i4l/isdn_tty.c | |||
@@ -0,0 +1,3911 @@ | |||
1 | /* $Id: isdn_tty.c,v 1.1.2.3 2004/02/10 01:07:13 keil Exp $ | ||
2 | * | ||
3 | * Linux ISDN subsystem, tty functions and AT-command emulator (linklevel). | ||
4 | * | ||
5 | * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de) | ||
6 | * Copyright 1995,96 by Thinking Objects Software GmbH Wuerzburg | ||
7 | * | ||
8 | * This software may be used and distributed according to the terms | ||
9 | * of the GNU General Public License, incorporated herein by reference. | ||
10 | * | ||
11 | */ | ||
12 | #undef ISDN_TTY_STAT_DEBUG | ||
13 | |||
14 | #include <linux/config.h> | ||
15 | #include <linux/isdn.h> | ||
16 | #include <linux/delay.h> | ||
17 | #include "isdn_common.h" | ||
18 | #include "isdn_tty.h" | ||
19 | #ifdef CONFIG_ISDN_AUDIO | ||
20 | #include "isdn_audio.h" | ||
21 | #define VBUF 0x3e0 | ||
22 | #define VBUFX (VBUF/16) | ||
23 | #endif | ||
24 | |||
25 | #define FIX_FILE_TRANSFER | ||
26 | #define DUMMY_HAYES_AT | ||
27 | |||
28 | /* Prototypes */ | ||
29 | |||
30 | static int isdn_tty_edit_at(const char *, int, modem_info *); | ||
31 | static void isdn_tty_check_esc(const u_char *, u_char, int, int *, u_long *); | ||
32 | static void isdn_tty_modem_reset_regs(modem_info *, int); | ||
33 | static void isdn_tty_cmd_ATA(modem_info *); | ||
34 | static void isdn_tty_flush_buffer(struct tty_struct *); | ||
35 | static void isdn_tty_modem_result(int, modem_info *); | ||
36 | #ifdef CONFIG_ISDN_AUDIO | ||
37 | static int isdn_tty_countDLE(unsigned char *, int); | ||
38 | #endif | ||
39 | |||
40 | /* Leave this unchanged unless you know what you do! */ | ||
41 | #define MODEM_PARANOIA_CHECK | ||
42 | #define MODEM_DO_RESTART | ||
43 | |||
44 | static int bit2si[8] = | ||
45 | {1, 5, 7, 7, 7, 7, 7, 7}; | ||
46 | static int si2bit[8] = | ||
47 | {4, 1, 4, 4, 4, 4, 4, 4}; | ||
48 | |||
49 | char *isdn_tty_revision = "$Revision: 1.1.2.3 $"; | ||
50 | |||
51 | |||
52 | /* isdn_tty_try_read() is called from within isdn_tty_rcv_skb() | ||
53 | * to stuff incoming data directly into a tty's flip-buffer. This | ||
54 | * is done to speed up tty-receiving if the receive-queue is empty. | ||
55 | * This routine MUST be called with interrupts off. | ||
56 | * Return: | ||
57 | * 1 = Success | ||
58 | * 0 = Failure, data has to be buffered and later processed by | ||
59 | * isdn_tty_readmodem(). | ||
60 | */ | ||
61 | static int | ||
62 | isdn_tty_try_read(modem_info * info, struct sk_buff *skb) | ||
63 | { | ||
64 | int c; | ||
65 | int len; | ||
66 | struct tty_struct *tty; | ||
67 | |||
68 | if (info->online) { | ||
69 | if ((tty = info->tty)) { | ||
70 | if (info->mcr & UART_MCR_RTS) { | ||
71 | c = TTY_FLIPBUF_SIZE - tty->flip.count; | ||
72 | len = skb->len | ||
73 | #ifdef CONFIG_ISDN_AUDIO | ||
74 | + ISDN_AUDIO_SKB_DLECOUNT(skb) | ||
75 | #endif | ||
76 | ; | ||
77 | if (c >= len) { | ||
78 | #ifdef CONFIG_ISDN_AUDIO | ||
79 | if (ISDN_AUDIO_SKB_DLECOUNT(skb)) | ||
80 | while (skb->len--) { | ||
81 | if (*skb->data == DLE) | ||
82 | tty_insert_flip_char(tty, DLE, 0); | ||
83 | tty_insert_flip_char(tty, *skb->data++, 0); | ||
84 | } else { | ||
85 | #endif | ||
86 | memcpy(tty->flip.char_buf_ptr, | ||
87 | skb->data, len); | ||
88 | tty->flip.count += len; | ||
89 | tty->flip.char_buf_ptr += len; | ||
90 | memset(tty->flip.flag_buf_ptr, 0, len); | ||
91 | tty->flip.flag_buf_ptr += len; | ||
92 | #ifdef CONFIG_ISDN_AUDIO | ||
93 | } | ||
94 | #endif | ||
95 | if (info->emu.mdmreg[REG_CPPP] & BIT_CPPP) | ||
96 | tty->flip.flag_buf_ptr[len - 1] = 0xff; | ||
97 | schedule_delayed_work(&tty->flip.work, 1); | ||
98 | kfree_skb(skb); | ||
99 | return 1; | ||
100 | } | ||
101 | } | ||
102 | } | ||
103 | } | ||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | /* isdn_tty_readmodem() is called periodically from within timer-interrupt. | ||
108 | * It tries getting received data from the receive queue an stuff it into | ||
109 | * the tty's flip-buffer. | ||
110 | */ | ||
111 | void | ||
112 | isdn_tty_readmodem(void) | ||
113 | { | ||
114 | int resched = 0; | ||
115 | int midx; | ||
116 | int i; | ||
117 | int c; | ||
118 | int r; | ||
119 | struct tty_struct *tty; | ||
120 | modem_info *info; | ||
121 | |||
122 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
123 | if ((midx = dev->m_idx[i]) >= 0) { | ||
124 | info = &dev->mdm.info[midx]; | ||
125 | if (info->online) { | ||
126 | r = 0; | ||
127 | #ifdef CONFIG_ISDN_AUDIO | ||
128 | isdn_audio_eval_dtmf(info); | ||
129 | if ((info->vonline & 1) && (info->emu.vpar[1])) | ||
130 | isdn_audio_eval_silence(info); | ||
131 | #endif | ||
132 | if ((tty = info->tty)) { | ||
133 | if (info->mcr & UART_MCR_RTS) { | ||
134 | c = TTY_FLIPBUF_SIZE - tty->flip.count; | ||
135 | if (c > 0) { | ||
136 | r = isdn_readbchan(info->isdn_driver, info->isdn_channel, | ||
137 | tty->flip.char_buf_ptr, | ||
138 | tty->flip.flag_buf_ptr, c, NULL); | ||
139 | /* CISCO AsyncPPP Hack */ | ||
140 | if (!(info->emu.mdmreg[REG_CPPP] & BIT_CPPP)) | ||
141 | memset(tty->flip.flag_buf_ptr, 0, r); | ||
142 | tty->flip.count += r; | ||
143 | tty->flip.flag_buf_ptr += r; | ||
144 | tty->flip.char_buf_ptr += r; | ||
145 | if (r) | ||
146 | schedule_delayed_work(&tty->flip.work, 1); | ||
147 | } | ||
148 | } else | ||
149 | r = 1; | ||
150 | } else | ||
151 | r = 1; | ||
152 | if (r) { | ||
153 | info->rcvsched = 0; | ||
154 | resched = 1; | ||
155 | } else | ||
156 | info->rcvsched = 1; | ||
157 | } | ||
158 | } | ||
159 | } | ||
160 | if (!resched) | ||
161 | isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 0); | ||
162 | } | ||
163 | |||
164 | int | ||
165 | isdn_tty_rcv_skb(int i, int di, int channel, struct sk_buff *skb) | ||
166 | { | ||
167 | ulong flags; | ||
168 | int midx; | ||
169 | #ifdef CONFIG_ISDN_AUDIO | ||
170 | int ifmt; | ||
171 | #endif | ||
172 | modem_info *info; | ||
173 | |||
174 | if ((midx = dev->m_idx[i]) < 0) { | ||
175 | /* if midx is invalid, packet is not for tty */ | ||
176 | return 0; | ||
177 | } | ||
178 | info = &dev->mdm.info[midx]; | ||
179 | #ifdef CONFIG_ISDN_AUDIO | ||
180 | ifmt = 1; | ||
181 | |||
182 | if ((info->vonline) && (!info->emu.vpar[4])) | ||
183 | isdn_audio_calc_dtmf(info, skb->data, skb->len, ifmt); | ||
184 | if ((info->vonline & 1) && (info->emu.vpar[1])) | ||
185 | isdn_audio_calc_silence(info, skb->data, skb->len, ifmt); | ||
186 | #endif | ||
187 | if ((info->online < 2) | ||
188 | #ifdef CONFIG_ISDN_AUDIO | ||
189 | && (!(info->vonline & 1)) | ||
190 | #endif | ||
191 | ) { | ||
192 | /* If Modem not listening, drop data */ | ||
193 | kfree_skb(skb); | ||
194 | return 1; | ||
195 | } | ||
196 | if (info->emu.mdmreg[REG_T70] & BIT_T70) { | ||
197 | if (info->emu.mdmreg[REG_T70] & BIT_T70_EXT) { | ||
198 | /* T.70 decoding: throw away the T.70 header (2 or 4 bytes) */ | ||
199 | if (skb->data[0] == 3) /* pure data packet -> 4 byte headers */ | ||
200 | skb_pull(skb, 4); | ||
201 | else | ||
202 | if (skb->data[0] == 1) /* keepalive packet -> 2 byte hdr */ | ||
203 | skb_pull(skb, 2); | ||
204 | } else | ||
205 | /* T.70 decoding: Simply throw away the T.70 header (4 bytes) */ | ||
206 | if ((skb->data[0] == 1) && ((skb->data[1] == 0) || (skb->data[1] == 1))) | ||
207 | skb_pull(skb, 4); | ||
208 | } | ||
209 | #ifdef CONFIG_ISDN_AUDIO | ||
210 | ISDN_AUDIO_SKB_DLECOUNT(skb) = 0; | ||
211 | ISDN_AUDIO_SKB_LOCK(skb) = 0; | ||
212 | if (info->vonline & 1) { | ||
213 | /* voice conversion/compression */ | ||
214 | switch (info->emu.vpar[3]) { | ||
215 | case 2: | ||
216 | case 3: | ||
217 | case 4: | ||
218 | /* adpcm | ||
219 | * Since compressed data takes less | ||
220 | * space, we can overwrite the buffer. | ||
221 | */ | ||
222 | skb_trim(skb, isdn_audio_xlaw2adpcm(info->adpcmr, | ||
223 | ifmt, | ||
224 | skb->data, | ||
225 | skb->data, | ||
226 | skb->len)); | ||
227 | break; | ||
228 | case 5: | ||
229 | /* a-law */ | ||
230 | if (!ifmt) | ||
231 | isdn_audio_ulaw2alaw(skb->data, skb->len); | ||
232 | break; | ||
233 | case 6: | ||
234 | /* u-law */ | ||
235 | if (ifmt) | ||
236 | isdn_audio_alaw2ulaw(skb->data, skb->len); | ||
237 | break; | ||
238 | } | ||
239 | ISDN_AUDIO_SKB_DLECOUNT(skb) = | ||
240 | isdn_tty_countDLE(skb->data, skb->len); | ||
241 | } | ||
242 | #ifdef CONFIG_ISDN_TTY_FAX | ||
243 | else { | ||
244 | if (info->faxonline & 2) { | ||
245 | isdn_tty_fax_bitorder(info, skb); | ||
246 | ISDN_AUDIO_SKB_DLECOUNT(skb) = | ||
247 | isdn_tty_countDLE(skb->data, skb->len); | ||
248 | } | ||
249 | } | ||
250 | #endif | ||
251 | #endif | ||
252 | /* Try to deliver directly via tty-flip-buf if queue is empty */ | ||
253 | spin_lock_irqsave(&info->readlock, flags); | ||
254 | if (skb_queue_empty(&dev->drv[di]->rpqueue[channel])) | ||
255 | if (isdn_tty_try_read(info, skb)) { | ||
256 | spin_unlock_irqrestore(&info->readlock, flags); | ||
257 | return 1; | ||
258 | } | ||
259 | /* Direct deliver failed or queue wasn't empty. | ||
260 | * Queue up for later dequeueing via timer-irq. | ||
261 | */ | ||
262 | __skb_queue_tail(&dev->drv[di]->rpqueue[channel], skb); | ||
263 | dev->drv[di]->rcvcount[channel] += | ||
264 | (skb->len | ||
265 | #ifdef CONFIG_ISDN_AUDIO | ||
266 | + ISDN_AUDIO_SKB_DLECOUNT(skb) | ||
267 | #endif | ||
268 | ); | ||
269 | spin_unlock_irqrestore(&info->readlock, flags); | ||
270 | /* Schedule dequeuing */ | ||
271 | if ((dev->modempoll) && (info->rcvsched)) | ||
272 | isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1); | ||
273 | return 1; | ||
274 | } | ||
275 | |||
276 | void | ||
277 | isdn_tty_cleanup_xmit(modem_info * info) | ||
278 | { | ||
279 | skb_queue_purge(&info->xmit_queue); | ||
280 | #ifdef CONFIG_ISDN_AUDIO | ||
281 | skb_queue_purge(&info->dtmf_queue); | ||
282 | #endif | ||
283 | } | ||
284 | |||
285 | static void | ||
286 | isdn_tty_tint(modem_info * info) | ||
287 | { | ||
288 | struct sk_buff *skb = skb_dequeue(&info->xmit_queue); | ||
289 | int len, slen; | ||
290 | |||
291 | if (!skb) | ||
292 | return; | ||
293 | len = skb->len; | ||
294 | if ((slen = isdn_writebuf_skb_stub(info->isdn_driver, | ||
295 | info->isdn_channel, 1, skb)) == len) { | ||
296 | struct tty_struct *tty = info->tty; | ||
297 | info->send_outstanding++; | ||
298 | info->msr &= ~UART_MSR_CTS; | ||
299 | info->lsr &= ~UART_LSR_TEMT; | ||
300 | tty_wakeup(tty); | ||
301 | return; | ||
302 | } | ||
303 | if (slen < 0) { | ||
304 | /* Error: no channel, already shutdown, or wrong parameter */ | ||
305 | dev_kfree_skb(skb); | ||
306 | return; | ||
307 | } | ||
308 | skb_queue_head(&info->xmit_queue, skb); | ||
309 | } | ||
310 | |||
311 | #ifdef CONFIG_ISDN_AUDIO | ||
312 | static int | ||
313 | isdn_tty_countDLE(unsigned char *buf, int len) | ||
314 | { | ||
315 | int count = 0; | ||
316 | |||
317 | while (len--) | ||
318 | if (*buf++ == DLE) | ||
319 | count++; | ||
320 | return count; | ||
321 | } | ||
322 | |||
323 | /* This routine is called from within isdn_tty_write() to perform | ||
324 | * DLE-decoding when sending audio-data. | ||
325 | */ | ||
326 | static int | ||
327 | isdn_tty_handleDLEdown(modem_info * info, atemu * m, int len) | ||
328 | { | ||
329 | unsigned char *p = &info->xmit_buf[info->xmit_count]; | ||
330 | int count = 0; | ||
331 | |||
332 | while (len > 0) { | ||
333 | if (m->lastDLE) { | ||
334 | m->lastDLE = 0; | ||
335 | switch (*p) { | ||
336 | case DLE: | ||
337 | /* Escape code */ | ||
338 | if (len > 1) | ||
339 | memmove(p, p + 1, len - 1); | ||
340 | p--; | ||
341 | count++; | ||
342 | break; | ||
343 | case ETX: | ||
344 | /* End of data */ | ||
345 | info->vonline |= 4; | ||
346 | return count; | ||
347 | case DC4: | ||
348 | /* Abort RX */ | ||
349 | info->vonline &= ~1; | ||
350 | #ifdef ISDN_DEBUG_MODEM_VOICE | ||
351 | printk(KERN_DEBUG | ||
352 | "DLEdown: got DLE-DC4, send DLE-ETX on ttyI%d\n", | ||
353 | info->line); | ||
354 | #endif | ||
355 | isdn_tty_at_cout("\020\003", info); | ||
356 | if (!info->vonline) { | ||
357 | #ifdef ISDN_DEBUG_MODEM_VOICE | ||
358 | printk(KERN_DEBUG | ||
359 | "DLEdown: send VCON on ttyI%d\n", | ||
360 | info->line); | ||
361 | #endif | ||
362 | isdn_tty_at_cout("\r\nVCON\r\n", info); | ||
363 | } | ||
364 | /* Fall through */ | ||
365 | case 'q': | ||
366 | case 's': | ||
367 | /* Silence */ | ||
368 | if (len > 1) | ||
369 | memmove(p, p + 1, len - 1); | ||
370 | p--; | ||
371 | break; | ||
372 | } | ||
373 | } else { | ||
374 | if (*p == DLE) | ||
375 | m->lastDLE = 1; | ||
376 | else | ||
377 | count++; | ||
378 | } | ||
379 | p++; | ||
380 | len--; | ||
381 | } | ||
382 | if (len < 0) { | ||
383 | printk(KERN_WARNING "isdn_tty: len<0 in DLEdown\n"); | ||
384 | return 0; | ||
385 | } | ||
386 | return count; | ||
387 | } | ||
388 | |||
389 | /* This routine is called from within isdn_tty_write() when receiving | ||
390 | * audio-data. It interrupts receiving, if an character other than | ||
391 | * ^S or ^Q is sent. | ||
392 | */ | ||
393 | static int | ||
394 | isdn_tty_end_vrx(const char *buf, int c) | ||
395 | { | ||
396 | char ch; | ||
397 | |||
398 | while (c--) { | ||
399 | ch = *buf; | ||
400 | if ((ch != 0x11) && (ch != 0x13)) | ||
401 | return 1; | ||
402 | buf++; | ||
403 | } | ||
404 | return 0; | ||
405 | } | ||
406 | |||
407 | static int voice_cf[7] = | ||
408 | {0, 0, 4, 3, 2, 0, 0}; | ||
409 | |||
410 | #endif /* CONFIG_ISDN_AUDIO */ | ||
411 | |||
412 | /* isdn_tty_senddown() is called either directly from within isdn_tty_write() | ||
413 | * or via timer-interrupt from within isdn_tty_modem_xmit(). It pulls | ||
414 | * outgoing data from the tty's xmit-buffer, handles voice-decompression or | ||
415 | * T.70 if necessary, and finally queues it up for sending via isdn_tty_tint. | ||
416 | */ | ||
417 | static void | ||
418 | isdn_tty_senddown(modem_info * info) | ||
419 | { | ||
420 | int buflen; | ||
421 | int skb_res; | ||
422 | #ifdef CONFIG_ISDN_AUDIO | ||
423 | int audio_len; | ||
424 | #endif | ||
425 | struct sk_buff *skb; | ||
426 | |||
427 | #ifdef CONFIG_ISDN_AUDIO | ||
428 | if (info->vonline & 4) { | ||
429 | info->vonline &= ~6; | ||
430 | if (!info->vonline) { | ||
431 | #ifdef ISDN_DEBUG_MODEM_VOICE | ||
432 | printk(KERN_DEBUG | ||
433 | "senddown: send VCON on ttyI%d\n", | ||
434 | info->line); | ||
435 | #endif | ||
436 | isdn_tty_at_cout("\r\nVCON\r\n", info); | ||
437 | } | ||
438 | } | ||
439 | #endif | ||
440 | if (!(buflen = info->xmit_count)) | ||
441 | return; | ||
442 | if ((info->emu.mdmreg[REG_CTS] & BIT_CTS) != 0) | ||
443 | info->msr &= ~UART_MSR_CTS; | ||
444 | info->lsr &= ~UART_LSR_TEMT; | ||
445 | /* info->xmit_count is modified here and in isdn_tty_write(). | ||
446 | * So we return here if isdn_tty_write() is in the | ||
447 | * critical section. | ||
448 | */ | ||
449 | atomic_inc(&info->xmit_lock); | ||
450 | if (!(atomic_dec_and_test(&info->xmit_lock))) | ||
451 | return; | ||
452 | if (info->isdn_driver < 0) { | ||
453 | info->xmit_count = 0; | ||
454 | return; | ||
455 | } | ||
456 | skb_res = dev->drv[info->isdn_driver]->interface->hl_hdrlen + 4; | ||
457 | #ifdef CONFIG_ISDN_AUDIO | ||
458 | if (info->vonline & 2) | ||
459 | audio_len = buflen * voice_cf[info->emu.vpar[3]]; | ||
460 | else | ||
461 | audio_len = 0; | ||
462 | skb = dev_alloc_skb(skb_res + buflen + audio_len); | ||
463 | #else | ||
464 | skb = dev_alloc_skb(skb_res + buflen); | ||
465 | #endif | ||
466 | if (!skb) { | ||
467 | printk(KERN_WARNING | ||
468 | "isdn_tty: Out of memory in ttyI%d senddown\n", | ||
469 | info->line); | ||
470 | return; | ||
471 | } | ||
472 | skb_reserve(skb, skb_res); | ||
473 | memcpy(skb_put(skb, buflen), info->xmit_buf, buflen); | ||
474 | info->xmit_count = 0; | ||
475 | #ifdef CONFIG_ISDN_AUDIO | ||
476 | if (info->vonline & 2) { | ||
477 | /* For now, ifmt is fixed to 1 (alaw), since this | ||
478 | * is used with ISDN everywhere in the world, except | ||
479 | * US, Canada and Japan. | ||
480 | * Later, when US-ISDN protocols are implemented, | ||
481 | * this setting will depend on the D-channel protocol. | ||
482 | */ | ||
483 | int ifmt = 1; | ||
484 | |||
485 | /* voice conversion/decompression */ | ||
486 | switch (info->emu.vpar[3]) { | ||
487 | case 2: | ||
488 | case 3: | ||
489 | case 4: | ||
490 | /* adpcm, compatible to ZyXel 1496 modem | ||
491 | * with ROM revision 6.01 | ||
492 | */ | ||
493 | audio_len = isdn_audio_adpcm2xlaw(info->adpcms, | ||
494 | ifmt, | ||
495 | skb->data, | ||
496 | skb_put(skb, audio_len), | ||
497 | buflen); | ||
498 | skb_pull(skb, buflen); | ||
499 | skb_trim(skb, audio_len); | ||
500 | break; | ||
501 | case 5: | ||
502 | /* a-law */ | ||
503 | if (!ifmt) | ||
504 | isdn_audio_alaw2ulaw(skb->data, | ||
505 | buflen); | ||
506 | break; | ||
507 | case 6: | ||
508 | /* u-law */ | ||
509 | if (ifmt) | ||
510 | isdn_audio_ulaw2alaw(skb->data, | ||
511 | buflen); | ||
512 | break; | ||
513 | } | ||
514 | } | ||
515 | #endif /* CONFIG_ISDN_AUDIO */ | ||
516 | if (info->emu.mdmreg[REG_T70] & BIT_T70) { | ||
517 | /* Add T.70 simplified header */ | ||
518 | if (info->emu.mdmreg[REG_T70] & BIT_T70_EXT) | ||
519 | memcpy(skb_push(skb, 2), "\1\0", 2); | ||
520 | else | ||
521 | memcpy(skb_push(skb, 4), "\1\0\1\0", 4); | ||
522 | } | ||
523 | skb_queue_tail(&info->xmit_queue, skb); | ||
524 | } | ||
525 | |||
526 | /************************************************************ | ||
527 | * | ||
528 | * Modem-functions | ||
529 | * | ||
530 | * mostly "stolen" from original Linux-serial.c and friends. | ||
531 | * | ||
532 | ************************************************************/ | ||
533 | |||
534 | /* The next routine is called once from within timer-interrupt | ||
535 | * triggered within isdn_tty_modem_ncarrier(). It calls | ||
536 | * isdn_tty_modem_result() to stuff a "NO CARRIER" Message | ||
537 | * into the tty's flip-buffer. | ||
538 | */ | ||
539 | static void | ||
540 | isdn_tty_modem_do_ncarrier(unsigned long data) | ||
541 | { | ||
542 | modem_info *info = (modem_info *) data; | ||
543 | isdn_tty_modem_result(RESULT_NO_CARRIER, info); | ||
544 | } | ||
545 | |||
546 | /* Next routine is called, whenever the DTR-signal is raised. | ||
547 | * It checks the ncarrier-flag, and triggers the above routine | ||
548 | * when necessary. The ncarrier-flag is set, whenever DTR goes | ||
549 | * low. | ||
550 | */ | ||
551 | static void | ||
552 | isdn_tty_modem_ncarrier(modem_info * info) | ||
553 | { | ||
554 | if (info->ncarrier) { | ||
555 | info->nc_timer.expires = jiffies + HZ; | ||
556 | add_timer(&info->nc_timer); | ||
557 | } | ||
558 | } | ||
559 | |||
560 | /* | ||
561 | * return the usage calculated by si and layer 2 protocol | ||
562 | */ | ||
563 | int | ||
564 | isdn_calc_usage(int si, int l2) | ||
565 | { | ||
566 | int usg = ISDN_USAGE_MODEM; | ||
567 | |||
568 | #ifdef CONFIG_ISDN_AUDIO | ||
569 | if (si == 1) { | ||
570 | switch(l2) { | ||
571 | case ISDN_PROTO_L2_MODEM: | ||
572 | usg = ISDN_USAGE_MODEM; | ||
573 | break; | ||
574 | #ifdef CONFIG_ISDN_TTY_FAX | ||
575 | case ISDN_PROTO_L2_FAX: | ||
576 | usg = ISDN_USAGE_FAX; | ||
577 | break; | ||
578 | #endif | ||
579 | case ISDN_PROTO_L2_TRANS: | ||
580 | default: | ||
581 | usg = ISDN_USAGE_VOICE; | ||
582 | break; | ||
583 | } | ||
584 | } | ||
585 | #endif | ||
586 | return(usg); | ||
587 | } | ||
588 | |||
589 | /* isdn_tty_dial() performs dialing of a tty an the necessary | ||
590 | * setup of the lower levels before that. | ||
591 | */ | ||
592 | static void | ||
593 | isdn_tty_dial(char *n, modem_info * info, atemu * m) | ||
594 | { | ||
595 | int usg = ISDN_USAGE_MODEM; | ||
596 | int si = 7; | ||
597 | int l2 = m->mdmreg[REG_L2PROT]; | ||
598 | u_long flags; | ||
599 | isdn_ctrl cmd; | ||
600 | int i; | ||
601 | int j; | ||
602 | |||
603 | for (j = 7; j >= 0; j--) | ||
604 | if (m->mdmreg[REG_SI1] & (1 << j)) { | ||
605 | si = bit2si[j]; | ||
606 | break; | ||
607 | } | ||
608 | usg = isdn_calc_usage(si, l2); | ||
609 | #ifdef CONFIG_ISDN_AUDIO | ||
610 | if ((si == 1) && | ||
611 | (l2 != ISDN_PROTO_L2_MODEM) | ||
612 | #ifdef CONFIG_ISDN_TTY_FAX | ||
613 | && (l2 != ISDN_PROTO_L2_FAX) | ||
614 | #endif | ||
615 | ) { | ||
616 | l2 = ISDN_PROTO_L2_TRANS; | ||
617 | usg = ISDN_USAGE_VOICE; | ||
618 | } | ||
619 | #endif | ||
620 | m->mdmreg[REG_SI1I] = si2bit[si]; | ||
621 | spin_lock_irqsave(&dev->lock, flags); | ||
622 | i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1, m->msn); | ||
623 | if (i < 0) { | ||
624 | spin_unlock_irqrestore(&dev->lock, flags); | ||
625 | isdn_tty_modem_result(RESULT_NO_DIALTONE, info); | ||
626 | } else { | ||
627 | info->isdn_driver = dev->drvmap[i]; | ||
628 | info->isdn_channel = dev->chanmap[i]; | ||
629 | info->drv_index = i; | ||
630 | dev->m_idx[i] = info->line; | ||
631 | dev->usage[i] |= ISDN_USAGE_OUTGOING; | ||
632 | info->last_dir = 1; | ||
633 | strcpy(info->last_num, n); | ||
634 | isdn_info_update(); | ||
635 | spin_unlock_irqrestore(&dev->lock, flags); | ||
636 | cmd.driver = info->isdn_driver; | ||
637 | cmd.arg = info->isdn_channel; | ||
638 | cmd.command = ISDN_CMD_CLREAZ; | ||
639 | isdn_command(&cmd); | ||
640 | strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver)); | ||
641 | cmd.driver = info->isdn_driver; | ||
642 | cmd.command = ISDN_CMD_SETEAZ; | ||
643 | isdn_command(&cmd); | ||
644 | cmd.driver = info->isdn_driver; | ||
645 | cmd.command = ISDN_CMD_SETL2; | ||
646 | info->last_l2 = l2; | ||
647 | cmd.arg = info->isdn_channel + (l2 << 8); | ||
648 | isdn_command(&cmd); | ||
649 | cmd.driver = info->isdn_driver; | ||
650 | cmd.command = ISDN_CMD_SETL3; | ||
651 | cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8); | ||
652 | #ifdef CONFIG_ISDN_TTY_FAX | ||
653 | if (l2 == ISDN_PROTO_L2_FAX) { | ||
654 | cmd.parm.fax = info->fax; | ||
655 | info->fax->direction = ISDN_TTY_FAX_CONN_OUT; | ||
656 | } | ||
657 | #endif | ||
658 | isdn_command(&cmd); | ||
659 | cmd.driver = info->isdn_driver; | ||
660 | cmd.arg = info->isdn_channel; | ||
661 | sprintf(cmd.parm.setup.phone, "%s", n); | ||
662 | sprintf(cmd.parm.setup.eazmsn, "%s", | ||
663 | isdn_map_eaz2msn(m->msn, info->isdn_driver)); | ||
664 | cmd.parm.setup.si1 = si; | ||
665 | cmd.parm.setup.si2 = m->mdmreg[REG_SI2]; | ||
666 | cmd.command = ISDN_CMD_DIAL; | ||
667 | info->dialing = 1; | ||
668 | info->emu.carrierwait = 0; | ||
669 | strcpy(dev->num[i], n); | ||
670 | isdn_info_update(); | ||
671 | isdn_command(&cmd); | ||
672 | isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1); | ||
673 | } | ||
674 | } | ||
675 | |||
676 | /* isdn_tty_hangup() disassociates a tty from the real | ||
677 | * ISDN-line (hangup). The usage-status is cleared | ||
678 | * and some cleanup is done also. | ||
679 | */ | ||
680 | void | ||
681 | isdn_tty_modem_hup(modem_info * info, int local) | ||
682 | { | ||
683 | isdn_ctrl cmd; | ||
684 | int di, ch; | ||
685 | |||
686 | if (!info) | ||
687 | return; | ||
688 | |||
689 | di = info->isdn_driver; | ||
690 | ch = info->isdn_channel; | ||
691 | if (di < 0 || ch < 0) | ||
692 | return; | ||
693 | |||
694 | info->isdn_driver = -1; | ||
695 | info->isdn_channel = -1; | ||
696 | |||
697 | #ifdef ISDN_DEBUG_MODEM_HUP | ||
698 | printk(KERN_DEBUG "Mhup ttyI%d\n", info->line); | ||
699 | #endif | ||
700 | info->rcvsched = 0; | ||
701 | isdn_tty_flush_buffer(info->tty); | ||
702 | if (info->online) { | ||
703 | info->last_lhup = local; | ||
704 | info->online = 0; | ||
705 | isdn_tty_modem_result(RESULT_NO_CARRIER, info); | ||
706 | } | ||
707 | #ifdef CONFIG_ISDN_AUDIO | ||
708 | info->vonline = 0; | ||
709 | #ifdef CONFIG_ISDN_TTY_FAX | ||
710 | info->faxonline = 0; | ||
711 | info->fax->phase = ISDN_FAX_PHASE_IDLE; | ||
712 | #endif | ||
713 | info->emu.vpar[4] = 0; | ||
714 | info->emu.vpar[5] = 8; | ||
715 | if (info->dtmf_state) { | ||
716 | kfree(info->dtmf_state); | ||
717 | info->dtmf_state = NULL; | ||
718 | } | ||
719 | if (info->silence_state) { | ||
720 | kfree(info->silence_state); | ||
721 | info->silence_state = NULL; | ||
722 | } | ||
723 | if (info->adpcms) { | ||
724 | kfree(info->adpcms); | ||
725 | info->adpcms = NULL; | ||
726 | } | ||
727 | if (info->adpcmr) { | ||
728 | kfree(info->adpcmr); | ||
729 | info->adpcmr = NULL; | ||
730 | } | ||
731 | #endif | ||
732 | if ((info->msr & UART_MSR_RI) && | ||
733 | (info->emu.mdmreg[REG_RUNG] & BIT_RUNG)) | ||
734 | isdn_tty_modem_result(RESULT_RUNG, info); | ||
735 | info->msr &= ~(UART_MSR_DCD | UART_MSR_RI); | ||
736 | info->lsr |= UART_LSR_TEMT; | ||
737 | |||
738 | if (local) { | ||
739 | cmd.driver = di; | ||
740 | cmd.command = ISDN_CMD_HANGUP; | ||
741 | cmd.arg = ch; | ||
742 | isdn_command(&cmd); | ||
743 | } | ||
744 | |||
745 | isdn_all_eaz(di, ch); | ||
746 | info->emu.mdmreg[REG_RINGCNT] = 0; | ||
747 | isdn_free_channel(di, ch, 0); | ||
748 | |||
749 | if (info->drv_index >= 0) { | ||
750 | dev->m_idx[info->drv_index] = -1; | ||
751 | info->drv_index = -1; | ||
752 | } | ||
753 | } | ||
754 | |||
755 | /* | ||
756 | * Begin of a CAPI like interface, currently used only for | ||
757 | * supplementary service (CAPI 2.0 part III) | ||
758 | */ | ||
759 | #include <linux/isdn/capicmd.h> | ||
760 | |||
761 | int | ||
762 | isdn_tty_capi_facility(capi_msg *cm) { | ||
763 | return(-1); /* dummy */ | ||
764 | } | ||
765 | |||
766 | /* isdn_tty_suspend() tries to suspend the current tty connection | ||
767 | */ | ||
768 | static void | ||
769 | isdn_tty_suspend(char *id, modem_info * info, atemu * m) | ||
770 | { | ||
771 | isdn_ctrl cmd; | ||
772 | |||
773 | int l; | ||
774 | |||
775 | if (!info) | ||
776 | return; | ||
777 | |||
778 | #ifdef ISDN_DEBUG_MODEM_SERVICES | ||
779 | printk(KERN_DEBUG "Msusp ttyI%d\n", info->line); | ||
780 | #endif | ||
781 | l = strlen(id); | ||
782 | if ((info->isdn_driver >= 0)) { | ||
783 | cmd.parm.cmsg.Length = l+18; | ||
784 | cmd.parm.cmsg.Command = CAPI_FACILITY; | ||
785 | cmd.parm.cmsg.Subcommand = CAPI_REQ; | ||
786 | cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1; | ||
787 | cmd.parm.cmsg.para[0] = 3; /* 16 bit 0x0003 suplementary service */ | ||
788 | cmd.parm.cmsg.para[1] = 0; | ||
789 | cmd.parm.cmsg.para[2] = l + 3; | ||
790 | cmd.parm.cmsg.para[3] = 4; /* 16 bit 0x0004 Suspend */ | ||
791 | cmd.parm.cmsg.para[4] = 0; | ||
792 | cmd.parm.cmsg.para[5] = l; | ||
793 | strncpy(&cmd.parm.cmsg.para[6], id, l); | ||
794 | cmd.command = CAPI_PUT_MESSAGE; | ||
795 | cmd.driver = info->isdn_driver; | ||
796 | cmd.arg = info->isdn_channel; | ||
797 | isdn_command(&cmd); | ||
798 | } | ||
799 | } | ||
800 | |||
801 | /* isdn_tty_resume() tries to resume a suspended call | ||
802 | * setup of the lower levels before that. unfortunatly here is no | ||
803 | * checking for compatibility of used protocols implemented by Q931 | ||
804 | * It does the same things like isdn_tty_dial, the last command | ||
805 | * is different, may be we can merge it. | ||
806 | */ | ||
807 | |||
808 | static void | ||
809 | isdn_tty_resume(char *id, modem_info * info, atemu * m) | ||
810 | { | ||
811 | int usg = ISDN_USAGE_MODEM; | ||
812 | int si = 7; | ||
813 | int l2 = m->mdmreg[REG_L2PROT]; | ||
814 | isdn_ctrl cmd; | ||
815 | ulong flags; | ||
816 | int i; | ||
817 | int j; | ||
818 | int l; | ||
819 | |||
820 | l = strlen(id); | ||
821 | for (j = 7; j >= 0; j--) | ||
822 | if (m->mdmreg[REG_SI1] & (1 << j)) { | ||
823 | si = bit2si[j]; | ||
824 | break; | ||
825 | } | ||
826 | usg = isdn_calc_usage(si, l2); | ||
827 | #ifdef CONFIG_ISDN_AUDIO | ||
828 | if ((si == 1) && | ||
829 | (l2 != ISDN_PROTO_L2_MODEM) | ||
830 | #ifdef CONFIG_ISDN_TTY_FAX | ||
831 | && (l2 != ISDN_PROTO_L2_FAX) | ||
832 | #endif | ||
833 | ) { | ||
834 | l2 = ISDN_PROTO_L2_TRANS; | ||
835 | usg = ISDN_USAGE_VOICE; | ||
836 | } | ||
837 | #endif | ||
838 | m->mdmreg[REG_SI1I] = si2bit[si]; | ||
839 | spin_lock_irqsave(&dev->lock, flags); | ||
840 | i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1, m->msn); | ||
841 | if (i < 0) { | ||
842 | spin_unlock_irqrestore(&dev->lock, flags); | ||
843 | isdn_tty_modem_result(RESULT_NO_DIALTONE, info); | ||
844 | } else { | ||
845 | info->isdn_driver = dev->drvmap[i]; | ||
846 | info->isdn_channel = dev->chanmap[i]; | ||
847 | info->drv_index = i; | ||
848 | dev->m_idx[i] = info->line; | ||
849 | dev->usage[i] |= ISDN_USAGE_OUTGOING; | ||
850 | info->last_dir = 1; | ||
851 | // strcpy(info->last_num, n); | ||
852 | isdn_info_update(); | ||
853 | spin_unlock_irqrestore(&dev->lock, flags); | ||
854 | cmd.driver = info->isdn_driver; | ||
855 | cmd.arg = info->isdn_channel; | ||
856 | cmd.command = ISDN_CMD_CLREAZ; | ||
857 | isdn_command(&cmd); | ||
858 | strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver)); | ||
859 | cmd.driver = info->isdn_driver; | ||
860 | cmd.command = ISDN_CMD_SETEAZ; | ||
861 | isdn_command(&cmd); | ||
862 | cmd.driver = info->isdn_driver; | ||
863 | cmd.command = ISDN_CMD_SETL2; | ||
864 | info->last_l2 = l2; | ||
865 | cmd.arg = info->isdn_channel + (l2 << 8); | ||
866 | isdn_command(&cmd); | ||
867 | cmd.driver = info->isdn_driver; | ||
868 | cmd.command = ISDN_CMD_SETL3; | ||
869 | cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8); | ||
870 | isdn_command(&cmd); | ||
871 | cmd.driver = info->isdn_driver; | ||
872 | cmd.arg = info->isdn_channel; | ||
873 | cmd.parm.cmsg.Length = l+18; | ||
874 | cmd.parm.cmsg.Command = CAPI_FACILITY; | ||
875 | cmd.parm.cmsg.Subcommand = CAPI_REQ; | ||
876 | cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1; | ||
877 | cmd.parm.cmsg.para[0] = 3; /* 16 bit 0x0003 suplementary service */ | ||
878 | cmd.parm.cmsg.para[1] = 0; | ||
879 | cmd.parm.cmsg.para[2] = l+3; | ||
880 | cmd.parm.cmsg.para[3] = 5; /* 16 bit 0x0005 Resume */ | ||
881 | cmd.parm.cmsg.para[4] = 0; | ||
882 | cmd.parm.cmsg.para[5] = l; | ||
883 | strncpy(&cmd.parm.cmsg.para[6], id, l); | ||
884 | cmd.command =CAPI_PUT_MESSAGE; | ||
885 | info->dialing = 1; | ||
886 | // strcpy(dev->num[i], n); | ||
887 | isdn_info_update(); | ||
888 | isdn_command(&cmd); | ||
889 | isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1); | ||
890 | } | ||
891 | } | ||
892 | |||
893 | /* isdn_tty_send_msg() sends a message to a HL driver | ||
894 | * This is used for hybrid modem cards to send AT commands to it | ||
895 | */ | ||
896 | |||
897 | static void | ||
898 | isdn_tty_send_msg(modem_info * info, atemu * m, char *msg) | ||
899 | { | ||
900 | int usg = ISDN_USAGE_MODEM; | ||
901 | int si = 7; | ||
902 | int l2 = m->mdmreg[REG_L2PROT]; | ||
903 | isdn_ctrl cmd; | ||
904 | ulong flags; | ||
905 | int i; | ||
906 | int j; | ||
907 | int l; | ||
908 | |||
909 | l = strlen(msg); | ||
910 | if (!l) { | ||
911 | isdn_tty_modem_result(RESULT_ERROR, info); | ||
912 | return; | ||
913 | } | ||
914 | for (j = 7; j >= 0; j--) | ||
915 | if (m->mdmreg[REG_SI1] & (1 << j)) { | ||
916 | si = bit2si[j]; | ||
917 | break; | ||
918 | } | ||
919 | usg = isdn_calc_usage(si, l2); | ||
920 | #ifdef CONFIG_ISDN_AUDIO | ||
921 | if ((si == 1) && | ||
922 | (l2 != ISDN_PROTO_L2_MODEM) | ||
923 | #ifdef CONFIG_ISDN_TTY_FAX | ||
924 | && (l2 != ISDN_PROTO_L2_FAX) | ||
925 | #endif | ||
926 | ) { | ||
927 | l2 = ISDN_PROTO_L2_TRANS; | ||
928 | usg = ISDN_USAGE_VOICE; | ||
929 | } | ||
930 | #endif | ||
931 | m->mdmreg[REG_SI1I] = si2bit[si]; | ||
932 | spin_lock_irqsave(&dev->lock, flags); | ||
933 | i = isdn_get_free_channel(usg, l2, m->mdmreg[REG_L3PROT], -1, -1, m->msn); | ||
934 | if (i < 0) { | ||
935 | spin_unlock_irqrestore(&dev->lock, flags); | ||
936 | isdn_tty_modem_result(RESULT_NO_DIALTONE, info); | ||
937 | } else { | ||
938 | info->isdn_driver = dev->drvmap[i]; | ||
939 | info->isdn_channel = dev->chanmap[i]; | ||
940 | info->drv_index = i; | ||
941 | dev->m_idx[i] = info->line; | ||
942 | dev->usage[i] |= ISDN_USAGE_OUTGOING; | ||
943 | info->last_dir = 1; | ||
944 | isdn_info_update(); | ||
945 | spin_unlock_irqrestore(&dev->lock, flags); | ||
946 | cmd.driver = info->isdn_driver; | ||
947 | cmd.arg = info->isdn_channel; | ||
948 | cmd.command = ISDN_CMD_CLREAZ; | ||
949 | isdn_command(&cmd); | ||
950 | strcpy(cmd.parm.num, isdn_map_eaz2msn(m->msn, info->isdn_driver)); | ||
951 | cmd.driver = info->isdn_driver; | ||
952 | cmd.command = ISDN_CMD_SETEAZ; | ||
953 | isdn_command(&cmd); | ||
954 | cmd.driver = info->isdn_driver; | ||
955 | cmd.command = ISDN_CMD_SETL2; | ||
956 | info->last_l2 = l2; | ||
957 | cmd.arg = info->isdn_channel + (l2 << 8); | ||
958 | isdn_command(&cmd); | ||
959 | cmd.driver = info->isdn_driver; | ||
960 | cmd.command = ISDN_CMD_SETL3; | ||
961 | cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8); | ||
962 | isdn_command(&cmd); | ||
963 | cmd.driver = info->isdn_driver; | ||
964 | cmd.arg = info->isdn_channel; | ||
965 | cmd.parm.cmsg.Length = l+14; | ||
966 | cmd.parm.cmsg.Command = CAPI_MANUFACTURER; | ||
967 | cmd.parm.cmsg.Subcommand = CAPI_REQ; | ||
968 | cmd.parm.cmsg.adr.Controller = info->isdn_driver + 1; | ||
969 | cmd.parm.cmsg.para[0] = l+1; | ||
970 | strncpy(&cmd.parm.cmsg.para[1], msg, l); | ||
971 | cmd.parm.cmsg.para[l+1] = 0xd; | ||
972 | cmd.command =CAPI_PUT_MESSAGE; | ||
973 | /* info->dialing = 1; | ||
974 | strcpy(dev->num[i], n); | ||
975 | isdn_info_update(); | ||
976 | */ | ||
977 | isdn_command(&cmd); | ||
978 | } | ||
979 | } | ||
980 | |||
981 | static inline int | ||
982 | isdn_tty_paranoia_check(modem_info *info, char *name, const char *routine) | ||
983 | { | ||
984 | #ifdef MODEM_PARANOIA_CHECK | ||
985 | if (!info) { | ||
986 | printk(KERN_WARNING "isdn_tty: null info_struct for %s in %s\n", | ||
987 | name, routine); | ||
988 | return 1; | ||
989 | } | ||
990 | if (info->magic != ISDN_ASYNC_MAGIC) { | ||
991 | printk(KERN_WARNING "isdn_tty: bad magic for modem struct %s in %s\n", | ||
992 | name, routine); | ||
993 | return 1; | ||
994 | } | ||
995 | #endif | ||
996 | return 0; | ||
997 | } | ||
998 | |||
999 | /* | ||
1000 | * This routine is called to set the UART divisor registers to match | ||
1001 | * the specified baud rate for a serial port. | ||
1002 | */ | ||
1003 | static void | ||
1004 | isdn_tty_change_speed(modem_info * info) | ||
1005 | { | ||
1006 | uint cflag, | ||
1007 | cval, | ||
1008 | fcr, | ||
1009 | quot; | ||
1010 | int i; | ||
1011 | |||
1012 | if (!info->tty || !info->tty->termios) | ||
1013 | return; | ||
1014 | cflag = info->tty->termios->c_cflag; | ||
1015 | |||
1016 | quot = i = cflag & CBAUD; | ||
1017 | if (i & CBAUDEX) { | ||
1018 | i &= ~CBAUDEX; | ||
1019 | if (i < 1 || i > 2) | ||
1020 | info->tty->termios->c_cflag &= ~CBAUDEX; | ||
1021 | else | ||
1022 | i += 15; | ||
1023 | } | ||
1024 | if (quot) { | ||
1025 | info->mcr |= UART_MCR_DTR; | ||
1026 | isdn_tty_modem_ncarrier(info); | ||
1027 | } else { | ||
1028 | info->mcr &= ~UART_MCR_DTR; | ||
1029 | if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) { | ||
1030 | #ifdef ISDN_DEBUG_MODEM_HUP | ||
1031 | printk(KERN_DEBUG "Mhup in changespeed\n"); | ||
1032 | #endif | ||
1033 | if (info->online) | ||
1034 | info->ncarrier = 1; | ||
1035 | isdn_tty_modem_reset_regs(info, 0); | ||
1036 | isdn_tty_modem_hup(info, 1); | ||
1037 | } | ||
1038 | return; | ||
1039 | } | ||
1040 | /* byte size and parity */ | ||
1041 | cval = cflag & (CSIZE | CSTOPB); | ||
1042 | cval >>= 4; | ||
1043 | if (cflag & PARENB) | ||
1044 | cval |= UART_LCR_PARITY; | ||
1045 | if (!(cflag & PARODD)) | ||
1046 | cval |= UART_LCR_EPAR; | ||
1047 | fcr = 0; | ||
1048 | |||
1049 | /* CTS flow control flag and modem status interrupts */ | ||
1050 | if (cflag & CRTSCTS) { | ||
1051 | info->flags |= ISDN_ASYNC_CTS_FLOW; | ||
1052 | } else | ||
1053 | info->flags &= ~ISDN_ASYNC_CTS_FLOW; | ||
1054 | if (cflag & CLOCAL) | ||
1055 | info->flags &= ~ISDN_ASYNC_CHECK_CD; | ||
1056 | else { | ||
1057 | info->flags |= ISDN_ASYNC_CHECK_CD; | ||
1058 | } | ||
1059 | } | ||
1060 | |||
1061 | static int | ||
1062 | isdn_tty_startup(modem_info * info) | ||
1063 | { | ||
1064 | if (info->flags & ISDN_ASYNC_INITIALIZED) | ||
1065 | return 0; | ||
1066 | isdn_lock_drivers(); | ||
1067 | #ifdef ISDN_DEBUG_MODEM_OPEN | ||
1068 | printk(KERN_DEBUG "starting up ttyi%d ...\n", info->line); | ||
1069 | #endif | ||
1070 | /* | ||
1071 | * Now, initialize the UART | ||
1072 | */ | ||
1073 | info->mcr = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2; | ||
1074 | if (info->tty) | ||
1075 | clear_bit(TTY_IO_ERROR, &info->tty->flags); | ||
1076 | /* | ||
1077 | * and set the speed of the serial port | ||
1078 | */ | ||
1079 | isdn_tty_change_speed(info); | ||
1080 | |||
1081 | info->flags |= ISDN_ASYNC_INITIALIZED; | ||
1082 | info->msr |= (UART_MSR_DSR | UART_MSR_CTS); | ||
1083 | info->send_outstanding = 0; | ||
1084 | return 0; | ||
1085 | } | ||
1086 | |||
1087 | /* | ||
1088 | * This routine will shutdown a serial port; interrupts are disabled, and | ||
1089 | * DTR is dropped if the hangup on close termio flag is on. | ||
1090 | */ | ||
1091 | static void | ||
1092 | isdn_tty_shutdown(modem_info * info) | ||
1093 | { | ||
1094 | if (!(info->flags & ISDN_ASYNC_INITIALIZED)) | ||
1095 | return; | ||
1096 | #ifdef ISDN_DEBUG_MODEM_OPEN | ||
1097 | printk(KERN_DEBUG "Shutting down isdnmodem port %d ....\n", info->line); | ||
1098 | #endif | ||
1099 | isdn_unlock_drivers(); | ||
1100 | info->msr &= ~UART_MSR_RI; | ||
1101 | if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { | ||
1102 | info->mcr &= ~(UART_MCR_DTR | UART_MCR_RTS); | ||
1103 | if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) { | ||
1104 | isdn_tty_modem_reset_regs(info, 0); | ||
1105 | #ifdef ISDN_DEBUG_MODEM_HUP | ||
1106 | printk(KERN_DEBUG "Mhup in isdn_tty_shutdown\n"); | ||
1107 | #endif | ||
1108 | isdn_tty_modem_hup(info, 1); | ||
1109 | } | ||
1110 | } | ||
1111 | if (info->tty) | ||
1112 | set_bit(TTY_IO_ERROR, &info->tty->flags); | ||
1113 | |||
1114 | info->flags &= ~ISDN_ASYNC_INITIALIZED; | ||
1115 | } | ||
1116 | |||
1117 | /* isdn_tty_write() is the main send-routine. It is called from the upper | ||
1118 | * levels within the kernel to perform sending data. Depending on the | ||
1119 | * online-flag it either directs output to the at-command-interpreter or | ||
1120 | * to the lower level. Additional tasks done here: | ||
1121 | * - If online, check for escape-sequence (+++) | ||
1122 | * - If sending audio-data, call isdn_tty_DLEdown() to parse DLE-codes. | ||
1123 | * - If receiving audio-data, call isdn_tty_end_vrx() to abort if needed. | ||
1124 | * - If dialing, abort dial. | ||
1125 | */ | ||
1126 | static int | ||
1127 | isdn_tty_write(struct tty_struct *tty, const u_char * buf, int count) | ||
1128 | { | ||
1129 | int c; | ||
1130 | int total = 0; | ||
1131 | modem_info *info = (modem_info *) tty->driver_data; | ||
1132 | atemu *m = &info->emu; | ||
1133 | |||
1134 | if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_write")) | ||
1135 | return 0; | ||
1136 | /* See isdn_tty_senddown() */ | ||
1137 | atomic_inc(&info->xmit_lock); | ||
1138 | while (1) { | ||
1139 | c = count; | ||
1140 | if (c > info->xmit_size - info->xmit_count) | ||
1141 | c = info->xmit_size - info->xmit_count; | ||
1142 | if (info->isdn_driver >= 0 && c > dev->drv[info->isdn_driver]->maxbufsize) | ||
1143 | c = dev->drv[info->isdn_driver]->maxbufsize; | ||
1144 | if (c <= 0) | ||
1145 | break; | ||
1146 | if ((info->online > 1) | ||
1147 | #ifdef CONFIG_ISDN_AUDIO | ||
1148 | || (info->vonline & 3) | ||
1149 | #endif | ||
1150 | ) { | ||
1151 | #ifdef CONFIG_ISDN_AUDIO | ||
1152 | if (!info->vonline) | ||
1153 | #endif | ||
1154 | isdn_tty_check_esc(buf, m->mdmreg[REG_ESC], c, | ||
1155 | &(m->pluscount), | ||
1156 | &(m->lastplus)); | ||
1157 | memcpy(&(info->xmit_buf[info->xmit_count]), buf, c); | ||
1158 | #ifdef CONFIG_ISDN_AUDIO | ||
1159 | if (info->vonline) { | ||
1160 | int cc = isdn_tty_handleDLEdown(info, m, c); | ||
1161 | if (info->vonline & 2) { | ||
1162 | if (!cc) { | ||
1163 | /* If DLE decoding results in zero-transmit, but | ||
1164 | * c originally was non-zero, do a wakeup. | ||
1165 | */ | ||
1166 | tty_wakeup(tty); | ||
1167 | info->msr |= UART_MSR_CTS; | ||
1168 | info->lsr |= UART_LSR_TEMT; | ||
1169 | } | ||
1170 | info->xmit_count += cc; | ||
1171 | } | ||
1172 | if ((info->vonline & 3) == 1) { | ||
1173 | /* Do NOT handle Ctrl-Q or Ctrl-S | ||
1174 | * when in full-duplex audio mode. | ||
1175 | */ | ||
1176 | if (isdn_tty_end_vrx(buf, c)) { | ||
1177 | info->vonline &= ~1; | ||
1178 | #ifdef ISDN_DEBUG_MODEM_VOICE | ||
1179 | printk(KERN_DEBUG | ||
1180 | "got !^Q/^S, send DLE-ETX,VCON on ttyI%d\n", | ||
1181 | info->line); | ||
1182 | #endif | ||
1183 | isdn_tty_at_cout("\020\003\r\nVCON\r\n", info); | ||
1184 | } | ||
1185 | } | ||
1186 | } else | ||
1187 | if (TTY_IS_FCLASS1(info)) { | ||
1188 | int cc = isdn_tty_handleDLEdown(info, m, c); | ||
1189 | |||
1190 | if (info->vonline & 4) { /* ETX seen */ | ||
1191 | isdn_ctrl c; | ||
1192 | |||
1193 | c.command = ISDN_CMD_FAXCMD; | ||
1194 | c.driver = info->isdn_driver; | ||
1195 | c.arg = info->isdn_channel; | ||
1196 | c.parm.aux.cmd = ISDN_FAX_CLASS1_CTRL; | ||
1197 | c.parm.aux.subcmd = ETX; | ||
1198 | isdn_command(&c); | ||
1199 | } | ||
1200 | info->vonline = 0; | ||
1201 | #ifdef ISDN_DEBUG_MODEM_VOICE | ||
1202 | printk(KERN_DEBUG "fax dle cc/c %d/%d\n", cc, c); | ||
1203 | #endif | ||
1204 | info->xmit_count += cc; | ||
1205 | } else | ||
1206 | #endif | ||
1207 | info->xmit_count += c; | ||
1208 | } else { | ||
1209 | info->msr |= UART_MSR_CTS; | ||
1210 | info->lsr |= UART_LSR_TEMT; | ||
1211 | if (info->dialing) { | ||
1212 | info->dialing = 0; | ||
1213 | #ifdef ISDN_DEBUG_MODEM_HUP | ||
1214 | printk(KERN_DEBUG "Mhup in isdn_tty_write\n"); | ||
1215 | #endif | ||
1216 | isdn_tty_modem_result(RESULT_NO_CARRIER, info); | ||
1217 | isdn_tty_modem_hup(info, 1); | ||
1218 | } else | ||
1219 | c = isdn_tty_edit_at(buf, c, info); | ||
1220 | } | ||
1221 | buf += c; | ||
1222 | count -= c; | ||
1223 | total += c; | ||
1224 | } | ||
1225 | atomic_dec(&info->xmit_lock); | ||
1226 | if ((info->xmit_count) || (skb_queue_len(&info->xmit_queue))) { | ||
1227 | if (m->mdmreg[REG_DXMT] & BIT_DXMT) { | ||
1228 | isdn_tty_senddown(info); | ||
1229 | isdn_tty_tint(info); | ||
1230 | } | ||
1231 | isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1); | ||
1232 | } | ||
1233 | return total; | ||
1234 | } | ||
1235 | |||
1236 | static int | ||
1237 | isdn_tty_write_room(struct tty_struct *tty) | ||
1238 | { | ||
1239 | modem_info *info = (modem_info *) tty->driver_data; | ||
1240 | int ret; | ||
1241 | |||
1242 | if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_write_room")) | ||
1243 | return 0; | ||
1244 | if (!info->online) | ||
1245 | return info->xmit_size; | ||
1246 | ret = info->xmit_size - info->xmit_count; | ||
1247 | return (ret < 0) ? 0 : ret; | ||
1248 | } | ||
1249 | |||
1250 | static int | ||
1251 | isdn_tty_chars_in_buffer(struct tty_struct *tty) | ||
1252 | { | ||
1253 | modem_info *info = (modem_info *) tty->driver_data; | ||
1254 | |||
1255 | if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_chars_in_buffer")) | ||
1256 | return 0; | ||
1257 | if (!info->online) | ||
1258 | return 0; | ||
1259 | return (info->xmit_count); | ||
1260 | } | ||
1261 | |||
1262 | static void | ||
1263 | isdn_tty_flush_buffer(struct tty_struct *tty) | ||
1264 | { | ||
1265 | modem_info *info; | ||
1266 | |||
1267 | if (!tty) { | ||
1268 | return; | ||
1269 | } | ||
1270 | info = (modem_info *) tty->driver_data; | ||
1271 | if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_flush_buffer")) { | ||
1272 | return; | ||
1273 | } | ||
1274 | isdn_tty_cleanup_xmit(info); | ||
1275 | info->xmit_count = 0; | ||
1276 | wake_up_interruptible(&tty->write_wait); | ||
1277 | tty_wakeup(tty); | ||
1278 | } | ||
1279 | |||
1280 | static void | ||
1281 | isdn_tty_flush_chars(struct tty_struct *tty) | ||
1282 | { | ||
1283 | modem_info *info = (modem_info *) tty->driver_data; | ||
1284 | |||
1285 | if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_flush_chars")) | ||
1286 | return; | ||
1287 | if ((info->xmit_count) || (skb_queue_len(&info->xmit_queue))) | ||
1288 | isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1); | ||
1289 | } | ||
1290 | |||
1291 | /* | ||
1292 | * ------------------------------------------------------------ | ||
1293 | * isdn_tty_throttle() | ||
1294 | * | ||
1295 | * This routine is called by the upper-layer tty layer to signal that | ||
1296 | * incoming characters should be throttled. | ||
1297 | * ------------------------------------------------------------ | ||
1298 | */ | ||
1299 | static void | ||
1300 | isdn_tty_throttle(struct tty_struct *tty) | ||
1301 | { | ||
1302 | modem_info *info = (modem_info *) tty->driver_data; | ||
1303 | |||
1304 | if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_throttle")) | ||
1305 | return; | ||
1306 | if (I_IXOFF(tty)) | ||
1307 | info->x_char = STOP_CHAR(tty); | ||
1308 | info->mcr &= ~UART_MCR_RTS; | ||
1309 | } | ||
1310 | |||
1311 | static void | ||
1312 | isdn_tty_unthrottle(struct tty_struct *tty) | ||
1313 | { | ||
1314 | modem_info *info = (modem_info *) tty->driver_data; | ||
1315 | |||
1316 | if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_unthrottle")) | ||
1317 | return; | ||
1318 | if (I_IXOFF(tty)) { | ||
1319 | if (info->x_char) | ||
1320 | info->x_char = 0; | ||
1321 | else | ||
1322 | info->x_char = START_CHAR(tty); | ||
1323 | } | ||
1324 | info->mcr |= UART_MCR_RTS; | ||
1325 | } | ||
1326 | |||
1327 | /* | ||
1328 | * ------------------------------------------------------------ | ||
1329 | * isdn_tty_ioctl() and friends | ||
1330 | * ------------------------------------------------------------ | ||
1331 | */ | ||
1332 | |||
1333 | /* | ||
1334 | * isdn_tty_get_lsr_info - get line status register info | ||
1335 | * | ||
1336 | * Purpose: Let user call ioctl() to get info when the UART physically | ||
1337 | * is emptied. On bus types like RS485, the transmitter must | ||
1338 | * release the bus after transmitting. This must be done when | ||
1339 | * the transmit shift register is empty, not be done when the | ||
1340 | * transmit holding register is empty. This functionality | ||
1341 | * allows RS485 driver to be written in user space. | ||
1342 | */ | ||
1343 | static int | ||
1344 | isdn_tty_get_lsr_info(modem_info * info, uint __user * value) | ||
1345 | { | ||
1346 | u_char status; | ||
1347 | uint result; | ||
1348 | |||
1349 | status = info->lsr; | ||
1350 | result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); | ||
1351 | return put_user(result, value); | ||
1352 | } | ||
1353 | |||
1354 | |||
1355 | static int | ||
1356 | isdn_tty_tiocmget(struct tty_struct *tty, struct file *file) | ||
1357 | { | ||
1358 | modem_info *info = (modem_info *) tty->driver_data; | ||
1359 | u_char control, status; | ||
1360 | |||
1361 | if (isdn_tty_paranoia_check(info, tty->name, __FUNCTION__)) | ||
1362 | return -ENODEV; | ||
1363 | if (tty->flags & (1 << TTY_IO_ERROR)) | ||
1364 | return -EIO; | ||
1365 | |||
1366 | #ifdef ISDN_DEBUG_MODEM_IOCTL | ||
1367 | printk(KERN_DEBUG "ttyI%d ioctl TIOCMGET\n", info->line); | ||
1368 | #endif | ||
1369 | |||
1370 | control = info->mcr; | ||
1371 | status = info->msr; | ||
1372 | return ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) | ||
1373 | | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) | ||
1374 | | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0) | ||
1375 | | ((status & UART_MSR_RI) ? TIOCM_RNG : 0) | ||
1376 | | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0) | ||
1377 | | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0); | ||
1378 | } | ||
1379 | |||
1380 | static int | ||
1381 | isdn_tty_tiocmset(struct tty_struct *tty, struct file *file, | ||
1382 | unsigned int set, unsigned int clear) | ||
1383 | { | ||
1384 | modem_info *info = (modem_info *) tty->driver_data; | ||
1385 | |||
1386 | if (isdn_tty_paranoia_check(info, tty->name, __FUNCTION__)) | ||
1387 | return -ENODEV; | ||
1388 | if (tty->flags & (1 << TTY_IO_ERROR)) | ||
1389 | return -EIO; | ||
1390 | |||
1391 | #ifdef ISDN_DEBUG_MODEM_IOCTL | ||
1392 | printk(KERN_DEBUG "ttyI%d ioctl TIOCMxxx: %x %x\n", info->line, set, clear); | ||
1393 | #endif | ||
1394 | |||
1395 | if (set & TIOCM_RTS) | ||
1396 | info->mcr |= UART_MCR_RTS; | ||
1397 | if (set & TIOCM_DTR) { | ||
1398 | info->mcr |= UART_MCR_DTR; | ||
1399 | isdn_tty_modem_ncarrier(info); | ||
1400 | } | ||
1401 | |||
1402 | if (clear & TIOCM_RTS) | ||
1403 | info->mcr &= ~UART_MCR_RTS; | ||
1404 | if (clear & TIOCM_DTR) { | ||
1405 | info->mcr &= ~UART_MCR_DTR; | ||
1406 | if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) { | ||
1407 | isdn_tty_modem_reset_regs(info, 0); | ||
1408 | #ifdef ISDN_DEBUG_MODEM_HUP | ||
1409 | printk(KERN_DEBUG "Mhup in TIOCMSET\n"); | ||
1410 | #endif | ||
1411 | if (info->online) | ||
1412 | info->ncarrier = 1; | ||
1413 | isdn_tty_modem_hup(info, 1); | ||
1414 | } | ||
1415 | } | ||
1416 | return 0; | ||
1417 | } | ||
1418 | |||
1419 | static int | ||
1420 | isdn_tty_ioctl(struct tty_struct *tty, struct file *file, | ||
1421 | uint cmd, ulong arg) | ||
1422 | { | ||
1423 | modem_info *info = (modem_info *) tty->driver_data; | ||
1424 | int retval; | ||
1425 | |||
1426 | if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_ioctl")) | ||
1427 | return -ENODEV; | ||
1428 | if (tty->flags & (1 << TTY_IO_ERROR)) | ||
1429 | return -EIO; | ||
1430 | switch (cmd) { | ||
1431 | case TCSBRK: /* SVID version: non-zero arg --> no break */ | ||
1432 | #ifdef ISDN_DEBUG_MODEM_IOCTL | ||
1433 | printk(KERN_DEBUG "ttyI%d ioctl TCSBRK\n", info->line); | ||
1434 | #endif | ||
1435 | retval = tty_check_change(tty); | ||
1436 | if (retval) | ||
1437 | return retval; | ||
1438 | tty_wait_until_sent(tty, 0); | ||
1439 | return 0; | ||
1440 | case TCSBRKP: /* support for POSIX tcsendbreak() */ | ||
1441 | #ifdef ISDN_DEBUG_MODEM_IOCTL | ||
1442 | printk(KERN_DEBUG "ttyI%d ioctl TCSBRKP\n", info->line); | ||
1443 | #endif | ||
1444 | retval = tty_check_change(tty); | ||
1445 | if (retval) | ||
1446 | return retval; | ||
1447 | tty_wait_until_sent(tty, 0); | ||
1448 | return 0; | ||
1449 | case TIOCGSOFTCAR: | ||
1450 | #ifdef ISDN_DEBUG_MODEM_IOCTL | ||
1451 | printk(KERN_DEBUG "ttyI%d ioctl TIOCGSOFTCAR\n", info->line); | ||
1452 | #endif | ||
1453 | return put_user(C_CLOCAL(tty) ? 1 : 0, (ulong __user *) arg); | ||
1454 | case TIOCSSOFTCAR: | ||
1455 | #ifdef ISDN_DEBUG_MODEM_IOCTL | ||
1456 | printk(KERN_DEBUG "ttyI%d ioctl TIOCSSOFTCAR\n", info->line); | ||
1457 | #endif | ||
1458 | if (get_user(arg, (ulong __user *) arg)) | ||
1459 | return -EFAULT; | ||
1460 | tty->termios->c_cflag = | ||
1461 | ((tty->termios->c_cflag & ~CLOCAL) | | ||
1462 | (arg ? CLOCAL : 0)); | ||
1463 | return 0; | ||
1464 | case TIOCSERGETLSR: /* Get line status register */ | ||
1465 | #ifdef ISDN_DEBUG_MODEM_IOCTL | ||
1466 | printk(KERN_DEBUG "ttyI%d ioctl TIOCSERGETLSR\n", info->line); | ||
1467 | #endif | ||
1468 | return isdn_tty_get_lsr_info(info, (uint __user *) arg); | ||
1469 | default: | ||
1470 | #ifdef ISDN_DEBUG_MODEM_IOCTL | ||
1471 | printk(KERN_DEBUG "UNKNOWN ioctl 0x%08x on ttyi%d\n", cmd, info->line); | ||
1472 | #endif | ||
1473 | return -ENOIOCTLCMD; | ||
1474 | } | ||
1475 | return 0; | ||
1476 | } | ||
1477 | |||
1478 | static void | ||
1479 | isdn_tty_set_termios(struct tty_struct *tty, struct termios *old_termios) | ||
1480 | { | ||
1481 | modem_info *info = (modem_info *) tty->driver_data; | ||
1482 | |||
1483 | if (!old_termios) | ||
1484 | isdn_tty_change_speed(info); | ||
1485 | else { | ||
1486 | if (tty->termios->c_cflag == old_termios->c_cflag) | ||
1487 | return; | ||
1488 | isdn_tty_change_speed(info); | ||
1489 | if ((old_termios->c_cflag & CRTSCTS) && | ||
1490 | !(tty->termios->c_cflag & CRTSCTS)) { | ||
1491 | tty->hw_stopped = 0; | ||
1492 | } | ||
1493 | } | ||
1494 | } | ||
1495 | |||
1496 | /* | ||
1497 | * ------------------------------------------------------------ | ||
1498 | * isdn_tty_open() and friends | ||
1499 | * ------------------------------------------------------------ | ||
1500 | */ | ||
1501 | static int | ||
1502 | isdn_tty_block_til_ready(struct tty_struct *tty, struct file *filp, modem_info * info) | ||
1503 | { | ||
1504 | DECLARE_WAITQUEUE(wait, NULL); | ||
1505 | int do_clocal = 0; | ||
1506 | int retval; | ||
1507 | |||
1508 | /* | ||
1509 | * If the device is in the middle of being closed, then block | ||
1510 | * until it's done, and then try again. | ||
1511 | */ | ||
1512 | if (tty_hung_up_p(filp) || | ||
1513 | (info->flags & ISDN_ASYNC_CLOSING)) { | ||
1514 | if (info->flags & ISDN_ASYNC_CLOSING) | ||
1515 | interruptible_sleep_on(&info->close_wait); | ||
1516 | #ifdef MODEM_DO_RESTART | ||
1517 | if (info->flags & ISDN_ASYNC_HUP_NOTIFY) | ||
1518 | return -EAGAIN; | ||
1519 | else | ||
1520 | return -ERESTARTSYS; | ||
1521 | #else | ||
1522 | return -EAGAIN; | ||
1523 | #endif | ||
1524 | } | ||
1525 | /* | ||
1526 | * If non-blocking mode is set, then make the check up front | ||
1527 | * and then exit. | ||
1528 | */ | ||
1529 | if ((filp->f_flags & O_NONBLOCK) || | ||
1530 | (tty->flags & (1 << TTY_IO_ERROR))) { | ||
1531 | if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) | ||
1532 | return -EBUSY; | ||
1533 | info->flags |= ISDN_ASYNC_NORMAL_ACTIVE; | ||
1534 | return 0; | ||
1535 | } | ||
1536 | if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) { | ||
1537 | if (info->normal_termios.c_cflag & CLOCAL) | ||
1538 | do_clocal = 1; | ||
1539 | } else { | ||
1540 | if (tty->termios->c_cflag & CLOCAL) | ||
1541 | do_clocal = 1; | ||
1542 | } | ||
1543 | /* | ||
1544 | * Block waiting for the carrier detect and the line to become | ||
1545 | * free (i.e., not in use by the callout). While we are in | ||
1546 | * this loop, info->count is dropped by one, so that | ||
1547 | * isdn_tty_close() knows when to free things. We restore it upon | ||
1548 | * exit, either normal or abnormal. | ||
1549 | */ | ||
1550 | retval = 0; | ||
1551 | add_wait_queue(&info->open_wait, &wait); | ||
1552 | #ifdef ISDN_DEBUG_MODEM_OPEN | ||
1553 | printk(KERN_DEBUG "isdn_tty_block_til_ready before block: ttyi%d, count = %d\n", | ||
1554 | info->line, info->count); | ||
1555 | #endif | ||
1556 | if (!(tty_hung_up_p(filp))) | ||
1557 | info->count--; | ||
1558 | info->blocked_open++; | ||
1559 | while (1) { | ||
1560 | set_current_state(TASK_INTERRUPTIBLE); | ||
1561 | if (tty_hung_up_p(filp) || | ||
1562 | !(info->flags & ISDN_ASYNC_INITIALIZED)) { | ||
1563 | #ifdef MODEM_DO_RESTART | ||
1564 | if (info->flags & ISDN_ASYNC_HUP_NOTIFY) | ||
1565 | retval = -EAGAIN; | ||
1566 | else | ||
1567 | retval = -ERESTARTSYS; | ||
1568 | #else | ||
1569 | retval = -EAGAIN; | ||
1570 | #endif | ||
1571 | break; | ||
1572 | } | ||
1573 | if (!(info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) && | ||
1574 | !(info->flags & ISDN_ASYNC_CLOSING) && | ||
1575 | (do_clocal || (info->msr & UART_MSR_DCD))) { | ||
1576 | break; | ||
1577 | } | ||
1578 | if (signal_pending(current)) { | ||
1579 | retval = -ERESTARTSYS; | ||
1580 | break; | ||
1581 | } | ||
1582 | #ifdef ISDN_DEBUG_MODEM_OPEN | ||
1583 | printk(KERN_DEBUG "isdn_tty_block_til_ready blocking: ttyi%d, count = %d\n", | ||
1584 | info->line, info->count); | ||
1585 | #endif | ||
1586 | schedule(); | ||
1587 | } | ||
1588 | current->state = TASK_RUNNING; | ||
1589 | remove_wait_queue(&info->open_wait, &wait); | ||
1590 | if (!tty_hung_up_p(filp)) | ||
1591 | info->count++; | ||
1592 | info->blocked_open--; | ||
1593 | #ifdef ISDN_DEBUG_MODEM_OPEN | ||
1594 | printk(KERN_DEBUG "isdn_tty_block_til_ready after blocking: ttyi%d, count = %d\n", | ||
1595 | info->line, info->count); | ||
1596 | #endif | ||
1597 | if (retval) | ||
1598 | return retval; | ||
1599 | info->flags |= ISDN_ASYNC_NORMAL_ACTIVE; | ||
1600 | return 0; | ||
1601 | } | ||
1602 | |||
1603 | /* | ||
1604 | * This routine is called whenever a serial port is opened. It | ||
1605 | * enables interrupts for a serial port, linking in its async structure into | ||
1606 | * the IRQ chain. It also performs the serial-specific | ||
1607 | * initialization for the tty structure. | ||
1608 | */ | ||
1609 | static int | ||
1610 | isdn_tty_open(struct tty_struct *tty, struct file *filp) | ||
1611 | { | ||
1612 | modem_info *info; | ||
1613 | int retval, line; | ||
1614 | |||
1615 | line = tty->index; | ||
1616 | if (line < 0 || line > ISDN_MAX_CHANNELS) | ||
1617 | return -ENODEV; | ||
1618 | info = &dev->mdm.info[line]; | ||
1619 | if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_open")) | ||
1620 | return -ENODEV; | ||
1621 | if (!try_module_get(info->owner)) { | ||
1622 | printk(KERN_WARNING "%s: cannot reserve module\n", __FUNCTION__); | ||
1623 | return -ENODEV; | ||
1624 | } | ||
1625 | #ifdef ISDN_DEBUG_MODEM_OPEN | ||
1626 | printk(KERN_DEBUG "isdn_tty_open %s, count = %d\n", tty->name, | ||
1627 | info->count); | ||
1628 | #endif | ||
1629 | info->count++; | ||
1630 | tty->driver_data = info; | ||
1631 | info->tty = tty; | ||
1632 | /* | ||
1633 | * Start up serial port | ||
1634 | */ | ||
1635 | retval = isdn_tty_startup(info); | ||
1636 | if (retval) { | ||
1637 | #ifdef ISDN_DEBUG_MODEM_OPEN | ||
1638 | printk(KERN_DEBUG "isdn_tty_open return after startup\n"); | ||
1639 | #endif | ||
1640 | module_put(info->owner); | ||
1641 | return retval; | ||
1642 | } | ||
1643 | retval = isdn_tty_block_til_ready(tty, filp, info); | ||
1644 | if (retval) { | ||
1645 | #ifdef ISDN_DEBUG_MODEM_OPEN | ||
1646 | printk(KERN_DEBUG "isdn_tty_open return after isdn_tty_block_til_ready \n"); | ||
1647 | #endif | ||
1648 | module_put(info->owner); | ||
1649 | return retval; | ||
1650 | } | ||
1651 | #ifdef ISDN_DEBUG_MODEM_OPEN | ||
1652 | printk(KERN_DEBUG "isdn_tty_open ttyi%d successful...\n", info->line); | ||
1653 | #endif | ||
1654 | dev->modempoll++; | ||
1655 | #ifdef ISDN_DEBUG_MODEM_OPEN | ||
1656 | printk(KERN_DEBUG "isdn_tty_open normal exit\n"); | ||
1657 | #endif | ||
1658 | return 0; | ||
1659 | } | ||
1660 | |||
1661 | static void | ||
1662 | isdn_tty_close(struct tty_struct *tty, struct file *filp) | ||
1663 | { | ||
1664 | modem_info *info = (modem_info *) tty->driver_data; | ||
1665 | ulong timeout; | ||
1666 | |||
1667 | if (!info || isdn_tty_paranoia_check(info, tty->name, "isdn_tty_close")) | ||
1668 | return; | ||
1669 | if (tty_hung_up_p(filp)) { | ||
1670 | #ifdef ISDN_DEBUG_MODEM_OPEN | ||
1671 | printk(KERN_DEBUG "isdn_tty_close return after tty_hung_up_p\n"); | ||
1672 | #endif | ||
1673 | return; | ||
1674 | } | ||
1675 | if ((tty->count == 1) && (info->count != 1)) { | ||
1676 | /* | ||
1677 | * Uh, oh. tty->count is 1, which means that the tty | ||
1678 | * structure will be freed. Info->count should always | ||
1679 | * be one in these conditions. If it's greater than | ||
1680 | * one, we've got real problems, since it means the | ||
1681 | * serial port won't be shutdown. | ||
1682 | */ | ||
1683 | printk(KERN_ERR "isdn_tty_close: bad port count; tty->count is 1, " | ||
1684 | "info->count is %d\n", info->count); | ||
1685 | info->count = 1; | ||
1686 | } | ||
1687 | if (--info->count < 0) { | ||
1688 | printk(KERN_ERR "isdn_tty_close: bad port count for ttyi%d: %d\n", | ||
1689 | info->line, info->count); | ||
1690 | info->count = 0; | ||
1691 | } | ||
1692 | if (info->count) { | ||
1693 | #ifdef ISDN_DEBUG_MODEM_OPEN | ||
1694 | printk(KERN_DEBUG "isdn_tty_close after info->count != 0\n"); | ||
1695 | #endif | ||
1696 | return; | ||
1697 | } | ||
1698 | info->flags |= ISDN_ASYNC_CLOSING; | ||
1699 | /* | ||
1700 | * Save the termios structure, since this port may have | ||
1701 | * separate termios for callout and dialin. | ||
1702 | */ | ||
1703 | if (info->flags & ISDN_ASYNC_NORMAL_ACTIVE) | ||
1704 | info->normal_termios = *tty->termios; | ||
1705 | if (info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) | ||
1706 | info->callout_termios = *tty->termios; | ||
1707 | |||
1708 | tty->closing = 1; | ||
1709 | /* | ||
1710 | * At this point we stop accepting input. To do this, we | ||
1711 | * disable the receive line status interrupts, and tell the | ||
1712 | * interrupt driver to stop checking the data ready bit in the | ||
1713 | * line status register. | ||
1714 | */ | ||
1715 | if (info->flags & ISDN_ASYNC_INITIALIZED) { | ||
1716 | tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */ | ||
1717 | /* | ||
1718 | * Before we drop DTR, make sure the UART transmitter | ||
1719 | * has completely drained; this is especially | ||
1720 | * important if there is a transmit FIFO! | ||
1721 | */ | ||
1722 | timeout = jiffies + HZ; | ||
1723 | while (!(info->lsr & UART_LSR_TEMT)) { | ||
1724 | set_current_state(TASK_INTERRUPTIBLE); | ||
1725 | schedule_timeout(20); | ||
1726 | if (time_after(jiffies,timeout)) | ||
1727 | break; | ||
1728 | } | ||
1729 | } | ||
1730 | dev->modempoll--; | ||
1731 | isdn_tty_shutdown(info); | ||
1732 | |||
1733 | if (tty->driver->flush_buffer) | ||
1734 | tty->driver->flush_buffer(tty); | ||
1735 | tty_ldisc_flush(tty); | ||
1736 | info->tty = NULL; | ||
1737 | info->ncarrier = 0; | ||
1738 | tty->closing = 0; | ||
1739 | module_put(info->owner); | ||
1740 | if (info->blocked_open) { | ||
1741 | msleep_interruptible(500); | ||
1742 | wake_up_interruptible(&info->open_wait); | ||
1743 | } | ||
1744 | info->flags &= ~(ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CLOSING); | ||
1745 | wake_up_interruptible(&info->close_wait); | ||
1746 | #ifdef ISDN_DEBUG_MODEM_OPEN | ||
1747 | printk(KERN_DEBUG "isdn_tty_close normal exit\n"); | ||
1748 | #endif | ||
1749 | } | ||
1750 | |||
1751 | /* | ||
1752 | * isdn_tty_hangup() --- called by tty_hangup() when a hangup is signaled. | ||
1753 | */ | ||
1754 | static void | ||
1755 | isdn_tty_hangup(struct tty_struct *tty) | ||
1756 | { | ||
1757 | modem_info *info = (modem_info *) tty->driver_data; | ||
1758 | |||
1759 | if (isdn_tty_paranoia_check(info, tty->name, "isdn_tty_hangup")) | ||
1760 | return; | ||
1761 | isdn_tty_shutdown(info); | ||
1762 | info->count = 0; | ||
1763 | info->flags &= ~(ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE); | ||
1764 | info->tty = NULL; | ||
1765 | wake_up_interruptible(&info->open_wait); | ||
1766 | } | ||
1767 | |||
1768 | /* This routine initializes all emulator-data. | ||
1769 | */ | ||
1770 | static void | ||
1771 | isdn_tty_reset_profile(atemu * m) | ||
1772 | { | ||
1773 | m->profile[0] = 0; | ||
1774 | m->profile[1] = 0; | ||
1775 | m->profile[2] = 43; | ||
1776 | m->profile[3] = 13; | ||
1777 | m->profile[4] = 10; | ||
1778 | m->profile[5] = 8; | ||
1779 | m->profile[6] = 3; | ||
1780 | m->profile[7] = 60; | ||
1781 | m->profile[8] = 2; | ||
1782 | m->profile[9] = 6; | ||
1783 | m->profile[10] = 7; | ||
1784 | m->profile[11] = 70; | ||
1785 | m->profile[12] = 0x45; | ||
1786 | m->profile[13] = 4; | ||
1787 | m->profile[14] = ISDN_PROTO_L2_X75I; | ||
1788 | m->profile[15] = ISDN_PROTO_L3_TRANS; | ||
1789 | m->profile[16] = ISDN_SERIAL_XMIT_SIZE / 16; | ||
1790 | m->profile[17] = ISDN_MODEM_WINSIZE; | ||
1791 | m->profile[18] = 4; | ||
1792 | m->profile[19] = 0; | ||
1793 | m->profile[20] = 0; | ||
1794 | m->profile[23] = 0; | ||
1795 | m->pmsn[0] = '\0'; | ||
1796 | m->plmsn[0] = '\0'; | ||
1797 | } | ||
1798 | |||
1799 | #ifdef CONFIG_ISDN_AUDIO | ||
1800 | static void | ||
1801 | isdn_tty_modem_reset_vpar(atemu * m) | ||
1802 | { | ||
1803 | m->vpar[0] = 2; /* Voice-device (2 = phone line) */ | ||
1804 | m->vpar[1] = 0; /* Silence detection level (0 = none ) */ | ||
1805 | m->vpar[2] = 70; /* Silence interval (7 sec. ) */ | ||
1806 | m->vpar[3] = 2; /* Compression type (1 = ADPCM-2 ) */ | ||
1807 | m->vpar[4] = 0; /* DTMF detection level (0 = softcode ) */ | ||
1808 | m->vpar[5] = 8; /* DTMF interval (8 * 5 ms. ) */ | ||
1809 | } | ||
1810 | #endif | ||
1811 | |||
1812 | #ifdef CONFIG_ISDN_TTY_FAX | ||
1813 | static void | ||
1814 | isdn_tty_modem_reset_faxpar(modem_info * info) | ||
1815 | { | ||
1816 | T30_s *f = info->fax; | ||
1817 | |||
1818 | f->code = 0; | ||
1819 | f->phase = ISDN_FAX_PHASE_IDLE; | ||
1820 | f->direction = 0; | ||
1821 | f->resolution = 1; /* fine */ | ||
1822 | f->rate = 5; /* 14400 bit/s */ | ||
1823 | f->width = 0; | ||
1824 | f->length = 0; | ||
1825 | f->compression = 0; | ||
1826 | f->ecm = 0; | ||
1827 | f->binary = 0; | ||
1828 | f->scantime = 0; | ||
1829 | memset(&f->id[0], 32, FAXIDLEN - 1); | ||
1830 | f->id[FAXIDLEN - 1] = 0; | ||
1831 | f->badlin = 0; | ||
1832 | f->badmul = 0; | ||
1833 | f->bor = 0; | ||
1834 | f->nbc = 0; | ||
1835 | f->cq = 0; | ||
1836 | f->cr = 0; | ||
1837 | f->ctcrty = 0; | ||
1838 | f->minsp = 0; | ||
1839 | f->phcto = 30; | ||
1840 | f->rel = 0; | ||
1841 | memset(&f->pollid[0], 32, FAXIDLEN - 1); | ||
1842 | f->pollid[FAXIDLEN - 1] = 0; | ||
1843 | } | ||
1844 | #endif | ||
1845 | |||
1846 | static void | ||
1847 | isdn_tty_modem_reset_regs(modem_info * info, int force) | ||
1848 | { | ||
1849 | atemu *m = &info->emu; | ||
1850 | if ((m->mdmreg[REG_DTRR] & BIT_DTRR) || force) { | ||
1851 | memcpy(m->mdmreg, m->profile, ISDN_MODEM_NUMREG); | ||
1852 | memcpy(m->msn, m->pmsn, ISDN_MSNLEN); | ||
1853 | memcpy(m->lmsn, m->plmsn, ISDN_LMSNLEN); | ||
1854 | info->xmit_size = m->mdmreg[REG_PSIZE] * 16; | ||
1855 | } | ||
1856 | #ifdef CONFIG_ISDN_AUDIO | ||
1857 | isdn_tty_modem_reset_vpar(m); | ||
1858 | #endif | ||
1859 | #ifdef CONFIG_ISDN_TTY_FAX | ||
1860 | isdn_tty_modem_reset_faxpar(info); | ||
1861 | #endif | ||
1862 | m->mdmcmdl = 0; | ||
1863 | } | ||
1864 | |||
1865 | static void | ||
1866 | modem_write_profile(atemu * m) | ||
1867 | { | ||
1868 | memcpy(m->profile, m->mdmreg, ISDN_MODEM_NUMREG); | ||
1869 | memcpy(m->pmsn, m->msn, ISDN_MSNLEN); | ||
1870 | memcpy(m->plmsn, m->lmsn, ISDN_LMSNLEN); | ||
1871 | if (dev->profd) | ||
1872 | send_sig(SIGIO, dev->profd, 1); | ||
1873 | } | ||
1874 | |||
1875 | static struct tty_operations modem_ops = { | ||
1876 | .open = isdn_tty_open, | ||
1877 | .close = isdn_tty_close, | ||
1878 | .write = isdn_tty_write, | ||
1879 | .flush_chars = isdn_tty_flush_chars, | ||
1880 | .write_room = isdn_tty_write_room, | ||
1881 | .chars_in_buffer = isdn_tty_chars_in_buffer, | ||
1882 | .flush_buffer = isdn_tty_flush_buffer, | ||
1883 | .ioctl = isdn_tty_ioctl, | ||
1884 | .throttle = isdn_tty_throttle, | ||
1885 | .unthrottle = isdn_tty_unthrottle, | ||
1886 | .set_termios = isdn_tty_set_termios, | ||
1887 | .hangup = isdn_tty_hangup, | ||
1888 | .tiocmget = isdn_tty_tiocmget, | ||
1889 | .tiocmset = isdn_tty_tiocmset, | ||
1890 | }; | ||
1891 | |||
1892 | int | ||
1893 | isdn_tty_modem_init(void) | ||
1894 | { | ||
1895 | isdn_modem_t *m; | ||
1896 | int i, retval; | ||
1897 | modem_info *info; | ||
1898 | |||
1899 | m = &dev->mdm; | ||
1900 | m->tty_modem = alloc_tty_driver(ISDN_MAX_CHANNELS); | ||
1901 | if (!m->tty_modem) | ||
1902 | return -ENOMEM; | ||
1903 | m->tty_modem->name = "ttyI"; | ||
1904 | m->tty_modem->devfs_name = "isdn/ttyI"; | ||
1905 | m->tty_modem->major = ISDN_TTY_MAJOR; | ||
1906 | m->tty_modem->minor_start = 0; | ||
1907 | m->tty_modem->type = TTY_DRIVER_TYPE_SERIAL; | ||
1908 | m->tty_modem->subtype = SERIAL_TYPE_NORMAL; | ||
1909 | m->tty_modem->init_termios = tty_std_termios; | ||
1910 | m->tty_modem->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; | ||
1911 | m->tty_modem->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; | ||
1912 | m->tty_modem->driver_name = "isdn_tty"; | ||
1913 | tty_set_operations(m->tty_modem, &modem_ops); | ||
1914 | retval = tty_register_driver(m->tty_modem); | ||
1915 | if (retval) { | ||
1916 | printk(KERN_WARNING "isdn_tty: Couldn't register modem-device\n"); | ||
1917 | goto err; | ||
1918 | } | ||
1919 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
1920 | info = &m->info[i]; | ||
1921 | #ifdef CONFIG_ISDN_TTY_FAX | ||
1922 | if (!(info->fax = kmalloc(sizeof(T30_s), GFP_KERNEL))) { | ||
1923 | printk(KERN_ERR "Could not allocate fax t30-buffer\n"); | ||
1924 | retval = -ENOMEM; | ||
1925 | goto err_unregister; | ||
1926 | } | ||
1927 | #endif | ||
1928 | #ifdef MODULE | ||
1929 | info->owner = THIS_MODULE; | ||
1930 | #endif | ||
1931 | spin_lock_init(&info->readlock); | ||
1932 | init_MUTEX(&info->write_sem); | ||
1933 | sprintf(info->last_cause, "0000"); | ||
1934 | sprintf(info->last_num, "none"); | ||
1935 | info->last_dir = 0; | ||
1936 | info->last_lhup = 1; | ||
1937 | info->last_l2 = -1; | ||
1938 | info->last_si = 0; | ||
1939 | isdn_tty_reset_profile(&info->emu); | ||
1940 | isdn_tty_modem_reset_regs(info, 1); | ||
1941 | info->magic = ISDN_ASYNC_MAGIC; | ||
1942 | info->line = i; | ||
1943 | info->tty = NULL; | ||
1944 | info->x_char = 0; | ||
1945 | info->count = 0; | ||
1946 | info->blocked_open = 0; | ||
1947 | init_waitqueue_head(&info->open_wait); | ||
1948 | init_waitqueue_head(&info->close_wait); | ||
1949 | info->isdn_driver = -1; | ||
1950 | info->isdn_channel = -1; | ||
1951 | info->drv_index = -1; | ||
1952 | info->xmit_size = ISDN_SERIAL_XMIT_SIZE; | ||
1953 | init_timer(&info->nc_timer); | ||
1954 | info->nc_timer.function = isdn_tty_modem_do_ncarrier; | ||
1955 | info->nc_timer.data = (unsigned long) info; | ||
1956 | skb_queue_head_init(&info->xmit_queue); | ||
1957 | #ifdef CONFIG_ISDN_AUDIO | ||
1958 | skb_queue_head_init(&info->dtmf_queue); | ||
1959 | #endif | ||
1960 | if (!(info->xmit_buf = kmalloc(ISDN_SERIAL_XMIT_MAX + 5, GFP_KERNEL))) { | ||
1961 | printk(KERN_ERR "Could not allocate modem xmit-buffer\n"); | ||
1962 | retval = -ENOMEM; | ||
1963 | goto err_unregister; | ||
1964 | } | ||
1965 | /* Make room for T.70 header */ | ||
1966 | info->xmit_buf += 4; | ||
1967 | } | ||
1968 | return 0; | ||
1969 | err_unregister: | ||
1970 | for (i--; i >= 0; i--) { | ||
1971 | info = &m->info[i]; | ||
1972 | #ifdef CONFIG_ISDN_TTY_FAX | ||
1973 | kfree(info->fax); | ||
1974 | #endif | ||
1975 | kfree(info->xmit_buf - 4); | ||
1976 | } | ||
1977 | tty_unregister_driver(m->tty_modem); | ||
1978 | err: | ||
1979 | put_tty_driver(m->tty_modem); | ||
1980 | m->tty_modem = NULL; | ||
1981 | return retval; | ||
1982 | } | ||
1983 | |||
1984 | void | ||
1985 | isdn_tty_exit(void) | ||
1986 | { | ||
1987 | modem_info *info; | ||
1988 | int i; | ||
1989 | |||
1990 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
1991 | info = &dev->mdm.info[i]; | ||
1992 | isdn_tty_cleanup_xmit(info); | ||
1993 | #ifdef CONFIG_ISDN_TTY_FAX | ||
1994 | kfree(info->fax); | ||
1995 | #endif | ||
1996 | kfree(info->xmit_buf - 4); | ||
1997 | } | ||
1998 | tty_unregister_driver(dev->mdm.tty_modem); | ||
1999 | put_tty_driver(dev->mdm.tty_modem); | ||
2000 | dev->mdm.tty_modem = NULL; | ||
2001 | } | ||
2002 | |||
2003 | |||
2004 | /* | ||
2005 | * isdn_tty_match_icall(char *MSN, atemu *tty_emulator, int dev_idx) | ||
2006 | * match the MSN against the MSNs (glob patterns) defined for tty_emulator, | ||
2007 | * and return 0 for match, 1 for no match, 2 if MSN could match if longer. | ||
2008 | */ | ||
2009 | |||
2010 | static int | ||
2011 | isdn_tty_match_icall(char *cid, atemu *emu, int di) | ||
2012 | { | ||
2013 | #ifdef ISDN_DEBUG_MODEM_ICALL | ||
2014 | printk(KERN_DEBUG "m_fi: msn=%s lmsn=%s mmsn=%s mreg[SI1]=%d mreg[SI2]=%d\n", | ||
2015 | emu->msn, emu->lmsn, isdn_map_eaz2msn(emu->msn, di), | ||
2016 | emu->mdmreg[REG_SI1], emu->mdmreg[REG_SI2]); | ||
2017 | #endif | ||
2018 | if (strlen(emu->lmsn)) { | ||
2019 | char *p = emu->lmsn; | ||
2020 | char *q; | ||
2021 | int tmp; | ||
2022 | int ret = 0; | ||
2023 | |||
2024 | while (1) { | ||
2025 | if ((q = strchr(p, ';'))) | ||
2026 | *q = '\0'; | ||
2027 | if ((tmp = isdn_msncmp(cid, isdn_map_eaz2msn(p, di))) > ret) | ||
2028 | ret = tmp; | ||
2029 | #ifdef ISDN_DEBUG_MODEM_ICALL | ||
2030 | printk(KERN_DEBUG "m_fi: lmsnX=%s mmsn=%s -> tmp=%d\n", | ||
2031 | p, isdn_map_eaz2msn(emu->msn, di), tmp); | ||
2032 | #endif | ||
2033 | if (q) { | ||
2034 | *q = ';'; | ||
2035 | p = q; | ||
2036 | p++; | ||
2037 | } | ||
2038 | if (!tmp) | ||
2039 | return 0; | ||
2040 | if (!q) | ||
2041 | break; | ||
2042 | } | ||
2043 | return ret; | ||
2044 | } else { | ||
2045 | int tmp; | ||
2046 | tmp = isdn_msncmp(cid, isdn_map_eaz2msn(emu->msn, di)); | ||
2047 | #ifdef ISDN_DEBUG_MODEM_ICALL | ||
2048 | printk(KERN_DEBUG "m_fi: mmsn=%s -> tmp=%d\n", | ||
2049 | isdn_map_eaz2msn(emu->msn, di), tmp); | ||
2050 | #endif | ||
2051 | return tmp; | ||
2052 | } | ||
2053 | } | ||
2054 | |||
2055 | /* | ||
2056 | * An incoming call-request has arrived. | ||
2057 | * Search the tty-devices for an appropriate device and bind | ||
2058 | * it to the ISDN-Channel. | ||
2059 | * Return: | ||
2060 | * | ||
2061 | * 0 = No matching device found. | ||
2062 | * 1 = A matching device found. | ||
2063 | * 3 = No match found, but eventually would match, if | ||
2064 | * CID is longer. | ||
2065 | */ | ||
2066 | int | ||
2067 | isdn_tty_find_icall(int di, int ch, setup_parm *setup) | ||
2068 | { | ||
2069 | char *eaz; | ||
2070 | int i; | ||
2071 | int wret; | ||
2072 | int idx; | ||
2073 | int si1; | ||
2074 | int si2; | ||
2075 | char *nr; | ||
2076 | ulong flags; | ||
2077 | |||
2078 | if (!setup->phone[0]) { | ||
2079 | nr = "0"; | ||
2080 | printk(KERN_INFO "isdn_tty: Incoming call without OAD, assuming '0'\n"); | ||
2081 | } else | ||
2082 | nr = setup->phone; | ||
2083 | si1 = (int) setup->si1; | ||
2084 | si2 = (int) setup->si2; | ||
2085 | if (!setup->eazmsn[0]) { | ||
2086 | printk(KERN_WARNING "isdn_tty: Incoming call without CPN, assuming '0'\n"); | ||
2087 | eaz = "0"; | ||
2088 | } else | ||
2089 | eaz = setup->eazmsn; | ||
2090 | #ifdef ISDN_DEBUG_MODEM_ICALL | ||
2091 | printk(KERN_DEBUG "m_fi: eaz=%s si1=%d si2=%d\n", eaz, si1, si2); | ||
2092 | #endif | ||
2093 | wret = 0; | ||
2094 | spin_lock_irqsave(&dev->lock, flags); | ||
2095 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
2096 | modem_info *info = &dev->mdm.info[i]; | ||
2097 | |||
2098 | if (info->count == 0) | ||
2099 | continue; | ||
2100 | if ((info->emu.mdmreg[REG_SI1] & si2bit[si1]) && /* SI1 is matching */ | ||
2101 | (info->emu.mdmreg[REG_SI2] == si2)) { /* SI2 is matching */ | ||
2102 | idx = isdn_dc2minor(di, ch); | ||
2103 | #ifdef ISDN_DEBUG_MODEM_ICALL | ||
2104 | printk(KERN_DEBUG "m_fi: match1 wret=%d\n", wret); | ||
2105 | printk(KERN_DEBUG "m_fi: idx=%d flags=%08lx drv=%d ch=%d usg=%d\n", idx, | ||
2106 | info->flags, info->isdn_driver, info->isdn_channel, | ||
2107 | dev->usage[idx]); | ||
2108 | #endif | ||
2109 | if ( | ||
2110 | #ifndef FIX_FILE_TRANSFER | ||
2111 | (info->flags & ISDN_ASYNC_NORMAL_ACTIVE) && | ||
2112 | #endif | ||
2113 | (info->isdn_driver == -1) && | ||
2114 | (info->isdn_channel == -1) && | ||
2115 | (USG_NONE(dev->usage[idx]))) { | ||
2116 | int matchret; | ||
2117 | |||
2118 | if ((matchret = isdn_tty_match_icall(eaz, &info->emu, di)) > wret) | ||
2119 | wret = matchret; | ||
2120 | if (!matchret) { /* EAZ is matching */ | ||
2121 | info->isdn_driver = di; | ||
2122 | info->isdn_channel = ch; | ||
2123 | info->drv_index = idx; | ||
2124 | dev->m_idx[idx] = info->line; | ||
2125 | dev->usage[idx] &= ISDN_USAGE_EXCLUSIVE; | ||
2126 | dev->usage[idx] |= isdn_calc_usage(si1, info->emu.mdmreg[REG_L2PROT]); | ||
2127 | strcpy(dev->num[idx], nr); | ||
2128 | strcpy(info->emu.cpn, eaz); | ||
2129 | info->emu.mdmreg[REG_SI1I] = si2bit[si1]; | ||
2130 | info->emu.mdmreg[REG_PLAN] = setup->plan; | ||
2131 | info->emu.mdmreg[REG_SCREEN] = setup->screen; | ||
2132 | isdn_info_update(); | ||
2133 | spin_unlock_irqrestore(&dev->lock, flags); | ||
2134 | printk(KERN_INFO "isdn_tty: call from %s, -> RING on ttyI%d\n", nr, | ||
2135 | info->line); | ||
2136 | info->msr |= UART_MSR_RI; | ||
2137 | isdn_tty_modem_result(RESULT_RING, info); | ||
2138 | isdn_timer_ctrl(ISDN_TIMER_MODEMRING, 1); | ||
2139 | return 1; | ||
2140 | } | ||
2141 | } | ||
2142 | } | ||
2143 | } | ||
2144 | spin_unlock_irqrestore(&dev->lock, flags); | ||
2145 | printk(KERN_INFO "isdn_tty: call from %s -> %s %s\n", nr, eaz, | ||
2146 | ((dev->drv[di]->flags & DRV_FLAG_REJBUS) && (wret != 2))? "rejected" : "ignored"); | ||
2147 | return (wret == 2)?3:0; | ||
2148 | } | ||
2149 | |||
2150 | #define TTY_IS_ACTIVE(info) \ | ||
2151 | (info->flags & (ISDN_ASYNC_NORMAL_ACTIVE | ISDN_ASYNC_CALLOUT_ACTIVE)) | ||
2152 | |||
2153 | int | ||
2154 | isdn_tty_stat_callback(int i, isdn_ctrl *c) | ||
2155 | { | ||
2156 | int mi; | ||
2157 | modem_info *info; | ||
2158 | char *e; | ||
2159 | |||
2160 | if (i < 0) | ||
2161 | return 0; | ||
2162 | if ((mi = dev->m_idx[i]) >= 0) { | ||
2163 | info = &dev->mdm.info[mi]; | ||
2164 | switch (c->command) { | ||
2165 | case ISDN_STAT_CINF: | ||
2166 | printk(KERN_DEBUG "CHARGEINFO on ttyI%d: %ld %s\n", info->line, c->arg, c->parm.num); | ||
2167 | info->emu.charge = (unsigned) simple_strtoul(c->parm.num, &e, 10); | ||
2168 | if (e == (char *)c->parm.num) | ||
2169 | info->emu.charge = 0; | ||
2170 | |||
2171 | break; | ||
2172 | case ISDN_STAT_BSENT: | ||
2173 | #ifdef ISDN_TTY_STAT_DEBUG | ||
2174 | printk(KERN_DEBUG "tty_STAT_BSENT ttyI%d\n", info->line); | ||
2175 | #endif | ||
2176 | if ((info->isdn_driver == c->driver) && | ||
2177 | (info->isdn_channel == c->arg)) { | ||
2178 | info->msr |= UART_MSR_CTS; | ||
2179 | if (info->send_outstanding) | ||
2180 | if (!(--info->send_outstanding)) | ||
2181 | info->lsr |= UART_LSR_TEMT; | ||
2182 | isdn_tty_tint(info); | ||
2183 | return 1; | ||
2184 | } | ||
2185 | break; | ||
2186 | case ISDN_STAT_CAUSE: | ||
2187 | #ifdef ISDN_TTY_STAT_DEBUG | ||
2188 | printk(KERN_DEBUG "tty_STAT_CAUSE ttyI%d\n", info->line); | ||
2189 | #endif | ||
2190 | /* Signal cause to tty-device */ | ||
2191 | strncpy(info->last_cause, c->parm.num, 5); | ||
2192 | return 1; | ||
2193 | case ISDN_STAT_DISPLAY: | ||
2194 | #ifdef ISDN_TTY_STAT_DEBUG | ||
2195 | printk(KERN_DEBUG "tty_STAT_DISPLAY ttyI%d\n", info->line); | ||
2196 | #endif | ||
2197 | /* Signal display to tty-device */ | ||
2198 | if ((info->emu.mdmreg[REG_DISPLAY] & BIT_DISPLAY) && | ||
2199 | !(info->emu.mdmreg[REG_RESPNUM] & BIT_RESPNUM)) { | ||
2200 | isdn_tty_at_cout("\r\n", info); | ||
2201 | isdn_tty_at_cout("DISPLAY: ", info); | ||
2202 | isdn_tty_at_cout(c->parm.display, info); | ||
2203 | isdn_tty_at_cout("\r\n", info); | ||
2204 | } | ||
2205 | return 1; | ||
2206 | case ISDN_STAT_DCONN: | ||
2207 | #ifdef ISDN_TTY_STAT_DEBUG | ||
2208 | printk(KERN_DEBUG "tty_STAT_DCONN ttyI%d\n", info->line); | ||
2209 | #endif | ||
2210 | if (TTY_IS_ACTIVE(info)) { | ||
2211 | if (info->dialing == 1) { | ||
2212 | info->dialing = 2; | ||
2213 | return 1; | ||
2214 | } | ||
2215 | } | ||
2216 | break; | ||
2217 | case ISDN_STAT_DHUP: | ||
2218 | #ifdef ISDN_TTY_STAT_DEBUG | ||
2219 | printk(KERN_DEBUG "tty_STAT_DHUP ttyI%d\n", info->line); | ||
2220 | #endif | ||
2221 | if (TTY_IS_ACTIVE(info)) { | ||
2222 | if (info->dialing == 1) | ||
2223 | isdn_tty_modem_result(RESULT_BUSY, info); | ||
2224 | if (info->dialing > 1) | ||
2225 | isdn_tty_modem_result(RESULT_NO_CARRIER, info); | ||
2226 | info->dialing = 0; | ||
2227 | #ifdef ISDN_DEBUG_MODEM_HUP | ||
2228 | printk(KERN_DEBUG "Mhup in ISDN_STAT_DHUP\n"); | ||
2229 | #endif | ||
2230 | isdn_tty_modem_hup(info, 0); | ||
2231 | return 1; | ||
2232 | } | ||
2233 | break; | ||
2234 | case ISDN_STAT_BCONN: | ||
2235 | #ifdef ISDN_TTY_STAT_DEBUG | ||
2236 | printk(KERN_DEBUG "tty_STAT_BCONN ttyI%d\n", info->line); | ||
2237 | #endif | ||
2238 | /* Wake up any processes waiting | ||
2239 | * for incoming call of this device when | ||
2240 | * DCD follow the state of incoming carrier | ||
2241 | */ | ||
2242 | if (info->blocked_open && | ||
2243 | (info->emu.mdmreg[REG_DCD] & BIT_DCD)) { | ||
2244 | wake_up_interruptible(&info->open_wait); | ||
2245 | } | ||
2246 | |||
2247 | /* Schedule CONNECT-Message to any tty | ||
2248 | * waiting for it and | ||
2249 | * set DCD-bit of its modem-status. | ||
2250 | */ | ||
2251 | if (TTY_IS_ACTIVE(info) || | ||
2252 | (info->blocked_open && (info->emu.mdmreg[REG_DCD] & BIT_DCD))) { | ||
2253 | info->msr |= UART_MSR_DCD; | ||
2254 | info->emu.charge = 0; | ||
2255 | if (info->dialing & 0xf) | ||
2256 | info->last_dir = 1; | ||
2257 | else | ||
2258 | info->last_dir = 0; | ||
2259 | info->dialing = 0; | ||
2260 | info->rcvsched = 1; | ||
2261 | if (USG_MODEM(dev->usage[i])) { | ||
2262 | if (info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM) { | ||
2263 | strcpy(info->emu.connmsg, c->parm.num); | ||
2264 | isdn_tty_modem_result(RESULT_CONNECT, info); | ||
2265 | } else | ||
2266 | isdn_tty_modem_result(RESULT_CONNECT64000, info); | ||
2267 | } | ||
2268 | if (USG_VOICE(dev->usage[i])) | ||
2269 | isdn_tty_modem_result(RESULT_VCON, info); | ||
2270 | return 1; | ||
2271 | } | ||
2272 | break; | ||
2273 | case ISDN_STAT_BHUP: | ||
2274 | #ifdef ISDN_TTY_STAT_DEBUG | ||
2275 | printk(KERN_DEBUG "tty_STAT_BHUP ttyI%d\n", info->line); | ||
2276 | #endif | ||
2277 | if (TTY_IS_ACTIVE(info)) { | ||
2278 | #ifdef ISDN_DEBUG_MODEM_HUP | ||
2279 | printk(KERN_DEBUG "Mhup in ISDN_STAT_BHUP\n"); | ||
2280 | #endif | ||
2281 | isdn_tty_modem_hup(info, 0); | ||
2282 | return 1; | ||
2283 | } | ||
2284 | break; | ||
2285 | case ISDN_STAT_NODCH: | ||
2286 | #ifdef ISDN_TTY_STAT_DEBUG | ||
2287 | printk(KERN_DEBUG "tty_STAT_NODCH ttyI%d\n", info->line); | ||
2288 | #endif | ||
2289 | if (TTY_IS_ACTIVE(info)) { | ||
2290 | if (info->dialing) { | ||
2291 | info->dialing = 0; | ||
2292 | info->last_l2 = -1; | ||
2293 | info->last_si = 0; | ||
2294 | sprintf(info->last_cause, "0000"); | ||
2295 | isdn_tty_modem_result(RESULT_NO_DIALTONE, info); | ||
2296 | } | ||
2297 | isdn_tty_modem_hup(info, 0); | ||
2298 | return 1; | ||
2299 | } | ||
2300 | break; | ||
2301 | case ISDN_STAT_UNLOAD: | ||
2302 | #ifdef ISDN_TTY_STAT_DEBUG | ||
2303 | printk(KERN_DEBUG "tty_STAT_UNLOAD ttyI%d\n", info->line); | ||
2304 | #endif | ||
2305 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
2306 | info = &dev->mdm.info[i]; | ||
2307 | if (info->isdn_driver == c->driver) { | ||
2308 | if (info->online) | ||
2309 | isdn_tty_modem_hup(info, 1); | ||
2310 | } | ||
2311 | } | ||
2312 | return 1; | ||
2313 | #ifdef CONFIG_ISDN_TTY_FAX | ||
2314 | case ISDN_STAT_FAXIND: | ||
2315 | if (TTY_IS_ACTIVE(info)) { | ||
2316 | isdn_tty_fax_command(info, c); | ||
2317 | } | ||
2318 | break; | ||
2319 | #endif | ||
2320 | #ifdef CONFIG_ISDN_AUDIO | ||
2321 | case ISDN_STAT_AUDIO: | ||
2322 | if (TTY_IS_ACTIVE(info)) { | ||
2323 | switch(c->parm.num[0]) { | ||
2324 | case ISDN_AUDIO_DTMF: | ||
2325 | if (info->vonline) { | ||
2326 | isdn_audio_put_dle_code(info, | ||
2327 | c->parm.num[1]); | ||
2328 | } | ||
2329 | break; | ||
2330 | } | ||
2331 | } | ||
2332 | break; | ||
2333 | #endif | ||
2334 | } | ||
2335 | } | ||
2336 | return 0; | ||
2337 | } | ||
2338 | |||
2339 | /********************************************************************* | ||
2340 | Modem-Emulator-Routines | ||
2341 | *********************************************************************/ | ||
2342 | |||
2343 | #define cmdchar(c) ((c>=' ')&&(c<=0x7f)) | ||
2344 | |||
2345 | /* | ||
2346 | * Put a message from the AT-emulator into receive-buffer of tty, | ||
2347 | * convert CR, LF, and BS to values in modem-registers 3, 4 and 5. | ||
2348 | */ | ||
2349 | void | ||
2350 | isdn_tty_at_cout(char *msg, modem_info * info) | ||
2351 | { | ||
2352 | struct tty_struct *tty; | ||
2353 | atemu *m = &info->emu; | ||
2354 | char *p; | ||
2355 | char c; | ||
2356 | u_long flags; | ||
2357 | struct sk_buff *skb = NULL; | ||
2358 | char *sp = NULL; | ||
2359 | |||
2360 | if (!msg) { | ||
2361 | printk(KERN_WARNING "isdn_tty: Null-Message in isdn_tty_at_cout\n"); | ||
2362 | return; | ||
2363 | } | ||
2364 | spin_lock_irqsave(&info->readlock, flags); | ||
2365 | tty = info->tty; | ||
2366 | if ((info->flags & ISDN_ASYNC_CLOSING) || (!tty)) { | ||
2367 | spin_unlock_irqrestore(&info->readlock, flags); | ||
2368 | return; | ||
2369 | } | ||
2370 | |||
2371 | /* use queue instead of direct flip, if online and */ | ||
2372 | /* data is in queue or flip buffer is full */ | ||
2373 | if ((info->online) && (((tty->flip.count + strlen(msg)) >= TTY_FLIPBUF_SIZE) || | ||
2374 | (!skb_queue_empty(&dev->drv[info->isdn_driver]->rpqueue[info->isdn_channel])))) { | ||
2375 | skb = alloc_skb(strlen(msg), GFP_ATOMIC); | ||
2376 | if (!skb) { | ||
2377 | spin_unlock_irqrestore(&info->readlock, flags); | ||
2378 | return; | ||
2379 | } | ||
2380 | sp = skb_put(skb, strlen(msg)); | ||
2381 | #ifdef CONFIG_ISDN_AUDIO | ||
2382 | ISDN_AUDIO_SKB_DLECOUNT(skb) = 0; | ||
2383 | ISDN_AUDIO_SKB_LOCK(skb) = 0; | ||
2384 | #endif | ||
2385 | } | ||
2386 | |||
2387 | for (p = msg; *p; p++) { | ||
2388 | switch (*p) { | ||
2389 | case '\r': | ||
2390 | c = m->mdmreg[REG_CR]; | ||
2391 | break; | ||
2392 | case '\n': | ||
2393 | c = m->mdmreg[REG_LF]; | ||
2394 | break; | ||
2395 | case '\b': | ||
2396 | c = m->mdmreg[REG_BS]; | ||
2397 | break; | ||
2398 | default: | ||
2399 | c = *p; | ||
2400 | } | ||
2401 | if (skb) { | ||
2402 | *sp++ = c; | ||
2403 | } else { | ||
2404 | if (tty->flip.count >= TTY_FLIPBUF_SIZE) | ||
2405 | break; | ||
2406 | tty_insert_flip_char(tty, c, 0); | ||
2407 | } | ||
2408 | } | ||
2409 | if (skb) { | ||
2410 | __skb_queue_tail(&dev->drv[info->isdn_driver]->rpqueue[info->isdn_channel], skb); | ||
2411 | dev->drv[info->isdn_driver]->rcvcount[info->isdn_channel] += skb->len; | ||
2412 | spin_unlock_irqrestore(&info->readlock, flags); | ||
2413 | /* Schedule dequeuing */ | ||
2414 | if ((dev->modempoll) && (info->rcvsched)) | ||
2415 | isdn_timer_ctrl(ISDN_TIMER_MODEMREAD, 1); | ||
2416 | |||
2417 | } else { | ||
2418 | spin_unlock_irqrestore(&info->readlock, flags); | ||
2419 | schedule_delayed_work(&tty->flip.work, 1); | ||
2420 | } | ||
2421 | } | ||
2422 | |||
2423 | /* | ||
2424 | * Perform ATH Hangup | ||
2425 | */ | ||
2426 | static void | ||
2427 | isdn_tty_on_hook(modem_info * info) | ||
2428 | { | ||
2429 | if (info->isdn_channel >= 0) { | ||
2430 | #ifdef ISDN_DEBUG_MODEM_HUP | ||
2431 | printk(KERN_DEBUG "Mhup in isdn_tty_on_hook\n"); | ||
2432 | #endif | ||
2433 | isdn_tty_modem_hup(info, 1); | ||
2434 | } | ||
2435 | } | ||
2436 | |||
2437 | static void | ||
2438 | isdn_tty_off_hook(void) | ||
2439 | { | ||
2440 | printk(KERN_DEBUG "isdn_tty_off_hook\n"); | ||
2441 | } | ||
2442 | |||
2443 | #define PLUSWAIT1 (HZ/2) /* 0.5 sec. */ | ||
2444 | #define PLUSWAIT2 (HZ*3/2) /* 1.5 sec */ | ||
2445 | |||
2446 | /* | ||
2447 | * Check Buffer for Modem-escape-sequence, activate timer-callback to | ||
2448 | * isdn_tty_modem_escape() if sequence found. | ||
2449 | * | ||
2450 | * Parameters: | ||
2451 | * p pointer to databuffer | ||
2452 | * plus escape-character | ||
2453 | * count length of buffer | ||
2454 | * pluscount count of valid escape-characters so far | ||
2455 | * lastplus timestamp of last character | ||
2456 | */ | ||
2457 | static void | ||
2458 | isdn_tty_check_esc(const u_char * p, u_char plus, int count, int *pluscount, | ||
2459 | u_long *lastplus) | ||
2460 | { | ||
2461 | if (plus > 127) | ||
2462 | return; | ||
2463 | if (count > 3) { | ||
2464 | p += count - 3; | ||
2465 | count = 3; | ||
2466 | *pluscount = 0; | ||
2467 | } | ||
2468 | while (count > 0) { | ||
2469 | if (*(p++) == plus) { | ||
2470 | if ((*pluscount)++) { | ||
2471 | /* Time since last '+' > 0.5 sec. ? */ | ||
2472 | if (time_after(jiffies, *lastplus + PLUSWAIT1)) | ||
2473 | *pluscount = 1; | ||
2474 | } else { | ||
2475 | /* Time since last non-'+' < 1.5 sec. ? */ | ||
2476 | if (time_before(jiffies, *lastplus + PLUSWAIT2)) | ||
2477 | *pluscount = 0; | ||
2478 | } | ||
2479 | if ((*pluscount == 3) && (count == 1)) | ||
2480 | isdn_timer_ctrl(ISDN_TIMER_MODEMPLUS, 1); | ||
2481 | if (*pluscount > 3) | ||
2482 | *pluscount = 1; | ||
2483 | } else | ||
2484 | *pluscount = 0; | ||
2485 | *lastplus = jiffies; | ||
2486 | count--; | ||
2487 | } | ||
2488 | } | ||
2489 | |||
2490 | /* | ||
2491 | * Return result of AT-emulator to tty-receive-buffer, depending on | ||
2492 | * modem-register 12, bit 0 and 1. | ||
2493 | * For CONNECT-messages also switch to online-mode. | ||
2494 | * For RING-message handle auto-ATA if register 0 != 0 | ||
2495 | */ | ||
2496 | |||
2497 | static void | ||
2498 | isdn_tty_modem_result(int code, modem_info * info) | ||
2499 | { | ||
2500 | atemu *m = &info->emu; | ||
2501 | static char *msg[] = | ||
2502 | {"OK", "CONNECT", "RING", "NO CARRIER", "ERROR", | ||
2503 | "CONNECT 64000", "NO DIALTONE", "BUSY", "NO ANSWER", | ||
2504 | "RINGING", "NO MSN/EAZ", "VCON", "RUNG"}; | ||
2505 | char s[ISDN_MSNLEN+10]; | ||
2506 | |||
2507 | switch (code) { | ||
2508 | case RESULT_RING: | ||
2509 | m->mdmreg[REG_RINGCNT]++; | ||
2510 | if (m->mdmreg[REG_RINGCNT] == m->mdmreg[REG_RINGATA]) | ||
2511 | /* Automatically accept incoming call */ | ||
2512 | isdn_tty_cmd_ATA(info); | ||
2513 | break; | ||
2514 | case RESULT_NO_CARRIER: | ||
2515 | #ifdef ISDN_DEBUG_MODEM_HUP | ||
2516 | printk(KERN_DEBUG "modem_result: NO CARRIER %d %d\n", | ||
2517 | (info->flags & ISDN_ASYNC_CLOSING), | ||
2518 | (!info->tty)); | ||
2519 | #endif | ||
2520 | m->mdmreg[REG_RINGCNT] = 0; | ||
2521 | del_timer(&info->nc_timer); | ||
2522 | info->ncarrier = 0; | ||
2523 | if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) { | ||
2524 | return; | ||
2525 | } | ||
2526 | #ifdef CONFIG_ISDN_AUDIO | ||
2527 | if (info->vonline & 1) { | ||
2528 | #ifdef ISDN_DEBUG_MODEM_VOICE | ||
2529 | printk(KERN_DEBUG "res3: send DLE-ETX on ttyI%d\n", | ||
2530 | info->line); | ||
2531 | #endif | ||
2532 | /* voice-recording, add DLE-ETX */ | ||
2533 | isdn_tty_at_cout("\020\003", info); | ||
2534 | } | ||
2535 | if (info->vonline & 2) { | ||
2536 | #ifdef ISDN_DEBUG_MODEM_VOICE | ||
2537 | printk(KERN_DEBUG "res3: send DLE-DC4 on ttyI%d\n", | ||
2538 | info->line); | ||
2539 | #endif | ||
2540 | /* voice-playing, add DLE-DC4 */ | ||
2541 | isdn_tty_at_cout("\020\024", info); | ||
2542 | } | ||
2543 | #endif | ||
2544 | break; | ||
2545 | case RESULT_CONNECT: | ||
2546 | case RESULT_CONNECT64000: | ||
2547 | sprintf(info->last_cause, "0000"); | ||
2548 | if (!info->online) | ||
2549 | info->online = 2; | ||
2550 | break; | ||
2551 | case RESULT_VCON: | ||
2552 | #ifdef ISDN_DEBUG_MODEM_VOICE | ||
2553 | printk(KERN_DEBUG "res3: send VCON on ttyI%d\n", | ||
2554 | info->line); | ||
2555 | #endif | ||
2556 | sprintf(info->last_cause, "0000"); | ||
2557 | if (!info->online) | ||
2558 | info->online = 1; | ||
2559 | break; | ||
2560 | } /* switch(code) */ | ||
2561 | |||
2562 | if (m->mdmreg[REG_RESP] & BIT_RESP) { | ||
2563 | /* Show results */ | ||
2564 | if (m->mdmreg[REG_RESPNUM] & BIT_RESPNUM) { | ||
2565 | /* Show numeric results only */ | ||
2566 | sprintf(s, "\r\n%d\r\n", code); | ||
2567 | isdn_tty_at_cout(s, info); | ||
2568 | } else { | ||
2569 | if (code == RESULT_RING) { | ||
2570 | /* return if "show RUNG" and ringcounter>1 */ | ||
2571 | if ((m->mdmreg[REG_RUNG] & BIT_RUNG) && | ||
2572 | (m->mdmreg[REG_RINGCNT] > 1)) | ||
2573 | return; | ||
2574 | /* print CID, _before_ _every_ ring */ | ||
2575 | if (!(m->mdmreg[REG_CIDONCE] & BIT_CIDONCE)) { | ||
2576 | isdn_tty_at_cout("\r\nCALLER NUMBER: ", info); | ||
2577 | isdn_tty_at_cout(dev->num[info->drv_index], info); | ||
2578 | if (m->mdmreg[REG_CDN] & BIT_CDN) { | ||
2579 | isdn_tty_at_cout("\r\nCALLED NUMBER: ", info); | ||
2580 | isdn_tty_at_cout(info->emu.cpn, info); | ||
2581 | } | ||
2582 | } | ||
2583 | } | ||
2584 | isdn_tty_at_cout("\r\n", info); | ||
2585 | isdn_tty_at_cout(msg[code], info); | ||
2586 | switch (code) { | ||
2587 | case RESULT_CONNECT: | ||
2588 | switch (m->mdmreg[REG_L2PROT]) { | ||
2589 | case ISDN_PROTO_L2_MODEM: | ||
2590 | isdn_tty_at_cout(" ", info); | ||
2591 | isdn_tty_at_cout(m->connmsg, info); | ||
2592 | break; | ||
2593 | } | ||
2594 | break; | ||
2595 | case RESULT_RING: | ||
2596 | /* Append CPN, if enabled */ | ||
2597 | if ((m->mdmreg[REG_CPN] & BIT_CPN)) { | ||
2598 | sprintf(s, "/%s", m->cpn); | ||
2599 | isdn_tty_at_cout(s, info); | ||
2600 | } | ||
2601 | /* Print CID only once, _after_ 1st RING */ | ||
2602 | if ((m->mdmreg[REG_CIDONCE] & BIT_CIDONCE) && | ||
2603 | (m->mdmreg[REG_RINGCNT] == 1)) { | ||
2604 | isdn_tty_at_cout("\r\n", info); | ||
2605 | isdn_tty_at_cout("CALLER NUMBER: ", info); | ||
2606 | isdn_tty_at_cout(dev->num[info->drv_index], info); | ||
2607 | if (m->mdmreg[REG_CDN] & BIT_CDN) { | ||
2608 | isdn_tty_at_cout("\r\nCALLED NUMBER: ", info); | ||
2609 | isdn_tty_at_cout(info->emu.cpn, info); | ||
2610 | } | ||
2611 | } | ||
2612 | break; | ||
2613 | case RESULT_NO_CARRIER: | ||
2614 | case RESULT_NO_DIALTONE: | ||
2615 | case RESULT_BUSY: | ||
2616 | case RESULT_NO_ANSWER: | ||
2617 | m->mdmreg[REG_RINGCNT] = 0; | ||
2618 | /* Append Cause-Message if enabled */ | ||
2619 | if (m->mdmreg[REG_RESPXT] & BIT_RESPXT) { | ||
2620 | sprintf(s, "/%s", info->last_cause); | ||
2621 | isdn_tty_at_cout(s, info); | ||
2622 | } | ||
2623 | break; | ||
2624 | case RESULT_CONNECT64000: | ||
2625 | /* Append Protocol to CONNECT message */ | ||
2626 | switch (m->mdmreg[REG_L2PROT]) { | ||
2627 | case ISDN_PROTO_L2_X75I: | ||
2628 | case ISDN_PROTO_L2_X75UI: | ||
2629 | case ISDN_PROTO_L2_X75BUI: | ||
2630 | isdn_tty_at_cout("/X.75", info); | ||
2631 | break; | ||
2632 | case ISDN_PROTO_L2_HDLC: | ||
2633 | isdn_tty_at_cout("/HDLC", info); | ||
2634 | break; | ||
2635 | case ISDN_PROTO_L2_V11096: | ||
2636 | isdn_tty_at_cout("/V110/9600", info); | ||
2637 | break; | ||
2638 | case ISDN_PROTO_L2_V11019: | ||
2639 | isdn_tty_at_cout("/V110/19200", info); | ||
2640 | break; | ||
2641 | case ISDN_PROTO_L2_V11038: | ||
2642 | isdn_tty_at_cout("/V110/38400", info); | ||
2643 | break; | ||
2644 | } | ||
2645 | if (m->mdmreg[REG_T70] & BIT_T70) { | ||
2646 | isdn_tty_at_cout("/T.70", info); | ||
2647 | if (m->mdmreg[REG_T70] & BIT_T70_EXT) | ||
2648 | isdn_tty_at_cout("+", info); | ||
2649 | } | ||
2650 | break; | ||
2651 | } | ||
2652 | isdn_tty_at_cout("\r\n", info); | ||
2653 | } | ||
2654 | } | ||
2655 | if (code == RESULT_NO_CARRIER) { | ||
2656 | if ((info->flags & ISDN_ASYNC_CLOSING) || (!info->tty)) { | ||
2657 | return; | ||
2658 | } | ||
2659 | tty_ldisc_flush(info->tty); | ||
2660 | if ((info->flags & ISDN_ASYNC_CHECK_CD) && | ||
2661 | (!((info->flags & ISDN_ASYNC_CALLOUT_ACTIVE) && | ||
2662 | (info->flags & ISDN_ASYNC_CALLOUT_NOHUP)))) { | ||
2663 | tty_hangup(info->tty); | ||
2664 | } | ||
2665 | } | ||
2666 | } | ||
2667 | |||
2668 | |||
2669 | /* | ||
2670 | * Display a modem-register-value. | ||
2671 | */ | ||
2672 | static void | ||
2673 | isdn_tty_show_profile(int ridx, modem_info * info) | ||
2674 | { | ||
2675 | char v[6]; | ||
2676 | |||
2677 | sprintf(v, "\r\n%d", info->emu.mdmreg[ridx]); | ||
2678 | isdn_tty_at_cout(v, info); | ||
2679 | } | ||
2680 | |||
2681 | /* | ||
2682 | * Get MSN-string from char-pointer, set pointer to end of number | ||
2683 | */ | ||
2684 | static void | ||
2685 | isdn_tty_get_msnstr(char *n, char **p) | ||
2686 | { | ||
2687 | int limit = ISDN_MSNLEN - 1; | ||
2688 | |||
2689 | while (((*p[0] >= '0' && *p[0] <= '9') || | ||
2690 | /* Why a comma ??? */ | ||
2691 | (*p[0] == ',') || (*p[0] == ':')) && | ||
2692 | (limit--)) | ||
2693 | *n++ = *p[0]++; | ||
2694 | *n = '\0'; | ||
2695 | } | ||
2696 | |||
2697 | /* | ||
2698 | * Get phone-number from modem-commandbuffer | ||
2699 | */ | ||
2700 | static void | ||
2701 | isdn_tty_getdial(char *p, char *q,int cnt) | ||
2702 | { | ||
2703 | int first = 1; | ||
2704 | int limit = ISDN_MSNLEN - 1; /* MUST match the size of interface var to avoid | ||
2705 | buffer overflow */ | ||
2706 | |||
2707 | while (strchr(" 0123456789,#.*WPTS-", *p) && *p && --cnt>0) { | ||
2708 | if ((*p >= '0' && *p <= '9') || ((*p == 'S') && first) || | ||
2709 | (*p == '*') || (*p == '#')) { | ||
2710 | *q++ = *p; | ||
2711 | limit--; | ||
2712 | } | ||
2713 | if(!limit) | ||
2714 | break; | ||
2715 | p++; | ||
2716 | first = 0; | ||
2717 | } | ||
2718 | *q = 0; | ||
2719 | } | ||
2720 | |||
2721 | #define PARSE_ERROR { isdn_tty_modem_result(RESULT_ERROR, info); return; } | ||
2722 | #define PARSE_ERROR1 { isdn_tty_modem_result(RESULT_ERROR, info); return 1; } | ||
2723 | |||
2724 | static void | ||
2725 | isdn_tty_report(modem_info * info) | ||
2726 | { | ||
2727 | atemu *m = &info->emu; | ||
2728 | char s[80]; | ||
2729 | |||
2730 | isdn_tty_at_cout("\r\nStatistics of last connection:\r\n\r\n", info); | ||
2731 | sprintf(s, " Remote Number: %s\r\n", info->last_num); | ||
2732 | isdn_tty_at_cout(s, info); | ||
2733 | sprintf(s, " Direction: %s\r\n", info->last_dir ? "outgoing" : "incoming"); | ||
2734 | isdn_tty_at_cout(s, info); | ||
2735 | isdn_tty_at_cout(" Layer-2 Protocol: ", info); | ||
2736 | switch (info->last_l2) { | ||
2737 | case ISDN_PROTO_L2_X75I: | ||
2738 | isdn_tty_at_cout("X.75i", info); | ||
2739 | break; | ||
2740 | case ISDN_PROTO_L2_X75UI: | ||
2741 | isdn_tty_at_cout("X.75ui", info); | ||
2742 | break; | ||
2743 | case ISDN_PROTO_L2_X75BUI: | ||
2744 | isdn_tty_at_cout("X.75bui", info); | ||
2745 | break; | ||
2746 | case ISDN_PROTO_L2_HDLC: | ||
2747 | isdn_tty_at_cout("HDLC", info); | ||
2748 | break; | ||
2749 | case ISDN_PROTO_L2_V11096: | ||
2750 | isdn_tty_at_cout("V.110 9600 Baud", info); | ||
2751 | break; | ||
2752 | case ISDN_PROTO_L2_V11019: | ||
2753 | isdn_tty_at_cout("V.110 19200 Baud", info); | ||
2754 | break; | ||
2755 | case ISDN_PROTO_L2_V11038: | ||
2756 | isdn_tty_at_cout("V.110 38400 Baud", info); | ||
2757 | break; | ||
2758 | case ISDN_PROTO_L2_TRANS: | ||
2759 | isdn_tty_at_cout("transparent", info); | ||
2760 | break; | ||
2761 | case ISDN_PROTO_L2_MODEM: | ||
2762 | isdn_tty_at_cout("modem", info); | ||
2763 | break; | ||
2764 | case ISDN_PROTO_L2_FAX: | ||
2765 | isdn_tty_at_cout("fax", info); | ||
2766 | break; | ||
2767 | default: | ||
2768 | isdn_tty_at_cout("unknown", info); | ||
2769 | break; | ||
2770 | } | ||
2771 | if (m->mdmreg[REG_T70] & BIT_T70) { | ||
2772 | isdn_tty_at_cout("/T.70", info); | ||
2773 | if (m->mdmreg[REG_T70] & BIT_T70_EXT) | ||
2774 | isdn_tty_at_cout("+", info); | ||
2775 | } | ||
2776 | isdn_tty_at_cout("\r\n", info); | ||
2777 | isdn_tty_at_cout(" Service: ", info); | ||
2778 | switch (info->last_si) { | ||
2779 | case 1: | ||
2780 | isdn_tty_at_cout("audio\r\n", info); | ||
2781 | break; | ||
2782 | case 5: | ||
2783 | isdn_tty_at_cout("btx\r\n", info); | ||
2784 | break; | ||
2785 | case 7: | ||
2786 | isdn_tty_at_cout("data\r\n", info); | ||
2787 | break; | ||
2788 | default: | ||
2789 | sprintf(s, "%d\r\n", info->last_si); | ||
2790 | isdn_tty_at_cout(s, info); | ||
2791 | break; | ||
2792 | } | ||
2793 | sprintf(s, " Hangup location: %s\r\n", info->last_lhup ? "local" : "remote"); | ||
2794 | isdn_tty_at_cout(s, info); | ||
2795 | sprintf(s, " Last cause: %s\r\n", info->last_cause); | ||
2796 | isdn_tty_at_cout(s, info); | ||
2797 | } | ||
2798 | |||
2799 | /* | ||
2800 | * Parse AT&.. commands. | ||
2801 | */ | ||
2802 | static int | ||
2803 | isdn_tty_cmd_ATand(char **p, modem_info * info) | ||
2804 | { | ||
2805 | atemu *m = &info->emu; | ||
2806 | int i; | ||
2807 | char rb[100]; | ||
2808 | |||
2809 | #define MAXRB (sizeof(rb) - 1) | ||
2810 | |||
2811 | switch (*p[0]) { | ||
2812 | case 'B': | ||
2813 | /* &B - Set Buffersize */ | ||
2814 | p[0]++; | ||
2815 | i = isdn_getnum(p); | ||
2816 | if ((i < 0) || (i > ISDN_SERIAL_XMIT_MAX)) | ||
2817 | PARSE_ERROR1; | ||
2818 | #ifdef CONFIG_ISDN_AUDIO | ||
2819 | if ((m->mdmreg[REG_SI1] & 1) && (i > VBUF)) | ||
2820 | PARSE_ERROR1; | ||
2821 | #endif | ||
2822 | m->mdmreg[REG_PSIZE] = i / 16; | ||
2823 | info->xmit_size = m->mdmreg[REG_PSIZE] * 16; | ||
2824 | switch (m->mdmreg[REG_L2PROT]) { | ||
2825 | case ISDN_PROTO_L2_V11096: | ||
2826 | case ISDN_PROTO_L2_V11019: | ||
2827 | case ISDN_PROTO_L2_V11038: | ||
2828 | info->xmit_size /= 10; | ||
2829 | } | ||
2830 | break; | ||
2831 | case 'C': | ||
2832 | /* &C - DCD Status */ | ||
2833 | p[0]++; | ||
2834 | switch (isdn_getnum(p)) { | ||
2835 | case 0: | ||
2836 | m->mdmreg[REG_DCD] &= ~BIT_DCD; | ||
2837 | break; | ||
2838 | case 1: | ||
2839 | m->mdmreg[REG_DCD] |= BIT_DCD; | ||
2840 | break; | ||
2841 | default: | ||
2842 | PARSE_ERROR1 | ||
2843 | } | ||
2844 | break; | ||
2845 | case 'D': | ||
2846 | /* &D - Set DTR-Low-behavior */ | ||
2847 | p[0]++; | ||
2848 | switch (isdn_getnum(p)) { | ||
2849 | case 0: | ||
2850 | m->mdmreg[REG_DTRHUP] &= ~BIT_DTRHUP; | ||
2851 | m->mdmreg[REG_DTRR] &= ~BIT_DTRR; | ||
2852 | break; | ||
2853 | case 2: | ||
2854 | m->mdmreg[REG_DTRHUP] |= BIT_DTRHUP; | ||
2855 | m->mdmreg[REG_DTRR] &= ~BIT_DTRR; | ||
2856 | break; | ||
2857 | case 3: | ||
2858 | m->mdmreg[REG_DTRHUP] |= BIT_DTRHUP; | ||
2859 | m->mdmreg[REG_DTRR] |= BIT_DTRR; | ||
2860 | break; | ||
2861 | default: | ||
2862 | PARSE_ERROR1 | ||
2863 | } | ||
2864 | break; | ||
2865 | case 'E': | ||
2866 | /* &E -Set EAZ/MSN */ | ||
2867 | p[0]++; | ||
2868 | isdn_tty_get_msnstr(m->msn, p); | ||
2869 | break; | ||
2870 | case 'F': | ||
2871 | /* &F -Set Factory-Defaults */ | ||
2872 | p[0]++; | ||
2873 | if (info->msr & UART_MSR_DCD) | ||
2874 | PARSE_ERROR1; | ||
2875 | isdn_tty_reset_profile(m); | ||
2876 | isdn_tty_modem_reset_regs(info, 1); | ||
2877 | break; | ||
2878 | #ifdef DUMMY_HAYES_AT | ||
2879 | case 'K': | ||
2880 | /* only for be compilant with common scripts */ | ||
2881 | /* &K Flowcontrol - no function */ | ||
2882 | p[0]++; | ||
2883 | isdn_getnum(p); | ||
2884 | break; | ||
2885 | #endif | ||
2886 | case 'L': | ||
2887 | /* &L -Set Numbers to listen on */ | ||
2888 | p[0]++; | ||
2889 | i = 0; | ||
2890 | while (*p[0] && (strchr("0123456789,-*[]?;", *p[0])) && | ||
2891 | (i < ISDN_LMSNLEN)) | ||
2892 | m->lmsn[i++] = *p[0]++; | ||
2893 | m->lmsn[i] = '\0'; | ||
2894 | break; | ||
2895 | case 'R': | ||
2896 | /* &R - Set V.110 bitrate adaption */ | ||
2897 | p[0]++; | ||
2898 | i = isdn_getnum(p); | ||
2899 | switch (i) { | ||
2900 | case 0: | ||
2901 | /* Switch off V.110, back to X.75 */ | ||
2902 | m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I; | ||
2903 | m->mdmreg[REG_SI2] = 0; | ||
2904 | info->xmit_size = m->mdmreg[REG_PSIZE] * 16; | ||
2905 | break; | ||
2906 | case 9600: | ||
2907 | m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_V11096; | ||
2908 | m->mdmreg[REG_SI2] = 197; | ||
2909 | info->xmit_size = m->mdmreg[REG_PSIZE] * 16 / 10; | ||
2910 | break; | ||
2911 | case 19200: | ||
2912 | m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_V11019; | ||
2913 | m->mdmreg[REG_SI2] = 199; | ||
2914 | info->xmit_size = m->mdmreg[REG_PSIZE] * 16 / 10; | ||
2915 | break; | ||
2916 | case 38400: | ||
2917 | m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_V11038; | ||
2918 | m->mdmreg[REG_SI2] = 198; /* no existing standard for this */ | ||
2919 | info->xmit_size = m->mdmreg[REG_PSIZE] * 16 / 10; | ||
2920 | break; | ||
2921 | default: | ||
2922 | PARSE_ERROR1; | ||
2923 | } | ||
2924 | /* Switch off T.70 */ | ||
2925 | m->mdmreg[REG_T70] &= ~(BIT_T70 | BIT_T70_EXT); | ||
2926 | /* Set Service 7 */ | ||
2927 | m->mdmreg[REG_SI1] |= 4; | ||
2928 | break; | ||
2929 | case 'S': | ||
2930 | /* &S - Set Windowsize */ | ||
2931 | p[0]++; | ||
2932 | i = isdn_getnum(p); | ||
2933 | if ((i > 0) && (i < 9)) | ||
2934 | m->mdmreg[REG_WSIZE] = i; | ||
2935 | else | ||
2936 | PARSE_ERROR1; | ||
2937 | break; | ||
2938 | case 'V': | ||
2939 | /* &V - Show registers */ | ||
2940 | p[0]++; | ||
2941 | isdn_tty_at_cout("\r\n", info); | ||
2942 | for (i = 0; i < ISDN_MODEM_NUMREG; i++) { | ||
2943 | sprintf(rb, "S%02d=%03d%s", i, | ||
2944 | m->mdmreg[i], ((i + 1) % 10) ? " " : "\r\n"); | ||
2945 | isdn_tty_at_cout(rb, info); | ||
2946 | } | ||
2947 | sprintf(rb, "\r\nEAZ/MSN: %.50s\r\n", | ||
2948 | strlen(m->msn) ? m->msn : "None"); | ||
2949 | isdn_tty_at_cout(rb, info); | ||
2950 | if (strlen(m->lmsn)) { | ||
2951 | isdn_tty_at_cout("\r\nListen: ", info); | ||
2952 | isdn_tty_at_cout(m->lmsn, info); | ||
2953 | isdn_tty_at_cout("\r\n", info); | ||
2954 | } | ||
2955 | break; | ||
2956 | case 'W': | ||
2957 | /* &W - Write Profile */ | ||
2958 | p[0]++; | ||
2959 | switch (*p[0]) { | ||
2960 | case '0': | ||
2961 | p[0]++; | ||
2962 | modem_write_profile(m); | ||
2963 | break; | ||
2964 | default: | ||
2965 | PARSE_ERROR1; | ||
2966 | } | ||
2967 | break; | ||
2968 | case 'X': | ||
2969 | /* &X - Switch to BTX-Mode and T.70 */ | ||
2970 | p[0]++; | ||
2971 | switch (isdn_getnum(p)) { | ||
2972 | case 0: | ||
2973 | m->mdmreg[REG_T70] &= ~(BIT_T70 | BIT_T70_EXT); | ||
2974 | info->xmit_size = m->mdmreg[REG_PSIZE] * 16; | ||
2975 | break; | ||
2976 | case 1: | ||
2977 | m->mdmreg[REG_T70] |= BIT_T70; | ||
2978 | m->mdmreg[REG_T70] &= ~BIT_T70_EXT; | ||
2979 | m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I; | ||
2980 | info->xmit_size = 112; | ||
2981 | m->mdmreg[REG_SI1] = 4; | ||
2982 | m->mdmreg[REG_SI2] = 0; | ||
2983 | break; | ||
2984 | case 2: | ||
2985 | m->mdmreg[REG_T70] |= (BIT_T70 | BIT_T70_EXT); | ||
2986 | m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I; | ||
2987 | info->xmit_size = 112; | ||
2988 | m->mdmreg[REG_SI1] = 4; | ||
2989 | m->mdmreg[REG_SI2] = 0; | ||
2990 | break; | ||
2991 | default: | ||
2992 | PARSE_ERROR1; | ||
2993 | } | ||
2994 | break; | ||
2995 | default: | ||
2996 | PARSE_ERROR1; | ||
2997 | } | ||
2998 | return 0; | ||
2999 | } | ||
3000 | |||
3001 | static int | ||
3002 | isdn_tty_check_ats(int mreg, int mval, modem_info * info, atemu * m) | ||
3003 | { | ||
3004 | /* Some plausibility checks */ | ||
3005 | switch (mreg) { | ||
3006 | case REG_L2PROT: | ||
3007 | if (mval > ISDN_PROTO_L2_MAX) | ||
3008 | return 1; | ||
3009 | break; | ||
3010 | case REG_PSIZE: | ||
3011 | if ((mval * 16) > ISDN_SERIAL_XMIT_MAX) | ||
3012 | return 1; | ||
3013 | #ifdef CONFIG_ISDN_AUDIO | ||
3014 | if ((m->mdmreg[REG_SI1] & 1) && (mval > VBUFX)) | ||
3015 | return 1; | ||
3016 | #endif | ||
3017 | info->xmit_size = mval * 16; | ||
3018 | switch (m->mdmreg[REG_L2PROT]) { | ||
3019 | case ISDN_PROTO_L2_V11096: | ||
3020 | case ISDN_PROTO_L2_V11019: | ||
3021 | case ISDN_PROTO_L2_V11038: | ||
3022 | info->xmit_size /= 10; | ||
3023 | } | ||
3024 | break; | ||
3025 | case REG_SI1I: | ||
3026 | case REG_PLAN: | ||
3027 | case REG_SCREEN: | ||
3028 | /* readonly registers */ | ||
3029 | return 1; | ||
3030 | } | ||
3031 | return 0; | ||
3032 | } | ||
3033 | |||
3034 | /* | ||
3035 | * Perform ATS command | ||
3036 | */ | ||
3037 | static int | ||
3038 | isdn_tty_cmd_ATS(char **p, modem_info * info) | ||
3039 | { | ||
3040 | atemu *m = &info->emu; | ||
3041 | int bitpos; | ||
3042 | int mreg; | ||
3043 | int mval; | ||
3044 | int bval; | ||
3045 | |||
3046 | mreg = isdn_getnum(p); | ||
3047 | if (mreg < 0 || mreg >= ISDN_MODEM_NUMREG) | ||
3048 | PARSE_ERROR1; | ||
3049 | switch (*p[0]) { | ||
3050 | case '=': | ||
3051 | p[0]++; | ||
3052 | mval = isdn_getnum(p); | ||
3053 | if (mval < 0 || mval > 255) | ||
3054 | PARSE_ERROR1; | ||
3055 | if (isdn_tty_check_ats(mreg, mval, info, m)) | ||
3056 | PARSE_ERROR1; | ||
3057 | m->mdmreg[mreg] = mval; | ||
3058 | break; | ||
3059 | case '.': | ||
3060 | /* Set/Clear a single bit */ | ||
3061 | p[0]++; | ||
3062 | bitpos = isdn_getnum(p); | ||
3063 | if ((bitpos < 0) || (bitpos > 7)) | ||
3064 | PARSE_ERROR1; | ||
3065 | switch (*p[0]) { | ||
3066 | case '=': | ||
3067 | p[0]++; | ||
3068 | bval = isdn_getnum(p); | ||
3069 | if (bval < 0 || bval > 1) | ||
3070 | PARSE_ERROR1; | ||
3071 | if (bval) | ||
3072 | mval = m->mdmreg[mreg] | (1 << bitpos); | ||
3073 | else | ||
3074 | mval = m->mdmreg[mreg] & ~(1 << bitpos); | ||
3075 | if (isdn_tty_check_ats(mreg, mval, info, m)) | ||
3076 | PARSE_ERROR1; | ||
3077 | m->mdmreg[mreg] = mval; | ||
3078 | break; | ||
3079 | case '?': | ||
3080 | p[0]++; | ||
3081 | isdn_tty_at_cout("\r\n", info); | ||
3082 | isdn_tty_at_cout((m->mdmreg[mreg] & (1 << bitpos)) ? "1" : "0", | ||
3083 | info); | ||
3084 | break; | ||
3085 | default: | ||
3086 | PARSE_ERROR1; | ||
3087 | } | ||
3088 | break; | ||
3089 | case '?': | ||
3090 | p[0]++; | ||
3091 | isdn_tty_show_profile(mreg, info); | ||
3092 | break; | ||
3093 | default: | ||
3094 | PARSE_ERROR1; | ||
3095 | break; | ||
3096 | } | ||
3097 | return 0; | ||
3098 | } | ||
3099 | |||
3100 | /* | ||
3101 | * Perform ATA command | ||
3102 | */ | ||
3103 | static void | ||
3104 | isdn_tty_cmd_ATA(modem_info * info) | ||
3105 | { | ||
3106 | atemu *m = &info->emu; | ||
3107 | isdn_ctrl cmd; | ||
3108 | int l2; | ||
3109 | |||
3110 | if (info->msr & UART_MSR_RI) { | ||
3111 | /* Accept incoming call */ | ||
3112 | info->last_dir = 0; | ||
3113 | strcpy(info->last_num, dev->num[info->drv_index]); | ||
3114 | m->mdmreg[REG_RINGCNT] = 0; | ||
3115 | info->msr &= ~UART_MSR_RI; | ||
3116 | l2 = m->mdmreg[REG_L2PROT]; | ||
3117 | #ifdef CONFIG_ISDN_AUDIO | ||
3118 | /* If more than one bit set in reg18, autoselect Layer2 */ | ||
3119 | if ((m->mdmreg[REG_SI1] & m->mdmreg[REG_SI1I]) != m->mdmreg[REG_SI1]) { | ||
3120 | if (m->mdmreg[REG_SI1I] == 1) { | ||
3121 | if ((l2 != ISDN_PROTO_L2_MODEM) && (l2 != ISDN_PROTO_L2_FAX)) | ||
3122 | l2 = ISDN_PROTO_L2_TRANS; | ||
3123 | } else | ||
3124 | l2 = ISDN_PROTO_L2_X75I; | ||
3125 | } | ||
3126 | #endif | ||
3127 | cmd.driver = info->isdn_driver; | ||
3128 | cmd.command = ISDN_CMD_SETL2; | ||
3129 | cmd.arg = info->isdn_channel + (l2 << 8); | ||
3130 | info->last_l2 = l2; | ||
3131 | isdn_command(&cmd); | ||
3132 | cmd.driver = info->isdn_driver; | ||
3133 | cmd.command = ISDN_CMD_SETL3; | ||
3134 | cmd.arg = info->isdn_channel + (m->mdmreg[REG_L3PROT] << 8); | ||
3135 | #ifdef CONFIG_ISDN_TTY_FAX | ||
3136 | if (l2 == ISDN_PROTO_L2_FAX) { | ||
3137 | cmd.parm.fax = info->fax; | ||
3138 | info->fax->direction = ISDN_TTY_FAX_CONN_IN; | ||
3139 | } | ||
3140 | #endif | ||
3141 | isdn_command(&cmd); | ||
3142 | cmd.driver = info->isdn_driver; | ||
3143 | cmd.arg = info->isdn_channel; | ||
3144 | cmd.command = ISDN_CMD_ACCEPTD; | ||
3145 | info->dialing = 16; | ||
3146 | info->emu.carrierwait = 0; | ||
3147 | isdn_command(&cmd); | ||
3148 | isdn_timer_ctrl(ISDN_TIMER_CARRIER, 1); | ||
3149 | } else | ||
3150 | isdn_tty_modem_result(RESULT_NO_ANSWER, info); | ||
3151 | } | ||
3152 | |||
3153 | #ifdef CONFIG_ISDN_AUDIO | ||
3154 | /* | ||
3155 | * Parse AT+F.. commands | ||
3156 | */ | ||
3157 | static int | ||
3158 | isdn_tty_cmd_PLUSF(char **p, modem_info * info) | ||
3159 | { | ||
3160 | atemu *m = &info->emu; | ||
3161 | char rs[20]; | ||
3162 | |||
3163 | if (!strncmp(p[0], "CLASS", 5)) { | ||
3164 | p[0] += 5; | ||
3165 | switch (*p[0]) { | ||
3166 | case '?': | ||
3167 | p[0]++; | ||
3168 | sprintf(rs, "\r\n%d", | ||
3169 | (m->mdmreg[REG_SI1] & 1) ? 8 : 0); | ||
3170 | #ifdef CONFIG_ISDN_TTY_FAX | ||
3171 | if (TTY_IS_FCLASS2(info)) | ||
3172 | sprintf(rs, "\r\n2"); | ||
3173 | else if (TTY_IS_FCLASS1(info)) | ||
3174 | sprintf(rs, "\r\n1"); | ||
3175 | #endif | ||
3176 | isdn_tty_at_cout(rs, info); | ||
3177 | break; | ||
3178 | case '=': | ||
3179 | p[0]++; | ||
3180 | switch (*p[0]) { | ||
3181 | case '0': | ||
3182 | p[0]++; | ||
3183 | m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I; | ||
3184 | m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_TRANS; | ||
3185 | m->mdmreg[REG_SI1] = 4; | ||
3186 | info->xmit_size = | ||
3187 | m->mdmreg[REG_PSIZE] * 16; | ||
3188 | break; | ||
3189 | #ifdef CONFIG_ISDN_TTY_FAX | ||
3190 | case '1': | ||
3191 | p[0]++; | ||
3192 | if (!(dev->global_features & | ||
3193 | ISDN_FEATURE_L3_FCLASS1)) | ||
3194 | PARSE_ERROR1; | ||
3195 | m->mdmreg[REG_SI1] = 1; | ||
3196 | m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_FAX; | ||
3197 | m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_FCLASS1; | ||
3198 | info->xmit_size = | ||
3199 | m->mdmreg[REG_PSIZE] * 16; | ||
3200 | break; | ||
3201 | case '2': | ||
3202 | p[0]++; | ||
3203 | if (!(dev->global_features & | ||
3204 | ISDN_FEATURE_L3_FCLASS2)) | ||
3205 | PARSE_ERROR1; | ||
3206 | m->mdmreg[REG_SI1] = 1; | ||
3207 | m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_FAX; | ||
3208 | m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_FCLASS2; | ||
3209 | info->xmit_size = | ||
3210 | m->mdmreg[REG_PSIZE] * 16; | ||
3211 | break; | ||
3212 | #endif | ||
3213 | case '8': | ||
3214 | p[0]++; | ||
3215 | /* L2 will change on dialout with si=1 */ | ||
3216 | m->mdmreg[REG_L2PROT] = ISDN_PROTO_L2_X75I; | ||
3217 | m->mdmreg[REG_L3PROT] = ISDN_PROTO_L3_TRANS; | ||
3218 | m->mdmreg[REG_SI1] = 5; | ||
3219 | info->xmit_size = VBUF; | ||
3220 | break; | ||
3221 | case '?': | ||
3222 | p[0]++; | ||
3223 | strcpy(rs, "\r\n0,"); | ||
3224 | #ifdef CONFIG_ISDN_TTY_FAX | ||
3225 | if (dev->global_features & | ||
3226 | ISDN_FEATURE_L3_FCLASS1) | ||
3227 | strcat(rs, "1,"); | ||
3228 | if (dev->global_features & | ||
3229 | ISDN_FEATURE_L3_FCLASS2) | ||
3230 | strcat(rs, "2,"); | ||
3231 | #endif | ||
3232 | strcat(rs, "8"); | ||
3233 | isdn_tty_at_cout(rs, info); | ||
3234 | break; | ||
3235 | default: | ||
3236 | PARSE_ERROR1; | ||
3237 | } | ||
3238 | break; | ||
3239 | default: | ||
3240 | PARSE_ERROR1; | ||
3241 | } | ||
3242 | return 0; | ||
3243 | } | ||
3244 | #ifdef CONFIG_ISDN_TTY_FAX | ||
3245 | return (isdn_tty_cmd_PLUSF_FAX(p, info)); | ||
3246 | #else | ||
3247 | PARSE_ERROR1; | ||
3248 | #endif | ||
3249 | } | ||
3250 | |||
3251 | /* | ||
3252 | * Parse AT+V.. commands | ||
3253 | */ | ||
3254 | static int | ||
3255 | isdn_tty_cmd_PLUSV(char **p, modem_info * info) | ||
3256 | { | ||
3257 | atemu *m = &info->emu; | ||
3258 | isdn_ctrl cmd; | ||
3259 | static char *vcmd[] = | ||
3260 | {"NH", "IP", "LS", "RX", "SD", "SM", "TX", "DD", NULL}; | ||
3261 | int i; | ||
3262 | int par1; | ||
3263 | int par2; | ||
3264 | char rs[20]; | ||
3265 | |||
3266 | i = 0; | ||
3267 | while (vcmd[i]) { | ||
3268 | if (!strncmp(vcmd[i], p[0], 2)) { | ||
3269 | p[0] += 2; | ||
3270 | break; | ||
3271 | } | ||
3272 | i++; | ||
3273 | } | ||
3274 | switch (i) { | ||
3275 | case 0: | ||
3276 | /* AT+VNH - Auto hangup feature */ | ||
3277 | switch (*p[0]) { | ||
3278 | case '?': | ||
3279 | p[0]++; | ||
3280 | isdn_tty_at_cout("\r\n1", info); | ||
3281 | break; | ||
3282 | case '=': | ||
3283 | p[0]++; | ||
3284 | switch (*p[0]) { | ||
3285 | case '1': | ||
3286 | p[0]++; | ||
3287 | break; | ||
3288 | case '?': | ||
3289 | p[0]++; | ||
3290 | isdn_tty_at_cout("\r\n1", info); | ||
3291 | break; | ||
3292 | default: | ||
3293 | PARSE_ERROR1; | ||
3294 | } | ||
3295 | break; | ||
3296 | default: | ||
3297 | PARSE_ERROR1; | ||
3298 | } | ||
3299 | break; | ||
3300 | case 1: | ||
3301 | /* AT+VIP - Reset all voice parameters */ | ||
3302 | isdn_tty_modem_reset_vpar(m); | ||
3303 | break; | ||
3304 | case 2: | ||
3305 | /* AT+VLS - Select device, accept incoming call */ | ||
3306 | switch (*p[0]) { | ||
3307 | case '?': | ||
3308 | p[0]++; | ||
3309 | sprintf(rs, "\r\n%d", m->vpar[0]); | ||
3310 | isdn_tty_at_cout(rs, info); | ||
3311 | break; | ||
3312 | case '=': | ||
3313 | p[0]++; | ||
3314 | switch (*p[0]) { | ||
3315 | case '0': | ||
3316 | p[0]++; | ||
3317 | m->vpar[0] = 0; | ||
3318 | break; | ||
3319 | case '2': | ||
3320 | p[0]++; | ||
3321 | m->vpar[0] = 2; | ||
3322 | break; | ||
3323 | case '?': | ||
3324 | p[0]++; | ||
3325 | isdn_tty_at_cout("\r\n0,2", info); | ||
3326 | break; | ||
3327 | default: | ||
3328 | PARSE_ERROR1; | ||
3329 | } | ||
3330 | break; | ||
3331 | default: | ||
3332 | PARSE_ERROR1; | ||
3333 | } | ||
3334 | break; | ||
3335 | case 3: | ||
3336 | /* AT+VRX - Start recording */ | ||
3337 | if (!m->vpar[0]) | ||
3338 | PARSE_ERROR1; | ||
3339 | if (info->online != 1) { | ||
3340 | isdn_tty_modem_result(RESULT_NO_ANSWER, info); | ||
3341 | return 1; | ||
3342 | } | ||
3343 | info->dtmf_state = isdn_audio_dtmf_init(info->dtmf_state); | ||
3344 | if (!info->dtmf_state) { | ||
3345 | printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n"); | ||
3346 | PARSE_ERROR1; | ||
3347 | } | ||
3348 | info->silence_state = isdn_audio_silence_init(info->silence_state); | ||
3349 | if (!info->silence_state) { | ||
3350 | printk(KERN_WARNING "isdn_tty: Couldn't malloc silence state\n"); | ||
3351 | PARSE_ERROR1; | ||
3352 | } | ||
3353 | if (m->vpar[3] < 5) { | ||
3354 | info->adpcmr = isdn_audio_adpcm_init(info->adpcmr, m->vpar[3]); | ||
3355 | if (!info->adpcmr) { | ||
3356 | printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n"); | ||
3357 | PARSE_ERROR1; | ||
3358 | } | ||
3359 | } | ||
3360 | #ifdef ISDN_DEBUG_AT | ||
3361 | printk(KERN_DEBUG "AT: +VRX\n"); | ||
3362 | #endif | ||
3363 | info->vonline |= 1; | ||
3364 | isdn_tty_modem_result(RESULT_CONNECT, info); | ||
3365 | return 0; | ||
3366 | break; | ||
3367 | case 4: | ||
3368 | /* AT+VSD - Silence detection */ | ||
3369 | switch (*p[0]) { | ||
3370 | case '?': | ||
3371 | p[0]++; | ||
3372 | sprintf(rs, "\r\n<%d>,<%d>", | ||
3373 | m->vpar[1], | ||
3374 | m->vpar[2]); | ||
3375 | isdn_tty_at_cout(rs, info); | ||
3376 | break; | ||
3377 | case '=': | ||
3378 | p[0]++; | ||
3379 | if ((*p[0]>='0') && (*p[0]<='9')) { | ||
3380 | par1 = isdn_getnum(p); | ||
3381 | if ((par1 < 0) || (par1 > 31)) | ||
3382 | PARSE_ERROR1; | ||
3383 | if (*p[0] != ',') | ||
3384 | PARSE_ERROR1; | ||
3385 | p[0]++; | ||
3386 | par2 = isdn_getnum(p); | ||
3387 | if ((par2 < 0) || (par2 > 255)) | ||
3388 | PARSE_ERROR1; | ||
3389 | m->vpar[1] = par1; | ||
3390 | m->vpar[2] = par2; | ||
3391 | break; | ||
3392 | } else | ||
3393 | if (*p[0] == '?') { | ||
3394 | p[0]++; | ||
3395 | isdn_tty_at_cout("\r\n<0-31>,<0-255>", | ||
3396 | info); | ||
3397 | break; | ||
3398 | } else | ||
3399 | PARSE_ERROR1; | ||
3400 | break; | ||
3401 | default: | ||
3402 | PARSE_ERROR1; | ||
3403 | } | ||
3404 | break; | ||
3405 | case 5: | ||
3406 | /* AT+VSM - Select compression */ | ||
3407 | switch (*p[0]) { | ||
3408 | case '?': | ||
3409 | p[0]++; | ||
3410 | sprintf(rs, "\r\n<%d>,<%d><8000>", | ||
3411 | m->vpar[3], | ||
3412 | m->vpar[1]); | ||
3413 | isdn_tty_at_cout(rs, info); | ||
3414 | break; | ||
3415 | case '=': | ||
3416 | p[0]++; | ||
3417 | switch (*p[0]) { | ||
3418 | case '2': | ||
3419 | case '3': | ||
3420 | case '4': | ||
3421 | case '5': | ||
3422 | case '6': | ||
3423 | par1 = isdn_getnum(p); | ||
3424 | if ((par1 < 2) || (par1 > 6)) | ||
3425 | PARSE_ERROR1; | ||
3426 | m->vpar[3] = par1; | ||
3427 | break; | ||
3428 | case '?': | ||
3429 | p[0]++; | ||
3430 | isdn_tty_at_cout("\r\n2;ADPCM;2;0;(8000)\r\n", | ||
3431 | info); | ||
3432 | isdn_tty_at_cout("3;ADPCM;3;0;(8000)\r\n", | ||
3433 | info); | ||
3434 | isdn_tty_at_cout("4;ADPCM;4;0;(8000)\r\n", | ||
3435 | info); | ||
3436 | isdn_tty_at_cout("5;ALAW;8;0;(8000)\r\n", | ||
3437 | info); | ||
3438 | isdn_tty_at_cout("6;ULAW;8;0;(8000)\r\n", | ||
3439 | info); | ||
3440 | break; | ||
3441 | default: | ||
3442 | PARSE_ERROR1; | ||
3443 | } | ||
3444 | break; | ||
3445 | default: | ||
3446 | PARSE_ERROR1; | ||
3447 | } | ||
3448 | break; | ||
3449 | case 6: | ||
3450 | /* AT+VTX - Start sending */ | ||
3451 | if (!m->vpar[0]) | ||
3452 | PARSE_ERROR1; | ||
3453 | if (info->online != 1) { | ||
3454 | isdn_tty_modem_result(RESULT_NO_ANSWER, info); | ||
3455 | return 1; | ||
3456 | } | ||
3457 | info->dtmf_state = isdn_audio_dtmf_init(info->dtmf_state); | ||
3458 | if (!info->dtmf_state) { | ||
3459 | printk(KERN_WARNING "isdn_tty: Couldn't malloc dtmf state\n"); | ||
3460 | PARSE_ERROR1; | ||
3461 | } | ||
3462 | if (m->vpar[3] < 5) { | ||
3463 | info->adpcms = isdn_audio_adpcm_init(info->adpcms, m->vpar[3]); | ||
3464 | if (!info->adpcms) { | ||
3465 | printk(KERN_WARNING "isdn_tty: Couldn't malloc adpcm state\n"); | ||
3466 | PARSE_ERROR1; | ||
3467 | } | ||
3468 | } | ||
3469 | #ifdef ISDN_DEBUG_AT | ||
3470 | printk(KERN_DEBUG "AT: +VTX\n"); | ||
3471 | #endif | ||
3472 | m->lastDLE = 0; | ||
3473 | info->vonline |= 2; | ||
3474 | isdn_tty_modem_result(RESULT_CONNECT, info); | ||
3475 | return 0; | ||
3476 | break; | ||
3477 | case 7: | ||
3478 | /* AT+VDD - DTMF detection */ | ||
3479 | switch (*p[0]) { | ||
3480 | case '?': | ||
3481 | p[0]++; | ||
3482 | sprintf(rs, "\r\n<%d>,<%d>", | ||
3483 | m->vpar[4], | ||
3484 | m->vpar[5]); | ||
3485 | isdn_tty_at_cout(rs, info); | ||
3486 | break; | ||
3487 | case '=': | ||
3488 | p[0]++; | ||
3489 | if ((*p[0]>='0') && (*p[0]<='9')) { | ||
3490 | if (info->online != 1) | ||
3491 | PARSE_ERROR1; | ||
3492 | par1 = isdn_getnum(p); | ||
3493 | if ((par1 < 0) || (par1 > 15)) | ||
3494 | PARSE_ERROR1; | ||
3495 | if (*p[0] != ',') | ||
3496 | PARSE_ERROR1; | ||
3497 | p[0]++; | ||
3498 | par2 = isdn_getnum(p); | ||
3499 | if ((par2 < 0) || (par2 > 255)) | ||
3500 | PARSE_ERROR1; | ||
3501 | m->vpar[4] = par1; | ||
3502 | m->vpar[5] = par2; | ||
3503 | cmd.driver = info->isdn_driver; | ||
3504 | cmd.command = ISDN_CMD_AUDIO; | ||
3505 | cmd.arg = info->isdn_channel + (ISDN_AUDIO_SETDD << 8); | ||
3506 | cmd.parm.num[0] = par1; | ||
3507 | cmd.parm.num[1] = par2; | ||
3508 | isdn_command(&cmd); | ||
3509 | break; | ||
3510 | } else | ||
3511 | if (*p[0] == '?') { | ||
3512 | p[0]++; | ||
3513 | isdn_tty_at_cout("\r\n<0-15>,<0-255>", | ||
3514 | info); | ||
3515 | break; | ||
3516 | } else | ||
3517 | PARSE_ERROR1; | ||
3518 | break; | ||
3519 | default: | ||
3520 | PARSE_ERROR1; | ||
3521 | } | ||
3522 | break; | ||
3523 | default: | ||
3524 | PARSE_ERROR1; | ||
3525 | } | ||
3526 | return 0; | ||
3527 | } | ||
3528 | #endif /* CONFIG_ISDN_AUDIO */ | ||
3529 | |||
3530 | /* | ||
3531 | * Parse and perform an AT-command-line. | ||
3532 | */ | ||
3533 | static void | ||
3534 | isdn_tty_parse_at(modem_info * info) | ||
3535 | { | ||
3536 | atemu *m = &info->emu; | ||
3537 | char *p; | ||
3538 | char ds[40]; | ||
3539 | |||
3540 | #ifdef ISDN_DEBUG_AT | ||
3541 | printk(KERN_DEBUG "AT: '%s'\n", m->mdmcmd); | ||
3542 | #endif | ||
3543 | for (p = &m->mdmcmd[2]; *p;) { | ||
3544 | switch (*p) { | ||
3545 | case ' ': | ||
3546 | p++; | ||
3547 | break; | ||
3548 | case 'A': | ||
3549 | /* A - Accept incoming call */ | ||
3550 | p++; | ||
3551 | isdn_tty_cmd_ATA(info); | ||
3552 | return; | ||
3553 | break; | ||
3554 | case 'D': | ||
3555 | /* D - Dial */ | ||
3556 | if (info->msr & UART_MSR_DCD) | ||
3557 | PARSE_ERROR; | ||
3558 | if (info->msr & UART_MSR_RI) { | ||
3559 | isdn_tty_modem_result(RESULT_NO_CARRIER, info); | ||
3560 | return; | ||
3561 | } | ||
3562 | isdn_tty_getdial(++p, ds, sizeof ds); | ||
3563 | p += strlen(p); | ||
3564 | if (!strlen(m->msn)) | ||
3565 | isdn_tty_modem_result(RESULT_NO_MSN_EAZ, info); | ||
3566 | else if (strlen(ds)) | ||
3567 | isdn_tty_dial(ds, info, m); | ||
3568 | else | ||
3569 | PARSE_ERROR; | ||
3570 | return; | ||
3571 | case 'E': | ||
3572 | /* E - Turn Echo on/off */ | ||
3573 | p++; | ||
3574 | switch (isdn_getnum(&p)) { | ||
3575 | case 0: | ||
3576 | m->mdmreg[REG_ECHO] &= ~BIT_ECHO; | ||
3577 | break; | ||
3578 | case 1: | ||
3579 | m->mdmreg[REG_ECHO] |= BIT_ECHO; | ||
3580 | break; | ||
3581 | default: | ||
3582 | PARSE_ERROR; | ||
3583 | } | ||
3584 | break; | ||
3585 | case 'H': | ||
3586 | /* H - On/Off-hook */ | ||
3587 | p++; | ||
3588 | switch (*p) { | ||
3589 | case '0': | ||
3590 | p++; | ||
3591 | isdn_tty_on_hook(info); | ||
3592 | break; | ||
3593 | case '1': | ||
3594 | p++; | ||
3595 | isdn_tty_off_hook(); | ||
3596 | break; | ||
3597 | default: | ||
3598 | isdn_tty_on_hook(info); | ||
3599 | break; | ||
3600 | } | ||
3601 | break; | ||
3602 | case 'I': | ||
3603 | /* I - Information */ | ||
3604 | p++; | ||
3605 | isdn_tty_at_cout("\r\nLinux ISDN", info); | ||
3606 | switch (*p) { | ||
3607 | case '0': | ||
3608 | case '1': | ||
3609 | p++; | ||
3610 | break; | ||
3611 | case '2': | ||
3612 | p++; | ||
3613 | isdn_tty_report(info); | ||
3614 | break; | ||
3615 | case '3': | ||
3616 | p++; | ||
3617 | sprintf(ds, "\r\n%d", info->emu.charge); | ||
3618 | isdn_tty_at_cout(ds, info); | ||
3619 | break; | ||
3620 | default:; | ||
3621 | } | ||
3622 | break; | ||
3623 | #ifdef DUMMY_HAYES_AT | ||
3624 | case 'L': | ||
3625 | case 'M': | ||
3626 | /* only for be compilant with common scripts */ | ||
3627 | /* no function */ | ||
3628 | p++; | ||
3629 | isdn_getnum(&p); | ||
3630 | break; | ||
3631 | #endif | ||
3632 | case 'O': | ||
3633 | /* O - Go online */ | ||
3634 | p++; | ||
3635 | if (info->msr & UART_MSR_DCD) | ||
3636 | /* if B-Channel is up */ | ||
3637 | isdn_tty_modem_result((m->mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM) ? RESULT_CONNECT:RESULT_CONNECT64000, info); | ||
3638 | else | ||
3639 | isdn_tty_modem_result(RESULT_NO_CARRIER, info); | ||
3640 | return; | ||
3641 | case 'Q': | ||
3642 | /* Q - Turn Emulator messages on/off */ | ||
3643 | p++; | ||
3644 | switch (isdn_getnum(&p)) { | ||
3645 | case 0: | ||
3646 | m->mdmreg[REG_RESP] |= BIT_RESP; | ||
3647 | break; | ||
3648 | case 1: | ||
3649 | m->mdmreg[REG_RESP] &= ~BIT_RESP; | ||
3650 | break; | ||
3651 | default: | ||
3652 | PARSE_ERROR; | ||
3653 | } | ||
3654 | break; | ||
3655 | case 'S': | ||
3656 | /* S - Set/Get Register */ | ||
3657 | p++; | ||
3658 | if (isdn_tty_cmd_ATS(&p, info)) | ||
3659 | return; | ||
3660 | break; | ||
3661 | case 'V': | ||
3662 | /* V - Numeric or ASCII Emulator-messages */ | ||
3663 | p++; | ||
3664 | switch (isdn_getnum(&p)) { | ||
3665 | case 0: | ||
3666 | m->mdmreg[REG_RESP] |= BIT_RESPNUM; | ||
3667 | break; | ||
3668 | case 1: | ||
3669 | m->mdmreg[REG_RESP] &= ~BIT_RESPNUM; | ||
3670 | break; | ||
3671 | default: | ||
3672 | PARSE_ERROR; | ||
3673 | } | ||
3674 | break; | ||
3675 | case 'Z': | ||
3676 | /* Z - Load Registers from Profile */ | ||
3677 | p++; | ||
3678 | if (info->msr & UART_MSR_DCD) { | ||
3679 | info->online = 0; | ||
3680 | isdn_tty_on_hook(info); | ||
3681 | } | ||
3682 | isdn_tty_modem_reset_regs(info, 1); | ||
3683 | break; | ||
3684 | case '+': | ||
3685 | p++; | ||
3686 | switch (*p) { | ||
3687 | #ifdef CONFIG_ISDN_AUDIO | ||
3688 | case 'F': | ||
3689 | p++; | ||
3690 | if (isdn_tty_cmd_PLUSF(&p, info)) | ||
3691 | return; | ||
3692 | break; | ||
3693 | case 'V': | ||
3694 | if ((!(m->mdmreg[REG_SI1] & 1)) || | ||
3695 | (m->mdmreg[REG_L2PROT] == ISDN_PROTO_L2_MODEM)) | ||
3696 | PARSE_ERROR; | ||
3697 | p++; | ||
3698 | if (isdn_tty_cmd_PLUSV(&p, info)) | ||
3699 | return; | ||
3700 | break; | ||
3701 | #endif /* CONFIG_ISDN_AUDIO */ | ||
3702 | case 'S': /* SUSPEND */ | ||
3703 | p++; | ||
3704 | isdn_tty_get_msnstr(ds, &p); | ||
3705 | isdn_tty_suspend(ds, info, m); | ||
3706 | break; | ||
3707 | case 'R': /* RESUME */ | ||
3708 | p++; | ||
3709 | isdn_tty_get_msnstr(ds, &p); | ||
3710 | isdn_tty_resume(ds, info, m); | ||
3711 | break; | ||
3712 | case 'M': /* MESSAGE */ | ||
3713 | p++; | ||
3714 | isdn_tty_send_msg(info, m, p); | ||
3715 | break; | ||
3716 | default: | ||
3717 | PARSE_ERROR; | ||
3718 | } | ||
3719 | break; | ||
3720 | case '&': | ||
3721 | p++; | ||
3722 | if (isdn_tty_cmd_ATand(&p, info)) | ||
3723 | return; | ||
3724 | break; | ||
3725 | default: | ||
3726 | PARSE_ERROR; | ||
3727 | } | ||
3728 | } | ||
3729 | #ifdef CONFIG_ISDN_AUDIO | ||
3730 | if (!info->vonline) | ||
3731 | #endif | ||
3732 | isdn_tty_modem_result(RESULT_OK, info); | ||
3733 | } | ||
3734 | |||
3735 | /* Need own toupper() because standard-toupper is not available | ||
3736 | * within modules. | ||
3737 | */ | ||
3738 | #define my_toupper(c) (((c>='a')&&(c<='z'))?(c&0xdf):c) | ||
3739 | |||
3740 | /* | ||
3741 | * Perform line-editing of AT-commands | ||
3742 | * | ||
3743 | * Parameters: | ||
3744 | * p inputbuffer | ||
3745 | * count length of buffer | ||
3746 | * channel index to line (minor-device) | ||
3747 | */ | ||
3748 | static int | ||
3749 | isdn_tty_edit_at(const char *p, int count, modem_info * info) | ||
3750 | { | ||
3751 | atemu *m = &info->emu; | ||
3752 | int total = 0; | ||
3753 | u_char c; | ||
3754 | char eb[2]; | ||
3755 | int cnt; | ||
3756 | |||
3757 | for (cnt = count; cnt > 0; p++, cnt--) { | ||
3758 | c = *p; | ||
3759 | total++; | ||
3760 | if (c == m->mdmreg[REG_CR] || c == m->mdmreg[REG_LF]) { | ||
3761 | /* Separator (CR or LF) */ | ||
3762 | m->mdmcmd[m->mdmcmdl] = 0; | ||
3763 | if (m->mdmreg[REG_ECHO] & BIT_ECHO) { | ||
3764 | eb[0] = c; | ||
3765 | eb[1] = 0; | ||
3766 | isdn_tty_at_cout(eb, info); | ||
3767 | } | ||
3768 | if ((m->mdmcmdl >= 2) && (!(strncmp(m->mdmcmd, "AT", 2)))) | ||
3769 | isdn_tty_parse_at(info); | ||
3770 | m->mdmcmdl = 0; | ||
3771 | continue; | ||
3772 | } | ||
3773 | if (c == m->mdmreg[REG_BS] && m->mdmreg[REG_BS] < 128) { | ||
3774 | /* Backspace-Function */ | ||
3775 | if ((m->mdmcmdl > 2) || (!m->mdmcmdl)) { | ||
3776 | if (m->mdmcmdl) | ||
3777 | m->mdmcmdl--; | ||
3778 | if (m->mdmreg[REG_ECHO] & BIT_ECHO) | ||
3779 | isdn_tty_at_cout("\b", info); | ||
3780 | } | ||
3781 | continue; | ||
3782 | } | ||
3783 | if (cmdchar(c)) { | ||
3784 | if (m->mdmreg[REG_ECHO] & BIT_ECHO) { | ||
3785 | eb[0] = c; | ||
3786 | eb[1] = 0; | ||
3787 | isdn_tty_at_cout(eb, info); | ||
3788 | } | ||
3789 | if (m->mdmcmdl < 255) { | ||
3790 | c = my_toupper(c); | ||
3791 | switch (m->mdmcmdl) { | ||
3792 | case 1: | ||
3793 | if (c == 'T') { | ||
3794 | m->mdmcmd[m->mdmcmdl] = c; | ||
3795 | m->mdmcmd[++m->mdmcmdl] = 0; | ||
3796 | break; | ||
3797 | } else | ||
3798 | m->mdmcmdl = 0; | ||
3799 | /* Fall through, check for 'A' */ | ||
3800 | case 0: | ||
3801 | if (c == 'A') { | ||
3802 | m->mdmcmd[m->mdmcmdl] = c; | ||
3803 | m->mdmcmd[++m->mdmcmdl] = 0; | ||
3804 | } | ||
3805 | break; | ||
3806 | default: | ||
3807 | m->mdmcmd[m->mdmcmdl] = c; | ||
3808 | m->mdmcmd[++m->mdmcmdl] = 0; | ||
3809 | } | ||
3810 | } | ||
3811 | } | ||
3812 | } | ||
3813 | return total; | ||
3814 | } | ||
3815 | |||
3816 | /* | ||
3817 | * Switch all modem-channels who are online and got a valid | ||
3818 | * escape-sequence 1.5 seconds ago, to command-mode. | ||
3819 | * This function is called every second via timer-interrupt from within | ||
3820 | * timer-dispatcher isdn_timer_function() | ||
3821 | */ | ||
3822 | void | ||
3823 | isdn_tty_modem_escape(void) | ||
3824 | { | ||
3825 | int ton = 0; | ||
3826 | int i; | ||
3827 | int midx; | ||
3828 | |||
3829 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) | ||
3830 | if (USG_MODEM(dev->usage[i])) | ||
3831 | if ((midx = dev->m_idx[i]) >= 0) { | ||
3832 | modem_info *info = &dev->mdm.info[midx]; | ||
3833 | if (info->online) { | ||
3834 | ton = 1; | ||
3835 | if ((info->emu.pluscount == 3) && | ||
3836 | time_after(jiffies , info->emu.lastplus + PLUSWAIT2)) { | ||
3837 | info->emu.pluscount = 0; | ||
3838 | info->online = 0; | ||
3839 | isdn_tty_modem_result(RESULT_OK, info); | ||
3840 | } | ||
3841 | } | ||
3842 | } | ||
3843 | isdn_timer_ctrl(ISDN_TIMER_MODEMPLUS, ton); | ||
3844 | } | ||
3845 | |||
3846 | /* | ||
3847 | * Put a RING-message to all modem-channels who have the RI-bit set. | ||
3848 | * This function is called every second via timer-interrupt from within | ||
3849 | * timer-dispatcher isdn_timer_function() | ||
3850 | */ | ||
3851 | void | ||
3852 | isdn_tty_modem_ring(void) | ||
3853 | { | ||
3854 | int ton = 0; | ||
3855 | int i; | ||
3856 | |||
3857 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
3858 | modem_info *info = &dev->mdm.info[i]; | ||
3859 | if (info->msr & UART_MSR_RI) { | ||
3860 | ton = 1; | ||
3861 | isdn_tty_modem_result(RESULT_RING, info); | ||
3862 | } | ||
3863 | } | ||
3864 | isdn_timer_ctrl(ISDN_TIMER_MODEMRING, ton); | ||
3865 | } | ||
3866 | |||
3867 | /* | ||
3868 | * For all online tty's, try sending data to | ||
3869 | * the lower levels. | ||
3870 | */ | ||
3871 | void | ||
3872 | isdn_tty_modem_xmit(void) | ||
3873 | { | ||
3874 | int ton = 1; | ||
3875 | int i; | ||
3876 | |||
3877 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
3878 | modem_info *info = &dev->mdm.info[i]; | ||
3879 | if (info->online) { | ||
3880 | ton = 1; | ||
3881 | isdn_tty_senddown(info); | ||
3882 | isdn_tty_tint(info); | ||
3883 | } | ||
3884 | } | ||
3885 | isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, ton); | ||
3886 | } | ||
3887 | |||
3888 | /* | ||
3889 | * Check all channels if we have a 'no carrier' timeout. | ||
3890 | * Timeout value is set by Register S7. | ||
3891 | */ | ||
3892 | void | ||
3893 | isdn_tty_carrier_timeout(void) | ||
3894 | { | ||
3895 | int ton = 0; | ||
3896 | int i; | ||
3897 | |||
3898 | for (i = 0; i < ISDN_MAX_CHANNELS; i++) { | ||
3899 | modem_info *info = &dev->mdm.info[i]; | ||
3900 | if (info->dialing) { | ||
3901 | if (info->emu.carrierwait++ > info->emu.mdmreg[REG_WAITC]) { | ||
3902 | info->dialing = 0; | ||
3903 | isdn_tty_modem_result(RESULT_NO_CARRIER, info); | ||
3904 | isdn_tty_modem_hup(info, 1); | ||
3905 | } | ||
3906 | else | ||
3907 | ton = 1; | ||
3908 | } | ||
3909 | } | ||
3910 | isdn_timer_ctrl(ISDN_TIMER_CARRIER, ton); | ||
3911 | } | ||
diff --git a/drivers/isdn/i4l/isdn_tty.h b/drivers/isdn/i4l/isdn_tty.h new file mode 100644 index 000000000000..2423a7ff0cc3 --- /dev/null +++ b/drivers/isdn/i4l/isdn_tty.h | |||
@@ -0,0 +1,122 @@ | |||
1 | /* $Id: isdn_tty.h,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $ | ||
2 | * | ||
3 | * header for Linux ISDN subsystem, tty related functions (linklevel). | ||
4 | * | ||
5 | * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de) | ||
6 | * Copyright 1995,96 by Thinking Objects Software GmbH Wuerzburg | ||
7 | * | ||
8 | * This software may be used and distributed according to the terms | ||
9 | * of the GNU General Public License, incorporated herein by reference. | ||
10 | * | ||
11 | */ | ||
12 | |||
13 | #include <linux/config.h> | ||
14 | |||
15 | #define DLE 0x10 | ||
16 | #define ETX 0x03 | ||
17 | #define DC4 0x14 | ||
18 | |||
19 | |||
20 | /* | ||
21 | * Definition of some special Registers of AT-Emulator | ||
22 | */ | ||
23 | #define REG_RINGATA 0 | ||
24 | #define REG_RINGCNT 1 /* ring counter register */ | ||
25 | #define REG_ESC 2 | ||
26 | #define REG_CR 3 | ||
27 | #define REG_LF 4 | ||
28 | #define REG_BS 5 | ||
29 | |||
30 | #define REG_WAITC 7 | ||
31 | |||
32 | #define REG_RESP 12 /* show response messages register */ | ||
33 | #define BIT_RESP 1 /* show response messages bit */ | ||
34 | #define REG_RESPNUM 12 /* show numeric responses register */ | ||
35 | #define BIT_RESPNUM 2 /* show numeric responses bit */ | ||
36 | #define REG_ECHO 12 | ||
37 | #define BIT_ECHO 4 | ||
38 | #define REG_DCD 12 | ||
39 | #define BIT_DCD 8 | ||
40 | #define REG_CTS 12 | ||
41 | #define BIT_CTS 16 | ||
42 | #define REG_DTRR 12 | ||
43 | #define BIT_DTRR 32 | ||
44 | #define REG_DSR 12 | ||
45 | #define BIT_DSR 64 | ||
46 | #define REG_CPPP 12 | ||
47 | #define BIT_CPPP 128 | ||
48 | |||
49 | #define REG_DXMT 13 | ||
50 | #define BIT_DXMT 1 | ||
51 | #define REG_T70 13 | ||
52 | #define BIT_T70 2 | ||
53 | #define BIT_T70_EXT 32 | ||
54 | #define REG_DTRHUP 13 | ||
55 | #define BIT_DTRHUP 4 | ||
56 | #define REG_RESPXT 13 | ||
57 | #define BIT_RESPXT 8 | ||
58 | #define REG_CIDONCE 13 | ||
59 | #define BIT_CIDONCE 16 | ||
60 | #define REG_RUNG 13 /* show RUNG message register */ | ||
61 | #define BIT_RUNG 64 /* show RUNG message bit */ | ||
62 | #define REG_DISPLAY 13 | ||
63 | #define BIT_DISPLAY 128 | ||
64 | |||
65 | #define REG_L2PROT 14 | ||
66 | #define REG_L3PROT 15 | ||
67 | #define REG_PSIZE 16 | ||
68 | #define REG_WSIZE 17 | ||
69 | #define REG_SI1 18 | ||
70 | #define REG_SI2 19 | ||
71 | #define REG_SI1I 20 | ||
72 | #define REG_PLAN 21 | ||
73 | #define REG_SCREEN 22 | ||
74 | |||
75 | #define REG_CPN 23 | ||
76 | #define BIT_CPN 1 | ||
77 | #define REG_CPNFCON 23 | ||
78 | #define BIT_CPNFCON 2 | ||
79 | #define REG_CDN 23 | ||
80 | #define BIT_CDN 4 | ||
81 | |||
82 | /* defines for result codes */ | ||
83 | #define RESULT_OK 0 | ||
84 | #define RESULT_CONNECT 1 | ||
85 | #define RESULT_RING 2 | ||
86 | #define RESULT_NO_CARRIER 3 | ||
87 | #define RESULT_ERROR 4 | ||
88 | #define RESULT_CONNECT64000 5 | ||
89 | #define RESULT_NO_DIALTONE 6 | ||
90 | #define RESULT_BUSY 7 | ||
91 | #define RESULT_NO_ANSWER 8 | ||
92 | #define RESULT_RINGING 9 | ||
93 | #define RESULT_NO_MSN_EAZ 10 | ||
94 | #define RESULT_VCON 11 | ||
95 | #define RESULT_RUNG 12 | ||
96 | |||
97 | #define TTY_IS_FCLASS1(info) \ | ||
98 | ((info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_FAX) && \ | ||
99 | (info->emu.mdmreg[REG_L3PROT] == ISDN_PROTO_L3_FCLASS1)) | ||
100 | #define TTY_IS_FCLASS2(info) \ | ||
101 | ((info->emu.mdmreg[REG_L2PROT] == ISDN_PROTO_L2_FAX) && \ | ||
102 | (info->emu.mdmreg[REG_L3PROT] == ISDN_PROTO_L3_FCLASS2)) | ||
103 | |||
104 | extern void isdn_tty_modem_escape(void); | ||
105 | extern void isdn_tty_modem_ring(void); | ||
106 | extern void isdn_tty_carrier_timeout(void); | ||
107 | extern void isdn_tty_modem_xmit(void); | ||
108 | extern int isdn_tty_modem_init(void); | ||
109 | extern void isdn_tty_exit(void); | ||
110 | extern void isdn_tty_readmodem(void); | ||
111 | extern int isdn_tty_find_icall(int, int, setup_parm *); | ||
112 | extern void isdn_tty_cleanup_xmit(modem_info *); | ||
113 | extern int isdn_tty_stat_callback(int, isdn_ctrl *); | ||
114 | extern int isdn_tty_rcv_skb(int, int, int, struct sk_buff *); | ||
115 | extern int isdn_tty_capi_facility(capi_msg *cm); | ||
116 | extern void isdn_tty_at_cout(char *, modem_info *); | ||
117 | extern void isdn_tty_modem_hup(modem_info *, int); | ||
118 | #ifdef CONFIG_ISDN_TTY_FAX | ||
119 | extern int isdn_tty_cmd_PLUSF_FAX(char **, modem_info *); | ||
120 | extern int isdn_tty_fax_command(modem_info *, isdn_ctrl *); | ||
121 | extern void isdn_tty_fax_bitorder(modem_info *, struct sk_buff *); | ||
122 | #endif | ||
diff --git a/drivers/isdn/i4l/isdn_ttyfax.c b/drivers/isdn/i4l/isdn_ttyfax.c new file mode 100644 index 000000000000..ce2c3ef92e46 --- /dev/null +++ b/drivers/isdn/i4l/isdn_ttyfax.c | |||
@@ -0,0 +1,1122 @@ | |||
1 | /* $Id: isdn_ttyfax.c,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $ | ||
2 | * | ||
3 | * Linux ISDN subsystem, tty_fax AT-command emulator (linklevel). | ||
4 | * | ||
5 | * Copyright 1999 by Armin Schindler (mac@melware.de) | ||
6 | * Copyright 1999 by Ralf Spachmann (mel@melware.de) | ||
7 | * Copyright 1999 by Cytronics & Melware | ||
8 | * | ||
9 | * This software may be used and distributed according to the terms | ||
10 | * of the GNU General Public License, incorporated herein by reference. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #undef ISDN_TTY_FAX_STAT_DEBUG | ||
15 | #undef ISDN_TTY_FAX_CMD_DEBUG | ||
16 | |||
17 | #include <linux/isdn.h> | ||
18 | #include "isdn_common.h" | ||
19 | #include "isdn_tty.h" | ||
20 | #include "isdn_ttyfax.h" | ||
21 | |||
22 | |||
23 | static char *isdn_tty_fax_revision = "$Revision: 1.1.2.2 $"; | ||
24 | |||
25 | #define PARSE_ERROR1 { isdn_tty_fax_modem_result(1, info); return 1; } | ||
26 | |||
27 | static char * | ||
28 | isdn_getrev(const char *revision) | ||
29 | { | ||
30 | char *rev; | ||
31 | char *p; | ||
32 | |||
33 | if ((p = strchr(revision, ':'))) { | ||
34 | rev = p + 2; | ||
35 | p = strchr(rev, '$'); | ||
36 | *--p = 0; | ||
37 | } else | ||
38 | rev = "???"; | ||
39 | return rev; | ||
40 | } | ||
41 | |||
42 | /* | ||
43 | * Fax Class 2 Modem results | ||
44 | * | ||
45 | */ | ||
46 | |||
47 | static void | ||
48 | isdn_tty_fax_modem_result(int code, modem_info * info) | ||
49 | { | ||
50 | atemu *m = &info->emu; | ||
51 | T30_s *f = info->fax; | ||
52 | char rs[50]; | ||
53 | char rss[50]; | ||
54 | char *rp; | ||
55 | int i; | ||
56 | static char *msg[] = | ||
57 | {"OK", "ERROR", "+FCON", "+FCSI:", "+FDIS:", | ||
58 | "+FHNG:", "+FDCS:", "CONNECT", "+FTSI:", | ||
59 | "+FCFR", "+FPTS:", "+FET:"}; | ||
60 | |||
61 | |||
62 | isdn_tty_at_cout("\r\n", info); | ||
63 | isdn_tty_at_cout(msg[code], info); | ||
64 | |||
65 | #ifdef ISDN_TTY_FAX_CMD_DEBUG | ||
66 | printk(KERN_DEBUG "isdn_tty: Fax send %s on ttyI%d\n", | ||
67 | msg[code], info->line); | ||
68 | #endif | ||
69 | switch (code) { | ||
70 | case 0: /* OK */ | ||
71 | break; | ||
72 | case 1: /* ERROR */ | ||
73 | break; | ||
74 | case 2: /* +FCON */ | ||
75 | /* Append CPN, if enabled */ | ||
76 | if ((m->mdmreg[REG_CPNFCON] & BIT_CPNFCON) && | ||
77 | (!(dev->usage[info->isdn_channel] & ISDN_USAGE_OUTGOING))) { | ||
78 | sprintf(rs, "/%s", m->cpn); | ||
79 | isdn_tty_at_cout(rs, info); | ||
80 | } | ||
81 | info->online = 1; | ||
82 | f->fet = 0; | ||
83 | if (f->phase == ISDN_FAX_PHASE_A) | ||
84 | f->phase = ISDN_FAX_PHASE_B; | ||
85 | break; | ||
86 | case 3: /* +FCSI */ | ||
87 | case 8: /* +FTSI */ | ||
88 | sprintf(rs, "\"%s\"", f->r_id); | ||
89 | isdn_tty_at_cout(rs, info); | ||
90 | break; | ||
91 | case 4: /* +FDIS */ | ||
92 | rs[0] = 0; | ||
93 | rp = &f->r_resolution; | ||
94 | for (i = 0; i < 8; i++) { | ||
95 | sprintf(rss, "%c%s", rp[i] + 48, | ||
96 | (i < 7) ? "," : ""); | ||
97 | strcat(rs, rss); | ||
98 | } | ||
99 | isdn_tty_at_cout(rs, info); | ||
100 | #ifdef ISDN_TTY_FAX_CMD_DEBUG | ||
101 | printk(KERN_DEBUG "isdn_tty: Fax DIS=%s on ttyI%d\n", | ||
102 | rs, info->line); | ||
103 | #endif | ||
104 | break; | ||
105 | case 5: /* +FHNG */ | ||
106 | sprintf(rs, "%d", f->code); | ||
107 | isdn_tty_at_cout(rs, info); | ||
108 | info->faxonline = 0; | ||
109 | break; | ||
110 | case 6: /* +FDCS */ | ||
111 | rs[0] = 0; | ||
112 | rp = &f->r_resolution; | ||
113 | for (i = 0; i < 8; i++) { | ||
114 | sprintf(rss, "%c%s", rp[i] + 48, | ||
115 | (i < 7) ? "," : ""); | ||
116 | strcat(rs, rss); | ||
117 | } | ||
118 | isdn_tty_at_cout(rs, info); | ||
119 | #ifdef ISDN_TTY_FAX_CMD_DEBUG | ||
120 | printk(KERN_DEBUG "isdn_tty: Fax DCS=%s on ttyI%d\n", | ||
121 | rs, info->line); | ||
122 | #endif | ||
123 | break; | ||
124 | case 7: /* CONNECT */ | ||
125 | info->faxonline |= 2; | ||
126 | break; | ||
127 | case 9: /* FCFR */ | ||
128 | break; | ||
129 | case 10: /* FPTS */ | ||
130 | isdn_tty_at_cout("1", info); | ||
131 | break; | ||
132 | case 11: /* FET */ | ||
133 | sprintf(rs, "%d", f->fet); | ||
134 | isdn_tty_at_cout(rs, info); | ||
135 | break; | ||
136 | } | ||
137 | |||
138 | isdn_tty_at_cout("\r\n", info); | ||
139 | |||
140 | switch (code) { | ||
141 | case 7: /* CONNECT */ | ||
142 | info->online = 2; | ||
143 | if (info->faxonline & 1) { | ||
144 | sprintf(rs, "%c", XON); | ||
145 | isdn_tty_at_cout(rs, info); | ||
146 | } | ||
147 | break; | ||
148 | } | ||
149 | } | ||
150 | |||
151 | int | ||
152 | isdn_tty_fax_command1(modem_info * info, isdn_ctrl * c) | ||
153 | { | ||
154 | static char *msg[] = | ||
155 | {"OK", "CONNECT", "NO CARRIER", "ERROR", "FCERROR"}; | ||
156 | |||
157 | #ifdef ISDN_TTY_FAX_CMD_DEBUG | ||
158 | printk(KERN_DEBUG "isdn_tty: FCLASS1 cmd(%d)\n", c->parm.aux.cmd); | ||
159 | #endif | ||
160 | if (c->parm.aux.cmd < ISDN_FAX_CLASS1_QUERY) { | ||
161 | if (info->online) | ||
162 | info->online = 1; | ||
163 | isdn_tty_at_cout("\r\n", info); | ||
164 | isdn_tty_at_cout(msg[c->parm.aux.cmd], info); | ||
165 | isdn_tty_at_cout("\r\n", info); | ||
166 | } | ||
167 | switch (c->parm.aux.cmd) { | ||
168 | case ISDN_FAX_CLASS1_CONNECT: | ||
169 | info->online = 2; | ||
170 | break; | ||
171 | case ISDN_FAX_CLASS1_OK: | ||
172 | case ISDN_FAX_CLASS1_FCERROR: | ||
173 | case ISDN_FAX_CLASS1_ERROR: | ||
174 | case ISDN_FAX_CLASS1_NOCARR: | ||
175 | break; | ||
176 | case ISDN_FAX_CLASS1_QUERY: | ||
177 | isdn_tty_at_cout("\r\n", info); | ||
178 | if (!c->parm.aux.para[0]) { | ||
179 | isdn_tty_at_cout(msg[ISDN_FAX_CLASS1_ERROR], info); | ||
180 | isdn_tty_at_cout("\r\n", info); | ||
181 | } else { | ||
182 | isdn_tty_at_cout(c->parm.aux.para, info); | ||
183 | isdn_tty_at_cout("\r\nOK\r\n", info); | ||
184 | } | ||
185 | break; | ||
186 | } | ||
187 | return (0); | ||
188 | } | ||
189 | |||
190 | int | ||
191 | isdn_tty_fax_command(modem_info * info, isdn_ctrl * c) | ||
192 | { | ||
193 | T30_s *f = info->fax; | ||
194 | char rs[10]; | ||
195 | |||
196 | if (TTY_IS_FCLASS1(info)) | ||
197 | return (isdn_tty_fax_command1(info, c)); | ||
198 | |||
199 | #ifdef ISDN_TTY_FAX_CMD_DEBUG | ||
200 | printk(KERN_DEBUG "isdn_tty: Fax cmd %d on ttyI%d\n", | ||
201 | f->r_code, info->line); | ||
202 | #endif | ||
203 | switch (f->r_code) { | ||
204 | case ISDN_TTY_FAX_FCON: | ||
205 | info->faxonline = 1; | ||
206 | isdn_tty_fax_modem_result(2, info); /* +FCON */ | ||
207 | return (0); | ||
208 | case ISDN_TTY_FAX_FCON_I: | ||
209 | info->faxonline = 16; | ||
210 | isdn_tty_fax_modem_result(2, info); /* +FCON */ | ||
211 | return (0); | ||
212 | case ISDN_TTY_FAX_RID: | ||
213 | if (info->faxonline & 1) | ||
214 | isdn_tty_fax_modem_result(3, info); /* +FCSI */ | ||
215 | if (info->faxonline & 16) | ||
216 | isdn_tty_fax_modem_result(8, info); /* +FTSI */ | ||
217 | return (0); | ||
218 | case ISDN_TTY_FAX_DIS: | ||
219 | isdn_tty_fax_modem_result(4, info); /* +FDIS */ | ||
220 | return (0); | ||
221 | case ISDN_TTY_FAX_HNG: | ||
222 | if (f->phase == ISDN_FAX_PHASE_C) { | ||
223 | if (f->direction == ISDN_TTY_FAX_CONN_IN) { | ||
224 | sprintf(rs, "%c%c", DLE, ETX); | ||
225 | isdn_tty_at_cout(rs, info); | ||
226 | } else { | ||
227 | sprintf(rs, "%c", 0x18); | ||
228 | isdn_tty_at_cout(rs, info); | ||
229 | } | ||
230 | info->faxonline &= ~2; /* leave data mode */ | ||
231 | info->online = 1; | ||
232 | } | ||
233 | f->phase = ISDN_FAX_PHASE_E; | ||
234 | isdn_tty_fax_modem_result(5, info); /* +FHNG */ | ||
235 | isdn_tty_fax_modem_result(0, info); /* OK */ | ||
236 | return (0); | ||
237 | case ISDN_TTY_FAX_DCS: | ||
238 | isdn_tty_fax_modem_result(6, info); /* +FDCS */ | ||
239 | isdn_tty_fax_modem_result(7, info); /* CONNECT */ | ||
240 | f->phase = ISDN_FAX_PHASE_C; | ||
241 | return (0); | ||
242 | case ISDN_TTY_FAX_TRAIN_OK: | ||
243 | isdn_tty_fax_modem_result(6, info); /* +FDCS */ | ||
244 | isdn_tty_fax_modem_result(0, info); /* OK */ | ||
245 | return (0); | ||
246 | case ISDN_TTY_FAX_SENT: | ||
247 | isdn_tty_fax_modem_result(0, info); /* OK */ | ||
248 | return (0); | ||
249 | case ISDN_TTY_FAX_CFR: | ||
250 | isdn_tty_fax_modem_result(9, info); /* +FCFR */ | ||
251 | return (0); | ||
252 | case ISDN_TTY_FAX_ET: | ||
253 | sprintf(rs, "%c%c", DLE, ETX); | ||
254 | isdn_tty_at_cout(rs, info); | ||
255 | isdn_tty_fax_modem_result(10, info); /* +FPTS */ | ||
256 | isdn_tty_fax_modem_result(11, info); /* +FET */ | ||
257 | isdn_tty_fax_modem_result(0, info); /* OK */ | ||
258 | info->faxonline &= ~2; /* leave data mode */ | ||
259 | info->online = 1; | ||
260 | f->phase = ISDN_FAX_PHASE_D; | ||
261 | return (0); | ||
262 | case ISDN_TTY_FAX_PTS: | ||
263 | isdn_tty_fax_modem_result(10, info); /* +FPTS */ | ||
264 | if (f->direction == ISDN_TTY_FAX_CONN_OUT) { | ||
265 | if (f->fet == 1) | ||
266 | f->phase = ISDN_FAX_PHASE_B; | ||
267 | if (f->fet == 0) | ||
268 | isdn_tty_fax_modem_result(0, info); /* OK */ | ||
269 | } | ||
270 | return (0); | ||
271 | case ISDN_TTY_FAX_EOP: | ||
272 | info->faxonline &= ~2; /* leave data mode */ | ||
273 | info->online = 1; | ||
274 | f->phase = ISDN_FAX_PHASE_D; | ||
275 | return (0); | ||
276 | |||
277 | } | ||
278 | return (-1); | ||
279 | } | ||
280 | |||
281 | |||
282 | void | ||
283 | isdn_tty_fax_bitorder(modem_info * info, struct sk_buff *skb) | ||
284 | { | ||
285 | __u8 LeftMask; | ||
286 | __u8 RightMask; | ||
287 | __u8 fBit; | ||
288 | __u8 Data; | ||
289 | int i; | ||
290 | |||
291 | if (!info->fax->bor) { | ||
292 | for (i = 0; i < skb->len; i++) { | ||
293 | Data = skb->data[i]; | ||
294 | for ( | ||
295 | LeftMask = 0x80, RightMask = 0x01; | ||
296 | LeftMask > RightMask; | ||
297 | LeftMask >>= 1, RightMask <<= 1 | ||
298 | ) { | ||
299 | fBit = (Data & LeftMask); | ||
300 | if (Data & RightMask) | ||
301 | Data |= LeftMask; | ||
302 | else | ||
303 | Data &= ~LeftMask; | ||
304 | if (fBit) | ||
305 | Data |= RightMask; | ||
306 | else | ||
307 | Data &= ~RightMask; | ||
308 | |||
309 | } | ||
310 | skb->data[i] = Data; | ||
311 | } | ||
312 | } | ||
313 | } | ||
314 | |||
315 | /* | ||
316 | * Parse AT+F.. FAX class 1 commands | ||
317 | */ | ||
318 | |||
319 | int | ||
320 | isdn_tty_cmd_FCLASS1(char **p, modem_info * info) | ||
321 | { | ||
322 | static char *cmd[] = | ||
323 | {"AE", "TS", "RS", "TM", "RM", "TH", "RH"}; | ||
324 | isdn_ctrl c; | ||
325 | int par, i; | ||
326 | u_long flags; | ||
327 | |||
328 | for (c.parm.aux.cmd = 0; c.parm.aux.cmd < 7; c.parm.aux.cmd++) | ||
329 | if (!strncmp(p[0], cmd[c.parm.aux.cmd], 2)) | ||
330 | break; | ||
331 | |||
332 | #ifdef ISDN_TTY_FAX_CMD_DEBUG | ||
333 | printk(KERN_DEBUG "isdn_tty_cmd_FCLASS1 (%s,%d)\n", p[0], c.parm.aux.cmd); | ||
334 | #endif | ||
335 | if (c.parm.aux.cmd == 7) | ||
336 | PARSE_ERROR1; | ||
337 | |||
338 | p[0] += 2; | ||
339 | switch (*p[0]) { | ||
340 | case '?': | ||
341 | p[0]++; | ||
342 | c.parm.aux.subcmd = AT_QUERY; | ||
343 | break; | ||
344 | case '=': | ||
345 | p[0]++; | ||
346 | if (*p[0] == '?') { | ||
347 | p[0]++; | ||
348 | c.parm.aux.subcmd = AT_EQ_QUERY; | ||
349 | } else { | ||
350 | par = isdn_getnum(p); | ||
351 | if ((par < 0) || (par > 255)) | ||
352 | PARSE_ERROR1; | ||
353 | c.parm.aux.subcmd = AT_EQ_VALUE; | ||
354 | c.parm.aux.para[0] = par; | ||
355 | } | ||
356 | break; | ||
357 | case 0: | ||
358 | c.parm.aux.subcmd = AT_COMMAND; | ||
359 | break; | ||
360 | default: | ||
361 | PARSE_ERROR1; | ||
362 | } | ||
363 | c.command = ISDN_CMD_FAXCMD; | ||
364 | #ifdef ISDN_TTY_FAX_CMD_DEBUG | ||
365 | printk(KERN_DEBUG "isdn_tty_cmd_FCLASS1 %d/%d/%d)\n", | ||
366 | c.parm.aux.cmd, c.parm.aux.subcmd, c.parm.aux.para[0]); | ||
367 | #endif | ||
368 | if (info->isdn_driver < 0) { | ||
369 | if ((c.parm.aux.subcmd == AT_EQ_VALUE) || | ||
370 | (c.parm.aux.subcmd == AT_COMMAND)) { | ||
371 | PARSE_ERROR1; | ||
372 | } | ||
373 | spin_lock_irqsave(&dev->lock, flags); | ||
374 | /* get a temporary connection to the first free fax driver */ | ||
375 | i = isdn_get_free_channel(ISDN_USAGE_FAX, ISDN_PROTO_L2_FAX, | ||
376 | ISDN_PROTO_L3_FCLASS1, -1, -1, "00"); | ||
377 | if (i < 0) { | ||
378 | spin_unlock_irqrestore(&dev->lock, flags); | ||
379 | PARSE_ERROR1; | ||
380 | } | ||
381 | info->isdn_driver = dev->drvmap[i]; | ||
382 | info->isdn_channel = dev->chanmap[i]; | ||
383 | info->drv_index = i; | ||
384 | dev->m_idx[i] = info->line; | ||
385 | spin_unlock_irqrestore(&dev->lock, flags); | ||
386 | c.driver = info->isdn_driver; | ||
387 | c.arg = info->isdn_channel; | ||
388 | isdn_command(&c); | ||
389 | spin_lock_irqsave(&dev->lock, flags); | ||
390 | isdn_free_channel(info->isdn_driver, info->isdn_channel, | ||
391 | ISDN_USAGE_FAX); | ||
392 | info->isdn_driver = -1; | ||
393 | info->isdn_channel = -1; | ||
394 | if (info->drv_index >= 0) { | ||
395 | dev->m_idx[info->drv_index] = -1; | ||
396 | info->drv_index = -1; | ||
397 | } | ||
398 | spin_unlock_irqrestore(&dev->lock, flags); | ||
399 | } else { | ||
400 | c.driver = info->isdn_driver; | ||
401 | c.arg = info->isdn_channel; | ||
402 | isdn_command(&c); | ||
403 | } | ||
404 | return 1; | ||
405 | } | ||
406 | |||
407 | /* | ||
408 | * Parse AT+F.. FAX class 2 commands | ||
409 | */ | ||
410 | |||
411 | int | ||
412 | isdn_tty_cmd_FCLASS2(char **p, modem_info * info) | ||
413 | { | ||
414 | atemu *m = &info->emu; | ||
415 | T30_s *f = info->fax; | ||
416 | isdn_ctrl cmd; | ||
417 | int par; | ||
418 | char rs[50]; | ||
419 | char rss[50]; | ||
420 | int maxdccval[] = | ||
421 | {1, 5, 2, 2, 3, 2, 0, 7}; | ||
422 | |||
423 | /* FAA still unchanged */ | ||
424 | if (!strncmp(p[0], "AA", 2)) { /* TODO */ | ||
425 | p[0] += 2; | ||
426 | switch (*p[0]) { | ||
427 | case '?': | ||
428 | p[0]++; | ||
429 | sprintf(rs, "\r\n%d", 0); | ||
430 | isdn_tty_at_cout(rs, info); | ||
431 | break; | ||
432 | case '=': | ||
433 | p[0]++; | ||
434 | par = isdn_getnum(p); | ||
435 | if ((par < 0) || (par > 255)) | ||
436 | PARSE_ERROR1; | ||
437 | break; | ||
438 | default: | ||
439 | PARSE_ERROR1; | ||
440 | } | ||
441 | return 0; | ||
442 | } | ||
443 | /* BADLIN=value - dummy 0=disable errorchk disabled, 1-255 nr. of lines for making page bad */ | ||
444 | if (!strncmp(p[0], "BADLIN", 6)) { | ||
445 | p[0] += 6; | ||
446 | switch (*p[0]) { | ||
447 | case '?': | ||
448 | p[0]++; | ||
449 | sprintf(rs, "\r\n%d", f->badlin); | ||
450 | isdn_tty_at_cout(rs, info); | ||
451 | break; | ||
452 | case '=': | ||
453 | p[0]++; | ||
454 | if (*p[0] == '?') { | ||
455 | p[0]++; | ||
456 | sprintf(rs, "\r\n0-255"); | ||
457 | isdn_tty_at_cout(rs, info); | ||
458 | } else { | ||
459 | par = isdn_getnum(p); | ||
460 | if ((par < 0) || (par > 255)) | ||
461 | PARSE_ERROR1; | ||
462 | f->badlin = par; | ||
463 | #ifdef ISDN_TTY_FAX_STAT_DEBUG | ||
464 | printk(KERN_DEBUG "isdn_tty: Fax FBADLIN=%d\n", par); | ||
465 | #endif | ||
466 | } | ||
467 | break; | ||
468 | default: | ||
469 | PARSE_ERROR1; | ||
470 | } | ||
471 | return 0; | ||
472 | } | ||
473 | /* BADMUL=value - dummy 0=disable errorchk disabled (treshold multiplier) */ | ||
474 | if (!strncmp(p[0], "BADMUL", 6)) { | ||
475 | p[0] += 6; | ||
476 | switch (*p[0]) { | ||
477 | case '?': | ||
478 | p[0]++; | ||
479 | sprintf(rs, "\r\n%d", f->badmul); | ||
480 | isdn_tty_at_cout(rs, info); | ||
481 | break; | ||
482 | case '=': | ||
483 | p[0]++; | ||
484 | if (*p[0] == '?') { | ||
485 | p[0]++; | ||
486 | sprintf(rs, "\r\n0-255"); | ||
487 | isdn_tty_at_cout(rs, info); | ||
488 | } else { | ||
489 | par = isdn_getnum(p); | ||
490 | if ((par < 0) || (par > 255)) | ||
491 | PARSE_ERROR1; | ||
492 | f->badmul = par; | ||
493 | #ifdef ISDN_TTY_FAX_STAT_DEBUG | ||
494 | printk(KERN_DEBUG "isdn_tty: Fax FBADMUL=%d\n", par); | ||
495 | #endif | ||
496 | } | ||
497 | break; | ||
498 | default: | ||
499 | PARSE_ERROR1; | ||
500 | } | ||
501 | return 0; | ||
502 | } | ||
503 | /* BOR=n - Phase C bit order, 0=direct, 1=reverse */ | ||
504 | if (!strncmp(p[0], "BOR", 3)) { | ||
505 | p[0] += 3; | ||
506 | switch (*p[0]) { | ||
507 | case '?': | ||
508 | p[0]++; | ||
509 | sprintf(rs, "\r\n%d", f->bor); | ||
510 | isdn_tty_at_cout(rs, info); | ||
511 | break; | ||
512 | case '=': | ||
513 | p[0]++; | ||
514 | if (*p[0] == '?') { | ||
515 | p[0]++; | ||
516 | sprintf(rs, "\r\n0,1"); | ||
517 | isdn_tty_at_cout(rs, info); | ||
518 | } else { | ||
519 | par = isdn_getnum(p); | ||
520 | if ((par < 0) || (par > 1)) | ||
521 | PARSE_ERROR1; | ||
522 | f->bor = par; | ||
523 | #ifdef ISDN_TTY_FAX_STAT_DEBUG | ||
524 | printk(KERN_DEBUG "isdn_tty: Fax FBOR=%d\n", par); | ||
525 | #endif | ||
526 | } | ||
527 | break; | ||
528 | default: | ||
529 | PARSE_ERROR1; | ||
530 | } | ||
531 | return 0; | ||
532 | } | ||
533 | /* NBC=n - No Best Capabilities */ | ||
534 | if (!strncmp(p[0], "NBC", 3)) { | ||
535 | p[0] += 3; | ||
536 | switch (*p[0]) { | ||
537 | case '?': | ||
538 | p[0]++; | ||
539 | sprintf(rs, "\r\n%d", f->nbc); | ||
540 | isdn_tty_at_cout(rs, info); | ||
541 | break; | ||
542 | case '=': | ||
543 | p[0]++; | ||
544 | if (*p[0] == '?') { | ||
545 | p[0]++; | ||
546 | sprintf(rs, "\r\n0,1"); | ||
547 | isdn_tty_at_cout(rs, info); | ||
548 | } else { | ||
549 | par = isdn_getnum(p); | ||
550 | if ((par < 0) || (par > 1)) | ||
551 | PARSE_ERROR1; | ||
552 | f->nbc = par; | ||
553 | #ifdef ISDN_TTY_FAX_STAT_DEBUG | ||
554 | printk(KERN_DEBUG "isdn_tty: Fax FNBC=%d\n", par); | ||
555 | #endif | ||
556 | } | ||
557 | break; | ||
558 | default: | ||
559 | PARSE_ERROR1; | ||
560 | } | ||
561 | return 0; | ||
562 | } | ||
563 | /* BUF? - Readonly buffersize readout */ | ||
564 | if (!strncmp(p[0], "BUF?", 4)) { | ||
565 | p[0] += 4; | ||
566 | #ifdef ISDN_TTY_FAX_STAT_DEBUG | ||
567 | printk(KERN_DEBUG "isdn_tty: Fax FBUF? (%d) \n", (16 * m->mdmreg[REG_PSIZE])); | ||
568 | #endif | ||
569 | p[0]++; | ||
570 | sprintf(rs, "\r\n %d ", (16 * m->mdmreg[REG_PSIZE])); | ||
571 | isdn_tty_at_cout(rs, info); | ||
572 | return 0; | ||
573 | } | ||
574 | /* CIG=string - local fax station id string for polling rx */ | ||
575 | if (!strncmp(p[0], "CIG", 3)) { | ||
576 | int i, r; | ||
577 | p[0] += 3; | ||
578 | switch (*p[0]) { | ||
579 | case '?': | ||
580 | p[0]++; | ||
581 | sprintf(rs, "\r\n\"%s\"", f->pollid); | ||
582 | isdn_tty_at_cout(rs, info); | ||
583 | break; | ||
584 | case '=': | ||
585 | p[0]++; | ||
586 | if (*p[0] == '?') { | ||
587 | p[0]++; | ||
588 | sprintf(rs, "\r\n\"STRING\""); | ||
589 | isdn_tty_at_cout(rs, info); | ||
590 | } else { | ||
591 | if (*p[0] == '"') | ||
592 | p[0]++; | ||
593 | for (i = 0; (*p[0]) && i < (FAXIDLEN - 1) && (*p[0] != '"'); i++) { | ||
594 | f->pollid[i] = *p[0]++; | ||
595 | } | ||
596 | if (*p[0] == '"') | ||
597 | p[0]++; | ||
598 | for (r = i; r < FAXIDLEN; r++) { | ||
599 | f->pollid[r] = 32; | ||
600 | } | ||
601 | f->pollid[FAXIDLEN - 1] = 0; | ||
602 | #ifdef ISDN_TTY_FAX_STAT_DEBUG | ||
603 | printk(KERN_DEBUG "isdn_tty: Fax local poll ID rx \"%s\"\n", f->pollid); | ||
604 | #endif | ||
605 | } | ||
606 | break; | ||
607 | default: | ||
608 | PARSE_ERROR1; | ||
609 | } | ||
610 | return 0; | ||
611 | } | ||
612 | /* CQ=n - copy qlty chk, 0= no chk, 1=only 1D chk, 2=1D+2D chk */ | ||
613 | if (!strncmp(p[0], "CQ", 2)) { | ||
614 | p[0] += 2; | ||
615 | switch (*p[0]) { | ||
616 | case '?': | ||
617 | p[0]++; | ||
618 | sprintf(rs, "\r\n%d", f->cq); | ||
619 | isdn_tty_at_cout(rs, info); | ||
620 | break; | ||
621 | case '=': | ||
622 | p[0]++; | ||
623 | if (*p[0] == '?') { | ||
624 | p[0]++; | ||
625 | sprintf(rs, "\r\n0,1,2"); | ||
626 | isdn_tty_at_cout(rs, info); | ||
627 | } else { | ||
628 | par = isdn_getnum(p); | ||
629 | if ((par < 0) || (par > 2)) | ||
630 | PARSE_ERROR1; | ||
631 | f->cq = par; | ||
632 | #ifdef ISDN_TTY_FAX_STAT_DEBUG | ||
633 | printk(KERN_DEBUG "isdn_tty: Fax FCQ=%d\n", par); | ||
634 | #endif | ||
635 | } | ||
636 | break; | ||
637 | default: | ||
638 | PARSE_ERROR1; | ||
639 | } | ||
640 | return 0; | ||
641 | } | ||
642 | /* CR=n - can receive? 0= no data rx or poll remote dev, 1=do receive data or poll remote dev */ | ||
643 | if (!strncmp(p[0], "CR", 2)) { | ||
644 | p[0] += 2; | ||
645 | switch (*p[0]) { | ||
646 | case '?': | ||
647 | p[0]++; | ||
648 | sprintf(rs, "\r\n%d", f->cr); /* read actual value from struct and print */ | ||
649 | isdn_tty_at_cout(rs, info); | ||
650 | break; | ||
651 | case '=': | ||
652 | p[0]++; | ||
653 | if (*p[0] == '?') { | ||
654 | p[0]++; | ||
655 | sprintf(rs, "\r\n0,1"); /* display online help */ | ||
656 | isdn_tty_at_cout(rs, info); | ||
657 | } else { | ||
658 | par = isdn_getnum(p); | ||
659 | if ((par < 0) || (par > 1)) | ||
660 | PARSE_ERROR1; | ||
661 | f->cr = par; | ||
662 | #ifdef ISDN_TTY_FAX_STAT_DEBUG | ||
663 | printk(KERN_DEBUG "isdn_tty: Fax FCR=%d\n", par); | ||
664 | #endif | ||
665 | } | ||
666 | break; | ||
667 | default: | ||
668 | PARSE_ERROR1; | ||
669 | } | ||
670 | return 0; | ||
671 | } | ||
672 | /* CTCRTY=value - ECM retry count */ | ||
673 | if (!strncmp(p[0], "CTCRTY", 6)) { | ||
674 | p[0] += 6; | ||
675 | switch (*p[0]) { | ||
676 | case '?': | ||
677 | p[0]++; | ||
678 | sprintf(rs, "\r\n%d", f->ctcrty); | ||
679 | isdn_tty_at_cout(rs, info); | ||
680 | break; | ||
681 | case '=': | ||
682 | p[0]++; | ||
683 | if (*p[0] == '?') { | ||
684 | p[0]++; | ||
685 | sprintf(rs, "\r\n0-255"); | ||
686 | isdn_tty_at_cout(rs, info); | ||
687 | } else { | ||
688 | par = isdn_getnum(p); | ||
689 | if ((par < 0) || (par > 255)) | ||
690 | PARSE_ERROR1; | ||
691 | f->ctcrty = par; | ||
692 | #ifdef ISDN_TTY_FAX_STAT_DEBUG | ||
693 | printk(KERN_DEBUG "isdn_tty: Fax FCTCRTY=%d\n", par); | ||
694 | #endif | ||
695 | } | ||
696 | break; | ||
697 | default: | ||
698 | PARSE_ERROR1; | ||
699 | } | ||
700 | return 0; | ||
701 | } | ||
702 | /* DCC=vr,br,wd,ln,df,ec,bf,st - DCE capabilities parms */ | ||
703 | if (!strncmp(p[0], "DCC", 3)) { | ||
704 | char *rp = &f->resolution; | ||
705 | int i; | ||
706 | |||
707 | p[0] += 3; | ||
708 | switch (*p[0]) { | ||
709 | case '?': | ||
710 | p[0]++; | ||
711 | strcpy(rs, "\r\n"); | ||
712 | for (i = 0; i < 8; i++) { | ||
713 | sprintf(rss, "%c%s", rp[i] + 48, | ||
714 | (i < 7) ? "," : ""); | ||
715 | strcat(rs, rss); | ||
716 | } | ||
717 | isdn_tty_at_cout(rs, info); | ||
718 | break; | ||
719 | case '=': | ||
720 | p[0]++; | ||
721 | if (*p[0] == '?') { | ||
722 | isdn_tty_at_cout("\r\n(0,1),(0-5),(0-2),(0-2),(0-3),(0-2),(0),(0-7)", info); | ||
723 | p[0]++; | ||
724 | } else { | ||
725 | for (i = 0; (((*p[0] >= '0') && (*p[0] <= '9')) || (*p[0] == ',')) && (i < 8); i++) { | ||
726 | if (*p[0] != ',') { | ||
727 | if ((*p[0] - 48) > maxdccval[i]) { | ||
728 | PARSE_ERROR1; | ||
729 | } | ||
730 | rp[i] = *p[0] - 48; | ||
731 | p[0]++; | ||
732 | if (*p[0] == ',') | ||
733 | p[0]++; | ||
734 | } else | ||
735 | p[0]++; | ||
736 | } | ||
737 | #ifdef ISDN_TTY_FAX_STAT_DEBUG | ||
738 | printk(KERN_DEBUG "isdn_tty: Fax FDCC capabilities DCE=%d,%d,%d,%d,%d,%d,%d,%d\n", | ||
739 | rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], rp[6], rp[7]); | ||
740 | #endif | ||
741 | } | ||
742 | break; | ||
743 | default: | ||
744 | PARSE_ERROR1; | ||
745 | } | ||
746 | return 0; | ||
747 | } | ||
748 | /* DIS=vr,br,wd,ln,df,ec,bf,st - current session parms */ | ||
749 | if (!strncmp(p[0], "DIS", 3)) { | ||
750 | char *rp = &f->resolution; | ||
751 | int i; | ||
752 | |||
753 | p[0] += 3; | ||
754 | switch (*p[0]) { | ||
755 | case '?': | ||
756 | p[0]++; | ||
757 | strcpy(rs, "\r\n"); | ||
758 | for (i = 0; i < 8; i++) { | ||
759 | sprintf(rss, "%c%s", rp[i] + 48, | ||
760 | (i < 7) ? "," : ""); | ||
761 | strcat(rs, rss); | ||
762 | } | ||
763 | isdn_tty_at_cout(rs, info); | ||
764 | break; | ||
765 | case '=': | ||
766 | p[0]++; | ||
767 | if (*p[0] == '?') { | ||
768 | isdn_tty_at_cout("\r\n(0,1),(0-5),(0-2),(0-2),(0-3),(0-2),(0),(0-7)", info); | ||
769 | p[0]++; | ||
770 | } else { | ||
771 | for (i = 0; (((*p[0] >= '0') && (*p[0] <= '9')) || (*p[0] == ',')) && (i < 8); i++) { | ||
772 | if (*p[0] != ',') { | ||
773 | if ((*p[0] - 48) > maxdccval[i]) { | ||
774 | PARSE_ERROR1; | ||
775 | } | ||
776 | rp[i] = *p[0] - 48; | ||
777 | p[0]++; | ||
778 | if (*p[0] == ',') | ||
779 | p[0]++; | ||
780 | } else | ||
781 | p[0]++; | ||
782 | } | ||
783 | #ifdef ISDN_TTY_FAX_STAT_DEBUG | ||
784 | printk(KERN_DEBUG "isdn_tty: Fax FDIS session parms=%d,%d,%d,%d,%d,%d,%d,%d\n", | ||
785 | rp[0], rp[1], rp[2], rp[3], rp[4], rp[5], rp[6], rp[7]); | ||
786 | #endif | ||
787 | } | ||
788 | break; | ||
789 | default: | ||
790 | PARSE_ERROR1; | ||
791 | } | ||
792 | return 0; | ||
793 | } | ||
794 | /* DR - Receive Phase C data command, initiates document reception */ | ||
795 | if (!strncmp(p[0], "DR", 2)) { | ||
796 | p[0] += 2; | ||
797 | if ((info->faxonline & 16) && /* incoming connection */ | ||
798 | ((f->phase == ISDN_FAX_PHASE_B) || (f->phase == ISDN_FAX_PHASE_D))) { | ||
799 | #ifdef ISDN_TTY_FAX_STAT_DEBUG | ||
800 | printk(KERN_DEBUG "isdn_tty: Fax FDR\n"); | ||
801 | #endif | ||
802 | f->code = ISDN_TTY_FAX_DR; | ||
803 | cmd.driver = info->isdn_driver; | ||
804 | cmd.arg = info->isdn_channel; | ||
805 | cmd.command = ISDN_CMD_FAXCMD; | ||
806 | isdn_command(&cmd); | ||
807 | if (f->phase == ISDN_FAX_PHASE_B) { | ||
808 | f->phase = ISDN_FAX_PHASE_C; | ||
809 | } else if (f->phase == ISDN_FAX_PHASE_D) { | ||
810 | switch (f->fet) { | ||
811 | case 0: /* next page will be received */ | ||
812 | f->phase = ISDN_FAX_PHASE_C; | ||
813 | isdn_tty_fax_modem_result(7, info); /* CONNECT */ | ||
814 | break; | ||
815 | case 1: /* next doc will be received */ | ||
816 | f->phase = ISDN_FAX_PHASE_B; | ||
817 | break; | ||
818 | case 2: /* fax session is terminating */ | ||
819 | f->phase = ISDN_FAX_PHASE_E; | ||
820 | break; | ||
821 | default: | ||
822 | PARSE_ERROR1; | ||
823 | } | ||
824 | } | ||
825 | } else { | ||
826 | PARSE_ERROR1; | ||
827 | } | ||
828 | return 1; | ||
829 | } | ||
830 | /* DT=df,vr,wd,ln - TX phase C data command (release DCE to proceed with negotiation) */ | ||
831 | if (!strncmp(p[0], "DT", 2)) { | ||
832 | int i, val[] = | ||
833 | {4, 0, 2, 3}; | ||
834 | char *rp = &f->resolution; | ||
835 | |||
836 | p[0] += 2; | ||
837 | if (!info->faxonline & 1) /* not outgoing connection */ | ||
838 | PARSE_ERROR1; | ||
839 | |||
840 | for (i = 0; (((*p[0] >= '0') && (*p[0] <= '9')) || (*p[0] == ',')) && (i < 4); i++) { | ||
841 | if (*p[0] != ',') { | ||
842 | if ((*p[0] - 48) > maxdccval[val[i]]) { | ||
843 | PARSE_ERROR1; | ||
844 | } | ||
845 | rp[val[i]] = *p[0] - 48; | ||
846 | p[0]++; | ||
847 | if (*p[0] == ',') | ||
848 | p[0]++; | ||
849 | } else | ||
850 | p[0]++; | ||
851 | } | ||
852 | #ifdef ISDN_TTY_FAX_STAT_DEBUG | ||
853 | printk(KERN_DEBUG "isdn_tty: Fax FDT tx data command parms=%d,%d,%d,%d\n", | ||
854 | rp[4], rp[0], rp[2], rp[3]); | ||
855 | #endif | ||
856 | if ((f->phase == ISDN_FAX_PHASE_B) || (f->phase == ISDN_FAX_PHASE_D)) { | ||
857 | f->code = ISDN_TTY_FAX_DT; | ||
858 | cmd.driver = info->isdn_driver; | ||
859 | cmd.arg = info->isdn_channel; | ||
860 | cmd.command = ISDN_CMD_FAXCMD; | ||
861 | isdn_command(&cmd); | ||
862 | if (f->phase == ISDN_FAX_PHASE_D) { | ||
863 | f->phase = ISDN_FAX_PHASE_C; | ||
864 | isdn_tty_fax_modem_result(7, info); /* CONNECT */ | ||
865 | } | ||
866 | } else { | ||
867 | PARSE_ERROR1; | ||
868 | } | ||
869 | return 1; | ||
870 | } | ||
871 | /* ECM=n - Error mode control 0=disabled, 2=enabled, handled by DCE alone incl. buff of partial pages */ | ||
872 | if (!strncmp(p[0], "ECM", 3)) { | ||
873 | p[0] += 3; | ||
874 | switch (*p[0]) { | ||
875 | case '?': | ||
876 | p[0]++; | ||
877 | sprintf(rs, "\r\n%d", f->ecm); | ||
878 | isdn_tty_at_cout(rs, info); | ||
879 | break; | ||
880 | case '=': | ||
881 | p[0]++; | ||
882 | if (*p[0] == '?') { | ||
883 | p[0]++; | ||
884 | sprintf(rs, "\r\n0,2"); | ||
885 | isdn_tty_at_cout(rs, info); | ||
886 | } else { | ||
887 | par = isdn_getnum(p); | ||
888 | if ((par != 0) && (par != 2)) | ||
889 | PARSE_ERROR1; | ||
890 | f->ecm = par; | ||
891 | #ifdef ISDN_TTY_FAX_STAT_DEBUG | ||
892 | printk(KERN_DEBUG "isdn_tty: Fax FECM=%d\n", par); | ||
893 | #endif | ||
894 | } | ||
895 | break; | ||
896 | default: | ||
897 | PARSE_ERROR1; | ||
898 | } | ||
899 | return 0; | ||
900 | } | ||
901 | /* ET=n - End of page or document */ | ||
902 | if (!strncmp(p[0], "ET=", 3)) { | ||
903 | p[0] += 3; | ||
904 | if (*p[0] == '?') { | ||
905 | p[0]++; | ||
906 | sprintf(rs, "\r\n0-2"); | ||
907 | isdn_tty_at_cout(rs, info); | ||
908 | } else { | ||
909 | if ((f->phase != ISDN_FAX_PHASE_D) || (!info->faxonline & 1)) | ||
910 | PARSE_ERROR1; | ||
911 | par = isdn_getnum(p); | ||
912 | if ((par < 0) || (par > 2)) | ||
913 | PARSE_ERROR1; | ||
914 | f->fet = par; | ||
915 | f->code = ISDN_TTY_FAX_ET; | ||
916 | cmd.driver = info->isdn_driver; | ||
917 | cmd.arg = info->isdn_channel; | ||
918 | cmd.command = ISDN_CMD_FAXCMD; | ||
919 | isdn_command(&cmd); | ||
920 | #ifdef ISDN_TTY_FAX_STAT_DEBUG | ||
921 | printk(KERN_DEBUG "isdn_tty: Fax FET=%d\n", par); | ||
922 | #endif | ||
923 | return 1; | ||
924 | } | ||
925 | return 0; | ||
926 | } | ||
927 | /* K - terminate */ | ||
928 | if (!strncmp(p[0], "K", 1)) { | ||
929 | p[0] += 1; | ||
930 | if ((f->phase == ISDN_FAX_PHASE_IDLE) || (f->phase == ISDN_FAX_PHASE_E)) | ||
931 | PARSE_ERROR1; | ||
932 | isdn_tty_modem_hup(info, 1); | ||
933 | return 1; | ||
934 | } | ||
935 | /* LID=string - local fax ID */ | ||
936 | if (!strncmp(p[0], "LID", 3)) { | ||
937 | int i, r; | ||
938 | p[0] += 3; | ||
939 | switch (*p[0]) { | ||
940 | case '?': | ||
941 | p[0]++; | ||
942 | sprintf(rs, "\r\n\"%s\"", f->id); | ||
943 | isdn_tty_at_cout(rs, info); | ||
944 | break; | ||
945 | case '=': | ||
946 | p[0]++; | ||
947 | if (*p[0] == '?') { | ||
948 | p[0]++; | ||
949 | sprintf(rs, "\r\n\"STRING\""); | ||
950 | isdn_tty_at_cout(rs, info); | ||
951 | } else { | ||
952 | if (*p[0] == '"') | ||
953 | p[0]++; | ||
954 | for (i = 0; (*p[0]) && i < (FAXIDLEN - 1) && (*p[0] != '"'); i++) { | ||
955 | f->id[i] = *p[0]++; | ||
956 | } | ||
957 | if (*p[0] == '"') | ||
958 | p[0]++; | ||
959 | for (r = i; r < FAXIDLEN; r++) { | ||
960 | f->id[r] = 32; | ||
961 | } | ||
962 | f->id[FAXIDLEN - 1] = 0; | ||
963 | #ifdef ISDN_TTY_FAX_STAT_DEBUG | ||
964 | printk(KERN_DEBUG "isdn_tty: Fax local ID \"%s\"\n", f->id); | ||
965 | #endif | ||
966 | } | ||
967 | break; | ||
968 | default: | ||
969 | PARSE_ERROR1; | ||
970 | } | ||
971 | return 0; | ||
972 | } | ||
973 | |||
974 | /* MDL? - DCE Model */ | ||
975 | if (!strncmp(p[0], "MDL?", 4)) { | ||
976 | p[0] += 4; | ||
977 | #ifdef ISDN_TTY_FAX_STAT_DEBUG | ||
978 | printk(KERN_DEBUG "isdn_tty: FMDL?\n"); | ||
979 | #endif | ||
980 | isdn_tty_at_cout("\r\nisdn4linux", info); | ||
981 | return 0; | ||
982 | } | ||
983 | /* MFR? - DCE Manufacturer */ | ||
984 | if (!strncmp(p[0], "MFR?", 4)) { | ||
985 | p[0] += 4; | ||
986 | #ifdef ISDN_TTY_FAX_STAT_DEBUG | ||
987 | printk(KERN_DEBUG "isdn_tty: FMFR?\n"); | ||
988 | #endif | ||
989 | isdn_tty_at_cout("\r\nisdn4linux", info); | ||
990 | return 0; | ||
991 | } | ||
992 | /* MINSP=n - Minimum Speed for Phase C */ | ||
993 | if (!strncmp(p[0], "MINSP", 5)) { | ||
994 | p[0] += 5; | ||
995 | switch (*p[0]) { | ||
996 | case '?': | ||
997 | p[0]++; | ||
998 | sprintf(rs, "\r\n%d", f->minsp); | ||
999 | isdn_tty_at_cout(rs, info); | ||
1000 | break; | ||
1001 | case '=': | ||
1002 | p[0]++; | ||
1003 | if (*p[0] == '?') { | ||
1004 | p[0]++; | ||
1005 | sprintf(rs, "\r\n0-5"); | ||
1006 | isdn_tty_at_cout(rs, info); | ||
1007 | } else { | ||
1008 | par = isdn_getnum(p); | ||
1009 | if ((par < 0) || (par > 5)) | ||
1010 | PARSE_ERROR1; | ||
1011 | f->minsp = par; | ||
1012 | #ifdef ISDN_TTY_FAX_STAT_DEBUG | ||
1013 | printk(KERN_DEBUG "isdn_tty: Fax FMINSP=%d\n", par); | ||
1014 | #endif | ||
1015 | } | ||
1016 | break; | ||
1017 | default: | ||
1018 | PARSE_ERROR1; | ||
1019 | } | ||
1020 | return 0; | ||
1021 | } | ||
1022 | /* PHCTO=value - DTE phase C timeout */ | ||
1023 | if (!strncmp(p[0], "PHCTO", 5)) { | ||
1024 | p[0] += 5; | ||
1025 | switch (*p[0]) { | ||
1026 | case '?': | ||
1027 | p[0]++; | ||
1028 | sprintf(rs, "\r\n%d", f->phcto); | ||
1029 | isdn_tty_at_cout(rs, info); | ||
1030 | break; | ||
1031 | case '=': | ||
1032 | p[0]++; | ||
1033 | if (*p[0] == '?') { | ||
1034 | p[0]++; | ||
1035 | sprintf(rs, "\r\n0-255"); | ||
1036 | isdn_tty_at_cout(rs, info); | ||
1037 | } else { | ||
1038 | par = isdn_getnum(p); | ||
1039 | if ((par < 0) || (par > 255)) | ||
1040 | PARSE_ERROR1; | ||
1041 | f->phcto = par; | ||
1042 | #ifdef ISDN_TTY_FAX_STAT_DEBUG | ||
1043 | printk(KERN_DEBUG "isdn_tty: Fax FPHCTO=%d\n", par); | ||
1044 | #endif | ||
1045 | } | ||
1046 | break; | ||
1047 | default: | ||
1048 | PARSE_ERROR1; | ||
1049 | } | ||
1050 | return 0; | ||
1051 | } | ||
1052 | |||
1053 | /* REL=n - Phase C received EOL alignment */ | ||
1054 | if (!strncmp(p[0], "REL", 3)) { | ||
1055 | p[0] += 3; | ||
1056 | switch (*p[0]) { | ||
1057 | case '?': | ||
1058 | p[0]++; | ||
1059 | sprintf(rs, "\r\n%d", f->rel); | ||
1060 | isdn_tty_at_cout(rs, info); | ||
1061 | break; | ||
1062 | case '=': | ||
1063 | p[0]++; | ||
1064 | if (*p[0] == '?') { | ||
1065 | p[0]++; | ||
1066 | sprintf(rs, "\r\n0,1"); | ||
1067 | isdn_tty_at_cout(rs, info); | ||
1068 | } else { | ||
1069 | par = isdn_getnum(p); | ||
1070 | if ((par < 0) || (par > 1)) | ||
1071 | PARSE_ERROR1; | ||
1072 | f->rel = par; | ||
1073 | #ifdef ISDN_TTY_FAX_STAT_DEBUG | ||
1074 | printk(KERN_DEBUG "isdn_tty: Fax FREL=%d\n", par); | ||
1075 | #endif | ||
1076 | } | ||
1077 | break; | ||
1078 | default: | ||
1079 | PARSE_ERROR1; | ||
1080 | } | ||
1081 | return 0; | ||
1082 | } | ||
1083 | /* REV? - DCE Revision */ | ||
1084 | if (!strncmp(p[0], "REV?", 4)) { | ||
1085 | p[0] += 4; | ||
1086 | #ifdef ISDN_TTY_FAX_STAT_DEBUG | ||
1087 | printk(KERN_DEBUG "isdn_tty: FREV?\n"); | ||
1088 | #endif | ||
1089 | strcpy(rss, isdn_tty_fax_revision); | ||
1090 | sprintf(rs, "\r\nRev: %s", isdn_getrev(rss)); | ||
1091 | isdn_tty_at_cout(rs, info); | ||
1092 | return 0; | ||
1093 | } | ||
1094 | |||
1095 | /* Phase C Transmit Data Block Size */ | ||
1096 | if (!strncmp(p[0], "TBC=", 4)) { /* dummy, not used */ | ||
1097 | p[0] += 4; | ||
1098 | #ifdef ISDN_TTY_FAX_STAT_DEBUG | ||
1099 | printk(KERN_DEBUG "isdn_tty: Fax FTBC=%c\n", *p[0]); | ||
1100 | #endif | ||
1101 | switch (*p[0]) { | ||
1102 | case '0': | ||
1103 | p[0]++; | ||
1104 | break; | ||
1105 | default: | ||
1106 | PARSE_ERROR1; | ||
1107 | } | ||
1108 | return 0; | ||
1109 | } | ||
1110 | printk(KERN_DEBUG "isdn_tty: unknown token=>AT+F%s<\n", p[0]); | ||
1111 | PARSE_ERROR1; | ||
1112 | } | ||
1113 | |||
1114 | int | ||
1115 | isdn_tty_cmd_PLUSF_FAX(char **p, modem_info * info) | ||
1116 | { | ||
1117 | if (TTY_IS_FCLASS2(info)) | ||
1118 | return (isdn_tty_cmd_FCLASS2(p, info)); | ||
1119 | else if (TTY_IS_FCLASS1(info)) | ||
1120 | return (isdn_tty_cmd_FCLASS1(p, info)); | ||
1121 | PARSE_ERROR1; | ||
1122 | } | ||
diff --git a/drivers/isdn/i4l/isdn_ttyfax.h b/drivers/isdn/i4l/isdn_ttyfax.h new file mode 100644 index 000000000000..757a89010020 --- /dev/null +++ b/drivers/isdn/i4l/isdn_ttyfax.h | |||
@@ -0,0 +1,18 @@ | |||
1 | /* $Id: isdn_ttyfax.h,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $ | ||
2 | * | ||
3 | * header for Linux ISDN subsystem, tty_fax related functions (linklevel). | ||
4 | * | ||
5 | * Copyright 1999 by Armin Schindler (mac@melware.de) | ||
6 | * Copyright 1999 by Ralf Spachmann (mel@melware.de) | ||
7 | * Copyright 1999 by Cytronics & Melware | ||
8 | * | ||
9 | * This software may be used and distributed according to the terms | ||
10 | * of the GNU General Public License, incorporated herein by reference. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | |||
15 | #define XON 0x11 | ||
16 | #define XOFF 0x13 | ||
17 | #define DC2 0x12 | ||
18 | |||
diff --git a/drivers/isdn/i4l/isdn_v110.c b/drivers/isdn/i4l/isdn_v110.c new file mode 100644 index 000000000000..f47f2b9846d8 --- /dev/null +++ b/drivers/isdn/i4l/isdn_v110.c | |||
@@ -0,0 +1,617 @@ | |||
1 | /* $Id: isdn_v110.c,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $ | ||
2 | * | ||
3 | * Linux ISDN subsystem, V.110 related functions (linklevel). | ||
4 | * | ||
5 | * Copyright by Thomas Pfeiffer (pfeiffer@pds.de) | ||
6 | * | ||
7 | * This software may be used and distributed according to the terms | ||
8 | * of the GNU General Public License, incorporated herein by reference. | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #include <linux/string.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/slab.h> | ||
15 | #include <linux/mm.h> | ||
16 | #include <linux/delay.h> | ||
17 | |||
18 | #include <linux/isdn.h> | ||
19 | #include "isdn_v110.h" | ||
20 | |||
21 | #undef ISDN_V110_DEBUG | ||
22 | |||
23 | char *isdn_v110_revision = "$Revision: 1.1.2.2 $"; | ||
24 | |||
25 | #define V110_38400 255 | ||
26 | #define V110_19200 15 | ||
27 | #define V110_9600 3 | ||
28 | |||
29 | /* | ||
30 | * The following data are precoded matrices, online and offline matrix | ||
31 | * for 9600, 19200 und 38400, respectively | ||
32 | */ | ||
33 | static unsigned char V110_OnMatrix_9600[] = | ||
34 | {0xfc, 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, | ||
35 | 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, | ||
36 | 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, | ||
37 | 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xfd}; | ||
38 | |||
39 | static unsigned char V110_OffMatrix_9600[] = | ||
40 | {0xfc, 0xfc, 0xfc, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
41 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
42 | 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
43 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; | ||
44 | |||
45 | static unsigned char V110_OnMatrix_19200[] = | ||
46 | {0xf0, 0xf0, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7, | ||
47 | 0xfd, 0xff, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7, 0xff, 0xf7}; | ||
48 | |||
49 | static unsigned char V110_OffMatrix_19200[] = | ||
50 | {0xf0, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
51 | 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; | ||
52 | |||
53 | static unsigned char V110_OnMatrix_38400[] = | ||
54 | {0x00, 0x7f, 0x7f, 0x7f, 0x7f, 0xfd, 0x7f, 0x7f, 0x7f, 0x7f}; | ||
55 | |||
56 | static unsigned char V110_OffMatrix_38400[] = | ||
57 | {0x00, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff}; | ||
58 | |||
59 | /* | ||
60 | * FlipBits reorders sequences of keylen bits in one byte. | ||
61 | * E.g. source order 7654321 will be converted to 45670123 when keylen = 4, | ||
62 | * and to 67452301 when keylen = 2. This is necessary because ordering on | ||
63 | * the isdn line is the other way. | ||
64 | */ | ||
65 | static __inline unsigned char | ||
66 | FlipBits(unsigned char c, int keylen) | ||
67 | { | ||
68 | unsigned char b = c; | ||
69 | unsigned char bit = 128; | ||
70 | int i; | ||
71 | int j; | ||
72 | int hunks = (8 / keylen); | ||
73 | |||
74 | c = 0; | ||
75 | for (i = 0; i < hunks; i++) { | ||
76 | for (j = 0; j < keylen; j++) { | ||
77 | if (b & (bit >> j)) | ||
78 | c |= bit >> (keylen - j - 1); | ||
79 | } | ||
80 | bit >>= keylen; | ||
81 | } | ||
82 | return c; | ||
83 | } | ||
84 | |||
85 | |||
86 | /* isdn_v110_open allocates and initializes private V.110 data | ||
87 | * structures and returns a pointer to these. | ||
88 | */ | ||
89 | static isdn_v110_stream * | ||
90 | isdn_v110_open(unsigned char key, int hdrlen, int maxsize) | ||
91 | { | ||
92 | int i; | ||
93 | isdn_v110_stream *v; | ||
94 | |||
95 | if ((v = kmalloc(sizeof(isdn_v110_stream), GFP_ATOMIC)) == NULL) | ||
96 | return NULL; | ||
97 | memset(v, 0, sizeof(isdn_v110_stream)); | ||
98 | v->key = key; | ||
99 | v->nbits = 0; | ||
100 | for (i = 0; key & (1 << i); i++) | ||
101 | v->nbits++; | ||
102 | |||
103 | v->nbytes = 8 / v->nbits; | ||
104 | v->decodelen = 0; | ||
105 | |||
106 | switch (key) { | ||
107 | case V110_38400: | ||
108 | v->OnlineFrame = V110_OnMatrix_38400; | ||
109 | v->OfflineFrame = V110_OffMatrix_38400; | ||
110 | break; | ||
111 | case V110_19200: | ||
112 | v->OnlineFrame = V110_OnMatrix_19200; | ||
113 | v->OfflineFrame = V110_OffMatrix_19200; | ||
114 | break; | ||
115 | default: | ||
116 | v->OnlineFrame = V110_OnMatrix_9600; | ||
117 | v->OfflineFrame = V110_OffMatrix_9600; | ||
118 | break; | ||
119 | } | ||
120 | v->framelen = v->nbytes * 10; | ||
121 | v->SyncInit = 5; | ||
122 | v->introducer = 0; | ||
123 | v->dbit = 1; | ||
124 | v->b = 0; | ||
125 | v->skbres = hdrlen; | ||
126 | v->maxsize = maxsize - hdrlen; | ||
127 | if ((v->encodebuf = kmalloc(maxsize, GFP_ATOMIC)) == NULL) { | ||
128 | kfree(v); | ||
129 | return NULL; | ||
130 | } | ||
131 | return v; | ||
132 | } | ||
133 | |||
134 | /* isdn_v110_close frees private V.110 data structures */ | ||
135 | void | ||
136 | isdn_v110_close(isdn_v110_stream * v) | ||
137 | { | ||
138 | if (v == NULL) | ||
139 | return; | ||
140 | #ifdef ISDN_V110_DEBUG | ||
141 | printk(KERN_DEBUG "v110 close\n"); | ||
142 | #endif | ||
143 | kfree(v->encodebuf); | ||
144 | kfree(v); | ||
145 | } | ||
146 | |||
147 | |||
148 | /* | ||
149 | * ValidHeaderBytes return the number of valid bytes in v->decodebuf | ||
150 | */ | ||
151 | static int | ||
152 | ValidHeaderBytes(isdn_v110_stream * v) | ||
153 | { | ||
154 | int i; | ||
155 | for (i = 0; (i < v->decodelen) && (i < v->nbytes); i++) | ||
156 | if ((v->decodebuf[i] & v->key) != 0) | ||
157 | break; | ||
158 | return i; | ||
159 | } | ||
160 | |||
161 | /* | ||
162 | * SyncHeader moves the decodebuf ptr to the next valid header | ||
163 | */ | ||
164 | static void | ||
165 | SyncHeader(isdn_v110_stream * v) | ||
166 | { | ||
167 | unsigned char *rbuf = v->decodebuf; | ||
168 | int len = v->decodelen; | ||
169 | |||
170 | if (len == 0) | ||
171 | return; | ||
172 | for (rbuf++, len--; len > 0; len--, rbuf++) /* such den SyncHeader in buf ! */ | ||
173 | if ((*rbuf & v->key) == 0) /* erstes byte gefunden ? */ | ||
174 | break; /* jupp! */ | ||
175 | if (len) | ||
176 | memcpy(v->decodebuf, rbuf, len); | ||
177 | |||
178 | v->decodelen = len; | ||
179 | #ifdef ISDN_V110_DEBUG | ||
180 | printk(KERN_DEBUG "isdn_v110: Header resync\n"); | ||
181 | #endif | ||
182 | } | ||
183 | |||
184 | /* DecodeMatrix takes n (n>=1) matrices (v110 frames, 10 bytes) where | ||
185 | len is the number of matrix-lines. len must be a multiple of 10, i.e. | ||
186 | only complete matices must be given. | ||
187 | From these, netto data is extracted and returned in buf. The return-value | ||
188 | is the bytecount of the decoded data. | ||
189 | */ | ||
190 | static int | ||
191 | DecodeMatrix(isdn_v110_stream * v, unsigned char *m, int len, unsigned char *buf) | ||
192 | { | ||
193 | int line = 0; | ||
194 | int buflen = 0; | ||
195 | int mbit = 64; | ||
196 | int introducer = v->introducer; | ||
197 | int dbit = v->dbit; | ||
198 | unsigned char b = v->b; | ||
199 | |||
200 | while (line < len) { /* Are we done with all lines of the matrix? */ | ||
201 | if ((line % 10) == 0) { /* the 0. line of the matrix is always 0 ! */ | ||
202 | if (m[line] != 0x00) { /* not 0 ? -> error! */ | ||
203 | #ifdef ISDN_V110_DEBUG | ||
204 | printk(KERN_DEBUG "isdn_v110: DecodeMatrix, V110 Bad Header\n"); | ||
205 | /* returning now is not the right thing, though :-( */ | ||
206 | #endif | ||
207 | } | ||
208 | line++; /* next line of matrix */ | ||
209 | continue; | ||
210 | } else if ((line % 10) == 5) { /* in line 5 there's only e-bits ! */ | ||
211 | if ((m[line] & 0x70) != 0x30) { /* 011 has to be at the beginning! */ | ||
212 | #ifdef ISDN_V110_DEBUG | ||
213 | printk(KERN_DEBUG "isdn_v110: DecodeMatrix, V110 Bad 5th line\n"); | ||
214 | /* returning now is not the right thing, though :-( */ | ||
215 | #endif | ||
216 | } | ||
217 | line++; /* next line */ | ||
218 | continue; | ||
219 | } else if (!introducer) { /* every byte starts with 10 (stopbit, startbit) */ | ||
220 | introducer = (m[line] & mbit) ? 0 : 1; /* current bit of the matrix */ | ||
221 | next_byte: | ||
222 | if (mbit > 2) { /* was it the last bit in this line ? */ | ||
223 | mbit >>= 1; /* no -> take next */ | ||
224 | continue; | ||
225 | } /* otherwise start with leftmost bit in the next line */ | ||
226 | mbit = 64; | ||
227 | line++; | ||
228 | continue; | ||
229 | } else { /* otherwise we need to set a data bit */ | ||
230 | if (m[line] & mbit) /* was that bit set in the matrix ? */ | ||
231 | b |= dbit; /* yes -> set it in the data byte */ | ||
232 | else | ||
233 | b &= dbit - 1; /* no -> clear it in the data byte */ | ||
234 | if (dbit < 128) /* is that data byte done ? */ | ||
235 | dbit <<= 1; /* no, got the next bit */ | ||
236 | else { /* data byte is done */ | ||
237 | buf[buflen++] = b; /* copy byte into the output buffer */ | ||
238 | introducer = b = 0; /* init of the intro sequence and of the data byte */ | ||
239 | dbit = 1; /* next we look for the 0th bit */ | ||
240 | } | ||
241 | goto next_byte; /* look for next bit in the matrix */ | ||
242 | } | ||
243 | } | ||
244 | v->introducer = introducer; | ||
245 | v->dbit = dbit; | ||
246 | v->b = b; | ||
247 | return buflen; /* return number of bytes in the output buffer */ | ||
248 | } | ||
249 | |||
250 | /* | ||
251 | * DecodeStream receives V.110 coded data from the input stream. It recovers the | ||
252 | * original frames. | ||
253 | * The input stream doesn't need to be framed | ||
254 | */ | ||
255 | struct sk_buff * | ||
256 | isdn_v110_decode(isdn_v110_stream * v, struct sk_buff *skb) | ||
257 | { | ||
258 | int i; | ||
259 | int j; | ||
260 | int len; | ||
261 | unsigned char *v110_buf; | ||
262 | unsigned char *rbuf; | ||
263 | |||
264 | if (!skb) { | ||
265 | printk(KERN_WARNING "isdn_v110_decode called with NULL skb!\n"); | ||
266 | return NULL; | ||
267 | } | ||
268 | rbuf = skb->data; | ||
269 | len = skb->len; | ||
270 | if (v == NULL) { | ||
271 | /* invalid handle, no chance to proceed */ | ||
272 | printk(KERN_WARNING "isdn_v110_decode called with NULL stream!\n"); | ||
273 | dev_kfree_skb(skb); | ||
274 | return NULL; | ||
275 | } | ||
276 | if (v->decodelen == 0) /* cache empty? */ | ||
277 | for (; len > 0; len--, rbuf++) /* scan for SyncHeader in buf */ | ||
278 | if ((*rbuf & v->key) == 0) | ||
279 | break; /* found first byte */ | ||
280 | if (len == 0) { | ||
281 | dev_kfree_skb(skb); | ||
282 | return NULL; | ||
283 | } | ||
284 | /* copy new data to decode-buffer */ | ||
285 | memcpy(&(v->decodebuf[v->decodelen]), rbuf, len); | ||
286 | v->decodelen += len; | ||
287 | ReSync: | ||
288 | if (v->decodelen < v->nbytes) { /* got a new header ? */ | ||
289 | dev_kfree_skb(skb); | ||
290 | return NULL; /* no, try later */ | ||
291 | } | ||
292 | if (ValidHeaderBytes(v) != v->nbytes) { /* is that a valid header? */ | ||
293 | SyncHeader(v); /* no -> look for header */ | ||
294 | goto ReSync; | ||
295 | } | ||
296 | len = (v->decodelen - (v->decodelen % (10 * v->nbytes))) / v->nbytes; | ||
297 | if ((v110_buf = kmalloc(len, GFP_ATOMIC)) == NULL) { | ||
298 | printk(KERN_WARNING "isdn_v110_decode: Couldn't allocate v110_buf\n"); | ||
299 | dev_kfree_skb(skb); | ||
300 | return NULL; | ||
301 | } | ||
302 | for (i = 0; i < len; i++) { | ||
303 | v110_buf[i] = 0; | ||
304 | for (j = 0; j < v->nbytes; j++) | ||
305 | v110_buf[i] |= (v->decodebuf[(i * v->nbytes) + j] & v->key) << (8 - ((j + 1) * v->nbits)); | ||
306 | v110_buf[i] = FlipBits(v110_buf[i], v->nbits); | ||
307 | } | ||
308 | v->decodelen = (v->decodelen % (10 * v->nbytes)); | ||
309 | memcpy(v->decodebuf, &(v->decodebuf[len * v->nbytes]), v->decodelen); | ||
310 | |||
311 | skb_trim(skb, DecodeMatrix(v, v110_buf, len, skb->data)); | ||
312 | kfree(v110_buf); | ||
313 | if (skb->len) | ||
314 | return skb; | ||
315 | else { | ||
316 | kfree_skb(skb); | ||
317 | return NULL; | ||
318 | } | ||
319 | } | ||
320 | |||
321 | /* EncodeMatrix takes input data in buf, len is the bytecount. | ||
322 | Data is encoded into v110 frames in m. Return value is the number of | ||
323 | matrix-lines generated. | ||
324 | */ | ||
325 | static int | ||
326 | EncodeMatrix(unsigned char *buf, int len, unsigned char *m, int mlen) | ||
327 | { | ||
328 | int line = 0; | ||
329 | int i = 0; | ||
330 | int mbit = 128; | ||
331 | int dbit = 1; | ||
332 | int introducer = 3; | ||
333 | int ibit[] = {0, 1, 1}; | ||
334 | |||
335 | while ((i < len) && (line < mlen)) { /* while we still have input data */ | ||
336 | switch (line % 10) { /* in which line of the matrix are we? */ | ||
337 | case 0: | ||
338 | m[line++] = 0x00; /* line 0 is always 0 */ | ||
339 | mbit = 128; /* go on with the 7th bit */ | ||
340 | break; | ||
341 | case 5: | ||
342 | m[line++] = 0xbf; /* line 5 is always 10111111 */ | ||
343 | mbit = 128; /* go on with the 7th bit */ | ||
344 | break; | ||
345 | } | ||
346 | if (line >= mlen) { | ||
347 | printk(KERN_WARNING "isdn_v110 (EncodeMatrix): buffer full!\n"); | ||
348 | return line; | ||
349 | } | ||
350 | next_bit: | ||
351 | switch (mbit) { /* leftmost or rightmost bit ? */ | ||
352 | case 1: | ||
353 | line++; /* rightmost -> go to next line */ | ||
354 | if (line >= mlen) { | ||
355 | printk(KERN_WARNING "isdn_v110 (EncodeMatrix): buffer full!\n"); | ||
356 | return line; | ||
357 | } | ||
358 | case 128: | ||
359 | m[line] = 128; /* leftmost -> set byte to 1000000 */ | ||
360 | mbit = 64; /* current bit in the matrix line */ | ||
361 | continue; | ||
362 | } | ||
363 | if (introducer) { /* set 110 sequence ? */ | ||
364 | introducer--; /* set on digit less */ | ||
365 | m[line] |= ibit[introducer] ? mbit : 0; /* set corresponding bit */ | ||
366 | mbit >>= 1; /* bit of matrix line >> 1 */ | ||
367 | goto next_bit; /* and go on there */ | ||
368 | } /* else push data bits into the matrix! */ | ||
369 | m[line] |= (buf[i] & dbit) ? mbit : 0; /* set data bit in matrix */ | ||
370 | if (dbit == 128) { /* was it the last one? */ | ||
371 | dbit = 1; /* then go on with first bit of */ | ||
372 | i++; /* next byte in input buffer */ | ||
373 | if (i < len) /* input buffer done ? */ | ||
374 | introducer = 3; /* no, write introducer 110 */ | ||
375 | else { /* input buffer done ! */ | ||
376 | m[line] |= (mbit - 1) & 0xfe; /* set remaining bits in line to 1 */ | ||
377 | break; | ||
378 | } | ||
379 | } else /* not the last data bit */ | ||
380 | dbit <<= 1; /* then go to next data bit */ | ||
381 | mbit >>= 1; /* go to next bit of matrix */ | ||
382 | goto next_bit; | ||
383 | |||
384 | } | ||
385 | /* if necessary, generate remaining lines of the matrix... */ | ||
386 | if ((line) && ((line + 10) < mlen)) | ||
387 | switch (++line % 10) { | ||
388 | case 1: | ||
389 | m[line++] = 0xfe; | ||
390 | case 2: | ||
391 | m[line++] = 0xfe; | ||
392 | case 3: | ||
393 | m[line++] = 0xfe; | ||
394 | case 4: | ||
395 | m[line++] = 0xfe; | ||
396 | case 5: | ||
397 | m[line++] = 0xbf; | ||
398 | case 6: | ||
399 | m[line++] = 0xfe; | ||
400 | case 7: | ||
401 | m[line++] = 0xfe; | ||
402 | case 8: | ||
403 | m[line++] = 0xfe; | ||
404 | case 9: | ||
405 | m[line++] = 0xfe; | ||
406 | } | ||
407 | return line; /* that's how many lines we have */ | ||
408 | } | ||
409 | |||
410 | /* | ||
411 | * Build a sync frame. | ||
412 | */ | ||
413 | static struct sk_buff * | ||
414 | isdn_v110_sync(isdn_v110_stream *v) | ||
415 | { | ||
416 | struct sk_buff *skb; | ||
417 | |||
418 | if (v == NULL) { | ||
419 | /* invalid handle, no chance to proceed */ | ||
420 | printk(KERN_WARNING "isdn_v110_sync called with NULL stream!\n"); | ||
421 | return NULL; | ||
422 | } | ||
423 | if ((skb = dev_alloc_skb(v->framelen + v->skbres))) { | ||
424 | skb_reserve(skb, v->skbres); | ||
425 | memcpy(skb_put(skb, v->framelen), v->OfflineFrame, v->framelen); | ||
426 | } | ||
427 | return skb; | ||
428 | } | ||
429 | |||
430 | /* | ||
431 | * Build an idle frame. | ||
432 | */ | ||
433 | static struct sk_buff * | ||
434 | isdn_v110_idle(isdn_v110_stream *v) | ||
435 | { | ||
436 | struct sk_buff *skb; | ||
437 | |||
438 | if (v == NULL) { | ||
439 | /* invalid handle, no chance to proceed */ | ||
440 | printk(KERN_WARNING "isdn_v110_sync called with NULL stream!\n"); | ||
441 | return NULL; | ||
442 | } | ||
443 | if ((skb = dev_alloc_skb(v->framelen + v->skbres))) { | ||
444 | skb_reserve(skb, v->skbres); | ||
445 | memcpy(skb_put(skb, v->framelen), v->OnlineFrame, v->framelen); | ||
446 | } | ||
447 | return skb; | ||
448 | } | ||
449 | |||
450 | struct sk_buff * | ||
451 | isdn_v110_encode(isdn_v110_stream * v, struct sk_buff *skb) | ||
452 | { | ||
453 | int i; | ||
454 | int j; | ||
455 | int rlen; | ||
456 | int mlen; | ||
457 | int olen; | ||
458 | int size; | ||
459 | int sval1; | ||
460 | int sval2; | ||
461 | int nframes; | ||
462 | unsigned char *v110buf; | ||
463 | unsigned char *rbuf; | ||
464 | struct sk_buff *nskb; | ||
465 | |||
466 | if (v == NULL) { | ||
467 | /* invalid handle, no chance to proceed */ | ||
468 | printk(KERN_WARNING "isdn_v110_encode called with NULL stream!\n"); | ||
469 | return NULL; | ||
470 | } | ||
471 | if (!skb) { | ||
472 | /* invalid skb, no chance to proceed */ | ||
473 | printk(KERN_WARNING "isdn_v110_encode called with NULL skb!\n"); | ||
474 | return NULL; | ||
475 | } | ||
476 | rlen = skb->len; | ||
477 | nframes = (rlen + 3) / 4; | ||
478 | v110buf = v->encodebuf; | ||
479 | if ((nframes * 40) > v->maxsize) { | ||
480 | size = v->maxsize; | ||
481 | rlen = v->maxsize / 40; | ||
482 | } else | ||
483 | size = nframes * 40; | ||
484 | if (!(nskb = dev_alloc_skb(size + v->skbres + sizeof(int)))) { | ||
485 | printk(KERN_WARNING "isdn_v110_encode: Couldn't alloc skb\n"); | ||
486 | return NULL; | ||
487 | } | ||
488 | skb_reserve(nskb, v->skbres + sizeof(int)); | ||
489 | if (skb->len == 0) { | ||
490 | memcpy(skb_put(nskb, v->framelen), v->OnlineFrame, v->framelen); | ||
491 | *((int *)skb_push(nskb, sizeof(int))) = 0; | ||
492 | return nskb; | ||
493 | } | ||
494 | mlen = EncodeMatrix(skb->data, rlen, v110buf, size); | ||
495 | /* now distribute 2 or 4 bits each to the output stream! */ | ||
496 | rbuf = skb_put(nskb, size); | ||
497 | olen = 0; | ||
498 | sval1 = 8 - v->nbits; | ||
499 | sval2 = v->key << sval1; | ||
500 | for (i = 0; i < mlen; i++) { | ||
501 | v110buf[i] = FlipBits(v110buf[i], v->nbits); | ||
502 | for (j = 0; j < v->nbytes; j++) { | ||
503 | if (size--) | ||
504 | *rbuf++ = ~v->key | (((v110buf[i] << (j * v->nbits)) & sval2) >> sval1); | ||
505 | else { | ||
506 | printk(KERN_WARNING "isdn_v110_encode: buffers full!\n"); | ||
507 | goto buffer_full; | ||
508 | } | ||
509 | olen++; | ||
510 | } | ||
511 | } | ||
512 | buffer_full: | ||
513 | skb_trim(nskb, olen); | ||
514 | *((int *)skb_push(nskb, sizeof(int))) = rlen; | ||
515 | return nskb; | ||
516 | } | ||
517 | |||
518 | int | ||
519 | isdn_v110_stat_callback(int idx, isdn_ctrl * c) | ||
520 | { | ||
521 | isdn_v110_stream *v = NULL; | ||
522 | int i; | ||
523 | int ret; | ||
524 | |||
525 | if (idx < 0) | ||
526 | return 0; | ||
527 | switch (c->command) { | ||
528 | case ISDN_STAT_BSENT: | ||
529 | /* Keep the send-queue of the driver filled | ||
530 | * with frames: | ||
531 | * If number of outstanding frames < 3, | ||
532 | * send down an Idle-Frame (or an Sync-Frame, if | ||
533 | * v->SyncInit != 0). | ||
534 | */ | ||
535 | if (!(v = dev->v110[idx])) | ||
536 | return 0; | ||
537 | atomic_inc(&dev->v110use[idx]); | ||
538 | for (i=0; i * v->framelen < c->parm.length; i++) { | ||
539 | if (v->skbidle > 0) { | ||
540 | v->skbidle--; | ||
541 | ret = 1; | ||
542 | } else { | ||
543 | if (v->skbuser > 0) | ||
544 | v->skbuser--; | ||
545 | ret = 0; | ||
546 | } | ||
547 | } | ||
548 | for (i = v->skbuser + v->skbidle; i < 2; i++) { | ||
549 | struct sk_buff *skb; | ||
550 | if (v->SyncInit > 0) | ||
551 | skb = isdn_v110_sync(v); | ||
552 | else | ||
553 | skb = isdn_v110_idle(v); | ||
554 | if (skb) { | ||
555 | if (dev->drv[c->driver]->interface->writebuf_skb(c->driver, c->arg, 1, skb) <= 0) { | ||
556 | dev_kfree_skb(skb); | ||
557 | break; | ||
558 | } else { | ||
559 | if (v->SyncInit) | ||
560 | v->SyncInit--; | ||
561 | v->skbidle++; | ||
562 | } | ||
563 | } else | ||
564 | break; | ||
565 | } | ||
566 | atomic_dec(&dev->v110use[idx]); | ||
567 | return ret; | ||
568 | case ISDN_STAT_DHUP: | ||
569 | case ISDN_STAT_BHUP: | ||
570 | while (1) { | ||
571 | atomic_inc(&dev->v110use[idx]); | ||
572 | if (atomic_dec_and_test(&dev->v110use[idx])) { | ||
573 | isdn_v110_close(dev->v110[idx]); | ||
574 | dev->v110[idx] = NULL; | ||
575 | break; | ||
576 | } | ||
577 | mdelay(1); | ||
578 | } | ||
579 | break; | ||
580 | case ISDN_STAT_BCONN: | ||
581 | if (dev->v110emu[idx] && (dev->v110[idx] == NULL)) { | ||
582 | int hdrlen = dev->drv[c->driver]->interface->hl_hdrlen; | ||
583 | int maxsize = dev->drv[c->driver]->interface->maxbufsize; | ||
584 | atomic_inc(&dev->v110use[idx]); | ||
585 | switch (dev->v110emu[idx]) { | ||
586 | case ISDN_PROTO_L2_V11096: | ||
587 | dev->v110[idx] = isdn_v110_open(V110_9600, hdrlen, maxsize); | ||
588 | break; | ||
589 | case ISDN_PROTO_L2_V11019: | ||
590 | dev->v110[idx] = isdn_v110_open(V110_19200, hdrlen, maxsize); | ||
591 | break; | ||
592 | case ISDN_PROTO_L2_V11038: | ||
593 | dev->v110[idx] = isdn_v110_open(V110_38400, hdrlen, maxsize); | ||
594 | break; | ||
595 | default:; | ||
596 | } | ||
597 | if ((v = dev->v110[idx])) { | ||
598 | while (v->SyncInit) { | ||
599 | struct sk_buff *skb = isdn_v110_sync(v); | ||
600 | if (dev->drv[c->driver]->interface->writebuf_skb(c->driver, c->arg, 1, skb) <= 0) { | ||
601 | dev_kfree_skb(skb); | ||
602 | /* Unable to send, try later */ | ||
603 | break; | ||
604 | } | ||
605 | v->SyncInit--; | ||
606 | v->skbidle++; | ||
607 | } | ||
608 | } else | ||
609 | printk(KERN_WARNING "isdn_v110: Couldn't open stream for chan %d\n", idx); | ||
610 | atomic_dec(&dev->v110use[idx]); | ||
611 | } | ||
612 | break; | ||
613 | default: | ||
614 | return 0; | ||
615 | } | ||
616 | return 0; | ||
617 | } | ||
diff --git a/drivers/isdn/i4l/isdn_v110.h b/drivers/isdn/i4l/isdn_v110.h new file mode 100644 index 000000000000..08f274bbc438 --- /dev/null +++ b/drivers/isdn/i4l/isdn_v110.h | |||
@@ -0,0 +1,29 @@ | |||
1 | /* $Id: isdn_v110.h,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $ | ||
2 | * | ||
3 | * Linux ISDN subsystem, V.110 related functions (linklevel). | ||
4 | * | ||
5 | * Copyright by Thomas Pfeiffer (pfeiffer@pds.de) | ||
6 | * | ||
7 | * This software may be used and distributed according to the terms | ||
8 | * of the GNU General Public License, incorporated herein by reference. | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #ifndef _isdn_v110_h_ | ||
13 | #define _isdn_v110_h_ | ||
14 | |||
15 | /* | ||
16 | * isdn_v110_encode will take raw data and encode it using V.110 | ||
17 | */ | ||
18 | extern struct sk_buff *isdn_v110_encode(isdn_v110_stream *, struct sk_buff *); | ||
19 | |||
20 | /* | ||
21 | * isdn_v110_decode receives V.110 coded data from the stream and rebuilds | ||
22 | * frames from them. The source stream doesn't need to be framed. | ||
23 | */ | ||
24 | extern struct sk_buff *isdn_v110_decode(isdn_v110_stream *, struct sk_buff *); | ||
25 | |||
26 | extern int isdn_v110_stat_callback(int, isdn_ctrl *); | ||
27 | extern void isdn_v110_close(isdn_v110_stream * v); | ||
28 | |||
29 | #endif | ||
diff --git a/drivers/isdn/i4l/isdn_x25iface.c b/drivers/isdn/i4l/isdn_x25iface.c new file mode 100644 index 000000000000..4ab7600cf9c1 --- /dev/null +++ b/drivers/isdn/i4l/isdn_x25iface.c | |||
@@ -0,0 +1,328 @@ | |||
1 | /* $Id: isdn_x25iface.c,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $ | ||
2 | * | ||
3 | * Linux ISDN subsystem, X.25 related functions | ||
4 | * | ||
5 | * This software may be used and distributed according to the terms | ||
6 | * of the GNU General Public License, incorporated herein by reference. | ||
7 | * | ||
8 | * stuff needed to support the Linux X.25 PLP code on top of devices that | ||
9 | * can provide a lab_b service using the concap_proto mechanism. | ||
10 | * This module supports a network interface wich provides lapb_sematics | ||
11 | * -- as defined in Documentation/networking/x25-iface.txt -- to | ||
12 | * the upper layer and assumes that the lower layer provides a reliable | ||
13 | * data link service by means of the concap_device_ops callbacks. | ||
14 | * | ||
15 | * Only protocol specific stuff goes here. Device specific stuff | ||
16 | * goes to another -- device related -- concap_proto support source file. | ||
17 | * | ||
18 | */ | ||
19 | |||
20 | /* #include <linux/isdn.h> */ | ||
21 | #include <linux/netdevice.h> | ||
22 | #include <linux/concap.h> | ||
23 | #include <linux/wanrouter.h> | ||
24 | #include <net/x25device.h> | ||
25 | #include "isdn_x25iface.h" | ||
26 | |||
27 | /* for debugging messages not to cause an oops when device pointer is NULL*/ | ||
28 | #define MY_DEVNAME(dev) ( (dev) ? (dev)->name : "DEVICE UNSPECIFIED" ) | ||
29 | |||
30 | |||
31 | typedef struct isdn_x25iface_proto_data { | ||
32 | int magic; | ||
33 | enum wan_states state; | ||
34 | /* Private stuff, not to be accessed via proto_data. We provide the | ||
35 | other storage for the concap_proto instance here as well, | ||
36 | enabling us to allocate both with just one kmalloc(): */ | ||
37 | struct concap_proto priv; | ||
38 | } ix25_pdata_t; | ||
39 | |||
40 | |||
41 | |||
42 | /* is now in header file (extern): struct concap_proto * isdn_x25iface_proto_new(void); */ | ||
43 | void isdn_x25iface_proto_del( struct concap_proto * ); | ||
44 | int isdn_x25iface_proto_close( struct concap_proto * ); | ||
45 | int isdn_x25iface_proto_restart( struct concap_proto *, | ||
46 | struct net_device *, | ||
47 | struct concap_device_ops *); | ||
48 | int isdn_x25iface_xmit( struct concap_proto *, struct sk_buff * ); | ||
49 | int isdn_x25iface_receive( struct concap_proto *, struct sk_buff * ); | ||
50 | int isdn_x25iface_connect_ind( struct concap_proto * ); | ||
51 | int isdn_x25iface_disconn_ind( struct concap_proto * ); | ||
52 | |||
53 | |||
54 | static struct concap_proto_ops ix25_pops = { | ||
55 | &isdn_x25iface_proto_new, | ||
56 | &isdn_x25iface_proto_del, | ||
57 | &isdn_x25iface_proto_restart, | ||
58 | &isdn_x25iface_proto_close, | ||
59 | &isdn_x25iface_xmit, | ||
60 | &isdn_x25iface_receive, | ||
61 | &isdn_x25iface_connect_ind, | ||
62 | &isdn_x25iface_disconn_ind | ||
63 | }; | ||
64 | |||
65 | /* error message helper function */ | ||
66 | static void illegal_state_warn( unsigned state, unsigned char firstbyte) | ||
67 | { | ||
68 | printk( KERN_WARNING "isdn_x25iface: firstbyte %x illegal in" | ||
69 | "current state %d\n",firstbyte, state ); | ||
70 | } | ||
71 | |||
72 | /* check protocol data field for consistency */ | ||
73 | static int pdata_is_bad( ix25_pdata_t * pda ){ | ||
74 | |||
75 | if( pda && pda -> magic == ISDN_X25IFACE_MAGIC ) return 0; | ||
76 | printk( KERN_WARNING | ||
77 | "isdn_x25iface_xxx: illegal pointer to proto data\n" ); | ||
78 | return 1; | ||
79 | } | ||
80 | |||
81 | /* create a new x25 interface protocol instance | ||
82 | */ | ||
83 | struct concap_proto * isdn_x25iface_proto_new(void) | ||
84 | { | ||
85 | ix25_pdata_t * tmp = kmalloc(sizeof(ix25_pdata_t),GFP_KERNEL); | ||
86 | IX25DEBUG("isdn_x25iface_proto_new\n"); | ||
87 | if( tmp ){ | ||
88 | tmp -> magic = ISDN_X25IFACE_MAGIC; | ||
89 | tmp -> state = WAN_UNCONFIGURED; | ||
90 | /* private data space used to hold the concap_proto data. | ||
91 | Only to be accessed via the returned pointer */ | ||
92 | spin_lock_init(&tmp->priv.lock); | ||
93 | tmp -> priv.dops = NULL; | ||
94 | tmp -> priv.net_dev = NULL; | ||
95 | tmp -> priv.pops = &ix25_pops; | ||
96 | tmp -> priv.flags = 0; | ||
97 | tmp -> priv.proto_data = tmp; | ||
98 | return( &(tmp -> priv) ); | ||
99 | } | ||
100 | return NULL; | ||
101 | }; | ||
102 | |||
103 | /* close the x25iface encapsulation protocol | ||
104 | */ | ||
105 | int isdn_x25iface_proto_close(struct concap_proto *cprot){ | ||
106 | |||
107 | ix25_pdata_t *tmp; | ||
108 | int ret = 0; | ||
109 | ulong flags; | ||
110 | |||
111 | if( ! cprot ){ | ||
112 | printk( KERN_ERR "isdn_x25iface_proto_close: " | ||
113 | "invalid concap_proto pointer\n" ); | ||
114 | return -1; | ||
115 | } | ||
116 | IX25DEBUG( "isdn_x25iface_proto_close %s \n", MY_DEVNAME(cprot -> net_dev) ); | ||
117 | spin_lock_irqsave(&cprot->lock, flags); | ||
118 | cprot -> dops = NULL; | ||
119 | cprot -> net_dev = NULL; | ||
120 | tmp = cprot -> proto_data; | ||
121 | if( pdata_is_bad( tmp ) ){ | ||
122 | ret = -1; | ||
123 | } else { | ||
124 | tmp -> state = WAN_UNCONFIGURED; | ||
125 | } | ||
126 | spin_unlock_irqrestore(&cprot->lock, flags); | ||
127 | return ret; | ||
128 | } | ||
129 | |||
130 | /* Delete the x25iface encapsulation protocol instance | ||
131 | */ | ||
132 | void isdn_x25iface_proto_del(struct concap_proto *cprot){ | ||
133 | |||
134 | ix25_pdata_t * tmp; | ||
135 | |||
136 | IX25DEBUG( "isdn_x25iface_proto_del \n" ); | ||
137 | if( ! cprot ){ | ||
138 | printk( KERN_ERR "isdn_x25iface_proto_del: " | ||
139 | "concap_proto pointer is NULL\n" ); | ||
140 | return; | ||
141 | } | ||
142 | tmp = cprot -> proto_data; | ||
143 | if( tmp == NULL ){ | ||
144 | printk( KERN_ERR "isdn_x25iface_proto_del: inconsistent " | ||
145 | "proto_data pointer (maybe already deleted?)\n"); | ||
146 | return; | ||
147 | } | ||
148 | /* close if the protocol is still open */ | ||
149 | if( cprot -> dops ) isdn_x25iface_proto_close(cprot); | ||
150 | /* freeing the storage should be sufficient now. But some additional | ||
151 | settings might help to catch wild pointer bugs */ | ||
152 | tmp -> magic = 0; | ||
153 | cprot -> proto_data = NULL; | ||
154 | |||
155 | kfree( tmp ); | ||
156 | return; | ||
157 | } | ||
158 | |||
159 | /* (re-)initialize the data structures for x25iface encapsulation | ||
160 | */ | ||
161 | int isdn_x25iface_proto_restart(struct concap_proto *cprot, | ||
162 | struct net_device *ndev, | ||
163 | struct concap_device_ops *dops) | ||
164 | { | ||
165 | ix25_pdata_t * pda = cprot -> proto_data ; | ||
166 | ulong flags; | ||
167 | |||
168 | IX25DEBUG( "isdn_x25iface_proto_restart %s \n", MY_DEVNAME(ndev) ); | ||
169 | |||
170 | if ( pdata_is_bad( pda ) ) return -1; | ||
171 | |||
172 | if( !( dops && dops -> data_req && dops -> connect_req | ||
173 | && dops -> disconn_req ) ){ | ||
174 | printk( KERN_WARNING "isdn_x25iface_restart: required dops" | ||
175 | " missing\n" ); | ||
176 | isdn_x25iface_proto_close(cprot); | ||
177 | return -1; | ||
178 | } | ||
179 | spin_lock_irqsave(&cprot->lock, flags); | ||
180 | cprot -> net_dev = ndev; | ||
181 | cprot -> pops = &ix25_pops; | ||
182 | cprot -> dops = dops; | ||
183 | pda -> state = WAN_DISCONNECTED; | ||
184 | spin_unlock_irqrestore(&cprot->lock, flags); | ||
185 | return 0; | ||
186 | } | ||
187 | |||
188 | /* deliver a dl_data frame received from i4l HL driver to the network layer | ||
189 | */ | ||
190 | int isdn_x25iface_receive(struct concap_proto *cprot, struct sk_buff *skb) | ||
191 | { | ||
192 | IX25DEBUG( "isdn_x25iface_receive %s \n", MY_DEVNAME(cprot->net_dev) ); | ||
193 | if ( ( (ix25_pdata_t*) (cprot->proto_data) ) | ||
194 | -> state == WAN_CONNECTED ){ | ||
195 | if( skb_push(skb, 1)){ | ||
196 | skb -> data[0]=0x00; | ||
197 | skb->protocol = x25_type_trans(skb, cprot->net_dev); | ||
198 | netif_rx(skb); | ||
199 | return 0; | ||
200 | } | ||
201 | } | ||
202 | printk(KERN_WARNING "isdn_x25iface_receive %s: not connected, skb dropped\n", MY_DEVNAME(cprot->net_dev) ); | ||
203 | dev_kfree_skb(skb); | ||
204 | return -1; | ||
205 | } | ||
206 | |||
207 | /* a connection set up is indicated by lower layer | ||
208 | */ | ||
209 | int isdn_x25iface_connect_ind(struct concap_proto *cprot) | ||
210 | { | ||
211 | struct sk_buff * skb = dev_alloc_skb(1); | ||
212 | enum wan_states *state_p | ||
213 | = &( ( (ix25_pdata_t*) (cprot->proto_data) ) -> state); | ||
214 | IX25DEBUG( "isdn_x25iface_connect_ind %s \n" | ||
215 | , MY_DEVNAME(cprot->net_dev) ); | ||
216 | if( *state_p == WAN_UNCONFIGURED ){ | ||
217 | printk(KERN_WARNING | ||
218 | "isdn_x25iface_connect_ind while unconfigured %s\n" | ||
219 | , MY_DEVNAME(cprot->net_dev) ); | ||
220 | return -1; | ||
221 | } | ||
222 | *state_p = WAN_CONNECTED; | ||
223 | if( skb ){ | ||
224 | *( skb_put(skb, 1) ) = 0x01; | ||
225 | skb->protocol = x25_type_trans(skb, cprot->net_dev); | ||
226 | netif_rx(skb); | ||
227 | return 0; | ||
228 | } else { | ||
229 | printk(KERN_WARNING "isdn_x25iface_connect_ind: " | ||
230 | " out of memory -- disconnecting\n"); | ||
231 | cprot -> dops -> disconn_req(cprot); | ||
232 | return -1; | ||
233 | } | ||
234 | } | ||
235 | |||
236 | /* a disconnect is indicated by lower layer | ||
237 | */ | ||
238 | int isdn_x25iface_disconn_ind(struct concap_proto *cprot) | ||
239 | { | ||
240 | struct sk_buff *skb; | ||
241 | enum wan_states *state_p | ||
242 | = &( ( (ix25_pdata_t*) (cprot->proto_data) ) -> state); | ||
243 | IX25DEBUG( "isdn_x25iface_disconn_ind %s \n", MY_DEVNAME(cprot -> net_dev) ); | ||
244 | if( *state_p == WAN_UNCONFIGURED ){ | ||
245 | printk(KERN_WARNING | ||
246 | "isdn_x25iface_disconn_ind while unconfigured\n"); | ||
247 | return -1; | ||
248 | } | ||
249 | if(! cprot -> net_dev) return -1; | ||
250 | *state_p = WAN_DISCONNECTED; | ||
251 | skb = dev_alloc_skb(1); | ||
252 | if( skb ){ | ||
253 | *( skb_put(skb, 1) ) = 0x02; | ||
254 | skb->protocol = x25_type_trans(skb, cprot->net_dev); | ||
255 | netif_rx(skb); | ||
256 | return 0; | ||
257 | } else { | ||
258 | printk(KERN_WARNING "isdn_x25iface_disconn_ind:" | ||
259 | " out of memory\n"); | ||
260 | return -1; | ||
261 | } | ||
262 | } | ||
263 | |||
264 | /* process a frame handed over to us from linux network layer. First byte | ||
265 | semantics as defined in Documentation/networking/x25-iface.txt | ||
266 | */ | ||
267 | int isdn_x25iface_xmit(struct concap_proto *cprot, struct sk_buff *skb) | ||
268 | { | ||
269 | unsigned char firstbyte = skb->data[0]; | ||
270 | enum wan_states *state = &((ix25_pdata_t*)cprot->proto_data)->state; | ||
271 | int ret = 0; | ||
272 | IX25DEBUG( "isdn_x25iface_xmit: %s first=%x state=%d \n", MY_DEVNAME(cprot -> net_dev), firstbyte, *state ); | ||
273 | switch ( firstbyte ){ | ||
274 | case 0x00: /* dl_data request */ | ||
275 | if( *state == WAN_CONNECTED ){ | ||
276 | skb_pull(skb, 1); | ||
277 | cprot -> net_dev -> trans_start = jiffies; | ||
278 | ret = ( cprot -> dops -> data_req(cprot, skb) ); | ||
279 | /* prepare for future retransmissions */ | ||
280 | if( ret ) skb_push(skb,1); | ||
281 | return ret; | ||
282 | } | ||
283 | illegal_state_warn( *state, firstbyte ); | ||
284 | break; | ||
285 | case 0x01: /* dl_connect request */ | ||
286 | if( *state == WAN_DISCONNECTED ){ | ||
287 | *state = WAN_CONNECTING; | ||
288 | ret = cprot -> dops -> connect_req(cprot); | ||
289 | if(ret){ | ||
290 | /* reset state and notify upper layer about | ||
291 | * immidiatly failed attempts */ | ||
292 | isdn_x25iface_disconn_ind(cprot); | ||
293 | } | ||
294 | } else { | ||
295 | illegal_state_warn( *state, firstbyte ); | ||
296 | } | ||
297 | break; | ||
298 | case 0x02: /* dl_disconnect request */ | ||
299 | switch ( *state ){ | ||
300 | case WAN_DISCONNECTED: | ||
301 | /* Should not happen. However, give upper layer a | ||
302 | chance to recover from inconstistency but don't | ||
303 | trust the lower layer sending the disconn_confirm | ||
304 | when already disconnected */ | ||
305 | printk(KERN_WARNING "isdn_x25iface_xmit: disconnect " | ||
306 | " requested while disconnected\n" ); | ||
307 | isdn_x25iface_disconn_ind(cprot); | ||
308 | break; /* prevent infinite loops */ | ||
309 | case WAN_CONNECTING: | ||
310 | case WAN_CONNECTED: | ||
311 | *state = WAN_DISCONNECTED; | ||
312 | cprot -> dops -> disconn_req(cprot); | ||
313 | break; | ||
314 | default: | ||
315 | illegal_state_warn( *state, firstbyte ); | ||
316 | } | ||
317 | break; | ||
318 | case 0x03: /* changing lapb parameters requested */ | ||
319 | printk(KERN_WARNING "isdn_x25iface_xmit: setting of lapb" | ||
320 | " options not yet supported\n"); | ||
321 | break; | ||
322 | default: | ||
323 | printk(KERN_WARNING "isdn_x25iface_xmit: frame with illegal" | ||
324 | " first byte %x ignored:\n", firstbyte); | ||
325 | } | ||
326 | dev_kfree_skb(skb); | ||
327 | return 0; | ||
328 | } | ||
diff --git a/drivers/isdn/i4l/isdn_x25iface.h b/drivers/isdn/i4l/isdn_x25iface.h new file mode 100644 index 000000000000..41a3d4977466 --- /dev/null +++ b/drivers/isdn/i4l/isdn_x25iface.h | |||
@@ -0,0 +1,39 @@ | |||
1 | /* $Id: isdn_x25iface.h,v 1.1.2.2 2004/01/12 22:37:19 keil Exp $ | ||
2 | * | ||
3 | * header for Linux ISDN subsystem, x.25 related functions | ||
4 | * | ||
5 | * This software may be used and distributed according to the terms | ||
6 | * of the GNU General Public License, incorporated herein by reference. | ||
7 | * | ||
8 | */ | ||
9 | |||
10 | #ifndef _LINUX_ISDN_X25IFACE_H | ||
11 | #define _LINUX_ISDN_X25IFACE_H | ||
12 | |||
13 | #define ISDN_X25IFACE_MAGIC 0x1e75a2b9 | ||
14 | /* #define DEBUG_ISDN_X25 if you want isdn_x25 debugging messages */ | ||
15 | #ifdef DEBUG_ISDN_X25 | ||
16 | # define IX25DEBUG(fmt,args...) printk(KERN_DEBUG fmt , ## args) | ||
17 | #else | ||
18 | # define IX25DEBUG(fmt,args...) | ||
19 | #endif | ||
20 | |||
21 | #include <linux/skbuff.h> | ||
22 | #include <linux/wanrouter.h> | ||
23 | #include <linux/isdn.h> | ||
24 | #include <linux/concap.h> | ||
25 | |||
26 | extern struct concap_proto_ops * isdn_x25iface_concap_proto_ops_pt; | ||
27 | extern struct concap_proto * isdn_x25iface_proto_new(void); | ||
28 | |||
29 | |||
30 | |||
31 | #endif | ||
32 | |||
33 | |||
34 | |||
35 | |||
36 | |||
37 | |||
38 | |||
39 | |||