diff options
Diffstat (limited to 'drivers/isdn/pcbit/edss1.c')
-rw-r--r-- | drivers/isdn/pcbit/edss1.c | 325 |
1 files changed, 325 insertions, 0 deletions
diff --git a/drivers/isdn/pcbit/edss1.c b/drivers/isdn/pcbit/edss1.c new file mode 100644 index 000000000000..93ca7de5670b --- /dev/null +++ b/drivers/isdn/pcbit/edss1.c | |||
@@ -0,0 +1,325 @@ | |||
1 | /* | ||
2 | * DSS.1 Finite State Machine | ||
3 | * base: ITU-T Rec Q.931 | ||
4 | * | ||
5 | * Copyright (C) 1996 Universidade de Lisboa | ||
6 | * | ||
7 | * Written by Pedro Roque Marques (roque@di.fc.ul.pt) | ||
8 | * | ||
9 | * This software may be used and distributed according to the terms of | ||
10 | * the GNU General Public License, incorporated herein by reference. | ||
11 | */ | ||
12 | |||
13 | /* | ||
14 | * TODO: complete the FSM | ||
15 | * move state/event descriptions to a user space logger | ||
16 | */ | ||
17 | |||
18 | #include <linux/sched.h> | ||
19 | #include <linux/string.h> | ||
20 | #include <linux/kernel.h> | ||
21 | |||
22 | #include <linux/types.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/mm.h> | ||
25 | #include <linux/skbuff.h> | ||
26 | |||
27 | #include <linux/timer.h> | ||
28 | #include <asm/io.h> | ||
29 | |||
30 | #include <linux/isdnif.h> | ||
31 | |||
32 | #include "pcbit.h" | ||
33 | #include "edss1.h" | ||
34 | #include "layer2.h" | ||
35 | #include "callbacks.h" | ||
36 | |||
37 | |||
38 | extern void pcbit_state_change(struct pcbit_dev *, struct pcbit_chan *, | ||
39 | unsigned short i, unsigned short ev, | ||
40 | unsigned short f); | ||
41 | |||
42 | extern struct pcbit_dev * dev_pcbit[MAX_PCBIT_CARDS]; | ||
43 | |||
44 | char * isdn_state_table[] = { | ||
45 | "Closed", | ||
46 | "Call initiated", | ||
47 | "Overlap sending", | ||
48 | "Outgoing call proceeding", | ||
49 | "NOT DEFINED", | ||
50 | "Call delivered", | ||
51 | "Call present", | ||
52 | "Call received", | ||
53 | "Connect request", | ||
54 | "Incoming call proceeding", | ||
55 | "Active", | ||
56 | "Disconnect request", | ||
57 | "Disconnect indication", | ||
58 | "NOT DEFINED", | ||
59 | "NOT DEFINED", | ||
60 | "Suspend request", | ||
61 | "NOT DEFINED", | ||
62 | "Resume request", | ||
63 | "NOT DEFINED", | ||
64 | "Release Request", | ||
65 | "NOT DEFINED", | ||
66 | "NOT DEFINED", | ||
67 | "NOT DEFINED", | ||
68 | "NOT DEFINED", | ||
69 | "NOT DEFINED", | ||
70 | "Overlap receiving", | ||
71 | "Select protocol on B-Channel", | ||
72 | "Activate B-channel protocol" | ||
73 | }; | ||
74 | |||
75 | #ifdef DEBUG_ERRS | ||
76 | static | ||
77 | struct CauseValue { | ||
78 | byte nr; | ||
79 | char *descr; | ||
80 | } cvlist[]={ | ||
81 | {0x01,"Unallocated (unassigned) number"}, | ||
82 | {0x02,"No route to specified transit network"}, | ||
83 | {0x03,"No route to destination"}, | ||
84 | {0x04,"Send special information tone"}, | ||
85 | {0x05,"Misdialled trunk prefix"}, | ||
86 | {0x06,"Channel unacceptable"}, | ||
87 | {0x07,"Channel awarded and being delivered in an established channel"}, | ||
88 | {0x08,"Preemption"}, | ||
89 | {0x09,"Preemption - circuit reserved for reuse"}, | ||
90 | {0x10,"Normal call clearing"}, | ||
91 | {0x11,"User busy"}, | ||
92 | {0x12,"No user responding"}, | ||
93 | {0x13,"No answer from user (user alerted)"}, | ||
94 | {0x14,"Subscriber absent"}, | ||
95 | {0x15,"Call rejected"}, | ||
96 | {0x16,"Number changed"}, | ||
97 | {0x1a,"non-selected user clearing"}, | ||
98 | {0x1b,"Destination out of order"}, | ||
99 | {0x1c,"Invalid number format (address incomplete)"}, | ||
100 | {0x1d,"Facility rejected"}, | ||
101 | {0x1e,"Response to Status enquiry"}, | ||
102 | {0x1f,"Normal, unspecified"}, | ||
103 | {0x22,"No circuit/channel available"}, | ||
104 | {0x26,"Network out of order"}, | ||
105 | {0x27,"Permanent frame mode connection out-of-service"}, | ||
106 | {0x28,"Permanent frame mode connection operational"}, | ||
107 | {0x29,"Temporary failure"}, | ||
108 | {0x2a,"Switching equipment congestion"}, | ||
109 | {0x2b,"Access information discarded"}, | ||
110 | {0x2c,"Requested circuit/channel not available"}, | ||
111 | {0x2e,"Precedence call blocked"}, | ||
112 | {0x2f,"Resource unavailable, unspecified"}, | ||
113 | {0x31,"Quality of service unavailable"}, | ||
114 | {0x32,"Requested facility not subscribed"}, | ||
115 | {0x35,"Outgoing calls barred within CUG"}, | ||
116 | {0x37,"Incoming calls barred within CUG"}, | ||
117 | {0x39,"Bearer capability not authorized"}, | ||
118 | {0x3a,"Bearer capability not presently available"}, | ||
119 | {0x3e,"Inconsistency in designated outgoing access information and subscriber class"}, | ||
120 | {0x3f,"Service or option not available, unspecified"}, | ||
121 | {0x41,"Bearer capability not implemented"}, | ||
122 | {0x42,"Channel type not implemented"}, | ||
123 | {0x43,"Requested facility not implemented"}, | ||
124 | {0x44,"Only restricted digital information bearer capability is available"}, | ||
125 | {0x4f,"Service or option not implemented"}, | ||
126 | {0x51,"Invalid call reference value"}, | ||
127 | {0x52,"Identified channel does not exist"}, | ||
128 | {0x53,"A suspended call exists, but this call identity does not"}, | ||
129 | {0x54,"Call identity in use"}, | ||
130 | {0x55,"No call suspended"}, | ||
131 | {0x56,"Call having the requested call identity has been cleared"}, | ||
132 | {0x57,"User not member of CUG"}, | ||
133 | {0x58,"Incompatible destination"}, | ||
134 | {0x5a,"Non-existent CUG"}, | ||
135 | {0x5b,"Invalid transit network selection"}, | ||
136 | {0x5f,"Invalid message, unspecified"}, | ||
137 | {0x60,"Mandatory information element is missing"}, | ||
138 | {0x61,"Message type non-existent or not implemented"}, | ||
139 | {0x62,"Message not compatible with call state or message type non-existent or not implemented"}, | ||
140 | {0x63,"Information element/parameter non-existent or not implemented"}, | ||
141 | {0x64,"Invalid information element contents"}, | ||
142 | {0x65,"Message not compatible with call state"}, | ||
143 | {0x66,"Recovery on timer expiry"}, | ||
144 | {0x67,"Parameter non-existent or not implemented - passed on"}, | ||
145 | {0x6e,"Message with unrecognized parameter discarded"}, | ||
146 | {0x6f,"Protocol error, unspecified"}, | ||
147 | {0x7f,"Interworking, unspecified"} | ||
148 | }; | ||
149 | |||
150 | #endif | ||
151 | |||
152 | static struct isdn_event_desc { | ||
153 | unsigned short ev; | ||
154 | char * desc; | ||
155 | } isdn_event_table [] = { | ||
156 | {EV_USR_SETUP_REQ, "CC->L3: Setup Request"}, | ||
157 | {EV_USR_SETUP_RESP, "CC->L3: Setup Response"}, | ||
158 | {EV_USR_PROCED_REQ, "CC->L3: Proceeding Request"}, | ||
159 | {EV_USR_RELEASE_REQ, "CC->L3: Release Request"}, | ||
160 | |||
161 | {EV_NET_SETUP, "NET->TE: setup "}, | ||
162 | {EV_NET_CALL_PROC, "NET->TE: call proceeding"}, | ||
163 | {EV_NET_SETUP_ACK, "NET->TE: setup acknowledge (more info needed)"}, | ||
164 | {EV_NET_CONN, "NET->TE: connect"}, | ||
165 | {EV_NET_CONN_ACK, "NET->TE: connect acknowledge"}, | ||
166 | {EV_NET_DISC, "NET->TE: disconnect indication"}, | ||
167 | {EV_NET_RELEASE, "NET->TE: release"}, | ||
168 | {EV_NET_RELEASE_COMP, "NET->TE: release complete"}, | ||
169 | {EV_NET_SELP_RESP, "Board: Select B-channel protocol ack"}, | ||
170 | {EV_NET_ACTV_RESP, "Board: Activate B-channel protocol ack"}, | ||
171 | {EV_TIMER, "Timeout"}, | ||
172 | {0, "NULL"} | ||
173 | }; | ||
174 | |||
175 | char * strisdnevent(ushort ev) | ||
176 | { | ||
177 | struct isdn_event_desc * entry; | ||
178 | |||
179 | for (entry = isdn_event_table; entry->ev; entry++) | ||
180 | if (entry->ev == ev) | ||
181 | break; | ||
182 | |||
183 | return entry->desc; | ||
184 | } | ||
185 | |||
186 | /* | ||
187 | * Euro ISDN finite state machine | ||
188 | */ | ||
189 | |||
190 | static struct fsm_timer_entry fsm_timers[] = { | ||
191 | {ST_CALL_PROC, 10}, | ||
192 | {ST_DISC_REQ, 2}, | ||
193 | {ST_ACTIVE_SELP, 5}, | ||
194 | {ST_ACTIVE_ACTV, 5}, | ||
195 | {ST_INCM_PROC, 10}, | ||
196 | {ST_CONN_REQ, 2}, | ||
197 | {0xff, 0} | ||
198 | }; | ||
199 | |||
200 | static struct fsm_entry fsm_table[] = { | ||
201 | /* Connect Phase */ | ||
202 | /* Outgoing */ | ||
203 | {ST_NULL, ST_CALL_INIT, EV_USR_SETUP_REQ, cb_out_1}, | ||
204 | |||
205 | {ST_CALL_INIT, ST_OVER_SEND, EV_NET_SETUP_ACK, cb_notdone}, | ||
206 | {ST_CALL_INIT, ST_CALL_PROC, EV_NET_CALL_PROC, NULL}, | ||
207 | {ST_CALL_INIT, ST_NULL, EV_NET_DISC, cb_out_2}, | ||
208 | |||
209 | {ST_CALL_PROC, ST_ACTIVE_SELP, EV_NET_CONN, cb_out_2}, | ||
210 | {ST_CALL_PROC, ST_NULL, EV_NET_DISC, cb_disc_1}, | ||
211 | {ST_CALL_PROC, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2}, | ||
212 | |||
213 | /* Incoming */ | ||
214 | {ST_NULL, ST_CALL_PRES, EV_NET_SETUP, NULL}, | ||
215 | |||
216 | {ST_CALL_PRES, ST_INCM_PROC, EV_USR_PROCED_REQ, cb_in_1}, | ||
217 | {ST_CALL_PRES, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2}, | ||
218 | |||
219 | {ST_INCM_PROC, ST_CONN_REQ, EV_USR_SETUP_RESP, cb_in_2}, | ||
220 | {ST_INCM_PROC, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2}, | ||
221 | |||
222 | {ST_CONN_REQ, ST_ACTIVE_SELP, EV_NET_CONN_ACK, cb_in_3}, | ||
223 | |||
224 | /* Active */ | ||
225 | {ST_ACTIVE, ST_NULL, EV_NET_DISC, cb_disc_1}, | ||
226 | {ST_ACTIVE, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2}, | ||
227 | {ST_ACTIVE, ST_NULL, EV_NET_RELEASE, cb_disc_3}, | ||
228 | |||
229 | /* Disconnect */ | ||
230 | |||
231 | {ST_DISC_REQ, ST_NULL, EV_NET_DISC, cb_disc_1}, | ||
232 | {ST_DISC_REQ, ST_NULL, EV_NET_RELEASE, cb_disc_3}, | ||
233 | |||
234 | /* protocol selection */ | ||
235 | {ST_ACTIVE_SELP, ST_ACTIVE_ACTV, EV_NET_SELP_RESP, cb_selp_1}, | ||
236 | {ST_ACTIVE_SELP, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2}, | ||
237 | |||
238 | {ST_ACTIVE_ACTV, ST_ACTIVE, EV_NET_ACTV_RESP, cb_open}, | ||
239 | {ST_ACTIVE_ACTV, ST_DISC_REQ, EV_USR_RELEASE_REQ, cb_disc_2}, | ||
240 | |||
241 | /* Timers */ | ||
242 | {ST_CALL_PROC, ST_DISC_REQ, EV_TIMER, cb_disc_2}, | ||
243 | {ST_DISC_REQ, ST_NULL, EV_TIMER, cb_disc_3}, | ||
244 | {ST_ACTIVE_SELP, ST_DISC_REQ, EV_TIMER, cb_disc_2}, | ||
245 | {ST_ACTIVE_ACTV, ST_DISC_REQ, EV_TIMER, cb_disc_2}, | ||
246 | {ST_INCM_PROC, ST_DISC_REQ, EV_TIMER, cb_disc_2}, | ||
247 | {ST_CONN_REQ, ST_CONN_REQ, EV_TIMER, cb_in_2}, | ||
248 | |||
249 | {0xff, 0, 0, NULL} | ||
250 | }; | ||
251 | |||
252 | |||
253 | static void pcbit_fsm_timer(unsigned long data) | ||
254 | { | ||
255 | struct pcbit_dev *dev; | ||
256 | struct pcbit_chan *chan; | ||
257 | |||
258 | chan = (struct pcbit_chan *) data; | ||
259 | |||
260 | del_timer(&chan->fsm_timer); | ||
261 | chan->fsm_timer.function = NULL; | ||
262 | |||
263 | dev = chan2dev(chan); | ||
264 | |||
265 | if (dev == NULL) { | ||
266 | printk(KERN_WARNING "pcbit: timer for unknown device\n"); | ||
267 | return; | ||
268 | } | ||
269 | |||
270 | pcbit_fsm_event(dev, chan, EV_TIMER, NULL); | ||
271 | } | ||
272 | |||
273 | |||
274 | void pcbit_fsm_event(struct pcbit_dev *dev, struct pcbit_chan *chan, | ||
275 | unsigned short event, struct callb_data *data) | ||
276 | { | ||
277 | struct fsm_entry * action; | ||
278 | struct fsm_timer_entry *tentry; | ||
279 | unsigned long flags; | ||
280 | |||
281 | spin_lock_irqsave(&dev->lock, flags); | ||
282 | |||
283 | for (action = fsm_table; action->init != 0xff; action++) | ||
284 | if (action->init == chan->fsm_state && action->event == event) | ||
285 | break; | ||
286 | |||
287 | if (action->init == 0xff) { | ||
288 | |||
289 | spin_unlock_irqrestore(&dev->lock, flags); | ||
290 | printk(KERN_DEBUG "fsm error: event %x on state %x\n", | ||
291 | event, chan->fsm_state); | ||
292 | return; | ||
293 | } | ||
294 | |||
295 | if (chan->fsm_timer.function) { | ||
296 | del_timer(&chan->fsm_timer); | ||
297 | chan->fsm_timer.function = NULL; | ||
298 | } | ||
299 | |||
300 | chan->fsm_state = action->final; | ||
301 | |||
302 | pcbit_state_change(dev, chan, action->init, event, action->final); | ||
303 | |||
304 | for (tentry = fsm_timers; tentry->init != 0xff; tentry++) | ||
305 | if (tentry->init == chan->fsm_state) | ||
306 | break; | ||
307 | |||
308 | if (tentry->init != 0xff) { | ||
309 | init_timer(&chan->fsm_timer); | ||
310 | chan->fsm_timer.function = &pcbit_fsm_timer; | ||
311 | chan->fsm_timer.data = (ulong) chan; | ||
312 | chan->fsm_timer.expires = jiffies + tentry->timeout * HZ; | ||
313 | add_timer(&chan->fsm_timer); | ||
314 | } | ||
315 | |||
316 | spin_unlock_irqrestore(&dev->lock, flags); | ||
317 | |||
318 | if (action->callb) | ||
319 | action->callb(dev, chan, data); | ||
320 | |||
321 | } | ||
322 | |||
323 | |||
324 | |||
325 | |||